@naanlang/naan 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +21 -0
- package/README.md +46 -0
- package/dist/env_web.js +431 -0
- package/dist/naan.min.js +73 -0
- package/frameworks/browser/browser.nlg +30 -0
- package/frameworks/browser/pouchdb/pouchdb.min.js +7 -0
- package/frameworks/browser/spark-md5/spark-md5.min.js +1 -0
- package/frameworks/browser/sworker.js +192 -0
- package/frameworks/browser/terminals.nlg +787 -0
- package/frameworks/browser/workers.nlg +386 -0
- package/frameworks/client/apiclient.nlg +183 -0
- package/frameworks/client/client.nlg +29 -0
- package/frameworks/client/psm_client.nlg +572 -0
- package/frameworks/common/common.nlg +464 -0
- package/frameworks/common/preferences.nlg +131 -0
- package/frameworks/common/repltools.nlg +100 -0
- package/frameworks/common/watching.nlg +97 -0
- package/frameworks/node/apiserver.nlg +399 -0
- package/frameworks/node/filesystem.nlg +1012 -0
- package/frameworks/node/gitter.nlg +234 -0
- package/frameworks/node/node.nlg +81 -0
- package/frameworks/node/psm_server.nlg +465 -0
- package/frameworks/node/worker.nlg +560 -0
- package/frameworks/project/build.nlg +1229 -0
- package/frameworks/project/jszip.js +30 -0
- package/frameworks/project/jszip.nlg +87 -0
- package/frameworks/project/proj_cliser.nlg +139 -0
- package/frameworks/project/proj_console.nlg +137 -0
- package/frameworks/project/proj_folder.nlg +143 -0
- package/frameworks/project/proj_lambda.nlg +137 -0
- package/frameworks/project/proj_static.nlg +138 -0
- package/frameworks/project/project.nlg +29 -0
- package/frameworks/project/projects.nlg +587 -0
- package/frameworks/project/uglifyjs.js +1 -0
- package/frameworks/project/uglifyjs.nlg +79 -0
- package/frameworks/running/debugcom.nlg +163 -0
- package/frameworks/running/debugnub.nlg +565 -0
- package/frameworks/running/debugutil.nlg +510 -0
- package/frameworks/running/executors.nlg +767 -0
- package/frameworks/running/running.nlg +151 -0
- package/frameworks/running/sourcecode.nlg +346 -0
- package/frameworks/running/taskexec.nlg +196 -0
- package/frameworks/storage/dbt_pouch.nlg +773 -0
- package/frameworks/storage/file_manager.nlg +772 -0
- package/frameworks/storage/psm.nlg +326 -0
- package/frameworks/storage/psm_dbtables.nlg +814 -0
- package/frameworks/storage/resources.nlg +445 -0
- package/frameworks/storage/storage.nlg +52 -0
- package/lib/browser/env_web.js +431 -0
- package/lib/browser/env_webworker.js +146 -0
- package/lib/browser/require.js +1653 -0
- package/lib/core/naanlib.js +73 -0
- package/lib/env_node.js +282 -0
- package/lib/env_nodeworker.js +165 -0
- package/lib/node_repl.js +25 -0
- package/lib/node_repl_init.nlg +52 -0
- package/package.json +13 -0
- package/plugins/gitClient/clientops.nlg +317 -0
- package/plugins/gitClient/gitClient.nlg +63 -0
- package/plugins/gitClient/gitutils.nlg +1331 -0
- package/plugins/gitClient/minimatch.js +945 -0
- package/plugins/gitClient/minimatch_license.txt +15 -0
- package/plugins/gitClient/minimatch_readme.md +208 -0
- package/plugins/nodeLib/nodeLib.nlg +26 -0
- package/plugins/nodeLib/node_cp.nlg +65 -0
- package/plugins/nodeLib/node_fs.nlg +137 -0
- package/plugins/nodeLib/node_https.nlg +172 -0
- package/plugins/nodeLib/node_util.nlg +180 -0
- package/plugins/serviceAws/aws/aws-sdk.min.js +88 -0
- package/plugins/serviceAws/aws/lambda_rest_index.js +122 -0
- package/plugins/serviceAws/aws/lambda_rest_init.nlg +58 -0
- package/plugins/serviceAws/aws/lambda_ws_index.js +498 -0
- package/plugins/serviceAws/aws/lambda_ws_init.nlg +49 -0
- package/plugins/serviceAws/aws_cloudwatchlogs.nlg +122 -0
- package/plugins/serviceAws/aws_cognito.nlg +76 -0
- package/plugins/serviceAws/aws_dynamo.nlg +509 -0
- package/plugins/serviceAws/aws_lambda.nlg +95 -0
- package/plugins/serviceAws/aws_s3.nlg +134 -0
- package/plugins/serviceAws/aws_utils.nlg +86 -0
- package/plugins/serviceAws/dbt_aws.nlg +769 -0
- package/plugins/serviceAws/lambda_naan_control.nlg +175 -0
- package/plugins/serviceAws/psm_aws.nlg +258 -0
- package/plugins/serviceAws/serviceAws.nlg +38 -0
- package/plugins/serviceGitHub/gitops.nlg +522 -0
- package/plugins/serviceGitHub/psm_github.nlg +205 -0
- package/plugins/serviceGitHub/serviceGitHub.nlg +65 -0
- package/plugins/serviceGitLab/gitops.nlg +528 -0
- package/plugins/serviceGitLab/psm_gitlab.nlg +205 -0
- package/plugins/serviceGitLab/serviceGitLab.nlg +65 -0
- package/test/harness.nlg +312 -0
- package/test/node_test.js +33 -0
- package/test/test_01_core.nlg +768 -0
- package/test/test_02_context.nlg +269 -0
- package/test/test_03_jsinterop.nlg +137 -0
- package/test/test_04_numerics.nlg +450 -0
- package/test/test_05_strings.nlg +63 -0
- package/test/test_06_lingoparse.nlg +500 -0
- package/test/test_07_lingo.nlg +623 -0
|
@@ -0,0 +1,772 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* file_manager.nlg
|
|
3
|
+
* frameworks/storage
|
|
4
|
+
*
|
|
5
|
+
* File manager for working with contents of a folder.
|
|
6
|
+
*
|
|
7
|
+
* column positioning: // // !
|
|
8
|
+
*
|
|
9
|
+
* Copyright (c) 2020-2021 by Richard C. Zulch
|
|
10
|
+
*
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
/*
|
|
15
|
+
* FileManager
|
|
16
|
+
*
|
|
17
|
+
* The file manager provides an interface between the a storage client and the contents of an
|
|
18
|
+
* underlying folder, ensuring a consistent view of the file state with integrated caching and source
|
|
19
|
+
* control operations, and high-level functions for searching, indexing (one day), and managing the
|
|
20
|
+
* contents of the folder. The return value is the file manager object.
|
|
21
|
+
*
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
closure FileManager(fs, options, local files, watch, opqueue, willgit, hadgit) {
|
|
25
|
+
files = new(object, this)
|
|
26
|
+
files.fs = fs
|
|
27
|
+
files.nodes = [] // array of nodes, one per file or folder
|
|
28
|
+
files.stores = {} // file storage sessions
|
|
29
|
+
files.loadPending = new(nonce) // delay callers while working
|
|
30
|
+
watch = commonWatching.Watchable()
|
|
31
|
+
files.watch = watch.watch // watch for file changes
|
|
32
|
+
files.unWatch = watch.unwatch // stop watching for file changes
|
|
33
|
+
|
|
34
|
+
// compareNodes
|
|
35
|
+
//
|
|
36
|
+
// Compare path objects for sorting.
|
|
37
|
+
//
|
|
38
|
+
function compareNodes(a, b) {
|
|
39
|
+
if a.path < b.path
|
|
40
|
+
return (-1)
|
|
41
|
+
else if a.path > b.path
|
|
42
|
+
return (1)
|
|
43
|
+
0
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// pathIndex
|
|
47
|
+
//
|
|
48
|
+
// Find the specified path in the nodes list using a binary search on the sorted nodes, returning
|
|
49
|
+
// the index of the node or false.
|
|
50
|
+
//
|
|
51
|
+
function pathIndex(sought, local upper, lower, z) {
|
|
52
|
+
upper = files.nodes.length
|
|
53
|
+
if upper == 0
|
|
54
|
+
return (false) // not found
|
|
55
|
+
lower = z = 1
|
|
56
|
+
loop {
|
|
57
|
+
z = (upper + lower) / 2
|
|
58
|
+
if sought <= files.nodes[z-1].path
|
|
59
|
+
upper = z - 1
|
|
60
|
+
if sought >= files.nodes[z-1].path
|
|
61
|
+
lower = z + 1
|
|
62
|
+
if upper < lower
|
|
63
|
+
break
|
|
64
|
+
}
|
|
65
|
+
if lower - upper > 1
|
|
66
|
+
return (z - 1)
|
|
67
|
+
false
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// flatten
|
|
71
|
+
//
|
|
72
|
+
// Flatten the specified tree into an array of path objects. This accomodates the possibility
|
|
73
|
+
// that a node name is empty. In compliance with DBtables filesystem mapping, this uses pathsep
|
|
74
|
+
// as the pathname for an empty directory. So "a" is a file in the root, "/a" is a file in the
|
|
75
|
+
// empty-named directory in the root, and "a//b" is in the empty directory inside of "a", etc.
|
|
76
|
+
//
|
|
77
|
+
function flatten(tree, local nodes, pathsep) {
|
|
78
|
+
nodes = []
|
|
79
|
+
if !tree
|
|
80
|
+
return(nodes) // no tree to enumerate
|
|
81
|
+
pathsep = tree.data.pathsep
|
|
82
|
+
function addPaths(parent, node, local item, items, child) { // add nodes for tree children, if any
|
|
83
|
+
if parent == "" { // either root or empty directory
|
|
84
|
+
if node.name == ""
|
|
85
|
+
parent = pathsep // node in empty-named directory
|
|
86
|
+
else
|
|
87
|
+
parent = node.name } // node in root
|
|
88
|
+
else if parent == pathsep
|
|
89
|
+
parent = parent.concat(node.name) // child of empty directory
|
|
90
|
+
else if parent
|
|
91
|
+
parent = parent.concat(pathsep, node.name) // deeper than root
|
|
92
|
+
else
|
|
93
|
+
parent = "" // root itself
|
|
94
|
+
item = {
|
|
95
|
+
path: parent
|
|
96
|
+
name: node.name
|
|
97
|
+
}
|
|
98
|
+
if node.deleted
|
|
99
|
+
item.deleted = node.deleted // copy the deleted flag
|
|
100
|
+
nodes.push(item)
|
|
101
|
+
if !node.children
|
|
102
|
+
return (item)
|
|
103
|
+
items = [] // children
|
|
104
|
+
if array(node.children) { // e.g. "symlink" if not array
|
|
105
|
+
for child in node.children
|
|
106
|
+
if child.name.charAt(0) != "." { // ### should check hidden flag
|
|
107
|
+
child = addPaths(parent, child) // child becomes new node/subtree
|
|
108
|
+
if child
|
|
109
|
+
items.push(child) }
|
|
110
|
+
}
|
|
111
|
+
item.children = items.sort(compareNodes.proc)
|
|
112
|
+
item
|
|
113
|
+
} (false, tree)
|
|
114
|
+
nodes
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// difftree
|
|
118
|
+
//
|
|
119
|
+
// Update our nodes array with a new tree, returning the differences.
|
|
120
|
+
//
|
|
121
|
+
function difftree(updated, local original, added, deleted, changed, odex, udex, store) {
|
|
122
|
+
original = files.nodes
|
|
123
|
+
added = []
|
|
124
|
+
deleted = []
|
|
125
|
+
changed = []
|
|
126
|
+
updated = updated.sort(compareNodes.proc)
|
|
127
|
+
odex = udex = 0
|
|
128
|
+
while odex < original.length || udex < updated.length {
|
|
129
|
+
if odex >= original.length || original[odex].path > updated[udex].path
|
|
130
|
+
added.push(updated[udex++])
|
|
131
|
+
else if udex >= updated.length || original[odex].path < updated[udex].path {
|
|
132
|
+
store = files.stores[original[odex].path]
|
|
133
|
+
if store
|
|
134
|
+
store.deleted = true
|
|
135
|
+
deleted.push(original[odex++])
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
if original[odex].meta
|
|
139
|
+
updated[udex].meta = original[odex].meta // meta copied over
|
|
140
|
+
if original[odex].gitstat
|
|
141
|
+
updated[udex].gitstat = original[odex].gitstat // gitstat copied over
|
|
142
|
+
if original[odex].info != updated[udex].info // info is change items to compare
|
|
143
|
+
changed.push(original[odex])
|
|
144
|
+
++odex
|
|
145
|
+
++udex
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
files.nodes = updated
|
|
149
|
+
if added.length > 0 || deleted.length > 0 {
|
|
150
|
+
{ // return changes dictionary
|
|
151
|
+
added: added
|
|
152
|
+
deleted: deleted
|
|
153
|
+
changed: changed
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// parentPath
|
|
159
|
+
//
|
|
160
|
+
// Return the parent path of the specified node, or "" for the root.
|
|
161
|
+
//
|
|
162
|
+
function parentPath(node) {
|
|
163
|
+
path = node.path
|
|
164
|
+
if path.slice(-1) == files.pathsep
|
|
165
|
+
path = path.slice(0,-1)
|
|
166
|
+
path.split(files.pathsep).slice(0,-1).join(files.pathsep)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// opBeginOrReschedule
|
|
170
|
+
//
|
|
171
|
+
// Begin an asynchronous operation if we're idle, or schedule it to be performed when we're
|
|
172
|
+
// done with the current operation.
|
|
173
|
+
//
|
|
174
|
+
function opBeginOrReschedule(newproc) {
|
|
175
|
+
|
|
176
|
+
function opExecute(proc, local pending, result) {
|
|
177
|
+
files.loadPending.reset()
|
|
178
|
+
pending = new(nonce)
|
|
179
|
+
call(proc, function(error, data) {
|
|
180
|
+
pending.signal(list(error, data))
|
|
181
|
+
})
|
|
182
|
+
result = pending.wait()
|
|
183
|
+
files.loadPending.signal(result)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if opqueue
|
|
187
|
+
opqueue.push(newproc) // already have a runner
|
|
188
|
+
else {
|
|
189
|
+
opqueue = [newproc]
|
|
190
|
+
future(function() { // schedule a runner
|
|
191
|
+
while opqueue.length > 0 {
|
|
192
|
+
opExecute(opqueue.0)
|
|
193
|
+
opqueue.shift()
|
|
194
|
+
}
|
|
195
|
+
opqueue = false
|
|
196
|
+
}).run()
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// refreshDir
|
|
201
|
+
//
|
|
202
|
+
// Asynchronously refresh the specified directory, enumerating its children and updating our
|
|
203
|
+
// records. This emits change notifications to our watchers.
|
|
204
|
+
//
|
|
205
|
+
closure refreshDir(parpath) {
|
|
206
|
+
opBeginOrReschedule(closure(callback) {
|
|
207
|
+
files.fs.util.treeDirUpdate("", parpath, files.tree, function(error, changes, local updated) {
|
|
208
|
+
if error { // need better error reporting
|
|
209
|
+
ErrorDebuglog("FileManager.refreshDir failed", error)
|
|
210
|
+
return (callback(error))
|
|
211
|
+
}
|
|
212
|
+
updated = flatten(files.tree)
|
|
213
|
+
changes = difftree(updated)
|
|
214
|
+
if changes
|
|
215
|
+
watch.notify("folder", changes)
|
|
216
|
+
callback(false, files.nodes)
|
|
217
|
+
})
|
|
218
|
+
})
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// load
|
|
222
|
+
//
|
|
223
|
+
// Initialize our knowledge of the project by loading a tree of the files and folders. This can
|
|
224
|
+
// require some time so it takes place asynchronously once started.
|
|
225
|
+
//
|
|
226
|
+
files.load = closure load() {
|
|
227
|
+
update()
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// update
|
|
231
|
+
//
|
|
232
|
+
// Update our project storage tree and tell the watchers about any changes.
|
|
233
|
+
//
|
|
234
|
+
files.update = closure update() {
|
|
235
|
+
opBeginOrReschedule(closure(callback) {
|
|
236
|
+
files.fs.tree("", false, function (error, tree, local updated, changes) {
|
|
237
|
+
if error { // need better error reporting
|
|
238
|
+
ErrorDebuglog("FileManager.update failed", error)
|
|
239
|
+
return (callback(error))
|
|
240
|
+
}
|
|
241
|
+
files.tree = tree
|
|
242
|
+
files.pathsep = tree.data.pathsep
|
|
243
|
+
updated = flatten(tree)
|
|
244
|
+
changes = difftree(updated)
|
|
245
|
+
if changes
|
|
246
|
+
watch.notify("folder", changes)
|
|
247
|
+
schedGitRefresh() // schedule the initial git update
|
|
248
|
+
callback(false, files.nodes)
|
|
249
|
+
})
|
|
250
|
+
})
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// fileList
|
|
254
|
+
//
|
|
255
|
+
// Return an array of files in the project by local pathname. The first element is the root and
|
|
256
|
+
// will have a children array for tree-structured traversal.
|
|
257
|
+
// Each path object has:
|
|
258
|
+
// {
|
|
259
|
+
// path: -- relative pathname string
|
|
260
|
+
// name: -- just our node name
|
|
261
|
+
// [children:] -- array of child path objects, if a directory
|
|
262
|
+
// }
|
|
263
|
+
//
|
|
264
|
+
files.fileList = function fileList() {
|
|
265
|
+
files.nodes
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// waitFileList
|
|
269
|
+
//
|
|
270
|
+
// Wait until the filelist is loaded, and then callback or return.
|
|
271
|
+
//
|
|
272
|
+
files.waitFileList = closure waitFileList(callback) {
|
|
273
|
+
if callback
|
|
274
|
+
future(function() { apply(callback, files.loadPending.wait()) }).run()
|
|
275
|
+
else
|
|
276
|
+
files.loadPending.wait()
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// findConfig
|
|
280
|
+
//
|
|
281
|
+
// Make a find configuration dictionary defining a search pattern and parameters. This can be
|
|
282
|
+
// duplicated or modified freely.
|
|
283
|
+
//
|
|
284
|
+
files.findConfig = function findConfig() {
|
|
285
|
+
{ // ### these should come from files.fs (?)
|
|
286
|
+
pattern: ""
|
|
287
|
+
fixed: true
|
|
288
|
+
ignoreCase: true
|
|
289
|
+
returnText: true
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// findRegex
|
|
294
|
+
//
|
|
295
|
+
// Return a RegExp for the specified findConfig, or false if invalid.
|
|
296
|
+
//
|
|
297
|
+
files.findRegex = function findRegex(config, global, local pattern, flags, matchRE) { // ### move to files.fs (?)
|
|
298
|
+
pattern = config.pattern
|
|
299
|
+
flags = ""
|
|
300
|
+
if config.fixed
|
|
301
|
+
pattern = pattern.replace(RegExp("\\.|\\0|\\\\|\\[|\\||\\(|\\)|\\*|\\+|\\?|\\{", "g"), function(match) {
|
|
302
|
+
"\\".concat(match) // escape all special RegExp characters
|
|
303
|
+
})
|
|
304
|
+
if global
|
|
305
|
+
flags = "g"
|
|
306
|
+
if config.ignoreCase
|
|
307
|
+
flags = flags.concat("i")
|
|
308
|
+
try {
|
|
309
|
+
matchRE = RegExp(pattern, flags)
|
|
310
|
+
} catch {
|
|
311
|
+
if true {
|
|
312
|
+
debuglog("files.findRegex: invalid pattern", pattern)
|
|
313
|
+
return (false)
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
matchRE
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// findLines
|
|
320
|
+
//
|
|
321
|
+
// Find matching lines for the specified findConfig. This returns a standard result tuple, which
|
|
322
|
+
// on success will provide an output dictionary listing the matching files and lines. The output
|
|
323
|
+
// dictionary is structured as follows:
|
|
324
|
+
// {
|
|
325
|
+
// config: <findConfig> -- the configuration we used for the search
|
|
326
|
+
// files: [ -- array of files having results
|
|
327
|
+
// { node: <local> -- file node
|
|
328
|
+
// lines: [] } ] -- array of integer line numbers in that file, zero-based
|
|
329
|
+
// }
|
|
330
|
+
// What we really want is indexed search, but since there isn't time for that right now this will
|
|
331
|
+
// use grepLines or dirSearch if they are available, and otherwise read the files and apply the
|
|
332
|
+
// find pattern to each line.
|
|
333
|
+
//
|
|
334
|
+
files.findLines = closure findLines(config, local startMS, output, fileCount, foundFileCount, lineCount) {
|
|
335
|
+
|
|
336
|
+
// processGrep
|
|
337
|
+
// Apply the specified search method to our directory tree.
|
|
338
|
+
function processGrep(method, local curfile, curlines, error, line, lines) {
|
|
339
|
+
fileCount = files.nodes.length
|
|
340
|
+
`(error, lines) = method("", config)
|
|
341
|
+
if error
|
|
342
|
+
debuglog("search failed:", ErrorString(error))
|
|
343
|
+
else {
|
|
344
|
+
function startFile(path) {
|
|
345
|
+
if path {
|
|
346
|
+
curfile = files.nodes[pathIndex(path)]
|
|
347
|
+
if !curfile
|
|
348
|
+
debuglog("files.findLines: unknown path", path) }
|
|
349
|
+
else
|
|
350
|
+
curfile = false
|
|
351
|
+
curlines = []
|
|
352
|
+
}
|
|
353
|
+
function doneFile() {
|
|
354
|
+
if curfile && curlines.length > 0 {
|
|
355
|
+
output.files.push({
|
|
356
|
+
node: curfile
|
|
357
|
+
lines: curlines
|
|
358
|
+
})
|
|
359
|
+
++foundFileCount }
|
|
360
|
+
}
|
|
361
|
+
startFile()
|
|
362
|
+
for line in lines {
|
|
363
|
+
++lineCount
|
|
364
|
+
if line.0 != curfile.path {
|
|
365
|
+
doneFile()
|
|
366
|
+
startFile(line.0)
|
|
367
|
+
}
|
|
368
|
+
curlines.push([line.1, line.2])
|
|
369
|
+
}
|
|
370
|
+
doneFile() }
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
startMS = milliseconds()
|
|
374
|
+
output = {
|
|
375
|
+
config: config
|
|
376
|
+
files: []
|
|
377
|
+
}
|
|
378
|
+
fileCount = 0
|
|
379
|
+
foundFileCount = 0
|
|
380
|
+
lineCount = 0
|
|
381
|
+
|
|
382
|
+
if files.fs.grepLines
|
|
383
|
+
processGrep(files.fs.grepLines)
|
|
384
|
+
else if files.fs.dirSearch
|
|
385
|
+
processGrep(files.fs.dirSearch)
|
|
386
|
+
else {
|
|
387
|
+
asyncArray(files.nodes, 5, closure(node, index, elements, local error, lines) {
|
|
388
|
+
++fileCount
|
|
389
|
+
`(error, lines) = files.fs.readLines(node, config)
|
|
390
|
+
if error
|
|
391
|
+
debuglog("cannot search", node, ErrorString(error))
|
|
392
|
+
else if lines.length > 0 {
|
|
393
|
+
++foundFileCount
|
|
394
|
+
output.files.push({
|
|
395
|
+
node: node
|
|
396
|
+
lines: lines
|
|
397
|
+
})
|
|
398
|
+
lineCount += lines.length
|
|
399
|
+
}
|
|
400
|
+
}).wait()
|
|
401
|
+
}
|
|
402
|
+
output.elapsedMS = milliseconds()-startMS // elapsed time in milliseconds
|
|
403
|
+
output.foundCount = lineCount // number of lines containing the pattern
|
|
404
|
+
output.foundFileCount = foundFileCount // number of files containing the pattern
|
|
405
|
+
output
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// gitRefresh
|
|
409
|
+
//
|
|
410
|
+
// Refresh the git status of the project folder, and return a list of the changes.
|
|
411
|
+
//
|
|
412
|
+
files.gitRefresh = closure gitRefresh() {
|
|
413
|
+
if !files.fs.gitStatus
|
|
414
|
+
return // no git checking available
|
|
415
|
+
opBeginOrReschedule(closure(callback) {
|
|
416
|
+
willgit = true
|
|
417
|
+
files.fs.gitStatus(false, function (error, statlist, local changed, delgit, unknownFiles, unknownFileStatus, changes, classID) {
|
|
418
|
+
if error {
|
|
419
|
+
files.gitvalid = false
|
|
420
|
+
return (callback(error))
|
|
421
|
+
}
|
|
422
|
+
files.gitvalid = true
|
|
423
|
+
|
|
424
|
+
// setParentStatus
|
|
425
|
+
// Set the git status of the parents of a node. This is not used to clear status.
|
|
426
|
+
|
|
427
|
+
function setParentStatus(node, local parpath, parnode) {
|
|
428
|
+
parpath = parentPath(node)
|
|
429
|
+
if parpath != "" {
|
|
430
|
+
hadgit[parpath] = 1
|
|
431
|
+
delgit[parpath] = undefined
|
|
432
|
+
parnode = files.nodes[pathIndex(parpath)]
|
|
433
|
+
if parnode.gitstat != node.gitstat && parnode.gitstat != "**" {
|
|
434
|
+
if !parnode.gitstat
|
|
435
|
+
parnode.gitstat = node.gitstat // directory status becomes child status
|
|
436
|
+
else
|
|
437
|
+
parnode.gitstat = "**" // directory has diverse child status
|
|
438
|
+
changed.push(parnode) }
|
|
439
|
+
setParentStatus(parnode) } } // must visit all parents each time to ensure hadgit/delgit are complete
|
|
440
|
+
|
|
441
|
+
// setStatus
|
|
442
|
+
// Set or clear the status of a node.
|
|
443
|
+
|
|
444
|
+
function setStatus(path, status, local node) {
|
|
445
|
+
if (node = files.nodes[pathIndex(path)]) {
|
|
446
|
+
if node.gitstat != status {
|
|
447
|
+
node.gitstat = status
|
|
448
|
+
changed.push(node) }
|
|
449
|
+
if status // if no status then parent will be set directly
|
|
450
|
+
setParentStatus(node) } // always visit all parents
|
|
451
|
+
else {
|
|
452
|
+
unknownFiles.push(path)
|
|
453
|
+
unknownFileStatus[path] = status }
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
changed = []
|
|
457
|
+
unknownFiles = []
|
|
458
|
+
unknownFileStatus = {}
|
|
459
|
+
delgit = hadgit
|
|
460
|
+
hadgit = {}
|
|
461
|
+
willgit = false
|
|
462
|
+
for item in statlist {
|
|
463
|
+
hadgit[item.path] = 1
|
|
464
|
+
delgit[item.path] = undefined
|
|
465
|
+
setStatus(item.path, item.status)
|
|
466
|
+
}
|
|
467
|
+
if unknownFiles.length > 0 { // add any deleted files that git knows about
|
|
468
|
+
files.fs.util.treeAddFiles(unknownFiles, files.tree)
|
|
469
|
+
changes = difftree(flatten(files.tree))
|
|
470
|
+
for item in changes.added { // update node and parent status
|
|
471
|
+
item.gitstat = unknownFileStatus[item.path]
|
|
472
|
+
hadgit[item.path] = 1
|
|
473
|
+
delgit[item.path] = undefined
|
|
474
|
+
setParentStatus(item) }
|
|
475
|
+
}
|
|
476
|
+
if changes
|
|
477
|
+
classID = "folder"
|
|
478
|
+
else {
|
|
479
|
+
classID = "git" // status up date only
|
|
480
|
+
changes = { }
|
|
481
|
+
}
|
|
482
|
+
for item in delgit {
|
|
483
|
+
if item.deleted { // file that previously had deleted status is now gone
|
|
484
|
+
if !changes.deleted
|
|
485
|
+
changes.deleted = []
|
|
486
|
+
changes.deleted.push(item) } // not actually deleted at this time, but filtered in UI
|
|
487
|
+
else
|
|
488
|
+
setStatus(item, false) }
|
|
489
|
+
if changed.length > 0
|
|
490
|
+
changes.changed = changed
|
|
491
|
+
if changes.added.length > 0 || changes.changed.length > 0
|
|
492
|
+
watch.notify(classID, changes)
|
|
493
|
+
callback(false, files.nodes)
|
|
494
|
+
})
|
|
495
|
+
})
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// schedGitRefresh
|
|
499
|
+
//
|
|
500
|
+
// Schedule a git refresh. This ensures we only do one refresh per time interval.
|
|
501
|
+
//
|
|
502
|
+
files.schedGitRefresh = function schedGitRefresh() {
|
|
503
|
+
if !willgit {
|
|
504
|
+
willgit = true
|
|
505
|
+
future(gitRefresh, 1000) // one scheduled refresh per 1000 msec
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// path2node
|
|
510
|
+
//
|
|
511
|
+
// Return the node for a path, or false if not in the tree.
|
|
512
|
+
//
|
|
513
|
+
files.path2node = function path2node(path) {
|
|
514
|
+
files.nodes[pathIndex(path)]
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// delete
|
|
518
|
+
//
|
|
519
|
+
// Delete a file or directory.
|
|
520
|
+
//
|
|
521
|
+
files.delete = function delete(node, local parpath, options, result) {
|
|
522
|
+
parpath = parentPath(node)
|
|
523
|
+
if node.children
|
|
524
|
+
options = { recursive: true }
|
|
525
|
+
result = files.fs.delete(node.path, options)
|
|
526
|
+
refreshDir(parpath)
|
|
527
|
+
result
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// duplicate
|
|
531
|
+
//
|
|
532
|
+
// Duplicate a file or directory.
|
|
533
|
+
//
|
|
534
|
+
files.duplicate = function duplicate(node, local parpath, result) {
|
|
535
|
+
parpath = parentPath(node)
|
|
536
|
+
result = files.fs.util.duplicate(node.path)
|
|
537
|
+
refreshDir(parpath)
|
|
538
|
+
result
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// newFile
|
|
542
|
+
//
|
|
543
|
+
// Create a new empty file with an appropriate default name.
|
|
544
|
+
//
|
|
545
|
+
files.newFile = function newFile(node, local parpath, result) {
|
|
546
|
+
if node.children
|
|
547
|
+
parpath = node.path // create file in selected folder
|
|
548
|
+
else
|
|
549
|
+
parpath = parentPath(node) // create file in same folder as selected file
|
|
550
|
+
result = files.fs.util.newFile(parpath)
|
|
551
|
+
refreshDir(parpath)
|
|
552
|
+
result
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// newFolder
|
|
556
|
+
//
|
|
557
|
+
// Create a new directory with an appropriate default name.
|
|
558
|
+
//
|
|
559
|
+
files.newFolder = function newFolder(node, local parpath, result) {
|
|
560
|
+
if node.children
|
|
561
|
+
parpath = node.path // create folder in selected folder
|
|
562
|
+
else
|
|
563
|
+
parpath = parentPath(node) // create folder in same folder as selected file
|
|
564
|
+
result = files.fs.util.newFolder(parpath)
|
|
565
|
+
refreshDir(parpath)
|
|
566
|
+
result
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// rename
|
|
570
|
+
//
|
|
571
|
+
// Rename a file or directory to the new name.
|
|
572
|
+
//
|
|
573
|
+
files.rename = function rename(node, newname, local parpath, result) {
|
|
574
|
+
parpath = parentPath(node)
|
|
575
|
+
result = files.fs.copy(node.path, newname, { move: true })
|
|
576
|
+
refreshDir(parpath)
|
|
577
|
+
result
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// reveal
|
|
581
|
+
//
|
|
582
|
+
// Reveal a file or directory in the host shell.
|
|
583
|
+
//
|
|
584
|
+
files.reveal = function reveal(node) {
|
|
585
|
+
files.fs.reveal(node.path)
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// store
|
|
589
|
+
//
|
|
590
|
+
// Return a fimaStore object for the specified file, creating it if needed.
|
|
591
|
+
//
|
|
592
|
+
files.store = function store(node) {
|
|
593
|
+
if files.stores[node.path]
|
|
594
|
+
return
|
|
595
|
+
files.stores[node.path] = fimaStore(files, path2node(node.path))
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// stored
|
|
599
|
+
//
|
|
600
|
+
// Return a fimaStore object for the specified file if it already exists.
|
|
601
|
+
//
|
|
602
|
+
files.stored = function stored(node) {
|
|
603
|
+
files.stores[node.path]
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
// annotate
|
|
607
|
+
//
|
|
608
|
+
// Annotate a node with an editor annotations array.
|
|
609
|
+
//
|
|
610
|
+
files.annotate = function annotate(path, annotations, local node) {
|
|
611
|
+
node = path2node(path)
|
|
612
|
+
if node {
|
|
613
|
+
node.annotations = annotations
|
|
614
|
+
watch.notify("annotations", { changed: [node] })
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// clearAnnotations
|
|
619
|
+
//
|
|
620
|
+
// Clear editor annotations from all nodes.
|
|
621
|
+
//
|
|
622
|
+
files.clearAnnotations = function clearAnnotations(local changes, node) {
|
|
623
|
+
changes = []
|
|
624
|
+
for node in files.nodes
|
|
625
|
+
if node.annotations {
|
|
626
|
+
node.annotations = false
|
|
627
|
+
changes.push(node)
|
|
628
|
+
}
|
|
629
|
+
if changes.length > 0
|
|
630
|
+
watch.notify("annotations", { changed: changes })
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// finis
|
|
634
|
+
|
|
635
|
+
files
|
|
636
|
+
};
|
|
637
|
+
|
|
638
|
+
|
|
639
|
+
/*
|
|
640
|
+
* fimaStore
|
|
641
|
+
*
|
|
642
|
+
* A fimaStore object is responsible for storage services for a particular file being edited.
|
|
643
|
+
* This includes reading and writing the contents to and from the edit buffer, updating on changes or
|
|
644
|
+
* determining that a conflict exists between changes in storage and editor, etc. A fimaStore should
|
|
645
|
+
* be referenced independent of file location, so that someday it will be possible to accommodate
|
|
646
|
+
* renames and path changes without interrupting the editing.
|
|
647
|
+
*
|
|
648
|
+
*/
|
|
649
|
+
|
|
650
|
+
closure fimaStore(fm, node, local store) {
|
|
651
|
+
store = new(object, this)
|
|
652
|
+
store.fm = fm
|
|
653
|
+
store.fs = fm.fs
|
|
654
|
+
store.uuid = UUID()
|
|
655
|
+
store.name = node.name
|
|
656
|
+
store.path = node.path
|
|
657
|
+
store.deleted = node.deleted
|
|
658
|
+
|
|
659
|
+
// hashData
|
|
660
|
+
//
|
|
661
|
+
// Hash data of unknown type.
|
|
662
|
+
//
|
|
663
|
+
function hashData(data, local hash) {
|
|
664
|
+
try {
|
|
665
|
+
hash = HashMD5(data)
|
|
666
|
+
} catch {
|
|
667
|
+
if true
|
|
668
|
+
hash = 0x5fa5 // this data is not hashable
|
|
669
|
+
}
|
|
670
|
+
hash
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
// node
|
|
674
|
+
//
|
|
675
|
+
// Return the store's node.
|
|
676
|
+
|
|
677
|
+
store.node = function getnode() {
|
|
678
|
+
node
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// read
|
|
682
|
+
//
|
|
683
|
+
// Read contents of a file into a string, updating our hash record of the stored version.
|
|
684
|
+
//
|
|
685
|
+
store.read = closure read(options, callback) {
|
|
686
|
+
store.fs.readFile(store.path, options, function(error, data, local content) {
|
|
687
|
+
if !error {
|
|
688
|
+
store.storageHash = hashData(data)
|
|
689
|
+
content = { hash: store.storageHash }
|
|
690
|
+
if string(data)
|
|
691
|
+
content.text = data
|
|
692
|
+
else
|
|
693
|
+
content.binary = data }
|
|
694
|
+
callback(error, content)
|
|
695
|
+
})
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
// write
|
|
699
|
+
//
|
|
700
|
+
// Write a file with contents of a string, updating our hash record of the stored version.
|
|
701
|
+
//
|
|
702
|
+
store.write = closure write(data, options, callback) {
|
|
703
|
+
if !options
|
|
704
|
+
options = { }
|
|
705
|
+
options.md5 = store.storageHash
|
|
706
|
+
store.fs.writeFile(store.path, data, options, function(error, result) {
|
|
707
|
+
if !error
|
|
708
|
+
store.storageHash = result = hashData(data) // ### get this from result?
|
|
709
|
+
fm.schedGitRefresh()
|
|
710
|
+
callback(error, result)
|
|
711
|
+
})
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
// exists
|
|
715
|
+
//
|
|
716
|
+
// True iff the file still exists in the tree.
|
|
717
|
+
//
|
|
718
|
+
store.exists = function exists() {
|
|
719
|
+
store.fm.path2node(store.path)
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// gitLoadFileHead
|
|
723
|
+
//
|
|
724
|
+
// Get the contents of the file at the git Head, or report an error. This
|
|
725
|
+
//
|
|
726
|
+
store.gitLoadFileHead = closure gitLoadFileHead(callback) {
|
|
727
|
+
store.fs.gitLog(store.path, function (error, loglist, local data, content) {
|
|
728
|
+
if error && store.fm.gitvalid
|
|
729
|
+
callback(Error("gitLoadFileHead failed", error))
|
|
730
|
+
else if !(loglist.length >= 1)
|
|
731
|
+
callback(false, { }) // no revisions to report
|
|
732
|
+
else {
|
|
733
|
+
`(error, data) = store.fs.gitShowFile(store.path, loglist.0.hash)
|
|
734
|
+
if error
|
|
735
|
+
callback(Error("gitLoadFileHead couldn't retrieve revision", loglist.0.hash, error))
|
|
736
|
+
else {
|
|
737
|
+
store.storageHashRight = hashData(data)
|
|
738
|
+
content = { hash: store.storageHashRight }
|
|
739
|
+
if string(data)
|
|
740
|
+
content.text = data
|
|
741
|
+
else
|
|
742
|
+
content.binary = data
|
|
743
|
+
callback(false, content) }
|
|
744
|
+
}
|
|
745
|
+
})
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
// finis
|
|
749
|
+
|
|
750
|
+
store
|
|
751
|
+
};
|
|
752
|
+
|
|
753
|
+
|
|
754
|
+
/*
|
|
755
|
+
* fimaInit
|
|
756
|
+
*
|
|
757
|
+
* Initialize the file manager component.
|
|
758
|
+
*
|
|
759
|
+
*/
|
|
760
|
+
|
|
761
|
+
function fimaInit(local manifest) {
|
|
762
|
+
|
|
763
|
+
manifest = `(FileManager, fimaStore, fimaInit)
|
|
764
|
+
|
|
765
|
+
Naan.module.build(module.id, "fileManager", function(modobj, compobj) {
|
|
766
|
+
require("./storage.nlg")
|
|
767
|
+
commonWatching = require("../common/watching.nlg")
|
|
768
|
+
compobj.manifest = manifest
|
|
769
|
+
module.exports.FileManager = FileManager
|
|
770
|
+
})
|
|
771
|
+
|
|
772
|
+
}();
|