1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2026-01-22 19:04:38 +00:00

Compare commits

..

60 Commits

Author SHA1 Message Date
Jeremy Ruston
09193d49c4 Force a rebuild
I don't have time right now to resolve the merge conflicts with master
2025-09-17 14:06:58 +01:00
Jeremy Ruston
356e8e2f23 Fix failing test 2025-05-08 18:08:34 +01:00
Jeremy Ruston
26d9d9fb62 Fix jittery scrolling on lists 2025-05-08 18:05:21 +01:00
Jeremy Ruston
c0d9c4f9ec Make the paginated list more reusable 2025-05-08 18:05:08 +01:00
Jeremy Ruston
4351a3d906 Add timing information to filter traces 2025-05-08 15:32:45 +01:00
Jeremy Ruston
728243a66a Add global setting to enable filter observation 2025-05-07 20:49:57 +01:00
Jeremy Ruston
3f733796e5 Include shortcut prefix in traces 2025-05-07 20:49:28 +01:00
Jeremy Ruston
8711726ecd Useful to give the start method access to the widget 2025-05-02 10:29:53 +01:00
Jeremy Ruston
6eddffd7c5 Enable wikitext tests 2025-05-02 10:12:04 +01:00
Jeremy Ruston
29a5a1c571 Merge branch 'master' into filter-inspection 2025-04-30 17:02:26 +01:00
Jeremy Ruston
83da90d4cd Remove unneeded input boxes 2025-04-29 12:40:07 +01:00
Jeremy Ruston
6b2b1df9ad Consolidate identical adjacent lists
Makes things much clearer, but still some formatting and CSS improvements needed
2025-04-29 12:31:14 +01:00
Jeremy Ruston
d995e1a87d Fix tricky bug with a dodgy closure 2025-04-29 10:32:25 +01:00
Jeremy Ruston
60165678e4 Fix scrolling of observations 2025-04-29 09:03:58 +01:00
Jeremy Ruston
0b03e79339 New UI for observing filters 2025-04-25 17:40:25 +01:00
Jeremy Ruston
93546c5511 Get rid of reveal widget 2025-04-25 14:50:09 +01:00
Jeremy Ruston
e2e14db1f0 Better example filter 2025-04-25 14:36:04 +01:00
Jeremy Ruston
6c59263070 Typo 2025-04-25 14:35:43 +01:00
Jeremy Ruston
628a2a45e6 Make variable usage a bit more logical 2025-04-25 14:34:01 +01:00
Jeremy Ruston
51d79fc5ac Make build-site script more configurable to avoid Netlify timeouts 2025-04-25 14:19:30 +01:00
Jeremy Ruston
b4e48bb2b1 Full width buttons are easier to click 2025-04-21 12:26:56 +01:00
Jeremy Ruston
210311eccf Placeholder for advanced search edit box 2025-04-21 11:42:34 +01:00
Jeremy Ruston
2f767cdb28 Docs update 2025-04-21 11:42:18 +01:00
Jeremy Ruston
0a0838e753 Merge branch 'master' into filter-inspection 2025-04-16 14:31:11 +01:00
Jeremy Ruston
66196d5c2b More docs tweaks 2025-04-14 16:25:56 +01:00
Jeremy Ruston
483522ea09 Docs tweaks 2025-04-14 15:44:30 +01:00
Jeremy Ruston
3801e2536c Reorganise tiddlers
Some of the titles were not consistent
2025-04-14 10:58:40 +01:00
Jeremy Ruston
77aec1f8f8 Merge branch 'master' into filter-inspection 2025-04-13 17:56:33 +01:00
Jeremy Ruston
df529d7d7b Fix RSOD on invalid filter expressions 2025-04-13 17:51:39 +01:00
Jeremy Ruston
8dbb41ba2a Startings of a UI for inspectable filters 2025-04-11 15:42:54 +01:00
Jeremy Ruston
b632b75f70 Fix default tab 2025-04-11 14:43:44 +01:00
Jeremy Ruston
ef92b899ed Add remote inspection of preconfigured filters
There is no user interface yet, and it is currently hardcoded to inspect the filter "[all[shadows+tiddlers]tag[$:/tags/ViewTemplate]!is[draft]]" which is used in tiddler rendering. You can see the results in the sidebar More -> System starting with "$:/temp/filter-inspection/"
2025-04-11 13:33:09 +01:00
Jeremy Ruston
dcb8fa2f86 Only display advanced search tabs if there are more than one 2025-04-10 14:11:55 +01:00
Jeremy Ruston
c5894c64b9 Missed some styles from refactoring 2025-04-10 14:11:39 +01:00
Jeremy Ruston
04ad642be7 Refactor most of this PR into the "Internals" plugin 2025-04-09 22:01:43 +01:00
Jeremy Ruston
c8f17511f9 Improve positioning of the list "more" button 2025-04-07 18:51:31 +01:00
Jeremy Ruston
845b4ba3b5 Rename test 2025-04-07 18:41:40 +01:00
Jeremy Ruston
8ea00a05d3 Docs update 2025-04-07 17:31:13 +01:00
Jeremy Ruston
28698690b3 Use scrollable widget to maintain scroll position 2025-04-07 16:35:26 +01:00
Jeremy Ruston
3c6ec3f9bb Include separate entries for each evaluation of a run 2025-04-07 16:13:18 +01:00
Jeremy Ruston
3a2c81192e Docs for the inspect-filter macro 2025-04-07 10:42:37 +01:00
Jeremy Ruston
d29199ffa8 Fix failing test 2025-04-06 22:13:47 +01:00
Jeremy Ruston
e741816a70 Use parse tree to render source text of filter 2025-04-06 22:06:58 +01:00
Jeremy Ruston
002c319518 Remove unnecessary text 2025-04-04 10:09:32 +01:00
Jeremy Ruston
33964d460d Add expand/collapse buttons to lists 2025-04-04 10:07:08 +01:00
Jeremy Ruston
9b3e61ef10 Avoid scrolling filter heading 2025-04-03 22:09:28 +01:00
Jeremy Ruston
967e882040 Truncate lists with an "expand" button 2025-04-03 22:03:43 +01:00
Jeremy Ruston
06dfe365be Better docs example 2025-04-03 22:03:12 +01:00
Jeremy Ruston
3e1286013f Fix bug with operators that return an iterator 2025-04-03 21:46:36 +01:00
Jeremy Ruston
1972e8b5f3 Stripy backgrounds for lists 2025-04-03 21:22:33 +01:00
Jeremy Ruston
3cb6712ccb Minimum width for lists 2025-04-03 21:22:18 +01:00
Jeremy Ruston
8b3fbe3134 Fix typo 2025-04-03 14:35:37 +01:00
Jeremy Ruston
f6b39d1a40 Add horizontal presentation 2025-04-03 09:26:07 +01:00
Jeremy Ruston
1e10496fd6 Introduce filter visualisation 2025-04-02 22:05:43 +01:00
Jeremy Ruston
5226ca1f75 Comment out logging 2025-04-02 22:04:59 +01:00
Jeremy Ruston
037b4aa227 Add filter output list to JSON 2025-04-02 18:01:15 +01:00
Jeremy Ruston
ddeb4fd6e6 Fix filter output 2025-04-02 08:56:28 +01:00
Jeremy Ruston
8ff0cb8650 Fix typo 2025-04-01 17:53:53 +01:00
Jeremy Ruston
27075acbc6 Add "inspect" tab to advanced search and make the search box into a textarea 2025-04-01 12:43:13 +01:00
Jeremy Ruston
d4043fc1f4 Add inspect operator 2025-04-01 12:06:10 +01:00
49 changed files with 1187 additions and 580 deletions

View File

@@ -1,50 +0,0 @@
name: Calculate PR build size
on:
pull_request_target:
types: [opened, reopened, synchronize]
jobs:
calculate-build-size:
runs-on: ubuntu-latest
permissions:
pull-requests: read
contents: read
outputs:
pr_size: ${{ steps.get_sizes.outputs.pr_size }}
base_size: ${{ steps.get_sizes.outputs.base_size }}
steps:
- name: build-size-check
id: get_sizes
uses: TiddlyWiki/cerebrus@v4
with:
pr_number: ${{ github.event.pull_request.number }}
repo: ${{ github.repository }}
base_ref: ${{ github.base_ref }}
github_token: ${{ secrets.GITHUB_TOKEN }}
mode: size:calc
dispatch-followup:
needs: calculate-build-size
runs-on: ubuntu-latest
permissions:
actions: write # Required to dispatch another workflow
pull-requests: write
contents: read
steps:
- name: Trigger follow-up workflow
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
await github.rest.actions.createWorkflowDispatch({
owner: context.repo.owner,
repo: context.repo.repo,
workflow_id: 'pr-comment-build-size.yml',
ref: 'master',
inputs: {
pr_number: '${{ github.event.pull_request.number }}',
base_ref: '${{ github.event.pull_request.base.ref }}',
pr_size: '${{ needs.calculate-build-size.outputs.pr_size }}',
base_size: '${{ needs.calculate-build-size.outputs.base_size }}'
}
});

View File

@@ -1,36 +0,0 @@
name: Comment on PR build size (Trusted workflow)
on:
workflow_dispatch:
inputs:
pr_number:
required: true
type: string
base_ref:
required: true
type: string
pr_size:
required: true
type: string
base_size:
required: true
type: string
jobs:
comment-on-pr:
runs-on: ubuntu-latest
permissions:
pull-requests: write
contents: read
steps:
- name: Build and check size
uses: TiddlyWiki/cerebrus@v4
with:
pr_number: ${{ inputs.pr_number }}
repo: ${{ github.repository }}
base_ref: ${{ inputs.base_ref }}
github_token: ${{ secrets.GITHUB_TOKEN }}
mode: size:comment
pr_size: ${{ inputs.pr_size }}
base_size: ${{ inputs.base_size }}

View File

