Files
lms-fe/src/main/remoteModuleFiles.ts
T

70 lines
3.1 KiB
TypeScript

type Request = { action: 'list' } | { action: 'content'; path: string } | { action: 'search'; query: string }
export function remoteModuleFilesScript(root: string, request: Request): string {
const input = Buffer.from(JSON.stringify({ root, ...request })).toString('base64')
return `python3 - '${input}' <<'PY'
import base64, json, os, stat, sys
args = json.loads(base64.b64decode(sys.argv[1]))
root = os.path.realpath(args['root'])
skip = {'.git', 'build', 'install', 'log', 'node_modules', 'docker'}
extensions = {'.c', '.cc', '.cpp', '.cxx', '.h', '.hpp', '.hh', '.py', '.xml', '.urdf', '.xacro', '.yaml', '.yml', '.json', '.txt', '.md', '.cmake', '.launch', '.sh', '.bash', '.toml', '.ini', '.cfg', '.rviz'}
placeholder = 'Binary or large file preview is not available.'
def content(relative):
if os.path.isabs(relative) or '..' in relative.split('/'):
raise ValueError('Invalid module path')
path = os.path.realpath(os.path.join(root, relative))
if os.path.commonpath([root, path]) != root:
raise ValueError('Invalid module path')
info = os.stat(path)
if not stat.S_ISREG(info.st_mode):
raise ValueError('Not a regular file')
if os.path.splitext(path)[1].lower() not in extensions or info.st_size > 1048576:
return placeholder
with open(path, 'rb') as fp:
data = fp.read(1048577)
if len(data) > 1048576 or b'\\x00' in data:
return placeholder
return data.decode('utf-8', errors='replace')
def entries():
def fail(error):
raise error
for current, dirs, files in os.walk(root, onerror=fail, followlinks=False):
depth = len(os.path.relpath(current, root).split('/')) if current != root else 0
dirs[:] = sorted(d for d in dirs if d not in skip and not os.path.islink(os.path.join(current, d)))
if depth >= 10:
dirs[:] = []
continue
for name in dirs:
yield 'D', os.path.relpath(os.path.join(current, name), root)
for name in sorted(files):
path = os.path.join(current, name)
if not os.path.islink(path) and os.path.isfile(path):
yield 'F', os.path.relpath(path, root)
if args['action'] == 'content':
print(json.dumps({'content': content(args['path'])}))
elif args['action'] == 'list':
paths, folders = [], []
for kind, path in entries():
(folders if kind == 'D' else paths).append(path)
print(json.dumps({'filePaths': paths, 'folders': folders}))
else:
query = args['query'].strip().lower()
results = []
if len(query) >= 2:
for kind, path in entries():
if kind != 'F':
continue
text = content(path)
if text == placeholder:
continue
for number, line in enumerate(text.split('\\n'), 1):
if query in line.lower():
results.append({'file': path, 'lineNumber': number, 'lineText': (line.strip() or line)[:2000]})
if len(results) >= 200:
break
if len(results) >= 200:
break
print(json.dumps(results))
PY`
}