|
1 |
| -local lclient = require 'lclient'() |
2 |
| -local furi = require 'file-uri' |
3 |
| -local ws = require 'workspace' |
4 |
| -local files = require 'files' |
5 |
| -local diag = require 'provider.diagnostic' |
6 |
| -local util = require 'utility' |
7 |
| -local jsonb = require 'json-beautify' |
8 |
| -local lang = require 'language' |
9 |
| -local define = require 'proto.define' |
10 |
| -local config = require 'config.config' |
11 |
| -local fs = require 'bee.filesystem' |
12 |
| -local provider = require 'provider' |
| 1 | +local lang = require 'language' |
| 2 | +local platform = require 'bee.platform' |
| 3 | +local subprocess = require 'bee.subprocess' |
| 4 | +local json = require 'json' |
| 5 | +local jsonb = require 'json-beautify' |
| 6 | +local util = require 'utility' |
13 | 7 |
|
14 |
| -require 'plugin' |
15 |
| -require 'vm' |
16 | 8 |
|
17 |
| -lang(LOCALE) |
| 9 | +local numThreads = tonumber(NUM_THREADS or 1) |
18 | 10 |
|
19 |
| -if type(CHECK) ~= 'string' then |
20 |
| - print(lang.script('CLI_CHECK_ERROR_TYPE', type(CHECK))) |
21 |
| - return |
| 11 | +local exe = arg[-1] |
| 12 | +-- TODO: is this necessary? got it from the shell.lua helper in bee.lua tests |
| 13 | +if platform.os == 'windows' and not exe:match('%.[eE][xX][eE]$') then |
| 14 | + exe = exe..'.exe' |
22 | 15 | end
|
23 | 16 |
|
24 |
| -local rootPath = fs.absolute(fs.path(CHECK)):string() |
25 |
| -local rootUri = furi.encode(rootPath) |
26 |
| -if not rootUri then |
27 |
| - print(lang.script('CLI_CHECK_ERROR_URI', rootPath)) |
28 |
| - return |
| 17 | +local function logFileForThread(threadId) |
| 18 | + return LOGPATH .. '/check-partial-' .. threadId .. '.json' |
29 | 19 | end
|
30 |
| -rootUri = rootUri:gsub("/$", "") |
31 | 20 |
|
32 |
| -if CHECKLEVEL then |
33 |
| - if not define.DiagnosticSeverity[CHECKLEVEL] then |
34 |
| - print(lang.script('CLI_CHECK_ERROR_LEVEL', 'Error, Warning, Information, Hint')) |
35 |
| - return |
| 21 | +local function buildArgs(threadId) |
| 22 | + local args = {exe} |
| 23 | + local skipNext = false |
| 24 | + for i = 1, #arg do |
| 25 | + local arg = arg[i] |
| 26 | + -- --check needs to be transformed into --check_worker |
| 27 | + if arg:lower():match('^%-%-check$') or arg:lower():match('^%-%-check=') then |
| 28 | + args[#args + 1] = arg:gsub('%-%-%w*', '--check_worker') |
| 29 | + -- --check_out_path needs to be removed if we have more than one thread |
| 30 | + elseif arg:lower():match('%-%-check_out_path') and numThreads > 1 then |
| 31 | + if not arg:match('%-%-%w*=') then |
| 32 | + skipNext = true |
| 33 | + end |
| 34 | + else |
| 35 | + if skipNext then |
| 36 | + skipNext = false |
| 37 | + else |
| 38 | + args[#args + 1] = arg |
| 39 | + end |
| 40 | + end |
| 41 | + end |
| 42 | + args[#args + 1] = '--thread_id' |
| 43 | + args[#args + 1] = tostring(threadId) |
| 44 | + if numThreads > 1 then |
| 45 | + args[#args + 1] = '--quiet' |
| 46 | + args[#args + 1] = '--check_out_path' |
| 47 | + args[#args + 1] = logFileForThread(threadId) |
36 | 48 | end
|
| 49 | + return args |
37 | 50 | end
|
38 |
| -local checkLevel = define.DiagnosticSeverity[CHECKLEVEL] or define.DiagnosticSeverity.Warning |
39 |
| - |
40 |
| -util.enableCloseFunction() |
41 | 51 |
|
42 |
| -local lastClock = os.clock() |
43 |
| -local results = {} |
44 |
| - |
45 |
| -local function errorhandler(err) |
46 |
| - print(err) |
47 |
| - print(debug.traceback()) |
| 52 | +if numThreads > 1 then |
| 53 | + print(lang.script('CLI_CHECK_MULTIPLE_WORKERS', numThreads)) |
48 | 54 | end
|
49 | 55 |
|
50 |
| ----@async |
51 |
| -xpcall(lclient.start, errorhandler, lclient, function (client) |
52 |
| - client:registerFakers() |
53 |
| - |
54 |
| - client:initialize { |
55 |
| - rootUri = rootUri, |
56 |
| - } |
57 |
| - |
58 |
| - client:register('textDocument/publishDiagnostics', function (params) |
59 |
| - results[params.uri] = params.diagnostics |
60 |
| - end) |
61 |
| - |
62 |
| - io.write(lang.script('CLI_CHECK_INITING')) |
63 |
| - |
64 |
| - provider.updateConfig(rootUri) |
65 |
| - |
66 |
| - ws.awaitReady(rootUri) |
67 |
| - |
68 |
| - local disables = util.arrayToHash(config.get(rootUri, 'Lua.diagnostics.disable')) |
69 |
| - for name, serverity in pairs(define.DiagnosticDefaultSeverity) do |
70 |
| - serverity = config.get(rootUri, 'Lua.diagnostics.severity')[name] or 'Warning' |
71 |
| - if serverity:sub(-1) == '!' then |
72 |
| - serverity = serverity:sub(1, -2) |
73 |
| - end |
74 |
| - if define.DiagnosticSeverity[serverity] > checkLevel then |
75 |
| - disables[name] = true |
76 |
| - end |
| 56 | +local procs = {} |
| 57 | +for i = 1, numThreads do |
| 58 | + local process, err = subprocess.spawn({buildArgs(i)}) |
| 59 | + if err then |
| 60 | + print(err) |
77 | 61 | end
|
78 |
| - config.set(rootUri, 'Lua.diagnostics.disable', util.getTableKeys(disables, true)) |
79 |
| - |
80 |
| - local uris = files.getChildFiles(rootUri) |
81 |
| - local max = #uris |
82 |
| - for i, uri in ipairs(uris) do |
83 |
| - files.open(uri) |
84 |
| - diag.doDiagnostic(uri, true) |
85 |
| - -- Print regularly but always print the last entry to ensure that logs written to files don't look incomplete. |
86 |
| - if os.clock() - lastClock > 0.2 or i == #uris then |
87 |
| - lastClock = os.clock() |
88 |
| - client:update() |
89 |
| - local output = '\x0D' |
90 |
| - .. ('>'):rep(math.ceil(i / max * 20)) |
91 |
| - .. ('='):rep(20 - math.ceil(i / max * 20)) |
92 |
| - .. ' ' |
93 |
| - .. ('0'):rep(#tostring(max) - #tostring(i)) |
94 |
| - .. tostring(i) .. '/' .. tostring(max) |
95 |
| - io.write(output) |
96 |
| - local filesWithErrors = 0 |
97 |
| - local errors = 0 |
98 |
| - for _, diags in pairs(results) do |
99 |
| - filesWithErrors = filesWithErrors + 1 |
100 |
| - errors = errors + #diags |
101 |
| - end |
102 |
| - if errors > 0 then |
103 |
| - local errorDetails = ' [' .. lang.script('CLI_CHECK_PROGRESS', errors, filesWithErrors) .. ']' |
104 |
| - io.write(errorDetails) |
105 |
| - end |
106 |
| - io.flush() |
107 |
| - end |
| 62 | + if process then |
| 63 | + procs[#procs + 1] = process |
108 | 64 | end
|
109 |
| - io.write('\x0D') |
110 |
| -end) |
| 65 | +end |
111 | 66 |
|
112 |
| -local count = 0 |
113 |
| -for uri, result in pairs(results) do |
114 |
| - count = count + #result |
115 |
| - if #result == 0 then |
116 |
| - results[uri] = nil |
117 |
| - end |
| 67 | +for _, process in ipairs(procs) do |
| 68 | + process:wait() |
118 | 69 | end
|
119 | 70 |
|
120 |
| -if count == 0 then |
121 |
| - print(lang.script('CLI_CHECK_SUCCESS')) |
122 |
| -else |
123 |
| - local outpath = CHECK_OUT_PATH |
124 |
| - if outpath == nil then |
125 |
| - outpath = LOGPATH .. '/check.json' |
126 |
| - end |
127 |
| - util.saveFile(outpath, jsonb.beautify(results)) |
| 71 | +local outpath = CHECK_OUT_PATH |
| 72 | +if outpath == nil then |
| 73 | + outpath = LOGPATH .. '/check.json' |
| 74 | +end |
128 | 75 |
|
129 |
| - print(lang.script('CLI_CHECK_RESULTS', count, outpath)) |
| 76 | +if numThreads > 1 then |
| 77 | + local mergedResults = {} |
| 78 | + local count = 0 |
| 79 | + for i = 1, numThreads do |
| 80 | + local result = json.decode(util.loadFile(logFileForThread(i)) or '[]') |
| 81 | + for k, v in pairs(result) do |
| 82 | + local entries = mergedResults[k] or {} |
| 83 | + mergedResults[k] = entries |
| 84 | + for _, entry in ipairs(v) do |
| 85 | + entries[#entries + 1] = entry |
| 86 | + count = count + 1 |
| 87 | + end |
| 88 | + end |
| 89 | + end |
| 90 | + util.saveFile(outpath, jsonb.beautify(mergedResults)) |
| 91 | + if count == 0 then |
| 92 | + print(lang.script('CLI_CHECK_SUCCESS')) |
| 93 | + else |
| 94 | + print(lang.script('CLI_CHECK_RESULTS', count, outpath)) |
| 95 | + end |
130 | 96 | end
|
0 commit comments