Skip to content

Commit

Permalink
Merge pull request #2759 from obsidian-tasks-group/fix-boolean-parsing
Browse files Browse the repository at this point in the history
fix: allow filters with ( ) and " delimiters in Boolean filters
  • Loading branch information
claremacrae committed Apr 9, 2024
2 parents 658e19d + 9b6afa8 commit 7c8786e
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 9 deletions.
11 changes: 8 additions & 3 deletions src/Query/Filter/BooleanField.ts
Expand Up @@ -10,6 +10,7 @@ import { Field } from './Field';
import { FilterOrErrorMessage } from './FilterOrErrorMessage';
import { Filter } from './Filter';
import { BooleanDelimiters } from './BooleanDelimiters';
import { BooleanPreprocessor } from './BooleanPreprocessor';

/**
* BooleanField is a 'container' field type that parses a high-level filtering query of
Expand Down Expand Up @@ -82,15 +83,19 @@ export class BooleanField extends Field {
* @private
*/
private parseLineV1(line: string) {
const preprocessed = BooleanField.preprocessExpressionV1(line);
const parseResult = BooleanPreprocessor.preprocessExpression(line);
const simplifiedLine = parseResult.simplifiedLine;
const filters = parseResult.filters;
try {
// Convert the (preprocessed) line into a postfix logical expression
const postfixExpression = boonParse(preprocessed);
const postfixExpression: Token[] = boonParse(simplifiedLine);
// Construct sub-field map, i.e. have subFields include a filter function for every
// final token in the expression
for (const token of postfixExpression) {
if (token.name === 'IDENTIFIER' && token.value) {
const filter = token.value.trim();
const placeholder = token.value.trim();
const filter = filters[placeholder];
token.value = filter;
if (!(filter in this.subFields)) {
const parsedField = parseFilter(filter);
if (parsedField === null) {
Expand Down
Expand Up @@ -415,7 +415,12 @@ Input:
'( description regex matches /(buy|order|voucher|lakeland|purchase|\spresent)/i ) OR ( path includes Home/Shopping )'
=>
Result:
malformed boolean query -- Invalid token (check the documentation for guidelines)
( description regex matches /(buy|order|voucher|lakeland|purchase|\spresent)/i ) OR ( path includes Home/Shopping ) =>
OR (At least one of):
description regex matches /(buy|order|voucher|lakeland|purchase|\spresent)/i =>
using regex: '(buy|order|voucher|lakeland|purchase|\spresent)' with flag 'i'
path includes Home/Shopping


--------------------------------------------------------

Expand Down Expand Up @@ -447,7 +452,17 @@ Input:
'( filter by function ! 'NON_TASK,CANCELLED'.includes(task.status.type); ) OR ( filter by function const date = task.due.moment; return date ? !date.isValid() : false; ) OR ( filter by function task.due.moment?.isSameOrBefore(moment(), 'day') || false; ) OR ( filter by function task.urgency.toFixed(2) === 1.95.toFixed(2); ) OR ( filter by function (!task.isRecurring) && task.originalMarkdown.includes('🔁'); ) OR ( filter by function task.file.path.toLocaleLowerCase() === 'TASKS RELEASES/4.1.0 RELEASE.MD'.toLocaleLowerCase(); ) OR ( filter by function const taskDate = task.due.moment; const now = moment(); return taskDate?.isSame(now, 'day') || ( !taskDate && task.heading?.includes(now.format('YYYY-MM-DD')) ) || false; ) OR ( filter by function const wanted = '#context/home'; return task.heading?.includes(wanted) || task.tags.find( (tag) => tag === wanted ) && true || false; )'
=>
Result:
malformed boolean query -- Invalid token (check the documentation for guidelines)
( filter by function ! 'NON_TASK,CANCELLED'.includes(task.status.type); ) OR ( filter by function const date = task.due.moment; return date ? !date.isValid() : false; ) OR ( filter by function task.due.moment?.isSameOrBefore(moment(), 'day') || false; ) OR ( filter by function task.urgency.toFixed(2) === 1.95.toFixed(2); ) OR ( filter by function (!task.isRecurring) && task.originalMarkdown.includes('🔁'); ) OR ( filter by function task.file.path.toLocaleLowerCase() === 'TASKS RELEASES/4.1.0 RELEASE.MD'.toLocaleLowerCase(); ) OR ( filter by function const taskDate = task.due.moment; const now = moment(); return taskDate?.isSame(now, 'day') || ( !taskDate && task.heading?.includes(now.format('YYYY-MM-DD')) ) || false; ) OR ( filter by function const wanted = '#context/home'; return task.heading?.includes(wanted) || task.tags.find( (tag) => tag === wanted ) && true || false; ) =>
OR (At least one of):
filter by function ! 'NON_TASK,CANCELLED'.includes(task.status.type);
filter by function const date = task.due.moment; return date ? !date.isValid() : false;
filter by function task.due.moment?.isSameOrBefore(moment(), 'day') || false;
filter by function task.urgency.toFixed(2) === 1.95.toFixed(2);
filter by function (!task.isRecurring) && task.originalMarkdown.includes('🔁');
filter by function task.file.path.toLocaleLowerCase() === 'TASKS RELEASES/4.1.0 RELEASE.MD'.toLocaleLowerCase();
filter by function const taskDate = task.due.moment; const now = moment(); return taskDate?.isSame(now, 'day') || ( !taskDate && task.heading?.includes(now.format('YYYY-MM-DD')) ) || false;
filter by function const wanted = '#context/home'; return task.heading?.includes(wanted) || task.tags.find( (tag) => tag === wanted ) && true || false;


--------------------------------------------------------

Expand Down Expand Up @@ -832,7 +847,7 @@ Input:
'(description includes "hello world") OR (description includes "42")'
=>
Result:
malformed boolean query -- Unexpected character: h Expected ) character or separator (check the documentation for guidelines)
malformed boolean query -- Unexpected character: " (check the documentation for guidelines)

--------------------------------------------------------

Expand Down Expand Up @@ -2175,7 +2190,11 @@ Input:
'(path includes (some example) OR (path includes )some example()'
=>
Result:
malformed boolean query -- Invalid token (check the documentation for guidelines)
(path includes (some example) OR (path includes )some example() =>
OR (At least one of):
path includes (some example
path includes )some example(


--------------------------------------------------------

Expand All @@ -2193,7 +2212,7 @@ Input:
'(path includes )some example() OR (path includes (some example))'
=>
Result:
malformed boolean query -- Unexpected character: s. A closing parenthesis should be followed by another closing parenthesis or whitespace (check the documentation for guidelines)
malformed boolean query -- Invalid token (check the documentation for guidelines)

--------------------------------------------------------

Expand Down
2 changes: 1 addition & 1 deletion tests/Query/Filter/BooleanField.test.ts
Expand Up @@ -163,7 +163,7 @@ describe('boolean query - filter', () => {
const filter = createValidFilter('(description includes "hello world") OR (description includes "42")');
// TODO Fix this:
expect(explanationOrError(filter)).toMatchInlineSnapshot(
'"malformed boolean query -- Unexpected character: h Expected ) character or separator (check the documentation for guidelines)"',
'"malformed boolean query -- Unexpected character: " (check the documentation for guidelines)"',
);
});
});
Expand Down

0 comments on commit 7c8786e

Please sign in to comment.