@@ -1,18 +0,0 @@
name: Validate PR Paths
on:
pull_request_target:
types: [opened, reopened, synchronize]
jobs:
validate-pr:
runs-on: ubuntu-latest
steps:
- name: Validate PR
uses: TiddlyWiki/cerebrus@v4
with:
pr_number: ${{ github.event.pull_request.number }}
repo: ${{ github.repository }}
base_ref: ${{ github.base_ref }}
github_token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -107,20 +107,33 @@ fi
# /index.html Main site
# /external-(version).html External core version of main site
# /favicon.ico Favicon for main site
# /static.html Static rendering of default tiddlers
# /alltiddlers.html Static rendering of all tiddlers
# /static/* Static single tiddlers
# /static/static.css Static stylesheet
# /static/favicon.ico Favicon for static pages
node $TW5_BUILD_TIDDLYWIKI \
$TW5_BUILD_MAIN_EDITION \
--version \
--load $TW5_BUILD_OUTPUT/build.tid \
--output $TW5_BUILD_OUTPUT \
--build favicon static index external-js \
--build favicon index external-js \
|| exit 1
# /static.html Static rendering of default tiddlers
# /alltiddlers.html Static rendering of all tiddlers
# /static/* Static single tiddlers
# /static/static.css Static stylesheet
# /static/favicon.ico Favicon for static pages
# Conditionally build static files if $TW5_BUILD_STATIC variable is not set or is set to 0
if [ -z "$TW5_BUILD_STATIC" ] || [ "$TW5_BUILD_STATIC" = "0" ]; then
node $TW5_BUILD_TIDDLYWIKI \
$TW5_BUILD_MAIN_EDITION \
--version \
--load $TW5_BUILD_OUTPUT/build.tid \
--output $TW5_BUILD_OUTPUT \
--build static \
|| exit 1
fi
# /empty.html Empty
# /empty.hta For Internet Explorer
# /empty-external-core.html External core empty
@@ -190,6 +203,9 @@ node $TW5_BUILD_TIDDLYWIKI \
#
######################################################
# Conditionally build editions if $TW5_BUILD_EDITIONS variable is not set or is set to 0
if [ -z "$TW5_BUILD_EDITIONS" ] || [ "$TW5_BUILD_EDITIONS" = "0" ]; then
# /editions/xlsx-utils/index.html xlsx-utils edition
node $TW5_BUILD_TIDDLYWIKI \
./editions/xlsx-utils \
@@ -254,12 +270,18 @@ node $TW5_BUILD_TIDDLYWIKI \
--build index \
|| exit 1
fi
######################################################
#
# Plugin demos
#
######################################################
# Conditionally build plugin demos if $TW5_BUILD_PLUGIN_DEMOS variable is not set
if [ -z "$TW5_BUILD_PLUGIN_DEMOS" ] || [ "$TW5_BUILD_PLUGIN_DEMOS" = "0" ]; then
# /plugins/tiddlywiki/innerwiki/index.html Demo wiki with Innerwiki plugin
node $TW5_BUILD_TIDDLYWIKI \
@@ -364,12 +386,17 @@ node $TW5_BUILD_TIDDLYWIKI \
--rendertiddler $:/core/save/empty plugins/tiddlywiki/geospatial/empty.html text/plain \
|| exit 1
fi
######################################################
#
# Language editions
#
######################################################
# Conditionally build language editions if $TW5_BUILD_LANGUAGE_DEMOS variable is not set
if [ -z "$TW5_BUILD_LANGUAGE_DEMOS" ] || [ "$TW5_BUILD_LANGUAGE_DEMOS" = "0" ]; then
# Delete any existing static content
rm -rf $TW5_BUILD_OUTPUT/languages/de-AT/static/*
@@ -453,12 +480,17 @@ node $TW5_BUILD_TIDDLYWIKI \
--build empty index \
|| exit 1
fi
######################################################
#
# Plugin library
#
######################################################
# Conditionally build plugin library if $TW5_BUILD_PLUGIN_LIBRARY variable is not set
if [ -z "$TW5_BUILD_PLUGIN_LIBRARY" ] || [ "$TW5_BUILD_PLUGIN_LIBRARY" = "0" ]; then
node $TW5_BUILD_TIDDLYWIKI \
./editions/pluginlibrary \
--load $TW5_BUILD_OUTPUT/build.tid \
@@ -466,6 +498,8 @@ node $TW5_BUILD_TIDDLYWIKI \
--build library\
|| exit 1
fi
# Delete the temporary build tiddler
rm $TW5_BUILD_OUTPUT/build.tid || exit 1

View File

@@ -2732,14 +2732,15 @@ $tw.hooks.removeHook = function(hookName,definition) {
/*
Invoke the hook by key
*/
$tw.hooks.invokeHook = function(hookName /*, value,... */) {
var args = Array.prototype.slice.call(arguments,1);
if($tw.utils.hop($tw.hooks.names,hookName)) {
$tw.hooks.invokeHook = function(hookName, firstArgument /*, value,... */) {
if(Object.prototype.hasOwnProperty.call($tw.hooks.names,hookName)) {
var args = Array.prototype.slice.call(arguments,1);
for(var i = 0; i < $tw.hooks.names[hookName].length; i++) {
args[0] = $tw.hooks.names[hookName][i].apply(null,args);
}
return args[0];
}
return args[0];
return firstArgument;
};
/////////////////////////// Main boot function to decrypt tiddlers and then startup

View File

@@ -3,7 +3,9 @@ title: $:/language/Search/
DefaultResults/Caption: List
Filter/Caption: Filter
Filter/Hint: Search via a [[filter expression|https://tiddlywiki.com/static/Filters.html]]
Filter/Placeholder: Filter expression
Filter/Matches: //<small><<resultCount>> matches</small>//
Filter/FilterResults/Results/Caption: Results
Matches: //<small><<resultCount>> matches</small>//
Matches/All: All matches:
Matches/NoMatch: //No match//

View File

@@ -220,13 +220,25 @@ exports.filterTiddlers = function(filterString,widget,source) {
Compile a filter into a function with the signature fn(source,widget) where:
source: an iterator function for the source tiddlers, called source(iterator), where iterator is called as iterator(tiddler,title)
widget: an optional widget node for retrieving the current tiddler etc.
Parameters:
filterString: the filter string to compile
options: includes:
wrappers: a hashmap of wrapper functions to apply to the compiled filter function
*/
exports.compileFilter = function(filterString) {
exports.compileFilter = function(filterString,options) {
options = options || {};
var self = this;
var wrappers = options.wrappers || {};
// Invoke the hook to allow the filter to be inspected
wrappers = $tw.hooks.invokeHook("th-filter-evaluation",filterString,wrappers) || wrappers;
// Get the result from the cache if we can
if(!this.filterCache) {
this.filterCache = Object.create(null);
this.filterCacheCount = 0;
}
if(this.filterCache[filterString] !== undefined) {
if(this.filterCache[filterString] !== undefined && !wrappers.prefix && !wrappers.operation && !wrappers.operator && !wrappers.start && !wrappers.done) {
return this.filterCache[filterString];
}
var filterParseTree;
@@ -235,7 +247,14 @@ exports.compileFilter = function(filterString) {
} catch(e) {
// We do not cache this result, so it adjusts along with localization changes
return function(source,widget) {
return [$tw.language.getString("Error/Filter") + ": " + e];
if(wrappers.start) {
wrappers.start(source,widget);
}
var resultsArray = [$tw.language.getString("Error/Filter") + ": " + e];
if(wrappers.done) {
wrappers.done(resultsArray);
}
return resultsArray;
};
}
// Get the hashmap of filter operator functions
@@ -249,52 +268,64 @@ exports.compileFilter = function(filterString) {
var operationSubFunction = function(source,widget) {
var accumulator = source,
results = [],
currTiddlerTitle = widget && widget.getVariable("currentTiddler");
$tw.utils.each(operation.operators,function(operator) {
var operands = [],
operatorFunction;
if(!operator.operator) {
// Use the "title" operator if no operator is specified
operatorFunction = filterOperators.title;
} else if(!filterOperators[operator.operator]) {
// Unknown operators treated as "[unknown]" - at run time we can distinguish between a custom operator and falling back to the default "field" operator
operatorFunction = filterOperators["[unknown]"];
} else {
// Use the operator function
operatorFunction = filterOperators[operator.operator];
}
$tw.utils.each(operator.operands,function(operand) {
if(operand.indirect) {
operand.value = self.getTextReference(operand.text,"",currTiddlerTitle);
} else if(operand.variable) {
var varTree = $tw.utils.parseFilterVariable(operand.text);
operand.value = widgetClass.evaluateVariable(widget,varTree.name,{params: varTree.params, source: source})[0] || "";
} else {
operand.value = operand.text;
}
operands.push(operand.value);
});
// Invoke the appropriate filteroperator module
results = operatorFunction(accumulator,{
operator: operator.operator,
operand: operands.length > 0 ? operands[0] : undefined,
operands: operands,
prefix: operator.prefix,
suffix: operator.suffix,
suffixes: operator.suffixes,
regexp: operator.regexp
},{
wiki: self,
widget: widget
currTiddlerTitle = widget && widget.getVariable("currentTiddler"),
handleOperation = function() {
$tw.utils.each(operation.operators,function(operator) {
var operands = [],
operatorName,operatorFunction;
if(!operator.operator) {
// Use the "title" operator if no operator is specified
operatorName = "title";
} else if(!filterOperators[operator.operator]) {
// Unknown operators treated as "[unknown]" - at run time we can distinguish between a custom operator and falling back to the default "field" operator
operatorName = "[unknown]";
} else {
// Use the operator function
operatorName = operator.operator;
}
operatorFunction = filterOperators[operatorName];
$tw.utils.each(operator.operands,function(operand) {
if(operand.indirect) {
operand.value = self.getTextReference(operand.text,"",currTiddlerTitle);
} else if(operand.variable) {
var varTree = $tw.utils.parseFilterVariable(operand.text);
operand.value = widgetClass.evaluateVariable(widget,varTree.name,{params: varTree.params, source: source})[0] || "";
} else {
operand.value = operand.text;
}
operands.push(operand.value);
});
if($tw.utils.isArray(results)) {
accumulator = self.makeTiddlerIterator(results);
} else {
accumulator = results;
}
});
if($tw.utils.isArray(results)) {
// Wrap the filter operator module if required
if(wrappers.operator) {
operatorFunction = wrappers.operator.bind(self,operatorFunction);
}
// Invoke the appropriate filteroperator module
results = operatorFunction(accumulator,{
parseTree: operator,
operator: operator.operator,
operatorName: operatorName,
operand: operands.length > 0 ? operands[0] : undefined,
operands: operands,
prefix: operator.prefix,
suffix: operator.suffix,
suffixes: operator.suffixes,
regexp: operator.regexp
},{
wiki: self,
widget: widget
});
if($tw.utils.isArray(results)) {
accumulator = self.makeTiddlerIterator(results);
} else {
accumulator = results;
}
});
};
if(wrappers.operation) {
handleOperation = wrappers.operation.bind(self,handleOperation,operation);
}
handleOperation();
if($tw.utils.isArray(results)) {
return results;
} else {
var resultArray = [];
@@ -307,27 +338,45 @@ exports.compileFilter = function(filterString) {
var filterRunPrefixes = self.getFilterRunPrefixes();
// Wrap the operator functions in a wrapper function that depends on the prefix
operationFunctions.push((function() {
var options = {wiki: self, suffixes: operation.suffixes || []};
var prefixName;
switch(operation.prefix || "") {
case "": // No prefix means that the operation is unioned into the result
return filterRunPrefixes["or"](operationSubFunction, options);
prefixName = "or";
break;
case "=": // The results of the operation are pushed into the result without deduplication
return filterRunPrefixes["all"](operationSubFunction, options);
prefixName = "all";
break;
case "-": // The results of this operation are removed from the main result
return filterRunPrefixes["except"](operationSubFunction, options);
prefixName = "except";
break;
case "+": // This operation is applied to the main results so far
return filterRunPrefixes["and"](operationSubFunction, options);
prefixName = "and";
break;
case "~": // This operation is unioned into the result only if the main result so far is empty
return filterRunPrefixes["else"](operationSubFunction, options);
default:
if(operation.namedPrefix && filterRunPrefixes[operation.namedPrefix]) {
return filterRunPrefixes[operation.namedPrefix](operationSubFunction, options);
} else {
return function(results,source,widget) {
results.clear();
results.push($tw.language.getString("Error/FilterRunPrefix"));
};
}
prefixName = "else";
break;
default:
prefixName = operation.namedPrefix;
break;
}
if(prefixName && filterRunPrefixes[prefixName]) {
var options = {
wiki: self,
suffixes: operation.suffixes || [],
prefixName: prefixName,
prefix: operation.prefix
},
filterRunPrefixFunction = filterRunPrefixes[prefixName];
// Wrap the filter operator module if required
if(wrappers.prefix) {
filterRunPrefixFunction = wrappers.prefix.bind(self,filterRunPrefixFunction);
}
return filterRunPrefixFunction(operationSubFunction,options);
} else {
return function(results,source,widget) {
results.clear();
results.push($tw.language.getString("Error/FilterRunPrefix"));
};
}
})());
});
@@ -341,6 +390,9 @@ exports.compileFilter = function(filterString) {
if(!widget) {
widget = $tw.rootWidget;
}
if(wrappers.start) {
wrappers.start(source,widget);
}
var results = new $tw.utils.LinkedList();
self.filterRecursionCount = (self.filterRecursionCount || 0) + 1;
if(self.filterRecursionCount < MAX_FILTER_DEPTH) {
@@ -351,7 +403,11 @@ exports.compileFilter = function(filterString) {
results.push("/**-- Excessive filter recursion --**/");
}
self.filterRecursionCount = self.filterRecursionCount - 1;
return results.toArray();
var resultsArray = results.toArray();
if(wrappers.done) {
wrappers.done(resultsArray);
}
return resultsArray;
});
if(this.filterCacheCount >= 2000) {
// To prevent memory leak, we maintain an upper limit for cache size.
@@ -360,7 +416,9 @@ exports.compileFilter = function(filterString) {
this.filterCache = Object.create(null);
this.filterCacheCount = 0;
}
this.filterCache[filterString] = fnMeasured;
this.filterCacheCount++;
if(!wrappers.prefix && !wrappers.operator) {
this.filterCache[filterString] = fnMeasured;
this.filterCacheCount++;
}
return fnMeasured;
};

View File

@@ -1,37 +0,0 @@
/*\
title: $:/core/modules/filters/wikify.js
type: application/javascript
module-type: filteroperator
Filter operator wikifying each string in the input list and returning the result as a list of strings
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Export our filter function
*/
exports.wikify = function(source,operator,options) {
var output = operator.operands[0],
mode = operator.operands[1],
type = operator.operands[2],
results = [];
source(function(tiddler,title) {
var wikifier = new $tw.utils.Wikifier({
wiki: options.wiki,
widget: options.widget,
text: title,
type: type,
mode: mode,
output: output
});
results.push(wikifier.getResult());
});
return results;
};
})();

View File

@@ -50,7 +50,7 @@ exports.parse = function() {
var reEnd;
if(this.match[5]) {
// If so, it is a multiline definition and the end of the body is marked with \end
reEnd = new RegExp("((:?^|\\r?\\n)[^\\S\\n\\r]*\\\\end[^\\S\\n\\r]*(?:" + $tw.utils.escapeRegExp(this.match[2]) + ")?\\s*?(?:$|\\r?\\n))","mg");
reEnd = new RegExp("((:?^|\\r?\\n)[^\\S\\n\\r]*\\\\end[^\\S\\n\\r]*(?:" + $tw.utils.escapeRegExp(this.match[2]) + ")?(?:$|\\r?\\n))","mg");
} else {
// Otherwise, the end of the definition is marked by the end of the line
reEnd = /($|\r?\n)/mg;

View File

@@ -55,7 +55,7 @@ exports.parse = function() {
var reEnd;
if(this.match[3]) {
// If so, it is a multiline definition and the end of the body is marked with \end
reEnd = new RegExp("((?:^|\\r?\\n)[^\\S\\n\\r]*\\\\end[^\\S\\n\\r]*(?:" + $tw.utils.escapeRegExp(this.match[1]) + ")?\\s*?(?:$|\\r?\\n))","mg");
reEnd = new RegExp("((?:^|\\r?\\n)[^\\S\\n\\r]*\\\\end[^\\S\\n\\r]*(?:" + $tw.utils.escapeRegExp(this.match[1]) + ")?(?:$|\\r?\\n))","mg");
} else {
// Otherwise, the end of the definition is marked by the end of the line
reEnd = /($|\r?\n)/mg;

View File

@@ -1,108 +0,0 @@
/*\
title: $:/core/modules/utils/wikifier.js
type: application/javascript
module-type: utils
A high level helper class for parsing and wikification
\*/
(function(){
/*
Options include:
wiki: wiki to be used for wikification
widget: optional widget to be used as parent of wikified text
text: text to be parsed/wikified
type: type of the text
mode: inline or block
output: text, formattedtext, html, parsetree or widgettree
*/
function Wikifier(options) {
this.wiki = options.wiki || $tw.wiki;
this.widget = options.widget || $tw.rootWidget;
this.text = options.text || "";
this.type = options.type || "";
this.mode = options.mode || "block";
this.output = options.output || "text";
// Create the parse tree
this.parser = this.wiki.parseText(this.type,this.text,{
parseAsInline: this.mode === "inline"
});
// Create the widget tree
this.widgetNode = this.wiki.makeWidget(this.parser,{
document: $tw.fakeDocument,
parentWidget: this.widget
});
// Render the widget tree to the container
this.container = $tw.fakeDocument.createElement("div");
this.widgetNode.render(this.container,null);
};
Wikifier.prototype.refresh = function(changedTiddlers) {
// Refresh the widget tree
return this.widgetNode.refresh(changedTiddlers);
};
/*
Return the result string
*/
Wikifier.prototype.getResult = function() {
var result;
switch(this.output) {
case "text":
result = this.container.textContent;
break;
case "formattedtext":
result = this.container.formattedTextContent;
break;
case "html":
result = this.container.innerHTML;
break;
case "parsetree":
result = JSON.stringify(this.parser.tree,0,$tw.config.preferences.jsonSpaces);
break;
case "widgettree":
result = JSON.stringify(this.getWidgetTree(),0,$tw.config.preferences.jsonSpaces);
break;
}
return result;
};
/*
Return a string of the widget tree
*/
Wikifier.prototype.getWidgetTree = function() {
var copyNode = function(widgetNode,resultNode) {
var type = widgetNode.parseTreeNode.type;
resultNode.type = type;
switch(type) {
case "element":
resultNode.tag = widgetNode.parseTreeNode.tag;
break;
case "text":
resultNode.text = widgetNode.parseTreeNode.text;
break;
}
if(Object.keys(widgetNode.attributes || {}).length > 0) {
resultNode.attributes = {};
$tw.utils.each(widgetNode.attributes,function(attr,attrName) {
resultNode.attributes[attrName] = widgetNode.getAttribute(attrName);
});
}
if(Object.keys(widgetNode.children || {}).length > 0) {
resultNode.children = [];
$tw.utils.each(widgetNode.children,function(widgetChildNode) {
var node = {};
resultNode.children.push(node);
copyNode(widgetChildNode,node);
});
}
},
results = {};
copyNode(this.widgetNode,results);
return results;
};
exports.Wikifier = Wikifier;
})();

View File

@@ -36,22 +36,89 @@ Compute the internal state of the widget
WikifyWidget.prototype.execute = function() {
// Get our parameters
this.wikifyName = this.getAttribute("name");
// Create the wikifier
this.wikifier = new $tw.utils.Wikifier({
wiki: this.wiki,
widget: this,
text: this.getAttribute("text"),
type: this.getAttribute("type"),
mode: this.getAttribute("mode","block"),
output: this.getAttribute("output","text")
});
this.wikifyResult = this.wikifier.getResult();
this.wikifyText = this.getAttribute("text");
this.wikifyType = this.getAttribute("type");
this.wikifyMode = this.getAttribute("mode","block");
this.wikifyOutput = this.getAttribute("output","text");
// Create the parse tree
this.wikifyParser = this.wiki.parseText(this.wikifyType,this.wikifyText,{
parseAsInline: this.wikifyMode === "inline"
});
// Create the widget tree
this.wikifyWidgetNode = this.wiki.makeWidget(this.wikifyParser,{
document: $tw.fakeDocument,
parentWidget: this
});
// Render the widget tree to the container
this.wikifyContainer = $tw.fakeDocument.createElement("div");
this.wikifyWidgetNode.render(this.wikifyContainer,null);
this.wikifyResult = this.getResult();
// Set context variable
this.setVariable(this.wikifyName,this.wikifyResult);
// Construct the child widgets
this.makeChildWidgets();
};
/*
Return the result string
*/
WikifyWidget.prototype.getResult = function() {
var result;
switch(this.wikifyOutput) {
case "text":
result = this.wikifyContainer.textContent;
break;
case "formattedtext":
result = this.wikifyContainer.formattedTextContent;
break;
case "html":
result = this.wikifyContainer.innerHTML;
break;
case "parsetree":
result = JSON.stringify(this.wikifyParser.tree,0,$tw.config.preferences.jsonSpaces);
break;
case "widgettree":
result = JSON.stringify(this.getWidgetTree(),0,$tw.config.preferences.jsonSpaces);
break;
}
return result;
};
/*
Return a string of the widget tree
*/
WikifyWidget.prototype.getWidgetTree = function() {
var copyNode = function(widgetNode,resultNode) {
var type = widgetNode.parseTreeNode.type;
resultNode.type = type;
switch(type) {
case "element":
resultNode.tag = widgetNode.parseTreeNode.tag;
break;
case "text":
resultNode.text = widgetNode.parseTreeNode.text;
break;
}
if(Object.keys(widgetNode.attributes || {}).length > 0) {
resultNode.attributes = {};
$tw.utils.each(widgetNode.attributes,function(attr,attrName) {
resultNode.attributes[attrName] = widgetNode.getAttribute(attrName);
});
}
if(Object.keys(widgetNode.children || {}).length > 0) {
resultNode.children = [];
$tw.utils.each(widgetNode.children,function(widgetChildNode) {
var node = {};
resultNode.children.push(node);
copyNode(widgetChildNode,node);
});
}
},
results = {};
copyNode(this.wikifyWidgetNode,results);
return results;
};
/*
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
*/
@@ -63,9 +130,9 @@ WikifyWidget.prototype.refresh = function(changedTiddlers) {
return true;
} else {
// Refresh the widget tree
if(this.wikifier.refresh(changedTiddlers)) {
if(this.wikifyWidgetNode.refresh(changedTiddlers)) {
// Check if there was any change
var result = this.wikifier.getResult();
var result = this.getResult();
if(result !== this.wikifyResult) {
// If so, save the change
this.wikifyResult = result;

View File

@@ -34,39 +34,11 @@ caption: {{$:/language/Search/Filter/Caption}}
</$list>
\end
\procedure input-accept-actions()
\whitespace trim
<$list filter="[{$:/config/Search/NavigateOnEnter/enable}match[yes]]">
<$list-empty>
<$list filter="[<tiddler>get[text]!is[missing]] :else[<tiddler>get[text]is[shadow]]">
<$action-navigate $to={{{ [<tiddler>get[text]] }}}/>
</$list>
<$/list-empty>
<$action-navigate $to={{{ [<tiddler>get[text]] }}}/>
</$list>
\end
\procedure input-accept-variant-actions()
\whitespace trim
<$list filter="[{$:/config/Search/NavigateOnEnter/enable}match[yes]]">
<$list-empty>
<$list filter="[<tiddler>get[text]!is[missing]] :else[<tiddler>get[text]is[shadow]]">
<$list filter="[<__tiddler__>get[text]minlength[1]]">
<$action-sendmessage $message="tm-edit-tiddler" $param={{{ [<tiddler>get[text]] }}}/>
</$list>
</$list>
</$list-empty>
<$list filter="[<tiddler>get[text]minlength[1]]">
<$action-sendmessage $message="tm-edit-tiddler" $param={{{ [<tiddler>get[text]] }}}/>
</$list>
</$list>
\end
\whitespace trim
<<lingo Filter/Hint>>
<div class="tc-search tc-advanced-search">
<div class="tc-search tc-advanced-search tc-edit-max-width">
<$keyboard key="((input-tab-right))" actions=<<set-next-input-tab>> class="tc-small-gap-right">
<$keyboard key="((input-tab-left))" actions=<<set-previous-input-tab>>>
<$transclude $variable="keyboard-driven-input"
@@ -75,13 +47,16 @@ caption: {{$:/language/Search/Filter/Caption}}
refreshTitle="$:/temp/advancedsearch/refresh"
selectionStateTitle="$:/temp/advancedsearch/selected-item"
type="search"
tag="input"
tag="textarea"
focus={{$:/config/Search/AutoFocus}}
configTiddlerFilter="[[$:/temp/advancedsearch]]"
firstSearchFilterField="text"
inputAcceptActions=<<input-accept-actions>>
inputAcceptVariantActions=<<input-accept-variant-actions>>
inputAcceptActions=""
inputAcceptVariantActions=""
inputCancelActions=<<cancel-search-actions>>
minHeight="2em"
autoHeight="yes"
placeholder={{$:/language/Search/Filter/Placeholder}}
/>
</$keyboard>
</$keyboard>
@@ -90,13 +65,18 @@ caption: {{$:/language/Search/Filter/Caption}}
</$list>
</div>
<$reveal state="$:/temp/advancedsearch" type="nomatch" text="" tag="div" class="tc-search-results">
<$set name="resultCount" value="<$count filter={{$:/temp/advancedsearch}}/>">
<p><<lingo Filter/Matches>></p>
<$list filter={{$:/temp/advancedsearch}}>
<span class={{{[<currentTiddler>addsuffix[-primaryList]] -[[$:/temp/advancedsearch/selected-item]get[text]] :and[then[]else[tc-list-item-selected]] }}}>
<$transclude tiddler="$:/core/ui/ListItemTemplate"/>
</span>
</$list>
</$set>
</$reveal>
<%if [{$:/temp/advancedsearch}trim[]!match[]] %>
<div class="tc-search-results">
<%if [all[shadows+tiddlers]tag[$:/tags/AdvancedSearch/FilterResults]!has[draft.of]count[]compare:number:gt[1]] %>
<$macrocall
$name="tabs"
tabsList="[all[shadows+tiddlers]tag[$:/tags/AdvancedSearch/FilterResults]!has[draft.of]]"
default="$:/core/ui/AdvancedSearch/Filter/FilterResults/Results"
/>
<%else%>
<$list filter="[all[shadows+tiddlers]tag[$:/tags/AdvancedSearch/FilterResults]!has[draft.of]]">
<$transclude/>
</$list>
<%endif%>
</div>
<%endif%>

View File

@@ -0,0 +1,13 @@
title: $:/core/ui/AdvancedSearch/Filter/FilterResults/Results
tags: $:/tags/AdvancedSearch/FilterResults
caption: {{$:/language/Search/Filter/FilterResults/Results/Caption}}
\procedure lingo-base() $:/language/Search/
<$set name="resultCount" value="<$count filter={{$:/temp/advancedsearch}}/>">
<p><<lingo Filter/Matches>></p>
<$list filter={{$:/temp/advancedsearch}}>
<span class={{{[<currentTiddler>addsuffix[-primaryList]] -[[$:/temp/advancedsearch/selected-item]get[text]] :and[then[]else[tc-list-item-selected]] }}}>
<$transclude tiddler="$:/core/ui/ListItemTemplate"/>
</span>
</$list>
</$set>

View File

@@ -2,11 +2,8 @@ created: 20131127215321439
modified: 20140912135951542
title: $:/DefaultTiddlers
[[TiddlyWiki Pre-release]]
HelloThere
[[Quick Start]]
[[Find Out More]]
[[TiddlyWiki on the Web]]
[[Testimonials and Reviews]]
GettingStarted
Community
[[$:/plugins/tiddlywiki/internals]]
[[$:/AdvancedSearch]]
[[$:/plugins/tiddlywiki/internals/filterinspection/docs/inspectoperator]]
[[$:/plugins/tiddlywiki/internals/filterinspection/docs/InspectFilterMacro]]
[[$:/plugins/tiddlywiki/internals/filterinspection/docs/inspectfiltermacro/examples]]

View File

@@ -1,21 +0,0 @@
title: Operators/Wikify/TextMode
description: Simple wikify operator
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\parsermode inline
<$text text={{{ [subfilter{Filter}] }}}/>
+
title: Filter
[{Text}wikify[html],[inline],[text/vnd.tiddlywiki]]
+
title: Text
This is ''the text'' that is __wikified__
+
title: ExpectedResult
This is &lt;strong&gt;the text&lt;/strong&gt; that is &lt;u&gt;wikified&lt;/u&gt;

View File

@@ -1,64 +0,0 @@
title: Operators/Wikify/ParseTreeMode
description: Simple wikify operator
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\parsermode inline
<$text text={{{ [subfilter{Filter}] }}}/>
+
title: Filter
[{Text}wikify[parsetree],[inline],[text/vnd.tiddlywiki]]
+
title: Text
This is ''the text'' that is __wikified__
+
title: ExpectedResult
[
{
"type": "text",
"text": "This is ",
"start": 0,
"end": 8
},
{
"type": "element",
"tag": "strong",
"children": [
{
"type": "text",
"text": "the text",
"start": 10,
"end": 18
}
],
"start": 8,
"end": 20,
"rule": "bold"
},
{
"type": "text",
"text": " that is ",
"start": 20,
"end": 29
},
{
"type": "element",
"tag": "u",
"children": [
{
"type": "text",
"text": "wikified",
"start": 31,
"end": 39
}
],
"start": 29,
"end": 41,
"rule": "underscore"
}
]

View File

@@ -1,21 +0,0 @@
title: Operators/Wikify/TextMode
description: Simple wikify operator
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\parsermode inline
<$text text={{{ [subfilter{Filter}] }}}/>
+
title: Filter
[{Text}wikify[text],[inline],[text/vnd.tiddlywiki]]
+
title: Text
This is ''the text'' that is __wikified__
+
title: ExpectedResult
This is the text that is wikified

View File

@@ -116,22 +116,6 @@ describe("WikiText parser tests", function() {
);
});
it("should parse macro definitions with end statements followed by spaces", function() {
expect(parse("\\define myMacro()\nnothing\n\\end \n")).toEqual(
[{"type":"set","attributes":{"name":{"name":"name","type":"string","value":"myMacro"},"value":{"name":"value","type":"string","value":"nothing"}},"children":[],"params":[],"isMacroDefinition":true,"orderedAttributes":[{"name":"name","type":"string","value":"myMacro"},{"name":"value","type":"string","value":"nothing"}],"start":0,"end":33,"rule":"macrodef"}]
);
});
it("should parse macro definitions with named end statements followed by spaces", function() {
expect(parse("\\define myMacro()\nnothing\n\\end myMacro \n")).toEqual(
[{"type":"set","attributes":{"name":{"name":"name","type":"string","value":"myMacro"},"value":{"name":"value","type":"string","value":"nothing"}},"children":[],"params":[],"isMacroDefinition":true,"orderedAttributes":[{"name":"name","type":"string","value":"myMacro"},{"name":"value","type":"string","value":"nothing"}],"start":0,"end":40,"rule":"macrodef"}]
);
});
it("should parse procedure definitions with no parameters", function() {
expect(parse("\\procedure myMacro()\nnothing\n\\end\n")).toEqual(
@@ -148,22 +132,6 @@ describe("WikiText parser tests", function() {
);
});
it("should parse procedure definitions with end statements followed by spaces", function() {
expect(parse("\\procedure myMacro()\nnothing\n\\end \n")).toEqual(
[{"type":"set","attributes":{"name":{"name":"name","type":"string","value":"myMacro"},"value":{"name":"value","type":"string","value":"nothing"}},"children":[],"params":[],"orderedAttributes":[{"name":"name","type":"string","value":"myMacro"},{"name":"value","type":"string","value":"nothing"}],"isProcedureDefinition":true,"start":0,"end":36,"rule":"fnprocdef"}]
);
});
it("should parse procedure definitions with named end statements followed by spaces", function() {
expect(parse("\\procedure myMacro()\nnothing\n\\end myMacro \n")).toEqual(
[{"type":"set","attributes":{"name":{"name":"name","type":"string","value":"myMacro"},"value":{"name":"value","type":"string","value":"nothing"}},"children":[],"params":[],"orderedAttributes":[{"name":"name","type":"string","value":"myMacro"},{"name":"value","type":"string","value":"nothing"}],"isProcedureDefinition":true,"start":0,"end":43,"rule":"fnprocdef"}]
);
});
it("should parse procedure definitions with parameters", function() {
expect(parse("\\procedure myMacro(one,two,three,four:elephant)\nnothing\n\\end\n")).toEqual(
@@ -187,22 +155,6 @@ describe("WikiText parser tests", function() {
);
});
it("should parse function definitions with end statements followed by spaces", function() {
expect(parse("\\function myMacro()\nnothing\n\\end \n")).toEqual(
[{"type":"set","attributes":{"name":{"name":"name","type":"string","value":"myMacro"},"value":{"name":"value","type":"string","value":"nothing"}},"children":[],"params":[],"orderedAttributes":[{"name":"name","type":"string","value":"myMacro"},{"name":"value","type":"string","value":"nothing"}],"isFunctionDefinition":true,"start":0,"end":35,"rule":"fnprocdef"}]
);
});
it("should parse function definitions with named end statements followed by spaces", function() {
expect(parse("\\function myMacro()\nnothing\n\\end myMacro \n")).toEqual(
[{"type":"set","attributes":{"name":{"name":"name","type":"string","value":"myMacro"},"value":{"name":"value","type":"string","value":"nothing"}},"children":[],"params":[],"orderedAttributes":[{"name":"name","type":"string","value":"myMacro"},{"name":"value","type":"string","value":"nothing"}],"isFunctionDefinition":true,"start":0,"end":42,"rule":"fnprocdef"}]
);
});
it("should parse single line function definitions with no parameters", function() {
expect(parse("\\function myMacro() nothing\n")).toEqual(

View File

@@ -2,7 +2,8 @@
"description": "TiddlyWiki core tests",
"plugins": [
"tiddlywiki/jasmine",
"tiddlywiki/geospatial"
"tiddlywiki/geospatial",
"tiddlywiki/internals"
],
"themes": [
"tiddlywiki/vanilla",

View File

@@ -1,13 +1,20 @@
title: 中文社区 - Chinese Community
tags: Community
* 大家可以一起编辑的中文社区教程项目:
*# 主站:[ext[https://tw-cn.netlify.app/]]
*# 加速访问:[ext[https://tw-cn.cpolar.top/]]
*# 备用:[ext[https://tiddly-wiki-chinese-tutorial.vercel.app]]
* 太微中文交流论坛:[ext[https://talk.tidgi.fun/topic/6]]
* 太微官网汉化版:[ext[https://bramchen.github.io/tw5-docs/zh-Hans/]]
* 最适合新手的中文入门教程:[ext[https://keatonlao.github.io/tiddlywiki-xp/]]
* TiddlyWiki 爱好者 QQ 群: 946052860
*# [ext[点击链接加入群聊【TiddlyWiki爱好者】|https://qm.qq.com/q/13SFxVArlu]]
*# [ext[点击链接加入腾讯频道【太微TiddlyWiki】|https://pd.qq.com/s/474hgpll1]]
# A Chinese community tutorial program that people can edit together:
#* Main site: [ext[https://tw-cn.netlify.app/]]
#* Accelerated access: [ext[https://tw-cn.cpolar.top/]]
#* Alternate: [ext[https://tiddly-wiki-chinese-tutorial.vercel.app]]
# Tiddlywiki Chinese Chat Forum: [ext[https://talk.tidgi.fun/topic/6]]
# Chinese translation of Tiddlywiki official website [ext[https://bramchen.github.io/tw5-docs/zh-Hans/]]
# The best Chinese introductory tutorial for newbies [ext[https://keatonlao.github.io/tiddlywiki-xp/]]
---
# 大家可以一起编辑的中文社区教程项目:
#* 主站:[ext[https://tw-cn.netlify.app/]]
#* 加速访问:[ext[https://tw-cn.cpolar.top/]]
#* 备用:[ext[https://tiddly-wiki-chinese-tutorial.vercel.app]]
# 太微中文交流论坛:[ext[https://talk.tidgi.fun/topic/6]]
# 太微官网汉化版:[ext[https://bramchen.github.io/tw5-docs/zh-Hans/]]
# 最适合新手的中文入门教程:[ext[https://keatonlao.github.io/tiddlywiki-xp/]]

View File

@@ -3,10 +3,8 @@ modified: 20140912135951542
title: $:/DefaultTiddlers
type: text/vnd.tiddlywiki
HelloThere
[[Quick Start]]
[[Find Out More]]
[[TiddlyWiki on the Web]]
[[Testimonials and Reviews]]
GettingStarted
Community
[[$:/plugins/tiddlywiki/internals]]
[[$:/AdvancedSearch]]
[[$:/plugins/tiddlywiki/internals/filterinspection/docs/inspectoperator]]
[[$:/plugins/tiddlywiki/internals/filterinspection/docs/InspectFilterMacro]]
[[$:/plugins/tiddlywiki/internals/filterinspection/docs/inspectfiltermacro/examples]]

View File

@@ -2,11 +2,11 @@
"description": "Documentation from https://tiddlywiki.com",
"plugins": [
"tiddlywiki/browser-sniff",
"tiddlywiki/confetti",
"tiddlywiki/dynannotate",
"tiddlywiki/railroad",
"tiddlywiki/internals",
"tiddlywiki/menubar",
"tiddlywiki/railroad",
"tiddlywiki/confetti",
"tiddlywiki/dynannotate",
"tiddlywiki/tour"
],
"themes": [

View File

@@ -3,5 +3,5 @@
"name": "Confetti",
"description": "Animated confetti effect",
"list": "readme",
"stability": "STABILITY_2_STABLE"
"stability": "STABILITY_1_EXPERIMENTAL"
}

View File

@@ -3,5 +3,5 @@
"name": "Geospatial Utilities",
"description": "Geospatial utilities",
"list": "readme docs settings license",
"stability": "STABILITY_2_STABLE"
"stability": "STABILITY_1_EXPERIMENTAL"
}

View File

@@ -0,0 +1,13 @@
title: $:/plugins/tiddlywiki/internals/docs
!! `<<inspect-filter>>` Procedure
{{$:/plugins/tiddlywiki/internals/filterinspection/docs/InspectFilterMacro}}
!!! `<<inspect-filter>>` Procedure Examples
{{$:/plugins/tiddlywiki/internals/filterinspection/docs/inspectfiltermacro/examples}}
!! `inspect` Operator
{{$:/plugins/tiddlywiki/internals/filterinspection/docs/inspectoperator}}

View File

@@ -0,0 +1,8 @@
title: $:/plugins/tiddlywiki/internals/filterinspection/InspectResultsTab
tags: $:/tags/AdvancedSearch/FilterResults
caption: Inspect
list-after: $:/core/ui/AdvancedSearch/Filter/FilterResults/Results
Filter evaluation trace:
<$transclude $variable="inspect-filter" filter={{$:/temp/advancedsearch}} />

View File

@@ -0,0 +1,44 @@
title: $:/plugins/tiddlywiki/internals/filterinspection/ObserveResultsTab
tags: $:/tags/AdvancedSearch/FilterResults
caption: Observe
list-after: $:/plugins/tiddlywiki/internals/filterinspection/InspectResultsTab
Log traces of this filter expression in the background. A trace will be generated every time the filter is evaluated, regardless of the context. The trace is only saved if the results are different from previous traces.
<$checkbox tiddler="$:/config/FilterObservationionEnabled" field="text" checked="yes" unchecked="no" default="no">
Enable background filter observation
</$checkbox>
<$button>
<%if [all[shadows+tiddlers]tag[$:/tags/InspectableFilter]get[text]match{$:/temp/advancedsearch}count[]match[0]] %>
<$action-createtiddler $basetitle="$:/config/inspectable-filters/filter" tags="$:/tags/InspectableFilter" type="text/plain" text={{$:/temp/advancedsearch}}/>
<%endif%>
Enable logging for this filter
</$button>
<$list filter="[all[shadows+tiddlers]tag[$:/tags/InspectableFilter]!has[draft.of]sort[]]" variable="inspectableFilter">
<div class="tc-box tc-inspectable-filter-box">
<div class="tc-box-header">
<$text text={{{ [<inspectableFilter>get[text]] }}}/> <$link to=<<inspectableFilter>>>{{$:/core/images/open-window}}</$link>
</div>
<div class="tc-box-content">
<ol>
<$list filter="[all[shadows+tiddlers]tag[$:/tags/FilterInspectionOutput]!has[draft.of]!sort[modified]] :filter[<inspectableFilter>get[text]match{!!filter}]" variable="inspectionOutput">
<$let transclusion=<<inspectionOutput>>>
<li>
<div class="tc-box tc-inspectable-filter-trace-box">
<div class="tc-box-header">
<$text text={{{ [<inspectionOutput>get[modified]format:date[DDth mmm YYYY 0hh:0mm:0ss]] }}}/><$link to=<<inspectionOutput>>>{{$:/core/images/open-window}}</$link>
</div>
<div class="tc-box-content">
<$transclude $variable="inspect-filter-output-unframed" jsonOutput={{{ [<inspectionOutput>get[text]] }}} />
</div>
</div>
</li>
</$let>
</$list>
</ol>
</div>
</div>
</$list>

View File

@@ -0,0 +1,17 @@
caption: inspect-filter
title: $:/plugins/tiddlywiki/internals/filterinspection/docs/InspectFilterMacro
type: text/vnd.tiddlywiki
The <<.def inspect-filter>> procedure displays a schematic representation of the filter evaluation process, including the input, output and intermediate results of evaluating the specified filter. It is based on the [[inspect operator|$:/plugins/tiddlywiki/internals/filterinspection/docs/inspectoperator]].
By default the tabs are arranged horizontally above the content. To get vertical tabs, set the `orientation` parameter to `vertical`. This is useful for narrow windows or when the content is too wide to fit in a horizontal tab layout.
!! Parameters
;filter
: The filter to be inspected
;inputFilter
: Optionally, a filter defining the input titles for the filter to be inspected, defaulting to `[all[tiddlers]]`
;orientation
: Optionally, the orientation of the schematic representation, defaulting to `horizontal`. Set to `vertical` to display the tabs vertically

View File

@@ -0,0 +1,27 @@
caption: inspect
tags: [[Filter Operators]]
title: $:/plugins/tiddlywiki/internals/filterinspection/docs/inspectoperator
type: text/vnd.tiddlywiki
The ''inspect'' operator evaluates a filter with the specified input titles and returns a JSON object containing the input, output and intermediate results of evaluating the specified filter.
|Operator Purpose |Inspect the evaluation of a filter to aid debugging |
|Operator Input |A selection of titles |
|Operator Output |A JSON object containing the input, output and intermediate results of evaluating the specified filter |
|Operator Parameter |The filter to be inspected |
The output JSON object contains the following properties:
* `input`: the input titles passed to the filter
* `inputFilter`: the filter being inspected
* `output`: the output titles resulting from evaluating the filter
* `evaluationTime`: the time taken to evaluate the filter, in milliseconds
* `runs`: an array of objects, each of which represents a single run of the filter. Each object contains the following properties:
** `prefixName`: the name of the prefix operator that was used in this run. Shortcut prefixes like `+` and `-` are expanded to their full names, e.g. `and` and `except`
** `input`: the input titles passed to the prefix operator
** `operations`: an array of objects, each of which represents the evaluation of a single operation that was performed in this run. Each object contains the following properties:
*** `operators`: an array of objects, each of which represents a single operator that was used in this run. Each object contains the following properties:
**** `operatorName`: the name of the operator
**** `operands`: an array of string operands passed to the operator
**** `input`: the input titles passed to the operator
**** `output`: the output titles resulting from evaluating the operator

View File

@@ -0,0 +1,38 @@
title: $:/plugins/tiddlywiki/internals/filterinspection/docs/inspectfiltermacro/examples
tags: [[Macro Examples]]
type: text/vnd.tiddlywiki
\procedure .example(n,eg,egvar)
<$let eg={{{ [<egvar>!is[blank]getvariable[]] :else[<eg>] }}}>
<div class="doc-example">
<$macrocall $name="copy-to-clipboard-above-right" src=<<eg>>/>
<$codeblock code=<<eg>>/>
<$list filter=`[title<.state-prefix>addsuffix{!!title}addsuffix[/]addsuffix[$(n)$]]` variable=".state">
<$reveal state=<<.state>> type="nomatch" text="show">
<dl>
<dd><$button set=<<.state>> setTo="show">Try it</$button></dd>
</dl>
</$reveal>
<$reveal state=<<.state>> type="match" text="show">
<dl>
<dd><$button set=<<.state>> setTo="">Hide</$button></dd>
</dl>
<blockquote class="doc-example-result">
<$transclude $variable="eg" $mode="block"/>
</blockquote>
</$reveal>
</$list>
</div>
</$let>
\end .example
<$macrocall $name=".example" eg="""<$transclude $variable="inspect-filter" filter="1 2 3" inputFilter="[all[tiddlers]]"/>"""/>
<$macrocall $name=".example" eg="""<$transclude $variable="inspect-filter" filter="1 2 3" inputFilter="[all[tiddlers]]" orientation="vertical"/>"""/>
<$macrocall $name=".example" eg="""<$transclude $variable="inspect-filter" filter="[title<currentTiddler>] [{$:/palette}length[]] [[marker]]" inputFilter="[all[tiddlers]]"/>"""/>
<$macrocall $name=".example" eg="""<$transclude $variable="inspect-filter" filter="[all[shadows+tiddlers]tag[$:/tags/MenuBar]!has[draft.of]] -[all[tiddlers+shadows]tag[$:/tags/TopLeftBarlimit[1]then[]else[$:/plugins/tiddlywiki/menubar/items/topleftbar]]" inputFilter="[all[tiddlers]]"/>
"""/>
<$macrocall $name=".example" eg="""<$transclude $variable="inspect-filter" filter="[tags[]prefix[$:/]] :sort[length[]add[1]] +[first[2]tagging[]]" inputFilter="[all[tiddlers]]"/>"""/>

View File

@@ -0,0 +1,5 @@
title: $:/plugins/tiddlywiki/internals/filterinspection/InspectFilterProcedures/Filters/SlowFilter
tags: $:/tags/Filter
filter: [all[tiddlers+shadows]limit[500]] :filter[<currentTiddler>length[]reverse[]multiply[3.14]]
description: [Internals Plugin] A filter that takes a long time to process

View File

@@ -0,0 +1,214 @@
title: $:/plugins/tiddlywiki/internals/filterinspection/InspectFilterProcedures
tags: $:/tags/Global
\whitespace trim
\procedure inspect-list(jsonList)
<$let
transclusion={{{ [<jsonList>indexes[]count[]addprefix[-list-]] }}}
state=<<qualify "$:/temp/filter-inspector/list">>
stateMaxRows={{{ [<state>addsuffix[max-rows]] }}}
maxRows={{{ [<stateMaxRows>get[text]!match[]else[10]] }}}
stateScroll={{{ [<state>addsuffix[scroll]] }}}
>
<$text text=<<stateScroll>>/>
<$scrollable class="tc-box-content-list-scollable" bind=<<stateScroll>>>
<div class="tc-box-content-list">
<$list filter="[<jsonList>jsonindexes[]nsort[]limit<maxRows>]" variable="indexList">
<$list-template>
<div class="tc-box-content-list-item">
<$slot $name="list-item"/>
</div>
</$list-template>
<$list-empty>
<div class="tc-box-content-list-empty">
(No items)
</div>
</$list-empty>
</$list>
<%if [<jsonList>jsonindexes[]count[]compare:number:gt<maxRows>] %>
<div class="tc-box-content-list-more">
<$button class="tc-btn-invisible tc-box-content-full-width-button" style.fill="inherit">
<$action-setfield $tiddler=<<stateMaxRows>> text={{{ [<maxRows>add[10]] }}}/>
{{$:/core/images/chevron-down}}
<$text text="more"/>
</$button>
</div>
<%endif%>
</div>
</$scrollable>
\end inspect-list
\procedure inspect-foldable-text-list(jsonList,class:"tc-box")
<$let
transclusion={{{ [[link-list-]addsuffix<class>] }}}
>
<div class=<<class>>>
<$let
state=<<qualify "$:/temp/filter-inspector/list">>
stateFolded={{{ [<state>addsuffix[folded]] }}}
folded={{{ [<stateFolded>get[text]else[no]match[yes]] }}}
>
<div class="tc-box-header">
<$button class="tc-btn-invisible tc-box-content-full-width-button" style.fill="inherit">
<%if [<folded>match[yes]] %>
<$action-setfield $tiddler=<<stateFolded>> text="no"/>
<%else%>
<$action-setfield $tiddler=<<stateFolded>> text="yes"/>
<%endif%>
<$text text={{{ [<jsonList>jsonindexes[]count[]] }}}/>
{{$:/core/images/right-arrow}}
</$button>
</div>
<%if [<folded>!match[yes]] %>
<div class="tc-box-content">
<$transclude $variable="inspect-list" jsonList=<<jsonList>>>
<$fill $name="list-item">
<$text text={{{ [<jsonList>jsonget<indexList>] }}} />
</$fill>
</$transclude>
</div>
<%endif%>
</$let>
</div>
</$let>
\end inspect-foldable-text-list
\procedure inspect-operator(jsonOperator)
<div class="tc-box tc-inspect-operator-box">
<div class="tc-box-header">
<span class="">
<$text text={{{ [<jsonOperator>jsonget[prefix]] }}} />
<$text text={{{ [<jsonOperator>jsonget[operatorName]] }}} />
</span>
<%if [<jsonOperator>jsonindexes[suffixes]length[]compare:number:gt[0]] %>
<$list filter="[<jsonOperator>jsonindexes[suffixes]nsort[]]" variable="indexSuffix">
<span class="tc-pill">
:<$text text={{{ [<jsonOperator>jsonget[suffixes],<indexSuffix>] }}} />
</span>
</$list>
<%endif%>
<$list filter="[<jsonOperator>jsonindexes[operands]nsort[]]" variable="indexOperand">
<div class="tc-split-pill">
<div class="tc-split-pill-top">
<%if [<jsonOperator>jsonget[parseTree],[operands],<indexOperand>,[variable]match[true]] %>
<$text text="<"/><$text text={{{ [<jsonOperator>jsonget[parseTree],[operands],<indexOperand>,[text]] }}}/><$text text=">"/>
<%elseif [<jsonOperator>jsonget[parseTree],[operands],<indexOperand>,[indirect]match[true]] %>
<$text text="{"/><$text text={{{ [<jsonOperator>jsonget[parseTree],[operands],<indexOperand>,[text]] }}}/><$text text="}"/>
<%else%>
<$text text="["/><$text text={{{ [<jsonOperator>jsonget[parseTree],[operands],<indexOperand>,[text]] }}}/><$text text="]"/>
<%endif%>
</div>
<div class="tc-split-pill-bottom">
<$text text={{{ [<jsonOperator>jsonget[operands],<indexOperand>] }}} />
</div>
</div>
</$list>
</div>
<div class="tc-box-content">
Evaluation time <span class="tc-pill"><$text text={{{ [<jsonOperator>jsonget[evaluationTime]fixed[8]] }}}/></span> milliseconds
</div>
</div>
\end inspect-operator
\procedure inspect-operation(jsonOperation,indexOperation)
<div class="tc-box tc-inspect-operation-box">
<div class="tc-box-header">
<span class="">Evaluation</span>
<span class="tc-pill"><$text text=<<indexOperation>> /></span>
Evaluation time <span class="tc-pill"><$text text={{{ [<jsonOperation>jsonget[evaluationTime]fixed[8]] }}}/></span> milliseconds
</div>
<div class="tc-box-content">
<$list filter="[<jsonOperation>jsonindexes[operators]nsort[]]" variable="indexOperator">
<$let
transclusion={{{ [[operator-]addsuffix<indexOperator>] }}}
jsonOperator={{{ [<jsonOperation>jsonextract[operators],<indexOperator>] }}}
>
<$transclude $variable="inspect-foldable-text-list" jsonList={{{ [<jsonOperator>jsonextract[input]] }}} class="tc-box tc-inspect-input-box"/>
<$transclude $variable="inspect-operator" jsonOperator=<<jsonOperator>>/>
</$let>
</$list>
<$list filter="[<jsonOperation>jsonindexes[operators]nsort[]last[1]]" variable="indexOperator">
<$let
transclusion={{{ [[operator-]addsuffix<indexOperator>] }}}
jsonOperator={{{ [<jsonOperation>jsonextract[operators],<indexOperator>] }}}
>
<$transclude $variable="inspect-foldable-text-list" jsonList={{{ [<jsonOperator>jsonextract[output]] }}} class="tc-box tc-inspect-output-box"/>
</$let>
</$list>
</div>
</div>
\end inspect-operation
\procedure inspect-run(jsonRun)
<$transclude $variable="inspect-foldable-text-list" jsonList={{{ [<jsonRun>jsonextract[input]] }}} class="tc-box tc-inspect-input-box"/>
<div class="tc-box tc-inspect-run-box">
<div class="tc-box-header">
<span class="">
<%if [<jsonRun>jsonget[prefix]!match[]] %>
<$text text={{{ [<jsonRun>jsonget[prefix]] }}} />
<$text text=" shortcut for "/>
<$text text={{{ [<jsonRun>jsonget[prefixName]addprefix[:]] }}} />
<%else%>
<$text text={{{ [<jsonRun>jsonget[prefixName]addprefix[:]] }}} />
<%endif%>
</span>
<%if [<jsonRun>jsonindexes[suffixes]length[]compare:number:gt[0]] %>
<$list filter="[<jsonRun>jsonindexes[suffixes]nsort[]]" variable="indexSuffix">
<span class="tc-pill">
:<$text text={{{ [<jsonRun>jsonget[suffixes],<indexSuffix>] }}} />
</span>
</$list>
<%endif%>
Evaluation time <span class="tc-pill"><$text text={{{ [<jsonRun>jsonget[evaluationTime]fixed[8]] }}}/></span> milliseconds
</div>
<div class="tc-box-content">
<div class="tc-inspect-operations-wrapper">
<$list filter="[<jsonRun>jsonindexes[operations]nsort[]]" variable="indexOperation">
<$let transclusion={{{ [[operation-]addsuffix<indexOperation>] }}}>
<$transclude $variable="inspect-operation" jsonOperation={{{ [<jsonRun>jsonextract[operations],<indexOperation>] }}} indexOperation=<<indexOperation>>/>
</$let>
</$list>
</div>
</div>
</div>
\end inspect-run
\procedure inspect-filter-output-unframed(jsonOutput,orientation:"horizontal")
<$scrollable bind={{{ [<qualify "$:/temp/filter-inspector/">addsuffix<filter>] }}}>
<div class={{{ tc-inspect-filter-box tc-inspect-filter-box-unframed [<orientation>match[horizontal]then[tc-inspect-filter-box-horizontal]] +[join[ ]] }}}>
<$list filter="[<jsonOutput>jsonindexes[runs]nsort[]]" variable="indexRun">
<$let transclusion={{{ [[run-]addsuffix<indexRun>] }}}>
<$transclude $variable="inspect-run" jsonRun={{{ [<jsonOutput>jsonextract[runs],<indexRun>] }}}/>
</$let>
</$list>
<$transclude $variable="inspect-foldable-text-list" jsonList={{{ [<jsonOutput>jsonextract[output]] }}} class="tc-box tc-inspect-output-box"/>
</div>
</$scrollable>
\end inspect-filter-output-unframed
\procedure inspect-filter-output(jsonOutput,orientation:"horizontal")
<$scrollable bind={{{ [<qualify "$:/temp/filter-inspector/">addsuffix<filter>] }}}>
<div class={{{ tc-inspect-filter-box [<orientation>match[horizontal]then[tc-inspect-filter-box-horizontal]] +[join[ ]] }}}>
<div class="tc-box">
<div class="tc-box-header">
Filter
<span class="tc-pill"><$text text={{{ [<jsonOutput>jsonget[inputFilter]] }}}/></span>
Evaluation time <span class="tc-pill"><$text text={{{ [<jsonOutput>jsonget[evaluationTime]fixed[8]] }}}/></span> milliseconds
</div>
<div class="tc-box-content">
<$list filter="[<jsonOutput>jsonindexes[runs]nsort[]]" variable="indexRun">
<$let transclusion={{{ [[run-]addsuffix<indexRun>] }}}>
<$transclude $variable="inspect-run" jsonRun={{{ [<jsonOutput>jsonextract[runs],<indexRun>] }}}/>
</$let>
</$list>
<$transclude $variable="inspect-foldable-text-list" jsonList={{{ [<jsonOutput>jsonextract[output]] }}} class="tc-box tc-inspect-output-box"/>
</div>
</div>
</div>
</$scrollable>
\end inspect-filter-output
\procedure inspect-filter(filter,inputFilter:"[all[tiddlers]]",orientation:"horizontal")
<$transclude $variable="inspect-filter-output" jsonOutput={{{ [subfilter<inputFilter>inspect<filter>] }}} orientation=<<orientation>> />
\end inspect-filter

View File

@@ -0,0 +1,29 @@
/*\
title: $:/plugins/tiddlywiki/internals/filterinspection/modules/inspect.js
type: application/javascript
module-type: filteroperator
Filter operator for inspecting the evaluation of a filter
\*/
"use strict";
var getWrappers = $tw.plugins.internals.getWrappers;
/*
Export our filter function
*/
exports.inspect = function(source,operator,options) {
var inputFilter = operator.operands[0] || "",
results,
wrappers = getWrappers(function(output) {
results = output;
},inputFilter);
// Compile the filter with wrapper functions to log the details
var compiledFilter = options.wiki.compileFilter(inputFilter,{
wrappers: wrappers
});
compiledFilter.call(options.wiki,source,options.widget);
return [JSON.stringify(results)];
};

View File

@@ -0,0 +1,212 @@
/*\
title: $:/plugins/tiddlywiki/internals/filterinspection/modules/startup.js
type: application/javascript
module-type: startup
Install hooks
\*/
"use strict";
// Export name and synchronous status
exports.name = "internals-plugin";
exports.before = ["startup"];
exports.synchronous = true;
exports.startup = function() {
// Publish public methods
$tw.plugins = $tw.plugins || {};
$tw.plugins.internals = {
getWrappers: getWrappers
};
// We accumulate the output of the filter inspection into an array. We can't directly write to the wiki since we might be in
// the middle of the refresh cycle, when writes to the wiki are not allowed
var accumulator = [];
// Add our hook for each filter evaluation
$tw.hooks.addHook("th-filter-evaluation",function(filterString,wrappers) {
// Check that filter observation is enabled
if($tw.wiki.getTiddlerText("$:/config/FilterObservationionEnabled","no") !== "yes") {
return wrappers;
}
// Get the list of filters to be inspected
var inspectedFilters = $tw.wiki.getTiddlersWithTag("$:/tags/InspectableFilter");
// Check whether this is a filter we want to inspect
if(inspectedFilters.indexOf(filterString) === -1) {
return wrappers;
}
// Don't do anything if there are already wrappers
if(wrappers.prefix || wrappers.operation || wrappers.operator) {
return wrappers;
}
// Flush the accumulator, making each filter inspection output record into a separate tiddler
var flushAccumulator = function() {
if(accumulator.length) {
$tw.utils.each(accumulator,function(jsonInspectionOutput) {
// Get the output as a string
var stringInspectionOutput = JSON.stringify(jsonInspectionOutput);
// Compute the log prefix and its length
var logPrefix = `$:/temp/filter-inspection/${ + $tw.utils.stringifyDate(new Date())}: `,
logPrefixLength = logPrefix.length;
// Check if the the output is the same as the last log with the same filter
var logTitles = [];
$tw.wiki.each(function(tiddler,title) {
if(title.substring(logPrefixLength) === jsonInspectionOutput.inputFilter) {
logTitles.push(title);
}
});
var alreadyLogged = false;
$tw.utils.each(logTitles,function(title) {
var jsonLog = $tw.wiki.getTiddlerData(title);
if($tw.utils.isArrayEqual(jsonLog.output,jsonInspectionOutput.output)) {
alreadyLogged = true;
}
});
if(alreadyLogged) {
return;
}
// Add the log tiddler
var tiddlerFields = {
title: `${logPrefix}${jsonInspectionOutput.inputFilter}`,
tags: ["$:/tags/FilterInspectionOutput"],
text: stringInspectionOutput,
filter: jsonInspectionOutput.inputFilter,
type: "application/json"
}
// console.log("Adding " + JSON.stringify(tiddlerFields));
$tw.wiki.addTiddler(new $tw.Tiddler($tw.wiki.getCreationFields(),$tw.wiki.getModificationFields(),tiddlerFields));
});
accumulator = [];
}
};
// Get our wrappers
return getWrappers(function(output) {
// Schedule a flush
if(accumulator.length === 0) {
$tw.utils.nextTick(function() {
flushAccumulator();
});
}
accumulator.push(output);
},filterString);
});
};
/*
Return the wrappers for evaluating a given filter
fnDone is a function that will be called with the output of the filter evaluation as the single argument
inputFilter is the filter to be evaluated
*/
function getWrappers(fnDone,inputFilter) {
// Skeleton output record
var output = {
inputFilter: inputFilter,
input: [],
runs: []
};
// Keep track of where the current run and the current operation are being written
var currentRun,currentOperation;
// The starting evaluation time stamp
var filterStartTime;
// Compile the filter with wrapper functions to log the details
return {
start: function(source) {
// Save the input so that we have it in the output record
source(function(tiddler,title) {
output.input.push(title);
});
// Start the timer
filterStartTime = $tw.utils.timer();
},
prefix: function(filterRunPrefixFunction,operationFunction,innerOptions) {
// Function to be called at the start of each filter run
return function(results,innerSource,innerWidget) {
// Save the details of this filte run
var details ={
input: results.toArray(),
prefixName: innerOptions.prefixName,
prefix: innerOptions.prefix,
suffixes: innerOptions.suffixes,
operations: []
};
// Save the current run so that we can add operations to it
currentRun = details.operations;
// Save the start time
var startTime = $tw.utils.timer();
// Get the filter run prefix function
var innerResults = filterRunPrefixFunction.call(null,operationFunction,innerOptions);
// Invoke the filter run
innerResults(results,innerSource,innerWidget);
// Save the end time
details.evaluationTime = $tw.utils.timer(startTime);
// Save the results of the filter run
details.output = results.toArray();
output.runs.push(details);
};
},
operation: function(operationFunction,operation) {
// Record the operation
var details = {
operators: []
}
// Save the start time
var startTime = $tw.utils.timer();
// Keep track of where the current operation should be being written
currentOperation = details.operators;
// Invoke the operation
operationFunction();
// Save the end time
details.evaluationTime = $tw.utils.timer(startTime);
// Save the results of the operation
currentRun.push(details);
},
operator: function(operatorFunction,innerSource,innerOperator,innerOptions) {
// Record the operator
var details = {
operatorName: innerOperator.operatorName,
operands: innerOperator.operands,
parseTree: innerOperator.parseTree,
prefix: innerOperator.prefix,
suffix: innerOperator.suffix,
suffixes: innerOperator.suffixes,
regexp: innerOperator.regexp,
input: []
};
// Copy the input
innerSource(function(tiddler,title) {
details.input.push(title);
});
// Save this operation
currentOperation.push(details);
// Save the start time
var startTime = $tw.utils.timer();
// Invoke the operator
var innerResults = operatorFunction.apply(null,Array.prototype.slice.call(arguments,1));
// Save the end time
details.evaluationTime = $tw.utils.timer(startTime);
// Make sure the results are an array so that we can store them
if(!$tw.utils.isArray(innerResults)) {
var resultArray = [];
innerResults(function(tiddler,title) {
resultArray.push(title);
});
innerResults = resultArray;
}
// Store the results in the output
details.output = innerResults;
// Return the results
return innerResults;
},
done: function(results) {
// Store evaluation time
output.evaluationTime = $tw.utils.timer(filterStartTime);
// Save the results of the filter evaluation
output.output = results;
// console.log(`Inspected ${JSON.stringify(output,null,4)}`);
// Invoke the done function
if(fnDone) {
fnDone(output);
}
}
};
}

View File

@@ -0,0 +1,188 @@
title: $:/plugins/tiddlywiki/internals/filterinspection/styles
tags: $:/tags/Stylesheet
\rules only filteredtranscludeinline transcludeinline macrodef macrocallinline macrocallblock
/*
** Headed box and pills
*/
.tc-box {
border: 1px solid var(--box-foreground-color););
border-radius: 4px;
margin: 0 0 0.5em 0;
}
.tc-box .tc-box {
margin: 0.5em;
}
.tc-box-header {
padding: 0.25em;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
color: var(--box-background-color);
fill: var(--box-background-color);
background-color: var(--box-foreground-color);
display: flex;
align-items: center;
}
.tc-box-header svg {
width: 1em;
height: 1em;
margin-left: 0.25em;
}
.tc-box-header button {
color: var(--box-background-color);
background-color: var(--box-foreground-color);
}
.tc-box-content {
min-width: 4em;
padding: 0.25em;
border-bottom-left-radius: 3px;
border-bottom-right-radius: 3px;
color: var(--box-foreground-color);
fill: var(--box-foreground-color);
background-color: var(--box-background-color);
}
.tc-box-content-list-scollable {
margin: -0.25em;
max-height: 25vh;
width: 12em;
}
.tc-box-content-list {
}
.tc-box-content-list-item {
font-size: 0.7em;
line-height: 1.1;
padding: 0.25em;
}
.tc-box-content-list-more {
font-size: 0.7em;
line-height: 1.1;
padding: 0.25em;
}
.tc-box-content-full-width-button {
display: block;
width: 100%;
text-align: left;
}
.tc-box-content-list-more,
.tc-box-content-list-more .tc-btn-invisible {
color: var(--box-background-color);
fill: var(--box-background-color);
background-color: var(--box-foreground-color);
}
.tc-box-content-list-item:nth-child(even) {
background: rgb(255, 255, 255, 0.5);
}
.tc-box-content-list-empty {
}
.tc-pill {
padding: 0.125em 0.25em;
margin: 0 0.25em;
border-radius: 6px;
color: var(--box-foreground-color);
background-color: var(--box-background-color);
}
.tc-split-pill {
display: inline-block;
padding: 0.125em 0.25em;
margin: 0 0.25em;
border-radius: 6px;
color: var(--box-foreground-color);
background-color: var(--box-background-color);
}
.tc-split-pill-top {
border-bottom: 1px solid var(--box-foreground-color);
}
.tc-split-pill-bottom {
}
/*
** Filter Inspection
*/
.tc-inspect-filter-box {
--box-background-color: <<colour code-border>>;
--box-foreground-color: <<colour code-foreground>>;
}
.tc-inspectable-filter-box {
--box-background-color: #ffebe1;
--box-foreground-color: #ff3f00;
}
.tc-inspectable-filter-trace-box {
--box-background-color: #ffdec1;
--box-foreground-color: #ff7a00;
}
.tc-inspect-filter-box.tc-inspect-filter-box-horizontal {
display: flex;
}
.tc-inspect-filter-box.tc-inspect-filter-box-horizontal > .tc-box > .tc-box-content,
.tc-inspect-filter-box.tc-inspect-filter-box-horizontal.tc-inspect-filter-box-unframed,
.tc-inspect-filter-box.tc-inspect-filter-box-horizontal .tc-inspect-run-box > .tc-box-content,
.tc-inspect-filter-box.tc-inspect-filter-box-horizontal .tc-inspect-operation-box > .tc-box-content,
.tc-inspect-filter-box.tc-inspect-filter-box-horizontal .tc-inspect-operator-box > .tc-box-content {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: flex-start;
}
.tc-inspectable-filter-box > .tc-box-content > ol {
list-style: none;
padding-left: 0;
}
.tc-inspect-operations-wrapper {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
}
.tc-inspect-run-box {
--box-background-color: #ffffcc;
--box-foreground-color: #444400;
}
.tc-inspect-operation-box {
--box-background-color: #ccffcc;
--box-foreground-color: #004400;
}
.tc-inspect-operator-box {
--box-background-color: #ffcccc;
--box-foreground-color: #440000;
}
.tc-box.tc-inspect-input-box {
--box-background-color: #ffccff;
--box-foreground-color: #440044;
}
.tc-box.tc-inspect-output-box {
--box-background-color: #4fd3d3;
--box-foreground-color: #004444;
}

View File

@@ -0,0 +1,28 @@
title: $:/plugins/tiddlywiki/internals/filterinspection/tests/wikitext/Simple
description: Test filter inspection
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\procedure test-filter()
1 2 3 :sort[length[]add[1]]
\end
\function test-filter-wrapper()
[inspect<test-filter>]
\end
<$text text=<<test-filter>>/>
-
<$text text={{{ [<test-filter-wrapper>jsonindexes[]join[,]] }}}/>
-
<$text text={{{ [<test-filter-wrapper>jsonindexes[inputFilter]join[,]] }}}/>
-
<$text text={{{ [<test-filter-wrapper>jsonindexes[runs]join[,]] }}}/>
+
title: ExpectedResult
<p>1 2 3 :sort[length[]add[1]]-evaluationTime,input,inputFilter,output,runs--0,1,2,3</p>

View File

@@ -0,0 +1,3 @@
title: $:/plugins/tiddlywiki/internals/filterinspection/viewtemplatebody
<$transclude $variable="inspect-filter-output" jsonOutput={{!!text}} />

View File

@@ -0,0 +1,5 @@
title: $:/plugins/tiddlywiki/internals/filterinspection/cascades/viewtemplatebody
tags: $:/tags/ViewTemplateBodyFilter
list-before:
[tag[$:/tags/FilterInspectionOutput]type[application/json]then[$:/plugins/tiddlywiki/internals/filterinspection/viewtemplatebody]]

View File

@@ -0,0 +1,2 @@
title: $:/state/tab--1498284803
text: $:/core/ui/AdvancedSearch/Filter

View File

@@ -0,0 +1,2 @@
title: $:/state/tab--251342953
text: $:/plugins/tiddlywiki/internals/filterinspection/InspectResultsTab

View File

@@ -0,0 +1,2 @@
title: $:/config/FilterObservationionEnabled
text: yes

View File

@@ -0,0 +1,5 @@
title: $:/plugins/tiddlywiki/internals/testfilter
tags: $:/tags/InspectableFilter
type: text/plain
[list[$:/StoryList]]

View File

@@ -2,6 +2,6 @@
"title": "$:/plugins/tiddlywiki/internals",
"name": "Internals",
"description": "Tools for exploring the internals of TiddlyWiki",
"list": "readme",
"list": "readme docs",
"stability": "STABILITY_2_STABLE"
}

View File

@@ -1,10 +1,24 @@
title: $:/plugins/tiddlywiki/internals/readme
This plugin adds features to help explore the internals of TiddlyWiki:
This plugin adds several features to help explore the internals of TiddlyWiki, and to debug wikitext and filters.
* New preview panes showing:
** the parse tree
** the widget tree
** the raw HTML output
!! Inspecting Filter Traces
The first two include a dropdown for choosing block vs. inline parsing mode.
Filter inspection is based on a schematic visualisation that traces all the steps involved in evaluating a filter. These traces can be generated and accessed in several ways:
* Via two new tabs under the Advanced Search filter results:
** The ''Inspect'' tab shows the schematic trace for the current filter
** The ''Observe'' tab allows the current filter to be logged in the background. A new trace is generated every time the filter is evaluated, regardless of the context, if the results are different from previous evaluations
* Directly using the `<<inspect-filter>>` procedure, or the underlying `inspect[]` operator
Note that observing a filter is not the same as logging it. Observing a filter means that the filter is evaluated in the background, and a new trace is generated every time the filter is evaluated, regardless of the context. Logging a filter means that the filter is evaluated in the background, but only if the results are different from previous evaluations.
Filter observation has a performance impact, and disables certain optimisations such as caching of compiled filters.
!! Inspecting Parse Trees and Widget Trees
New preview panes for the tiddler editor that show
* the parse tree
* the widget tree
* the raw HTML output

View File

@@ -1,4 +0,0 @@
title: $:/plugins/tiddlywiki/internals/styles
tags: $:/tags/Stylesheet
\rules only filteredtranscludeinline transcludeinline macrodef macrocallinline macrocallblock

View File

@@ -3,5 +3,5 @@
"name": "Stacked View",
"description": "Stacked card storyview",
"list": "readme",
"stability": "STABILITY_2_STABLE"
"stability": "STABILITY_1_EXPERIMENTAL"
}

View File

@@ -4,5 +4,5 @@
"description": "A tour of TiddlyWiki",
"list": "readme docs settings",
"dependents": ["$:/plugins/tiddlywiki/confetti","$:/plugins/tiddlywiki/dynannotate"],
"stability": "STABILITY_2_STABLE"
"stability": "STABILITY_1_EXPERIMENTAL"
}

View File

@@ -3,5 +3,5 @@
"name": "TW5.com Docs",
"description": "Documentation from tiddlywiki.com",
"list": "readme",
"stability": "STABILITY_2_STABLE"
"stability": "STABILITY_1_EXPERIMENTAL"
}