Well, three stages:
- Get the text of the selected items in the active document
- Put a copy at the top of another file
- Delete the copy in the active document
Simpler is usually better (a short-cut for copy-paste to a second file is not a moon launch but anything with a deletion stage probably needs a little bit of care and cautious testing, and perhaps itâs a way of looking at some TaskPaper scripting basics, so:
Getting the text of the selected items.
We could do this with the clipboard, but TaskPaper offers richer tools, so why not use them â the selection capture (gathering both the text and the unique ids of the selected lines) might look like this:
// SELECTED items
function readSeln(editor) {
var lstSeln = editor
.selection
.selectedItems;
return {
ids: lstSeln.map(function (x) {
return x.id;
}),
text: ItemSerializer.serializeItems(
lstSeln,
editor.outline,
ItemSerializer.TEXTMimeType
)
};
}
Putting a copy at the top of another TaskPaper 3 document
Again, we could do this with paste, or simply using Bash to prepend text to the text file, but the TaskPaper API also lets us do something more interesting in the target document:
// Make COPY of items in other doc
function addCopyOfSeln(editor, options) {
var outline = editor.outline,
lstItems = ItemSerializer.deserializeItems(
options.lines,
outline,
ItemSerializer.TEXTMimeType
);
outline.groupUndoAndChanges(function () {
var root = outline.root;
root.insertChildrenBefore(
lstItems, root.firstChild
);
});
return lstItems[0].parent !== undefined;
}
Deleting the original lines from the active file
Assuming that the selection is still active, we could just use a delete keystroke now, but TaskPaper lets us use the unique ids of the copied lines, which gives a bit more confidence that we are likely to be deleting the right material
(Using .groupUndoAndChanges()
lets us wind the deletions back with a single Undo (âZ) if we have second thoughts after running the script)
// CLEAR original items
function clearSeln(editor, options) {
var lstID = options.ids;
if (lstID && lstID.length) {
var outline = editor.outline;
outline.groupUndoAndChanges(function () {
outline.removeItems(
lstID.map(function (strID) {
return outline.getItemForID(strID);
})
)
});
return true;
}
}
Weâll then need to pull out a few generic JavaScript for Automation functions for finding the specified target file, and perhaps creating it if it doesnât yet exist:
// expandTilde :: String -> FilePath
function expandTilde(strPath) {
return strPath.charCodeAt(0) === 126 ? ObjC.unwrap(
$(strPath)
.stringByExpandingTildeInPath
) : strPath;
}
// fileExists :: String -> Bool
function fileExists(strPath) {
var fm = $.NSFileManager.defaultManager,
error = $();
fm.attributesOfItemAtPathError(
strPath,
error
);
return error.code === undefined;
}
//writeFile :: FilePath -> String -> IO ()
function writeFile(strPath, strText) {
$.NSString.alloc.initWithUTF8String(strText)
.writeToFileAtomicallyEncodingError(
strPath, false,
$.NSUTF8StringEncoding, null
);
}
And we might glue the pieces together into something like this, in which the path of the other file is specified in the targetDocPath option at the end of the script. (If no file exists at that path, the script will create one, so do check for typos in the path you give âŚ)
Just a draft, so test it carefully before you entrust your work to it !
// Move selected TaskPaper 3 items to start of another TaskPaper 3 document
// 0.03
(function (dctOptions) {
'use strict';
// TASKPAPER CONTEXT
// SELECTED items
function readSeln(editor) {
var lstSeln = editor
.selection
.selectedItems;
return {
ids: lstSeln.map(function (x) {
return x.id;
}),
text: ItemSerializer.serializeItems(
lstSeln,
editor.outline,
ItemSerializer.TEXTMimeType
)
};
}
// Make COPY of items in other doc
function addCopyOfSeln(editor, options) {
var outline = editor.outline,
lstItems = ItemSerializer.deserializeItems(
options.lines,
outline,
ItemSerializer.TEXTMimeType
);
outline.groupUndoAndChanges(function () {
var root = outline.root;
root.insertChildrenBefore(
lstItems, root.firstChild
);
});
return lstItems[0].parent !== undefined;
}
// CLEARED original items
function clearSeln(editor, options) {
var lstID = options.ids;
if (lstID && lstID.length) {
var outline = editor.outline;
outline.groupUndoAndChanges(function () {
outline.removeItems(
lstID.map(function (strID) {
return outline.getItemForID(strID);
})
)
});
return true;
}
}
// JAVASCRIPT FOR AUTOMATION CONTEXT
// Generic file system functions:
// expandTilde :: String -> FilePath
function expandTilde(strPath) {
return strPath.charCodeAt(0) === 126 ? ObjC.unwrap(
$(strPath)
.stringByExpandingTildeInPath
) : strPath;
}
// fileExists :: String -> Bool
function fileExists(strPath) {
var fm = $.NSFileManager.defaultManager,
error = $();
fm.attributesOfItemAtPathError(
strPath,
error
);
return error.code === undefined;
}
//writeFile :: FilePath -> String -> IO ()
function writeFile(strPath, strText) {
$.NSString.alloc.initWithUTF8String(strText)
.writeToFileAtomicallyEncodingError(
strPath, true,
$.NSUTF8StringEncoding, null
);
}
// MAIN
var tp3 = Application("com.hogbaysoftware.TaskPaper3"),
ds = tp3.documents,
docSource = ds.length ? ds[0] : undefined;
// If a document is open in TaskPaper 3
if (docSource) {
// 1. Anything selected ? (text and ids) ?
var strSourceDocName = docSource.name(),
dctItems = docSource.evaluate({
script: readSeln.toString(),
withOptions: dctOptions
}),
lstID = dctItems.ids;
if (lstID && lstID.length) {
// 2. Placed in target document ?
var strFullPath = expandTilde(dctOptions.targetDocPath),
dctTarget = (function () {
var lstPaths = ds.file()
.map(function (path) {
return path && path.toString();
}),
iIndex = lstPaths.indexOf(strFullPath),
blnOpen = iIndex !== -1;
return {
doc: blnOpen ? ds[iIndex] : (
fileExists(strFullPath) || writeFile(
strFullPath, '\n'
),
tp3.open(strFullPath)
),
wasOpen: blnOpen
}
})(),
docTarget = dctTarget.doc,
blnWasOpen = dctTarget.wasOpen;
// If we have found or created a target document,
// Copy the text of the items to it
if (docTarget) {
var blnPlaced = docTarget.evaluate({
script: addCopyOfSeln.toString(),
withOptions: {
lines: dctItems.text
}
});
// 3. Removed from source document ?
if (blnPlaced) {
var docMain = ds.byName(strSourceDocName);
docMain.evaluate({
script: clearSeln.toString(),
withOptions: {
ids: lstID
}
})
if (dctOptions.reClose && !dctTarget.wasOpen) {
dctTarget.doc.close({
saving: 'yes'
});
} else dctTarget.doc.save();
var a = Application.currentApplication(),
sa = (a.includeStandardAdditions = true, a),
intItems = lstID.length;
a.displayNotification(dctItems.text, {
withTitle: intItems + ' item' + (
intItems > 1 ? 's' : ''
) + ' moved to',
subtitle: dctOptions.targetDocPath
});
}
}
}
}
})({
targetDocPath: '~/Desktop/Incubate.taskpaper',
reClose: false
});