183 const QCommandLineOption &qmlImportPathOption,
184 const QCommandLineOption &environmentOption,
185 const QCommandLineOption &qmlImportNoDefault)
187 QStringList importPaths;
188 if (parser.isSet(qmlImportPathOption)) {
189 const QStringList pathsFromOption =
190 QQmlToolingUtils::getAndWarnForInvalidDirsFromOption(parser, qmlImportPathOption);
191 qInfo().nospace().noquote() <<
"Using import directories passed by -I: \""
192 << pathsFromOption.join(u"\", \""_s) <<
"\".";
193 importPaths << pathsFromOption;
195 if (parser.isSet(environmentOption)) {
196 if (
const QStringList dirsFromEnv =
197 QQmlToolingUtils::getAndWarnForInvalidDirsFromEnv(u"QML_IMPORT_PATH"_s);
198 !dirsFromEnv.isEmpty()) {
199 qInfo().nospace().noquote()
200 <<
"Using import directories passed from environment variable "
201 "\"QML_IMPORT_PATH\": \""
202 << dirsFromEnv.join(u"\", \""_s) <<
"\".";
203 importPaths << dirsFromEnv;
206 if (
const QStringList dirsFromEnv2 =
207 QQmlToolingUtils::getAndWarnForInvalidDirsFromEnv(u"QML2_IMPORT_PATH"_s);
208 !dirsFromEnv2.isEmpty()) {
209 qInfo().nospace().noquote()
210 <<
"Using import directories passed from the deprecated environment variable "
211 "\"QML2_IMPORT_PATH\": \""
212 << dirsFromEnv2.join(u"\", \""_s) <<
"\".";
213 importPaths << dirsFromEnv2;
218 if (!parser.isSet(qmlImportNoDefault))
219 importPaths << QLibraryInfo::path(QLibraryInfo::QmlImportsPath);
251 int err = _setmode(_fileno(stdout), _O_BINARY);
253 perror(
"Cannot set mode for stdout");
254 err = _setmode(_fileno(stdin), _O_BINARY);
256 perror(
"Cannot set mode for stdin");
259 QHashSeed::setDeterministicGlobalSeed();
260 QCoreApplication app(argv, argc);
262 QCommandLineParser parser;
263 QQmlToolingSharedSettings settings(
"qmlls"_L1);
264 parser.setApplicationDescription(
"QML languageserver"_L1);
266 parser.addHelpOption();
267 QCommandLineOption waitOption(QStringList() <<
"w"_L1
269 "Waits the given number of seconds before startup"_L1,
271 parser.addOption(waitOption);
273 QCommandLineOption verboseOption(
274 QStringList() <<
"v"_L1
276 "Outputs extra information on the operations being performed"_L1);
277 parser.addOption(verboseOption);
279 QCommandLineOption logFileOption(QStringList() <<
"l"_L1
281 "Writes logging to the given file"_L1,
"logFile"_L1);
282 parser.addOption(logFileOption);
284 QString buildDir = QStringLiteral(u"buildDir");
285 QCommandLineOption buildDirOption(QStringList() <<
"b"_L1
287 "Adds a build dir to look up for qml information"_L1,
289 parser.addOption(buildDirOption);
290 settings.addOption(buildDir);
292 QString qmlImportPath = QStringLiteral(u"importPaths");
293 QCommandLineOption qmlImportPathOption(QStringList() <<
"I"_L1,
294 "Look for QML modules in the specified directory"_L1,
296 parser.addOption(qmlImportPathOption);
297 settings.addOption(qmlImportPath);
299 QCommandLineOption environmentOption(
300 QStringList() <<
"E"_L1,
301 "Use the QML_IMPORT_PATH environment variable to look for QML Modules"_L1);
302 parser.addOption(environmentOption);
304 QCommandLineOption writeDefaultsOption(
305 QStringList() <<
"write-defaults"_L1,
306 "Writes defaults settings to .qmlls.ini and exits (Warning: This "
307 "will overwrite any existing settings and comments!)"_L1);
308 parser.addOption(writeDefaultsOption);
310 QCommandLineOption ignoreSettings(QStringList() <<
"ignore-settings"_L1,
311 "Ignores all settings files and only takes "
312 "command line options into consideration"_L1);
313 parser.addOption(ignoreSettings);
315 QCommandLineOption noCMakeCallsOption(
316 QStringList() <<
"no-cmake-calls"_L1,
317 "Disables automatic CMake rebuilds and C++ file watching."_L1);
318 parser.addOption(noCMakeCallsOption);
319 settings.addOption(
"no-cmake-calls"_L1,
"false"_L1);
321 QCommandLineOption docDir({ {
"d"_L1,
"p"_L1,
"doc-dir"_L1 },
322 "Documentation path to use for the documentation hints feature"_L1,
325 parser.addOption(docDir);
326 settings.addOption(
"docDir"_L1);
328 QCommandLineOption qmlImportNoDefault(QStringList() <<
"bare"_L1,
329 "Do not include default import directories. "
330 "This may be used to run qmlls on a Boot2Qt project."_L1);
331 parser.addOption(qmlImportNoDefault);
332 const QString qmlImportNoDefaultSetting =
"DisableDefaultImports"_L1;
333 settings.addOption(qmlImportNoDefaultSetting,
false);
335 QCommandLineOption inputFile(QStringList() <<
"inputFile"_L1,
336 "Read from file instead of stdin"_L1,
"fileName"_L1);
337 inputFile.setFlags(QCommandLineOption::HiddenFromHelp);
338 parser.addOption(inputFile);
341 QCommandLineOption versionOption(
"version"_L1,
"Displays version information."_L1);
342 parser.addOption(versionOption);
346 if (parser.isSet(versionOption)) {
347 parser.showVersion();
351 if (parser.isSet(writeDefaultsOption)) {
352 return settings.writeDefaults() ? 0 : 1;
355 if (parser.isSet(logFileOption)) {
356 QString fileName = parser.value(logFileOption);
357 qInfo() <<
"will log to" << fileName;
358 logFile =
new QFile(fileName);
359 logFileLock =
new QMutex;
360 if (!logFile->open(QFile::WriteOnly | QFile::Truncate | QFile::Text)) {
361 qWarning(
"Failed to open file %s: %s", qPrintable(logFile->fileName()),
362 qPrintable(logFile->errorString()));
364 qInstallMessageHandler([](QtMsgType t,
const QMessageLogContext &,
const QString &msg) {
366 logFile->write(QString::number(
int(t)).toUtf8());
373 if (parser.isSet(waitOption)) {
374 int waitSeconds = parser.value(waitOption).toInt();
376 qDebug() <<
"waiting";
377 QThread::sleep(waitSeconds);
378 qDebug() <<
"starting";
381 QQmlLanguageServer qmlServer(
382 [&writeMutex](
const QByteArray &data) {
383 QMutexLocker l(&writeMutex);
384 std::cout.write(data.constData(), data.size());
387 (parser.isSet(ignoreSettings) ?
nullptr : &settings));
389 if (parser.isSet(verboseOption)) {
390 QLoggingCategory::setFilterRules(
"qt.languageserver*.debug=true\n"_L1);
391 qmlServer.codeModelManager()->setVerbose(
true);
393 if (parser.isSet(docDir))
394 qmlServer.codeModelManager()->setDocumentationRootPath(
395 QString::fromUtf8(parser.value(docDir).toUtf8()));
397 if (parser.isSet(buildDirOption)) {
398 const QStringList dirs =
399 QQmlToolingUtils::getAndWarnForInvalidDirsFromOption(parser, buildDirOption);
401 qInfo().nospace().noquote()
402 <<
"Using build directories passed by -b: \"" << dirs.join(u"\", \""_s) <<
"\".";
404 qmlServer.codeModelManager()->setBuildPathsForRootUrl(QByteArray(), dirs);
405 }
else if (QStringList dirsFromEnv =
406 QQmlToolingUtils::getAndWarnForInvalidDirsFromEnv(u"QMLLS_BUILD_DIRS"_s);
407 !dirsFromEnv.isEmpty()) {
411 qInfo().nospace().noquote() <<
"Using build directories passed from environment variable "
412 "\"QMLLS_BUILD_DIRS\": \""
413 << dirsFromEnv.join(u"\", \""_s) <<
"\".";
414 qmlServer.codeModelManager()->setBuildPathsForRootUrl(QByteArray(), dirsFromEnv);
416 qInfo() <<
"Using the build directories found in the .qmlls.ini file. Your build folder "
417 "might not be found if no .qmlls.ini files are present in the root source "
421 qmlServer.codeModelManager()->setImportPaths(
422 collectImportPaths(parser, qmlImportPathOption, environmentOption, qmlImportNoDefault));
424 const bool disableCMakeCallsViaEnvironment =
425 qmlGetConfigOption<
bool, qmlConvertBoolConfigOption>(
"QMLLS_NO_CMAKE_CALLS");
427 if (disableCMakeCallsViaEnvironment || parser.isSet(noCMakeCallsOption)) {
428 if (disableCMakeCallsViaEnvironment) {
429 qWarning() <<
"Disabling CMake calls via QMLLS_NO_CMAKE_CALLS environment variable.";
431 qWarning() <<
"Disabling CMake calls via command line switch.";
434 qmlServer.codeModelManager()->disableCMakeCalls();
436 qmlServer.codeModelManager()->tryEnableCMakeCalls();
439 auto r = parser.isSet(inputFile) && parser.value(inputFile) !=
"-"_L1
440 ? std::unique_ptr<AbstractReader>(std::make_unique<FileReader>(parser.value(inputFile)))
441 : std::unique_ptr<AbstractReader>(std::make_unique<StdinReader>());
442 QThread workerThread;
443 r->moveToThread(&workerThread);
444 QObject::connect(r.get(), &AbstractReader::receivedData, qmlServer.server(),
445 &QLanguageServer::receiveData);
446 QObject::connect(qmlServer.server(), &QLanguageServer::readNextMessage, r.get(),
447 &AbstractReader::readNextMessage);
448 auto exit = [&app, &workerThread]() {
451 QTimer::singleShot(100, &app, []() {
452 QCoreApplication::processEvents();
453 QCoreApplication::exit();
456 QObject::connect(r.get(), &StdinReader::eof, &app, exit);
457 QObject::connect(qmlServer.server(), &QLanguageServer::exit, exit);
459 emit r->readNextMessage();
460 workerThread.start();
464 return qmlServer.returnValue();