FoldingText for Atom User's Guide

Appendix A: Search Syntax

In this appendix you'll learn about FoldingText's search syntax.

The first thing to know about FoldingText's search syntax is that you may never need to lean it.

Most of the time you can just type in the text you are looking for and that's it. If you have more complex searching needs skim this chapter to see what's availibe. And then refere back to it when you need to look up a specific syntax.

Predicates

Search predicates describe what you are looking for. Try these searches:

  • one – All items that contain the text "one" (and their parents) display while the view hides everything else.

  • not one – Notice that not gets highlighted. This is because it's a keyword that effects the logic of the search. This search hides all items that contain the text "one".

  • one or – Notice that the search field shows an error for this search because the syntax is not complete. Complete the search by typing one or two and the error goes away.

Predicate Pattern

The full predicate pattern looks like this:

@<attribute> <relation> <search term>

But you don't need to include that entire pattern every time. Predicates use default values when needed. For example the following predicates are equal:

Inbox
@text Inbox
contains Inbox
@text contains Inbox

They are all equal because @text is the default attribute and contains is the default relation.

Attributes

Predicates start with the attribute that you want to test. To this point your predicates have used the built in @text attribute. But we can test against other attributes too:

  1. Mark an item complete using Control-Space.

  2. Now click on the "Complete" badge that displays after the item.

When you do that FoldingText sets the search to @status = complete and matches all completed items. This works because "complete" is stored in the @status attribute.

Relations

Relations determine the test that the predicate performs.

FoldingText predicates support: =, !=, <, >, <=, >=, beginswith, endswith, and matches relations. These comparisons are performed on case insensitive string values. You can change that behavior by providing a modifier:

@line contains [modifier] a value

i : Case insensitive compare, both the attribute value and the comparison value are converted to lower case before doing the compare. This is the default modifier.

s : Case sensitive compare, neither the attribute value or comparison value are modified before doing the compare.

n : Numeric compare, both the attribute value and comparison value are converted to numbers before doing the compare. For example the values "0" and "0.0" would be equal when doing a numeric compare, but not when doing a normal string compare.

d : Date compare, both the attribute value and comparison value are converted to dates before doing the compare.

Values

Sometimes you might need a value that has special meaning in the search syntax.

For example you might want to search for the word "and". But since "and" is part of the predicate syntax the search will be invalid. To resolve this enclose your the value in quotes.

Invalid because "and" is a keyword:

contains and

Valid because "and" is in quotes:

contains "and"

Tags

FoldingText has a special shortcut syntax for matching tags:

  • # matches any item that has tags.
  • #tagname matches any item that is tagged with "#tagname".

FoldingText stores tags as a comma separated list in a "@tags" attribute. To perform the previous searches without the special syntax you would do this:

  • @data-tags matches any item that has tags.
  • @data-tags matches "(^|,)one($|,)" matches any item that is tagged with "#tagname".

But those searches are long and messy, so I added the shortcut syntax.

Boolean expressions

You can combine predicates with logical and, or, and not operators. This search will match items that contain the text "one" or "two", but do not contain "three":

(one or two) and not three

Item Paths

To this point our predicates test agains all items in the outline. Item paths control which parts of the outline to test against. The item path concept is taken from Xpath.

Steps

Item paths are made of steps. Each step consists of an axis and a predicate. The axis determines which items to consider, and the predicate selects from those items. Each step selects items based on the results of the previous step.

Item paths work like file system paths:

/my heading/subhead

This path evaluates:

  1. The first step /my heading considers all top level items and select ones that contain my heading.

  2. The second step /subhead considers all children of the items selected in the fist step. It selects those that contain subhead.

  3. The children selected in the last step are the match results.

In the above example each step considered items along the / "child" axis. Here's an example that searchs along another axis:

/my heading//subhead

Notice the extra / in //subhead. That syntax means that the second step should search along the descendants axis. It will consider all descendants instead of just the direct children.

It's sometimes useful to select all items along a particular axis. In that case you can use the wildcard predicate *. Here's an example:

/*

That item path says to select all top level items.

Axes

Generally item paths follow the same axis rules and syntaxes as defined by Xpath.

ancestor : Returns all ancestors matching predicate. For example /my heading/ancestor::*

ancestor-or-self : Returns items and their ancestors matching predicate. For example /my heading/ancestor-or-self::*

descendant : Returns all descendants matching predicate. Same as '//' operator. For example /my heading/descendant::*

descendant-or-self : Returns items and their descendants matching predicate. For example /my heading/descendant-or-self::*

following : Returns all items following each of the previously matched items. For example /my heading/following::*

following-sibling : Returns sibling items following each of the previously matched items. For example /my heading/following-sibling::*

preceding : Returns all items preceding each of the previously matched items. For example /my heading/preceding::*

preceding-sibling : Returns sibling items preceding each of the previously matched items. For example /my heading/preceding-sibling::*

child : Searches child item, same as '/' operator. For example /my heading/child::*

parent : Searches parent item, same as '..' operator. For example /my heading/parent::*

Set Operations

Item paths support the set operations union, intersect, and except.

(/Inbox//* union //#today) except //#done

This path matches all items under the "Inbox" heading. Combines those with all items in the outline tagged "#today". And then removes all item items tagged with "#done".

Slicing Results

Sometimes you don't want all the results of your item path. In that case you can "slice" the results.

Determining your "next actions" for each project is one place where you might use this. This is what our outline looks like:

Next Actions Outline

Here's a first try for showing next actions:

/*//not @status complete

It says:

  1. For each top level project

  2. Show me items that are not yet complete

That's close, but it will show more then we want. We don't want to see all things we need to do, just the first thing, the next action. This is where slicing can help:

/*//not @status complete[0]

I've added [0] to the end and now the path only matches the first item that's not complete for each project.

/*//not @status complete[0]

You can slice the results of a particular step, as done above, or you can slice the results of an entire item path like this:

(/*//not @status complete)[0]

That path matches only the first incomplete item in my outline.

You can slice the results of a single predicate as in the first example. Or you can slice the results of an item path expression in parentheses as in the second example.

The full slice syntax is:

[start:end] // slices from start to end - 1
[start:] // slices from start through the rest of the list
[:end] // slices from the beginning to end - 1
[:] // slices through the entire list
[index] // slices item at given index