1 /***************************************************************************
2 main.cpp - description
3 -------------------
4 begin : Die Apr 23 22:16:35 CEST 2002
5 copyright : (C) 2002-2021 by Andre Simon
6 email : a.simon@mailbox.org
7
8 Highlight is a universal source code to formatted text converter.
9 Syntax highlighting is formatted by Cascading Style Sheets.
10 It's possible to easily enhance highlight's parsing database.
11
12 ***************************************************************************/
13
14
15 /*
16 This file is part of Highlight.
17
18 Highlight is free software: you can redistribute it and/or modify
19 it under the terms of the GNU General Public License as published by
20 the Free Software Foundation, either version 3 of the License, or
21 (at your option) any later version.
22
23 Highlight is distributed in the hope that it will be useful,
24 but WITHOUT ANY WARRANTY; without even the implied warranty of
25 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 GNU General Public License for more details.
27
28 You should have received a copy of the GNU General Public License
29 along with Highlight. If not, see <http://www.gnu.org/licenses/>.
30 */
31
32 #include <memory>
33 #include <algorithm>
34 #include <Diluculum/LuaState.hpp>
35
36 #include "main.h"
37 #include "datadir.h"
38 #include "syntaxreader.h"
39 #include "lspprofile.h"
40
41 #define MAX_LINE__WIDTH 80
42
43 using namespace std;
44
printVersionInfo(bool quietMode)45 void HLCmdLineApp::printVersionInfo(bool quietMode)
46 {
47 if (quietMode) {
48 cout << highlight::Info::getVersion() << "\n";
49 } else {
50 cout << "\n highlight version "
51 << highlight::Info::getVersion()
52 << "\n Copyright (C) 2002-2021 Andre Simon <a dot simon at mailbox.org>"
53 << "\n\n Argparser class"
54 << "\n Copyright (C) 2006-2008 Antonio Diaz Diaz <ant_diaz at teleline.es>"
55 << "\n\n Artistic Style Classes (3.1 rev. 672)"
56 << "\n Copyright (C) 2006-2018 by Jim Pattee <jimp03 at email.com>"
57 << "\n Copyright (C) 1998-2002 by Tal Davidson"
58 << "\n\n Diluculum Lua wrapper (1.0)"
59 << "\n Copyright (C) 2005-2013 by Leandro Motta Barros"
60 << "\n\n xterm 256 color matching functions"
61 << "\n Copyright (C) 2006 Wolfgang Frisch <wf at frexx.de>"
62 << "\n\n PicoJSON library"
63 << "\n Copyright (C) 2009-2010 Cybozu Labs, Inc."
64 << "\n Copyright (C) 2011-2014 Kazuho Oku"
65 << "\n\n This software is released under the terms of the GNU General "
66 << "Public License."
67 << "\n For more information about these matters, see the file named "
68 << "COPYING.\n\n";
69 }
70 }
71
printBadInstallationInfo()72 void HLCmdLineApp::printBadInstallationInfo()
73 {
74 cerr << "highlight: Data directory not found ("<<DataDir::LSB_DATA_DIR<<")."
75 " Bad installation or wrong "<< OPT_DATADIR << " parameter."
76 << "\n\nCopy the highlight files into one of the directories listed "
77 << "in INSTALL.\nYou may also set the data directory with "
78 << OPT_DATADIR << ".\n";
79 }
80
printInstalledFiles(const string & where,const string & wildcard,const string & kind,const string & option,const string & categoryFilterList)81 int HLCmdLineApp::printInstalledFiles(const string& where, const string& wildcard, const string& kind, const string& option, const string& categoryFilterList)
82 {
83 vector <string> filePaths;
84 string searchDir = where + wildcard;
85
86 bool directoryOK = Platform::getDirectoryEntries ( filePaths, searchDir, true );
87 if ( !directoryOK ) {
88 cerr << "highlight: Could not access directory "
89 << searchDir
90 << ", aborted.\n";
91 return EXIT_FAILURE;
92 }
93
94 sort ( filePaths.begin(), filePaths.end() );
95 string suffix, desc;
96 Diluculum::LuaValueMap categoryMap;
97 cout << "\nInstalled " << kind << "s";
98
99 if (categoryFilterList.size())
100 cout << " matching \""<<categoryFilterList<<"\"";
101
102 cout << " (located in " << where << "):\n\n";
103 int matchedFileCnt=0;
104 std::set<string> categoryNames;
105 std::set<string> categoryFilters;
106
107 istringstream valueStream;
108 string catFilter;
109 valueStream.str ( StringTools::change_case ( categoryFilterList,StringTools::CASE_LOWER ) );
110 while ( getline ( valueStream, catFilter, ';' ) ) {
111 categoryFilters.insert ( catFilter );
112 }
113
114 for (const auto &path : filePaths ) {
115 try {
116 Diluculum::LuaState ls;
117 highlight::SyntaxReader::initLuaState(ls, path,"");
118 ls.doFile(path);
119 desc = ls["Description"].value().asString();
120
121 suffix = ( path ).substr ( where.length() ) ;
122 suffix = suffix.substr ( 0, suffix.length()- wildcard.length()+1 );
123
124 unsigned int filterOKCnt=categoryFilters.size();
125 if (ls["Categories"].value() !=Diluculum::Nil){
126 filterOKCnt=0;
127
128 categoryMap = ls["Categories"].value().asTable();
129
130 for (Diluculum::LuaValueMap::const_iterator it = categoryMap.begin(); it != categoryMap.end(); ++it)
131 {
132 categoryNames.insert(it->second.asString());
133 if (categoryFilters.size() && categoryFilters.count(it->second.asString())) {
134 ++filterOKCnt;
135 }
136 }
137 }
138
139 if (filterOKCnt!=categoryFilters.size() && categoryFilters.size() ) continue;
140
141 matchedFileCnt++;
142 if (kind=="langDef") {
143 cout << setw ( 30 ) <<setiosflags ( ios::left ) <<desc<<": "<<suffix;
144
145 int extCnt=0;
146 for (StringMap::iterator it=dataDir.assocByExtension.begin(); it!=dataDir.assocByExtension.end(); it++) {
147 if (it->second==suffix ) {
148 cout << ((++extCnt==1)?" ( ":" ")<<it->first;
149 }
150 }
151 cout << ((extCnt)?" )":"");
152 } else {
153 cout << setw ( 30 ) <<setiosflags ( ios::left ) <<suffix<<": "<<desc;
154
155 }
156 cout << endl;
157 } catch (std::runtime_error &error) {
158 cout << "Failed to read '" << path<< "': " << error.what() << endl;
159 }
160 }
161
162 if (!matchedFileCnt) {
163 cout <<"No files found." << endl;
164 } else {
165
166 if (!categoryFilters.size()){
167 cout << "\nFound "<<kind<<" categories:\n\n";
168 for (std::set<string>::iterator it=categoryNames.begin(); it!=categoryNames.end(); ++it)
169 std::cout << *it<< ' ';
170 cout << "\n";
171 }
172
173 cout <<"\nUse name of the desired "<<kind
174 << " with --" << option << ". Filter categories with --list-cat." << endl;
175
176 if (kind=="theme") {
177 cout <<"\nAdd base16/ prefix to apply a Base16 theme." << endl;
178 }
179
180 printConfigInfo();
181 }
182
183 return EXIT_SUCCESS;
184 }
185
printDebugInfo(const highlight::SyntaxReader * lang,const string & langDefPath,int level)186 void HLCmdLineApp::printDebugInfo ( const highlight::SyntaxReader *lang,
187 const string & langDefPath, int level )
188 {
189 if (!lang) return;
190
191 map <int, string> HLStateMap;
192
193 cerr << "\nLoading language definition:\n" << langDefPath;
194 cerr << "\n\nDescription: " << lang->getDescription();
195 if (level>1) {
196 Diluculum::LuaState* luaState=lang->getLuaState();
197 if (luaState) {
198 cerr << "\n\nLUA GLOBALS:\n" ;
199 Diluculum::LuaValueMap::iterator it;
200 Diluculum::LuaValueMap glob =luaState->globals();
201 string elemName;
202 for(it = glob.begin(); it != glob.end(); it++) {
203 Diluculum::LuaValue first = it->first;
204 Diluculum::LuaValue second = it->second;
205 elemName = first.asString();
206 std::cerr << elemName<<": ";
207 switch (second.type()) {
208 case LUA_TSTRING:
209 cerr << "string [ "<<second.asString()<<" ]";
210 break;
211 case LUA_TNUMBER:
212 cerr << "number [ "<<second.asNumber()<<" ]";
213 if (elemName.find("HL_")==0 && elemName.find("HL_FORMAT")==string::npos)
214 HLStateMap[second.asNumber()] = elemName;
215 break;
216 case LUA_TBOOLEAN:
217 cerr << "boolean [ "<<second.asBoolean()<<" ]";
218 break;
219 default:
220 cerr << second.typeName();
221 }
222 cerr << endl;
223 }
224
225 }
226
227 highlight::RegexElement *re=NULL;
228 for ( unsigned int i=0; i<lang->getRegexElements().size(); i++ )
229 {
230 if (i==0)
231 cerr << "\nREGEX:\n";
232
233 re = lang->getRegexElements() [i];
234 cerr << "State "<<re->open<< " ("<< HLStateMap[re->open]<< "):\t"<<re->pattern <<"\n";
235 }
236
237 highlight::KeywordMap::iterator it;
238 highlight::KeywordMap keys=lang->getKeywords();
239 for ( it=keys.begin(); it!=keys.end(); it++ ) {
240 if (it==keys.begin())
241 cerr << "\nKEYWORDS:\n";
242
243 cerr << " "<< it->first << "("<< it->second << ")";
244 }
245 }
246 cerr <<"\n\n";
247 }
248
printConfigInfo()249 void HLCmdLineApp::printConfigInfo ( )
250 {
251 cout << "\nConfig file search directories:\n";
252 dataDir.printConfigPaths();
253 cout << "\nFiletype config file:\n"<<dataDir.getFiletypesConfPath ( "filetypes" ) <<"\n";
254 cout << endl;
255 #ifdef HL_DATA_DIR
256 cout << "Compiler directive HL_DATA_DIR = " <<HL_DATA_DIR<< "\n";
257 #endif
258 #ifdef HL_CONFIG_DIR
259 cout << "Compiler directive HL_CONFIG_DIR = " <<HL_CONFIG_DIR<< "\n";
260 #endif
261 cout << endl;
262 }
263
getNumDigits(int i)264 int HLCmdLineApp::getNumDigits ( int i )
265 {
266 int res=0;
267 while ( i ) {
268 i/=10;
269 ++res;
270 }
271 return res;
272 }
273
printProgressBar(int total,int count)274 void HLCmdLineApp::printProgressBar ( int total, int count )
275 {
276 if ( !total ) return;
277 int p=100*count / total;
278 int numProgressItems=p/10;
279 cout << "\r[";
280 for ( int i=0; i<10; i++ ) {
281 cout << ( ( i<numProgressItems ) ?"#":" " );
282 }
283 cout<< "] " <<setw ( 3 ) <<p<<"%, "<<count << " / " << total << " " <<flush;
284 if ( p==100 ) {
285 cout << endl;
286 }
287 }
288
printCurrentAction(const string & outfilePath,int total,int count,int countWidth)289 void HLCmdLineApp::printCurrentAction ( const string&outfilePath,
290 int total, int count, int countWidth )
291 {
292 cout << "Writing file "
293 << setw ( countWidth ) << count
294 << " of "
295 << total
296 << ": "
297 << outfilePath
298 << "\n";
299 }
300
printIOErrorReport(unsigned int numberErrorFiles,vector<string> & fileList,const string & action,const string & streamName)301 void HLCmdLineApp::printIOErrorReport ( unsigned int numberErrorFiles,
302 vector<string> & fileList,
303 const string &action, const string &streamName )
304 {
305 cerr << "highlight: Could not "
306 << action
307 << " file"
308 << ( ( numberErrorFiles>1 ) ?"s":"" ) <<":\n";
309
310 if (numberErrorFiles==1 && fileList[0].size()==0){
311 cerr<<streamName<<"\n";
312 }
313 else {
314 copy ( fileList.begin(), fileList.end(), ostream_iterator<string> ( cerr, "\n" ) );
315 }
316
317 if ( fileList.size() < numberErrorFiles ) {
318 cerr << "... ["
319 << ( numberErrorFiles - fileList.size() )
320 << " of "
321 << numberErrorFiles
322 << " failures not shown, use --"
323 << OPT_VERBOSE
324 << " switch to print all failures]\n";
325 }
326 }
327
collectPluginPaths(const vector<string> & plugins)328 vector <string> HLCmdLineApp::collectPluginPaths(const vector<string>& plugins)
329 {
330 vector<string> absolutePaths;
331 for (unsigned int i=0; i<plugins.size(); i++) {
332 if (Platform::fileExists(plugins[i])) {
333 absolutePaths.push_back(plugins[i]);
334 } else {
335 absolutePaths.push_back(dataDir.getPluginPath(plugins[i]+".lua"));
336 }
337 }
338 return absolutePaths;
339 }
340
run(const int argc,const char * argv[])341 int HLCmdLineApp::run ( const int argc, const char*argv[] )
342 {
343 CmdLineOptions options ( argc, argv );
344
345 // set data directory path, where /langDefs and /themes reside
346 string dataDirPath = ( options.getDataDir().empty() ) ? Platform::getAppPath() :options.getDataDir();
347
348 if ( options.printVersion() ) {
349 printVersionInfo(options.quietMode());
350 return EXIT_SUCCESS;
351 }
352
353 dataDir.initSearchDirectories ( dataDirPath );
354
355 if ( options.printHelp() ) {
356 Help::printHelp(options.getHelpTopic());
357 return EXIT_SUCCESS;
358 }
359
360 if ( options.printConfigInfo() ) {
361 printConfigInfo();
362 return EXIT_SUCCESS;
363 }
364
365 //call before printInstalledLanguages!
366 dataDir.loadFileTypeConfig ( "filetypes" );
367
368 // set CLI options; if profle is defined read from lsp.conf
369 std::string lsProfile(options.getLsProfile());
370 std::string lsExecutable(options.getLsExecutable()); ///< server executable path
371 std::string lsSyntax(options.getLsSyntax()); ///< language definition which can be enhanced using the LS
372 int lsDelay=options.getLsDelay();
373 std::vector<std::string> lsOptions = options.getLSOptions(); ///< server executable start options
374
375 if (lsProfile.size()) {
376 dataDir.loadLSPConfig("lsp");
377 if (dataDir.profileExists(lsProfile)) {
378 highlight::LSPProfile profile = dataDir.getProfile(lsProfile);
379 if (lsExecutable.empty())
380 lsExecutable = profile.executable;
381 if (lsSyntax.empty())
382 lsSyntax = profile.syntax;
383 if (lsOptions.empty())
384 lsOptions = profile.options;
385 if (lsDelay==0)
386 lsDelay = profile.delay;
387 } else {
388 cerr << "highlight: Unknown LSP profile '"<< lsProfile << "'.\n";
389 return EXIT_FAILURE;
390 }
391 }
392
393 string scriptKind=options.getListScriptKind();
394 if (scriptKind.length()) {
395 if ( scriptKind.find("theme")==0 ) {
396 return printInstalledFiles(dataDir.getThemePath(""), "*.theme", "theme", OPT_STYLE, options.getCategories());
397 }
398 else if ( scriptKind.find("plug")==0 ) {
399 return printInstalledFiles(dataDir.getPluginPath(""), "*.lua", "plug-in", OPT_PLUGIN, options.getCategories());
400 }
401 else if ( scriptKind.find("lang")==0 ) {
402 return printInstalledFiles(dataDir.getLangPath(""), "*.lang", "langDef", OPT_SYNTAX, options.getCategories());
403 } else {
404 cerr << "highlight: Unknown script type '"<< scriptKind << "'. Apply one of 'themes', 'langs' or 'plug-ins'.\n";
405 return EXIT_FAILURE;
406 }
407 }
408
409 const vector <string> inFileList=options.getInputFileNames();
410
411 if ( options.enableBatchMode() && inFileList[0].empty() ) {
412 return EXIT_FAILURE;
413 }
414
415 string themePath=options.getAbsThemePath().empty() ? dataDir.getThemePath ( options.getThemeName(), options.useBase16Theme() ): options.getAbsThemePath();
416
417 unique_ptr<highlight::CodeGenerator> generator ( highlight::CodeGenerator::getInstance ( options.getOutputType() ) );
418
419 if (options.checkSyntaxSupport()) {
420
421 if (!options.syntaxGiven() ) {
422 cerr << "highlight: Define a syntax to use this option\n";
423 return EXIT_FAILURE;
424 } else {
425 string syntaxByFile=options.getSyntaxByFilename();
426 string testSuffix = syntaxByFile.empty() ? options.getSyntax() : dataDir.getFileSuffix(syntaxByFile);
427 string resolvedSuffix (dataDir.guessFileType (testSuffix, syntaxByFile, syntaxByFile.empty(),false ));
428 string langDefPath (options.getAbsLangPath().empty() ? dataDir.getLangPath ( resolvedSuffix +".lang") : options.getAbsLangPath());
429
430 if (generator->loadLanguage( langDefPath ) == highlight::LOAD_OK) {
431 cout << "highlight: This syntax is supported\n";
432 return EXIT_SUCCESS;
433 } else {
434 cerr << "highlight: This syntax is not supported\n";
435 return EXIT_FAILURE;
436 }
437 }
438 }
439
440 generator->setHTMLAttachAnchors ( options.attachLineAnchors() );
441 generator->setHTMLOrderedList ( options.orderedList() );
442 generator->setHTMLInlineCSS ( options.inlineCSS() );
443 generator->setHTMLEnclosePreTag ( options.enclosePreTag() );
444 generator->setHTMLAnchorPrefix ( options.getAnchorPrefix() );
445 generator->setHTMLClassName ( options.getClassName() );
446
447 generator->setLATEXReplaceQuotes ( options.replaceQuotes() );
448 generator->setLATEXNoShorthands ( options.disableBabelShorthands() );
449 generator->setLATEXPrettySymbols ( options.prettySymbols() );
450 generator->setLATEXBeamerMode ( options.enableBeamerMode() );
451
452 generator->setRTFPageSize ( options.getPageSize() );
453 generator->setRTFCharStyles ( options.includeCharStyles() );
454 generator->setRTFPageColor ( options.includePageColor() );
455
456 generator->setSVGSize ( options.getSVGWidth(), options.getSVGHeight() );
457
458 generator->setESCCanvasPadding ( options.getCanvasPadding() );
459
460 if (options.useCRDelimiter())
461 generator->setEOLDelimiter('\r');
462
463 generator->setValidateInput ( options.validateInput() );
464 generator->setNumberWrappedLines ( options.numberWrappedLines() );
465
466 generator->setStyleInputPath ( options.getStyleInFilename() );
467 generator->setStyleOutputPath ( options.getStyleOutFilename() );
468 generator->setIncludeStyle ( options.includeStyleDef() );
469 generator->setPrintLineNumbers ( options.printLineNumbers(), options.getNumberStart() );
470 generator->setPrintZeroes ( options.fillLineNrZeroes() );
471 generator->setFragmentCode ( options.fragmentOutput() );
472 generator->setOmitVersionComment ( options.omitVersionInfo() );
473 generator->setIsolateTags ( options.isolateTags() );
474
475 generator->setKeepInjections ( options.keepInjections());
476 generator->setPreformatting ( options.getWrappingStyle(),
477 ( generator->getPrintLineNumbers() ) ?
478 options.getLineLength() - options.getNumberWidth() : options.getLineLength(),
479 options.getNumberSpaces() );
480
481 generator->setBaseFont ( options.getBaseFont() ) ;
482 generator->setBaseFontSize ( options.getBaseFontSize() ) ;
483 generator->setLineNumberWidth ( options.getNumberWidth() );
484 generator->disableTrailingNL(options.disableTrailingNL());
485 generator->setPluginParameter(options.getPluginParameter());
486
487 if (options.getLineRangeStart()>0 && options.getLineRangeEnd()>0){
488 generator->setStartingInputLine(options.getLineRangeStart());
489 generator->setMaxInputLineCnt(options.getLineRangeEnd());
490 }
491
492 bool styleFileWanted = !options.fragmentOutput() || options.styleOutPathDefined();
493
494 const vector <string> pluginFileList=collectPluginPaths( options.getPluginPaths());
495 for (unsigned int i=0; i<pluginFileList.size(); i++) {
496 if ( !generator->initPluginScript(pluginFileList[i]) ) {
497 cerr << "highlight: "
498 << generator->getPluginScriptError()
499 << " in "
500 << pluginFileList[i]
501 <<"\n";
502 return EXIT_FAILURE;
503 }
504 }
505
506 if ( !generator->initTheme ( themePath, options.isLsSemantic() ) ) {
507 cerr << "highlight: "
508 << generator->getThemeInitError()
509 << "\n";
510 return EXIT_FAILURE;
511 }
512
513 if ( options.printOnlyStyle() ) {
514 if (!options.formatSupportsExtStyle()) {
515 cerr << "highlight: output format supports no external styles.\n";
516 return EXIT_FAILURE;
517 }
518 bool useStdout = options.getStyleOutFilename() =="stdout" || options.forceStdout();
519 string cssOutFile=options.getOutDirectory() + options.getStyleOutFilename();
520 bool success=generator->printExternalStyle ( useStdout?"":cssOutFile );
521 if ( !success ) {
522 cerr << "highlight: Could not write " << cssOutFile <<".\n";
523 return EXIT_FAILURE;
524 }
525 return EXIT_SUCCESS;
526 }
527
528 bool formattingEnabled = generator->initIndentationScheme ( options.getIndentScheme() );
529
530 if ( !formattingEnabled && !options.getIndentScheme().empty() ) {
531 cerr << "highlight: Undefined indentation scheme "
532 << options.getIndentScheme()
533 << ".\n";
534 return EXIT_FAILURE;
535 }
536
537 //generator->setIndentationOptions(options.getAStyleOptions());
538
539 string outDirectory = options.getOutDirectory();
540 #ifndef WIN32
541 ifstream dirTest ( outDirectory.c_str() );
542 if ( !outDirectory.empty() && !options.quietMode() && !dirTest ) {
543 cerr << "highlight: Output directory \""
544 << outDirectory
545 << "\" does not exist.\n";
546 return EXIT_FAILURE;
547 }
548 dirTest.close();
549 #endif
550
551 bool initError=false, IOError=false, twoPassMode=false;
552 unsigned int fileCount=inFileList.size(),
553 fileCountWidth=getNumDigits ( fileCount ),
554 i=0,
555 numBadFormatting=0,
556 numBadInput=0,
557 numBadOutput=0;
558
559 vector<string> badFormattedFiles, badInputFiles, badOutputFiles;
560 std::set<string> usedFileNames;
561 string inFileName, outFilePath;
562 string suffix, lastSuffix;
563 string twoPassOutFile=Platform::getTempFilePath();
564 bool usesLSClient=false;
565
566 if ( options.syntaxGiven() ) { // user defined language definition, valid for all files
567 string syntaxByFile=options.getSyntaxByFilename();
568 string testSuffix = syntaxByFile.empty() ? options.getSyntax() : dataDir.getFileSuffix(syntaxByFile);
569 suffix = dataDir.guessFileType (testSuffix, syntaxByFile, syntaxByFile.empty(), options.getSingleOutFilename().length()==0 );
570 }
571
572 generator->setFilesCnt(fileCount);
573
574 while ( i < fileCount && !initError ) {
575
576 if ( Platform::fileSize(inFileList[i]) > options.getMaxFileSize() ) {
577
578 if ( numBadInput++ < IO_ERROR_REPORT_LENGTH || options.verbosityLevel() ) {
579 badInputFiles.push_back ( inFileList[i] + " (size)" );
580 }
581 ++i;
582 continue;
583 }
584
585 if (i==0 && twoPassMode) {
586 if ( !generator->initPluginScript(twoPassOutFile) ) {
587 cerr << "highlight: "
588 << generator->getPluginScriptError()
589 << " in "
590 << twoPassOutFile
591 <<"\n";
592 initError = true;
593 break;
594 }
595 }
596
597 if ( !options.syntaxGiven() ) { // determine file type for each file
598 suffix = dataDir.guessFileType ( dataDir.getFileSuffix ( inFileList[i] ), inFileList[i] );
599 }
600
601 if ( suffix.empty() && options.forceOutput()) suffix=options.getFallbackSyntax(); //avoid segfault
602
603 if ( suffix.empty() ) {
604 if ( !options.enableBatchMode() )
605 cerr << "highlight: Undefined language definition. Use --"
606 << OPT_SYNTAX << " option.\n";
607 if ( !options.forceOutput() ) {
608 initError = true;
609 break;
610 }
611 }
612
613 if ( suffix != lastSuffix ) {
614
615 string langDefPath=options.getAbsLangPath().empty() ? dataDir.getLangPath ( suffix+".lang" ) : options.getAbsLangPath();
616
617 if (!Platform::fileExists(langDefPath) && !options.getFallbackSyntax().empty()) {
618 langDefPath = dataDir.getLangPath ( options.getFallbackSyntax()+".lang" );
619 }
620
621 highlight::LoadResult loadRes= generator->loadLanguage( langDefPath );
622
623 if ( loadRes==highlight::LOAD_FAILED_REGEX ) {
624 cerr << "highlight: Regex error ( "
625 << generator->getSyntaxRegexError()
626 << " ) in "<<suffix<<".lang\n";
627 initError = true;
628 break;
629 } else if ( loadRes==highlight::LOAD_FAILED_LUA ) {
630 cerr << "highlight: Lua error ( "
631 << generator->getSyntaxLuaError()
632 << " ) in "<<suffix<<".lang\n";
633 initError = true;
634 break;
635 } else if ( loadRes==highlight::LOAD_FAILED ) {
636 // do also ignore error msg if --syntax parameter should be skipped
637 if ( ! (options.forceOutput() || options.quietMode() || options.isSkippedExt ( suffix )) ) {
638 cerr << "highlight: Unknown source file extension \""
639 << suffix
640 << "\". Consider the "
641 << (options.enableBatchMode() ? "--skip" : "--force or --syntax")
642 << " option.\n";
643 }
644 if ( !options.forceOutput() ) {
645 initError = true;
646 break;
647 }
648 }
649 if ( options.verbosityLevel() && loadRes==highlight::LOAD_OK ) {
650 printDebugInfo ( generator->getSyntaxReader(), langDefPath, options.verbosityLevel() );
651 }
652 lastSuffix = suffix;
653
654 string encoding= options.getEncoding();
655 //user has explicitly defined the encoding:
656 if (!options.encodingDefined()) {
657
658 //syntax definition setting:
659 string encodingHint= generator->getSyntaxEncodingHint();
660 if (encodingHint.size())
661 encoding=encodingHint;
662
663 // filetypes.conf setting has higher priority:
664 encodingHint= dataDir.getEncodingHint(suffix);
665 if (encodingHint.size())
666 encoding=encodingHint;
667 }
668 generator->setEncoding (encoding);
669
670 if (lsSyntax==suffix) {
671
672 if (options.getWrappingStyle()!=highlight::WRAP_DISABLED || options.getIndentScheme().size()) {
673 cerr << "highlight: no reformatting allowed with LSP options.\n";
674 initError = true;
675 break;
676 }
677
678 //LSP requires absolute paths
679 if (inFileList[i].empty()) {
680 cerr << "highlight: no input file path defined.\n";
681 initError = true;
682 break;
683 }
684
685 if ( lsExecutable.empty() ) {
686 cerr << "highlight: no LS executable defined. Consider the --ls-exec or --ls-profile options.\n";
687 initError = true;
688 break;
689 }
690
691 highlight::LSResult lsInitRes=generator->initLanguageServer ( lsExecutable, lsOptions,
692 options.getLsWorkspace(), lsSyntax,
693 lsDelay,
694 options.verbosityLevel() );
695 if ( lsInitRes==highlight::INIT_BAD_PIPE ) {
696 cerr << "highlight: language server connection failed\n";
697 initError = true;
698 break;
699 } else if ( lsInitRes==highlight::INIT_BAD_REQUEST ) {
700 cerr << "highlight: language server initialization failed\n";
701 initError = true;
702 break;
703 }
704 usesLSClient=true;
705
706 generator->lsAddSyntaxErrorInfo( (options.isLsHover() || options.isLsSemantic()) && options.isLsSyntaxError() );
707
708 if (options.isLsHover()) {
709 if (!generator->isHoverProvider()) {
710 cerr << "highlight: language server is no hover provider\n";
711 initError = true;
712 break;
713 }
714 generator->lsAddHoverInfo( true );
715 }
716 }
717 }
718
719 if (usesLSClient && lsSyntax==suffix) {
720 generator->lsOpenDocument(inFileList[i], suffix);
721
722 if (options.isLsSemantic()) {
723 if (!generator->isSemanticTokensProvider()) {
724 cerr << "highlight: language server is no semantic token provider\n";
725 initError = true;
726 break;
727 }
728 generator->lsAddSemanticInfo(inFileList[i], suffix);
729 }
730 }
731
732 if (twoPassMode && !generator->syntaxRequiresTwoPassRun()) {
733 ++i;
734 continue;
735 }
736
737 string::size_type pos= ( inFileList[i] ).find_last_of ( Platform::pathSeparator );
738 inFileName = inFileList[i].substr ( pos+1 );
739
740 if ( options.enableBatchMode() ) {
741 if (usedFileNames.count(inFileName)) {
742 string prefix=inFileList[i].substr (2, pos-1 );
743 replace (prefix.begin(), prefix.end(), Platform::pathSeparator, '_');
744 inFileName.insert(0, prefix);
745 } else {
746 usedFileNames.insert(inFileName);
747 }
748 if (!options.forceStdout()){
749 outFilePath = outDirectory;
750 outFilePath += inFileName;
751 outFilePath += options.getOutFileSuffix();
752 }
753 if ( !options.quietMode() && !options.forceStdout() ) {
754 if ( options.printProgress() ) {
755 printProgressBar ( fileCount, i+1 );
756 } else {
757 printCurrentAction ( outFilePath, fileCount, i+1, fileCountWidth );
758 }
759 }
760 } else if (!options.forceStdout()) {
761 outFilePath = options.getSingleOutFilename();
762 if ( outFilePath.size() && outFilePath==options.getSingleInFilename() ) {
763 cerr << "highlight: Output path equals input path: \""
764 << outFilePath << "\".\n";
765 initError = true;
766 break;
767 }
768 }
769
770 if ( options.useFNamesAsAnchors() ) {
771 generator->setHTMLAnchorPrefix ( inFileName );
772 }
773
774 generator->setTitle ( options.getDocumentTitle().empty() ?
775 inFileList[i]:options.getDocumentTitle() );
776
777 generator->setKeyWordCase ( options.getKeywordCase() );
778 highlight::ParseError error = generator->generateFile ( inFileList[i], outFilePath );
779
780 if ( error==highlight::BAD_INPUT ) {
781 if ( numBadInput++ < IO_ERROR_REPORT_LENGTH || options.verbosityLevel() ) {
782 badInputFiles.push_back ( inFileList[i] );
783 }
784 } else if ( error==highlight::BAD_OUTPUT ) {
785 if ( numBadOutput++ < IO_ERROR_REPORT_LENGTH || options.verbosityLevel() ) {
786 badOutputFiles.push_back ( outFilePath );
787 }
788 }
789 if ( formattingEnabled && !generator->formattingIsPossible() ) {
790 if ( numBadFormatting++ < IO_ERROR_REPORT_LENGTH || options.verbosityLevel() ) {
791 badFormattedFiles.push_back ( outFilePath );
792 }
793 }
794
795 if (usesLSClient && lsSyntax==suffix) {
796 //pyls hangs
797 generator->lsCloseDocument(inFileList[i], suffix);
798 }
799
800 ++i;
801
802 if (i==fileCount && outFilePath.size() && generator->requiresTwoPassParsing() && twoPassOutFile.size()
803 && !numBadInput && !numBadOutput && !twoPassMode) {
804
805 bool success=generator->printPersistentState(twoPassOutFile);
806 if ( !success ) {
807 cerr << "highlight: Could not write "<< twoPassOutFile <<".\n";
808 IOError = true;
809 } else {
810 twoPassMode=true;
811 if ( !options.quietMode() && !options.forceStdout() ) {
812 cout << "Enabling two-pass mode using "<<twoPassOutFile<<"\n";
813 }
814 //start over, add plug-in to list in next iteration
815 usedFileNames.clear();
816 generator->resetSyntaxReaders();
817 i=0;
818 lastSuffix.clear();
819 numBadFormatting=0;
820 badFormattedFiles.clear();
821 }
822 }
823 }
824
825 if ( i && !options.includeStyleDef()
826 && !options.inlineCSS()
827 && styleFileWanted
828 && options.formatSupportsExtStyle() ) {
829 string cssOutFile=outDirectory + options.getStyleOutFilename();
830 bool success=generator->printExternalStyle ( cssOutFile );
831 if ( !success ) {
832 cerr << "highlight: Could not write " << cssOutFile <<".\n";
833 IOError = true;
834 }
835 }
836
837 if ( i && options.printIndexFile() ) {
838 bool success=generator -> printIndexFile ( inFileList, outDirectory );
839 if ( !success ) {
840 cerr << "highlight: Could not write index file.\n";
841 IOError = true;
842 }
843 }
844
845 if ( numBadInput ) {
846 printIOErrorReport ( numBadInput, badInputFiles, "read input", "<stdin>" );
847 IOError = true;
848 }
849 if ( numBadOutput ) {
850 printIOErrorReport ( numBadOutput, badOutputFiles, "write output", "<stdout>" );
851 IOError = true;
852 }
853 if ( numBadFormatting ) {
854 printIOErrorReport ( numBadFormatting, badFormattedFiles, "reformat", "<stdout>" );
855 }
856
857 vector<string> posTestErrors = generator->getPosTestErrors();
858 if (posTestErrors.size()){
859 IOError = true;
860 printIOErrorReport ( posTestErrors.size(), posTestErrors, "validate", "<stdin>" );
861 }
862
863 if (twoPassMode) {
864 unlink(twoPassOutFile.c_str());
865 }
866
867 if (usesLSClient) {
868 generator->exitLanguageServer();
869 }
870
871 return ( initError || IOError ) ? EXIT_FAILURE : EXIT_SUCCESS;
872 }
873
main(const int argc,const char * argv[])874 int main ( const int argc, const char *argv[] )
875 {
876 HLCmdLineApp app;
877 return app.run ( argc, argv );
878 }
879