Possibly a trivial question… Is there a plugin or script for adding one or more tags to a selection of lines? I have found Rob Trew’s script for batch deletion but no counterpart for adding. Thank you in advance.
If you simply need to add plain tags like @today
, (without a value) then you could test the following draft.
( I have followed it with a newer batch tag-deletion script).
Let me know more, however, if you would like to batch-add @tag(value)
Draft script 1 of 2 (batch-adding tags)
// *ADD* ONE OR MORE TAGS TO SELECTED LINE(S)
// ver 0.01
//
// CHOOSE tag(s) from a sorted menu listing:
// 1. Any tags already found in the document
// 2. Any default tags specified at the end of this script
// Rough draft: Rob Trew Dec 2016. MIT license
// Rough draft: test before use
// Author: Rob Trew Dec 2016. MIT license
(function (dctOptions) {
'use strict';
// FT CONTEXT MAIN (1 of 2)
function fnTagSet(editor, options) {
// GENERIC FUNCTIONS
// concatMap :: (a -> [b]) -> [a] -> [b]
function concatMap(f, xs) {
return [].concat.apply([], xs.map(f));
}
// sortBy :: (a -> a -> Ordering) -> [a] -> [a]
function sortBy(f, xs) {
return xs.sort(f);
}
// nubBy :: (a -> a -> Bool) -> [a] -> [a]
function nubBy(fnEq, lst) {
var x = lst.length ? lst[0] : null;
return x ? [x].concat(
nubBy(
fnEq,
lst.slice(1)
.filter(function (y) {
return !fnEq(x, y);
})
)
) : [];
}
// Tags in document (sorted, unique)
var rs = editor.selectedRanges();
return {
allTags: sortBy(function (a, b) {
return a < b ? -1 : (a > b ? 1 : 0);
}, nubBy(function (x, y) {
return x === y;
}, options.defaultTags
.concat(concatMap(function (n) {
return Object.keys(n.tags());
}, editor.tree()
.nodes())))),
nodeIDs: concatMap(function (r) {
return concatMap(function (n) {
return n.id;
}, r.nodesInRange());
}, editor.selectedRanges())
}
}
// FT CONTEXT MAIN (2 of 2)
function fnDocUpdated(editor, options) {
var lstAdd = options.tagsChosen,
dctAdded = {};
if (lstAdd.length > 0) {
var tree = editor.tree();
tree.beginUpdates();
options.nodeIDs.forEach(function (id) {
var oNode = tree.nodeForID(id);
lstAdd.forEach(function (s) {
if (!oNode.hasTag(s)) {
oNode.addTag(s);
dctAdded[s] = (dctAdded[s] || 0) + 1;
}
});
});
tree.endUpdates();
}
return dctAdded;
}
// JAVASCRIPT FOR AUTOMATION CONTEXT
var ds = Application("FoldingText")
.documents,
d = ds.length ? ds[0] : undefined,
dctTags = d ? d.evaluate({
script: fnTagSet.toString(),
withOptions: dctOptions
}) : [],
lstTags = dctTags.allTags || {},
lngTags = lstTags.length;
if (lngTags > 0) {
if (lngTags > 1) {
var a = Application.currentApplication(),
sa = (a.includeStandardAdditions = true, a);
sa.activate();
// OFFER CHOICE OF TAGS TO ADD (IF MULTIPLE)
var varChoice = sa.chooseFromList(lstTags, {
withTitle: "Add tags to selected lines",
withPrompt: 'Tag(s) to add:',
defaultItems: lstTags[0],
okButtonName: 'OK',
cancelButtonName: 'Cancel',
multipleSelectionsAllowed: true,
emptySelectionAllowed: true
}),
lstAdd = varChoice ? varChoice : [],
lngAdd = lstAdd.length;
} else {
var lngAdd = 1,
lstAdd = lstTags;
}
// ADD ANY CHOSEN TAGS TO SELECTED LINES
if (lngAdd > 0) {
return d.evaluate({
script: fnDocUpdated.toString(),
withOptions: {
tagsChosen: lstAdd,
nodeIDs: dctTags.nodeIDs
}
});
}
}
})({
defaultTags: ['due', 'alert', 'today']
});
Draft script 2 of 2: batch-deleting tags
// *DELETE* ONE OR MORE TAGS FROM SELECTED LINE(S)
// ver 0.20
// If there is more than one tag,
// choose tag(s) to clear from a menu
// Rough draft: test before use
// Author: Rob Trew Dec 2016. MIT license
(function () {
'use strict';
// FT CONTEXT MAIN (1 of 2)
function fnTagSet(editor, options) {
// GENERIC FUNCTIONS
// concatMap :: (a -> [b]) -> [a] -> [b]
function concatMap(f, xs) {
return [].concat.apply([], xs.map(f));
}
// sortBy :: (a -> a -> Ordering) -> [a] -> [a]
function sortBy(f, xs) {
return xs.sort(f);
}
// nubBy :: (a -> a -> Bool) -> [a] -> [a]
function nubBy(fnEq, lst) {
var x = lst.length ? lst[0] : null;
return x ? [x].concat(
nubBy(
fnEq,
lst.slice(1)
.filter(function (y) {
return !fnEq(x, y);
})
)
) : [];
}
// Tags in selected nodes (sorted, unique)
var rs = editor.selectedRanges();
return {
tags: sortBy(function (a, b) {
return a < b ? -1 : (a > b ? 1 : 0);
}, nubBy(function (x, y) {
return x === y;
}, concatMap(function (r) {
return concatMap(function (n) {
return Object.keys(n.tags());
}, r.nodesInRange());
}, rs))),
nodeIDs: concatMap(function (r) {
return concatMap(function (n) {
return n.id;
}, r.nodesInRange());
}, rs)
}
}
// FT CONTEXT MAIN (2 of 2)
function fnDocUpdated(editor, options) {
var lstClear = options.tagsChosen,
dctCleared = {};
if (lstClear.length > 0) {
var tree = editor.tree();
tree.beginUpdates();
options.nodeIDs.forEach(function (id) {
var oNode = tree.nodeForID(id);
lstClear.forEach(function (s) {
if (oNode.hasTag(s)) {
oNode.removeTag(s);
dctCleared[s] = (dctCleared[s] || 0) +
1;
}
});
});
tree.endUpdates();
}
return dctCleared;
}
// JAVASCRIPT FOR AUTOMATION CONTEXT
var ds = Application("FoldingText")
.documents,
d = ds.length ? ds[0] : undefined,
dctSeln = d ? d.evaluate({
script: fnTagSet.toString(),
}) : [],
lstTags = dctSeln.tags || {},
lngTags = lstTags.length;
if (lngTags > 0) {
if (lngTags > 1) {
var a = Application.currentApplication(),
sa = (a.includeStandardAdditions = true, a);
sa.activate();
// OFFER CHOICE OF TAGS TO CLEAR (IF MULTIPLE)
var varChoice = sa.chooseFromList(lstTags, {
withTitle: "Clear tags from selected lines",
withPrompt: 'Tag(s) to clear:',
defaultItems: lstTags[0],
okButtonName: 'OK',
cancelButtonName: 'Cancel',
multipleSelectionsAllowed: true,
emptySelectionAllowed: true
}),
lstClear = varChoice ? varChoice : [],
lngClear = lstClear.length;
} else {
var lngClear = 1,
lstClear = lstTags;
}
//return [lngClear, lstClear];
// CLEAR ANY SELECTED TAGS
if (lngClear > 0) {
return d.evaluate({
script: fnDocUpdated.toString(),
withOptions: {
tagsChosen: lstClear,
nodeIDs: dctSeln.nodeIDs
}
});
}
}
})();
Very generous of you, @complexpoint, and customarily excellent. Thank you! This will do — I don’t need value tags right now.