|
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