Rob, many thanks for sharing these great functions. Very useful.
These seem to work fine:
expandTilde :: String -> FilePath
listDirectory :: FilePath -> [FilePath]
Although the output is NOT a path, it is just the name of the file or folder
It also shows hidden folders, which was surprising
readFile :: FilePath -> IO String
I’m having a bit of trouble with a few of them, perhaps you can show me the error of my ways.
I am running Yosemite 10.10.5.
function pathExistsAndisFolder(strPath)
Always returns false for ‘isFolder’
Even though it returns true for ‘exists’
and it definitely is a folder
fileType(strPath)
Always get error Error on line 85: TypeError: undefined is not an object (evaluating '$.NSWorkspace.sharedWorkspace')
on this line: $.NSWorkspace.sharedWorkspace
I ran a number of different tests on fileType(strPath), but I ended up with this simple script to demonstrate my problem:
'use strict'
var strPath = "/Users/myusername/Documents/Test"
var fileList = listDirectory(strPath);
console.log(fileList)
//--- RETURNS ---
/* .DS_Store,2014-06-17T21-27-45 sysPref Dock.txt,2014-06-17T21.27.45 sysPref Dock.png,NewFolder,NewFolder2,NewFolder3,NewFolder4,NewFolder5,RICH TEXT File.rtf,SubFolder 1,SymbolicLinker.service */
//--- PULLED FROM YOUR SCRIPT ---
// It causes an error when it calls `fileType()`
var lstMenu = listDirectory(strPath)
.filter(function (x) {
return fileType(
strPath + '/' + x
) === "com.hogbaysoftware.taskpaper.document";
});
//--- RETURNS ERROR ---
/*
Error on line 40: TypeError: undefined is not an object (evaluating '$.NSWorkspace.sharedWorkspace')
*/
// listDirectory :: FilePath -> [FilePath]
function listDirectory(strPath, fm) {
var fm = fm || $.NSFileManager.defaultManager;
return ObjC.unwrap(
fm.contentsOfDirectoryAtPathError(strPath, null))
.map(ObjC.unwrap);
}
// fileType :: FilePath -> UTI String
function fileType(strPath) {
var error = $();
return ObjC.unwrap(
$.NSWorkspace.sharedWorkspace
.typeOfFileError(strPath, error)
);
}
I also tried calling fileType() directly with a known fixed path to an existing file:
pathFull = "/Users/myusername/Documents/Test/RICH TEXT File.rtf"
fileType(pathFull)
Good questions – enterprising of you to start testing them immediately.
The thing to notice about pathExistsAndisFolder is its return type, which I think you are understandably assuming to be a simple boolean. Things to inspect: the type signature comment line preceding the function, the return section, and the point in the macro code in which (one of its) return value(s) is tested.
The fileType() dependency on the ObjC.import('AppKit') line is, of course harder to spot. Use of the $.NSWorkspace object requires that import.
Good luck !
UPDATE
I read your post too quickly on the fileExistsAndisFolder function – forgive me.
Would like to post your test code, with a sample of a path string that you are using ? Hard to comment without seeing that.
Good macro for creating template. However, I was looking for a macro to show similar kind of dialog box with all the taskpaper files in a particular folder to open it.
When I put the ObjC.import('AppKit') statement at the top of the script, then fileType() worked fine.
I have found that it is most helpful to users/readers of my scripts if I make sure to state in the function any dependencies and/or versions of software that are required.
I have also found it to improve readability to put the main script on top with the functions on bottom. In this case it would have made the ObjC.import('AppKit') statement standout and I likely would have caught that it was needed in the functions (or some of them).
That would be a bad assumption on your part.
It was clear that it returned an object, and the Results panel of Script Editor clearly showed that. So there was no confusion on my part about the return value.
Here’s my test script:
'use strict'
ObjC.import('AppKit');
var pathStr = "~/Documents/Test"
var pathFull = expandTilde(pathStr)
console.log(pathFull)
pathExistsAndisFolder(pathFull)
//~~~~~~~~~~~~~~~~~~~~~~~ END OF MAIN SCRIPT ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// expandTilde :: String -> FilePath
function expandTilde(strPath) {
return strPath.charCodeAt(0) === 126 ? ObjC.unwrap(
$(strPath)
.stringByExpandingTildeInPath
) : strPath;
}
// pathExistsAndisFolder :: String -> (Bool, Int)
function pathExistsAndisFolder(strPath) {
var ref = Ref();
return $.NSFileManager.defaultManager
.fileExistsAtPathIsDirectory(
strPath, ref) ? {
'exists': true,
'isFolder': ref[0] === 1
} : {
'exists': false
};
}
I just found this code on the Apple JXA Release Notes for 10.10, in the Explicit Pass-by-Reference section.
(sorry, link won’t work here. see at bottom)
This script works fine on Yosemite. It finds the folder (directory) that your script says is NOT a directory.
'use strict'
ObjC.import('AppKit');
var myPath = "/Users/username/Documents/Test"
testExplicitPassByReference(myPath)
//~~~~~~~~~~~~~~ END OF TEST MAIN ~~~~~~~~~~~~~~
// --- FUNCTION FROM APPLE JXA RELEASE NOTE FOR OS X 10.10 ---
// (had to correct errors: added the "var" for all the variables)
function testExplicitPassByReference(path) {
var ref = Ref()
var fm = $.NSFileManager.defaultManager
var exists = fm.fileExistsAtPathIsDirectory(path, ref)
if (exists) {
var isDirectory = ref[0]
return (path + ' is ' + (isDirectory ? '' : 'not ') + 'a directory; ')
}
else {
return (path + ' does not exist\n')
}
}
RETURNS:
Result:
"/Users/username/Documents/Test is a directory; "
I've been trying for far too long to get this script working. It's perhaps my novice level in Javascript. I'd appreciate someone to point out where things are going wrong. I'm getting a lot of Type Errors when I go to the debugger which leads me to believe that the conversion between string and Path names is the problem.
I realize this is a long time ago. I just had wanted this functionality and thought it might be a good place to learn some JXA skills. It's proven a much harder nut to crack than I thought.