This may be by design (and may not be relevant to ongoing Bike 2 work) but I wondered what the row type intention is when we:
- Select an empty existing line (of some row type) in Bike 1, and
- paste from a
com.hogbaysoftware.bike.xml
NSPasteBoard
It seems that:
- the clipboard type of the first pasted row is ignored, and
- the type of empty selection line is retained (if that type is not
"body"
)
To summarise, the XML "data-type"
of the first incoming line is preserved if
- we use the
osascript
interface’sdocument.import{from: XMLString as:"bike format"}
, or - simply open the XML file
but put aside (in favour of the existing type of the selected empty row) if we paste.
Test code (just copies XML to com.hogbaysoftware.bike.xml
NSPasteBoard – try pasting to an empty row, with its own type, in a Bike document)
Test :: expand disclosure triangle to view JS source
(() => {
"use strict";
ObjC.import("AppKit");
// ---------------------- MAIN -----------------------
const main = () => {
const xml = bikeFromForest(
// All imported rows have type "heading"
// for this test.
// All rows but the first seem to preserve that
// type on pasting to a selected empty row
// with a different type.
() => "data-type=\"heading\""
)(
x => x.root
)(
forest()
);
return (
copyTypedString(true)(
"com.hogbaysoftware.bike.xml"
)(
xml
),
"XML Copied to `com.hogbaysoftware.bike.xml` NSPasteBoard."
);
};
// bikeFromForest :: (a -> String) -> (a -> String) ->
// Forest a -> XML String
const bikeFromForest = fAttribs =>
// A com.hogbaysoftware.bike.xml encoding of a
// Forest of Any (a) values, given two (a -> String):
// 1. A function returning a [k=v] attribute(s)
// string for an <li> tag
// 2. A function returning contents for a <p> tag.
fInline => trees => {
const d = " ";
const go = indent => x => {
const
xs = nest(x),
dent = d + indent,
ddent = d + dent,
attribs = fAttribs(x),
liTag = 0 < attribs.length
? `<li ${attribs}>`
: "<li>";
return [
indent + liTag,
`${dent}<p>${fInline(x)}</p>`,
...0 < xs.length
? [
`${dent}<ul>`,
...xs.flatMap(go(ddent)),
`${dent}</ul>`
]
: [],
`${indent}</li>`
];
};
return [
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
"<html xmlns=\"http://www.w3.org/1999/xhtml\">",
" <head>",
" <meta charset=\"utf-8\"/>",
" </head>",
" <body>",
" <ul>",
trees.flatMap(
go(d.repeat(3))
)
.join("\n"),
" </ul>",
" </body>",
"</html>"
]
.join("\n");
};
// Test sample
const forest = () => [
Node("Alpha")([
Node("Beta")([
Node("Gamma")([]),
Node("Delta")([]),
Node("Epsilon")([])
]),
Node("Zeta")([Node("Eta")([])]),
Node("Theta")([
Node("Iota")([]),
Node("Kappa")([]),
Node("Lambda")([])
])
])
];
// ----------------------- JXA -----------------------
// copyTypedString :: Bool -> String -> String -> IO ()
const copyTypedString = blnClear =>
// public.html, public.rtf, public.utf8-plain-text
pbType => s => {
const pb = $.NSPasteboard.generalPasteboard;
return (
blnClear && pb.clearContents,
pb.setStringForType(
$(s),
$(pbType)
)
);
};
// --------------------- GENERIC ---------------------
// Node :: a -> [Tree a] -> Tree a
const Node = v =>
// Constructor for a Tree node which connects a
// value of some kind to a list of zero or
// more child trees.
xs => ({
root: v,
nest: xs || []
});
// nest :: Tree a -> [a]
const nest = tree => {
// Allowing for lazy (on-demand) evaluation.
// If the nest turns out to be a function –
// rather than a list – that function is applied
// here to the root, and returns a list.
const xs = tree.nest;
return "function" !== typeof xs
? xs
: xs(root(tree));
};
// root :: Tree a -> a
const root = tree =>
// The value attached to a tree node.
tree.root;
return main();
})();