Skip to content

Instantly share code, notes, and snippets.

@RobTrew
Last active October 3, 2022 18:43
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save RobTrew/3df0110db67282f200a3908cb0a4695e to your computer and use it in GitHub Desktop.
Save RobTrew/3df0110db67282f200a3908cb0a4695e to your computer and use it in GitHub Desktop.
Selected DEVONthink Sheet -> DEVONthink markdown record (MMD table)
((options) => {
'use strict';
// (NB JavaScript for Automation – JXA: Save as .scpt )
// Selected DEVONthink Sheet -> DEVONthink markdown record (MMD table)
// (New record containing MMD table created is same group as selection)
// Uses original fileName stem, with '.md' appended.
// Default alignment string is centered ':--:'
// (adjustable in options dictionary at foot of script)
// This is the tab formatted version
// (as in MMD Composer 'Clean up selected Table')
// For the whitespace pretty-printed version, see:
// https://gist.github.com/RobTrew/a6ef7c551f0874f6f3374e3bfbeb0b42
// Rob Trew 2017
// Ver 0.03
// Note that this copy is in ES6 JS and thus for Sierra onwards only.
// For a (Yosemite-onward) ES5 copy, paste into the Babel JS repl at
// https://babeljs.io/repl/
// GENERIC FUNCTIONS -----------------------------------------------------
// (++) :: [a] -> [a] -> [a]
const append = (xs, ys) => xs.concat(ys);
// comparing :: (a -> b) -> (a -> a -> Ordering)
const comparing = f =>
(x, y) => {
const
a = f(x),
b = f(y);
return a < b ? -1 : (a > b ? 1 : 0);
};
// concat :: [[a]] -> [a] | [String] -> String
const concat = xs =>
xs.length > 0 ? (() => {
const unit = typeof xs[0] === 'string' ? '' : [];
return unit.concat.apply(unit, xs);
})() : [];
// curry :: Function -> Function
const curry = (f, ...args) => {
const go = xs => xs.length >= f.length ? (f.apply(null, xs)) :
function () {
return go(xs.concat(Array.from(arguments)));
};
return go([].slice.call(args, 1));
};
// drop :: Int -> [a] -> [a]
// drop :: Int -> String -> String
const drop = (n, xs) => xs.slice(n);
// filter :: (a -> Bool) -> [a] -> [a]
const filter = (f, xs) => xs.filter(f);
// findIndex :: (a -> Bool) -> [a] -> Maybe Int
const findIndex = (p, xs) =>
xs.reduce((a, x, i) =>
a.nothing ? (
p(x) ? {
just: i,
nothing: false
} : a
) : a, {
nothing: true
});
// intercalate :: String -> [a] -> String
const intercalate = curry((s, xs) => xs.join(s));
// length :: [a] -> Int
const length = xs => xs.length;
// lines :: String -> [String]
const lines = s => s.split(/[\r\n]/);
// map :: (a -> b) -> [a] -> [b]
const map = (f, xs) => xs.map(f);
// maximumBy :: (a -> a -> Ordering) -> [a] -> a
const maximumBy = (f, xs) =>
xs.reduce((a, x) => a === undefined ? x : (
f(x, a) > 0 ? x : a
), undefined);
// replicate :: Int -> a -> [a]
const replicate = (n, x) =>
Array.from({
length: n
}, () => x);
// show :: Int -> a -> Indented String
// show :: a -> String
const show = (...x) =>
JSON.stringify.apply(
null, x.length > 1 ? [x[1], null, x[0]] : x
);
// splitOn :: a -> [a] -> [[a]]
// splitOn :: String -> String -> [String]
const splitOn = curry((needle, haystack) =>
typeof haystack === 'string' ? (
haystack.split(needle)
) : (function sp_(ndl, hay) {
const mbi = findIndex(x => ndl === x, hay);
return mbi.nothing ? (
[hay]
) : append(
[take(mbi.just, hay)],
sp_(ndl, drop(mbi.just + 1, hay))
);
})(needle, haystack));
// unconsMay :: [a] -> Maybe (a, [a])
const unconsMay = xs => xs.length > 0 ? {
just: [xs[0], xs.slice(1)],
nothing: false
} : {
nothing: true
};
// unlines :: [String] -> String
const unlines = xs => xs.join('\n');
// MMD TABLE -------------------------------------------------------------
// mmdTableFromTabbed :: String -> String
const mmdTableFromTabbed = curry((alignmentString, s) => {
const mbHeadTail = unconsMay(
map(splitOn('\t'), filter(x => length(x) > 0, lines(s)))
);
return mbHeadTail.nothing ? s : (() => {
const
ht = mbHeadTail.just,
t = ht[1];
return unlines(map(x => '|' + intercalate('\t|', x) + '\t|',
append(
[
ht[0],
replicate(
length(maximumBy(comparing(length), t)),
alignmentString
)
],
t
)
));
})();
});
// SELECTED DT SHEET -> NEW MD RECORD WITH SAME FILESTEM + '.md'
const
dt = Application('DEVONthink Pro'),
seln = dt.selection(),
mbRec = seln.length > 0 ? {
just: seln[0]
} : {
nothing: true
},
mbMD = mbRec.nothing ? (
mbRec
) : mbRec.just.type() === 'sheet' ? {
just: mmdTableFromTabbed(
options.alignmentString || ':--:',
mbRec.just.richText.text()
)
} : {
nothing: true,
msg: 'No DEVONthink Sheet selected'
};
return mbMD.nothing ? mbMD.msg : (
dt.createRecordWith({
name: mbRec.just.name() + '.md',
type: 'markdown',
content: mbMD.just
}, { in: dt.currentGroup()
}),
mbMD.just
);
})({
alignmentString: ':--:' // Adjust to ':---' or '---:' etc
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment