Repository: martinribelotta/embedded-ide Branch: master Commit: 6961d5fd91c0 Files: 750 Total size: 15.9 MB Directory structure: gitextract_j16isbcq/ ├── .gitignore ├── .travis.yml ├── 3rdpart/ │ ├── astyle/ │ │ ├── ASBeautifier.cpp │ │ ├── ASEnhancer.cpp │ │ ├── ASFormatter.cpp │ │ ├── ASLocalizer.cpp │ │ ├── ASLocalizer.h │ │ ├── ASResource.cpp │ │ ├── astyle.h │ │ ├── astyle.pri │ │ ├── astyle_main.cpp │ │ └── astyle_main.h │ ├── backward/ │ │ ├── backward.cpp │ │ ├── backward.hpp │ │ └── backward.pri │ ├── hoedown/ │ │ ├── LICENSE │ │ ├── README.md │ │ ├── hoedown.def │ │ ├── hoedown.pri │ │ ├── html_block_names.gperf │ │ └── src/ │ │ ├── autolink.c │ │ ├── autolink.h │ │ ├── buffer.c │ │ ├── buffer.h │ │ ├── document.c │ │ ├── document.h │ │ ├── escape.c │ │ ├── escape.h │ │ ├── hodedown_version.c │ │ ├── html.c │ │ ├── html.h │ │ ├── html_blocks.c │ │ ├── html_smartypants.c │ │ ├── stack.c │ │ ├── stack.h │ │ └── version.h │ ├── qdarkstyle/ │ │ ├── qdarkstype.pri │ │ ├── style.qrc │ │ └── style.qss │ ├── qhexview/ │ │ ├── .gitignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── include/ │ │ │ └── QHexView.h │ │ ├── qhexview.pri │ │ └── src/ │ │ └── QHexView.cpp │ └── qt-mustache-master/ │ ├── .gitignore │ ├── .travis.yml │ ├── README.md │ ├── qt-mustache.pri │ ├── qt-mustache.pro │ ├── src/ │ │ ├── mustache.cpp │ │ └── mustache.h │ └── tests/ │ ├── partial.mustache │ ├── specs/ │ │ ├── comments.json │ │ ├── comments.yml │ │ ├── delimiters.json │ │ ├── delimiters.yml │ │ ├── interpolation.json │ │ ├── interpolation.yml │ │ ├── inverted.json │ │ ├── inverted.yml │ │ ├── partials.json │ │ ├── partials.yml │ │ ├── sections.json │ │ └── sections.yml │ ├── test_mustache.cpp │ └── test_mustache.h ├── LICENSE ├── README.md ├── ci/ │ ├── BuildQSCI.mk │ ├── extract-qt-installer │ ├── qt-installer-silent.js │ ├── qt5124-linux-packages │ ├── qt5129-linux-packages │ ├── tests-ci.sh │ └── tests-environment.sh ├── docs/ │ ├── DIFF_TEMPLATES.md │ └── TOOL_CMDLINE.md ├── embedded-ide.pro ├── ide/ │ ├── appconfig.cpp │ ├── appconfig.h │ ├── binaryviewer.cpp │ ├── binaryviewer.h │ ├── buildmanager.cpp │ ├── buildmanager.h │ ├── buttoneditoritemdelegate.h │ ├── childprocess.cpp │ ├── childprocess.h │ ├── clangautocompletionprovider.cpp │ ├── clangautocompletionprovider.h │ ├── codetexteditor.cpp │ ├── codetexteditor.h │ ├── configwidget.cpp │ ├── configwidget.h │ ├── configwidget.ui │ ├── consoleinterceptor.cpp │ ├── consoleinterceptor.h │ ├── cpptexteditor.cpp │ ├── cpptexteditor.h │ ├── documentmanager.cpp │ ├── documentmanager.h │ ├── envinputdialog.cpp │ ├── envinputdialog.h │ ├── envinputdialog.ui │ ├── externaltoolmanager.cpp │ ├── externaltoolmanager.h │ ├── externaltoolmanager.ui │ ├── filereferencesdialog.cpp │ ├── filereferencesdialog.h │ ├── filereferencesdialog.ui │ ├── filesystemmanager.cpp │ ├── filesystemmanager.h │ ├── findandopenfiledialog.cpp │ ├── findandopenfiledialog.h │ ├── findandopenfiledialog.ui │ ├── findinfilesdialog.cpp │ ├── findinfilesdialog.h │ ├── findinfilesdialog.ui │ ├── findlineedit.cpp │ ├── findlineedit.h │ ├── findmakefiledialog.cpp │ ├── findmakefiledialog.h │ ├── findmakefiledialog.ui │ ├── formfindreplace.cpp │ ├── formfindreplace.h │ ├── formfindreplace.ui │ ├── icodemodelprovider.cpp │ ├── icodemodelprovider.h │ ├── ide.pro │ ├── idocumenteditor.cpp │ ├── idocumenteditor.h │ ├── imageviewer.cpp │ ├── imageviewer.h │ ├── main.cpp │ ├── mainwindow.cpp │ ├── mainwindow.h │ ├── mainwindow.ui │ ├── mapfileviewer.cpp │ ├── mapfileviewer.h │ ├── markdowneditor.cpp │ ├── markdowneditor.h │ ├── markdownview.cpp │ ├── markdownview.h │ ├── newprojectdialog.cpp │ ├── newprojectdialog.h │ ├── newprojectdialog.ui │ ├── newprojectfromremotedialog.cpp │ ├── newprojectfromremotedialog.h │ ├── newprojectfromremotedialog.ui │ ├── plaintexteditor.cpp │ ├── plaintexteditor.h │ ├── processlinebufferizer.cpp │ ├── processlinebufferizer.h │ ├── processmanager.cpp │ ├── processmanager.h │ ├── projectmanager.cpp │ ├── projectmanager.h │ ├── regexhtmltranslator.cpp │ ├── regexhtmltranslator.h │ ├── resources/ │ │ ├── default-global.json │ │ ├── default-local.json │ │ ├── fonts.qrc │ │ ├── iconactions.qrc │ │ ├── images/ │ │ │ ├── dark/ │ │ │ │ └── index.theme │ │ │ └── light/ │ │ │ └── index.theme │ │ ├── kinds.qrc │ │ ├── mimetypes.qrc │ │ ├── reference-code.c │ │ ├── resources.qrc │ │ ├── styles/ │ │ │ ├── Bespin.xml │ │ │ ├── Black board.xml │ │ │ ├── Choco.xml │ │ │ ├── Deep Black.xml │ │ │ ├── Default.xml │ │ │ ├── Hello Kitty.xml │ │ │ ├── HotFudgeSundae.xml │ │ │ ├── Material-Dark.xml │ │ │ ├── Mono Industrial.xml │ │ │ ├── Monokai.xml │ │ │ ├── MossyLawn.xml │ │ │ ├── Navajo.xml │ │ │ ├── Obsidian.xml │ │ │ ├── Plastic Code Wrap.xml │ │ │ ├── Ruby Blue.xml │ │ │ ├── Solarized-light.xml │ │ │ ├── Solarized.xml │ │ │ ├── Twilight.xml │ │ │ ├── Vibrant Ink.xml │ │ │ ├── Zenburn.xml │ │ │ ├── khaki.xml │ │ │ ├── tomorrow.xml │ │ │ └── vim Dark Blue.xml │ │ ├── styles.qrc │ │ └── templates/ │ │ └── empty.template │ ├── skeleton/ │ │ ├── bin/ │ │ │ ├── ftdi_rules.sh │ │ │ └── qt.conf │ │ ├── desktop-integration.sh │ │ ├── embedded-ide.desktop │ │ ├── embedded-ide.hardconf │ │ ├── embedded-ide.sh │ │ ├── embedded-ide.sh.wrapper │ │ └── ftdi-tools.sh │ ├── tar.h │ ├── templatefile.cpp │ ├── templatefile.h │ ├── templateitemwidget.cpp │ ├── templateitemwidget.h │ ├── templateitemwidget.ui │ ├── templatemanager.cpp │ ├── templatemanager.h │ ├── templatemanager.ui │ ├── textmessagebrocker.cpp │ ├── textmessagebrocker.h │ ├── translations/ │ │ └── es.ts │ ├── unsavedfilesdialog.cpp │ ├── unsavedfilesdialog.h │ ├── unsavedfilesdialog.ui │ ├── version.cpp │ └── version.h ├── mapview/ │ ├── main.cpp │ ├── mapview.pri │ ├── mapview.pro │ ├── mapviewmodel.cpp │ └── mapviewmodel.h ├── old/ │ ├── .gitignore │ ├── 3rdpart/ │ │ ├── QHexEdit/ │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── qhexedit.cpp │ │ │ ├── qhexedit.h │ │ │ ├── qhexedit.pri │ │ │ ├── qhexeditcomments.cpp │ │ │ ├── qhexeditcomments.h │ │ │ ├── qhexeditdata.cpp │ │ │ ├── qhexeditdata.h │ │ │ ├── qhexeditdatadevice.cpp │ │ │ ├── qhexeditdatadevice.h │ │ │ ├── qhexeditdatareader.cpp │ │ │ ├── qhexeditdatareader.h │ │ │ ├── qhexeditdatawriter.cpp │ │ │ ├── qhexeditdatawriter.h │ │ │ ├── qhexedithighlighter.cpp │ │ │ ├── qhexedithighlighter.h │ │ │ ├── qhexeditprivate.cpp │ │ │ ├── qhexeditprivate.h │ │ │ ├── sparserangemap.cpp │ │ │ └── sparserangemap.h │ │ ├── astyle/ │ │ │ ├── ASBeautifier.cpp │ │ │ ├── ASEnhancer.cpp │ │ │ ├── ASFormatter.cpp │ │ │ ├── ASLocalizer.cpp │ │ │ ├── ASLocalizer.h │ │ │ ├── ASResource.cpp │ │ │ ├── astyle.h │ │ │ ├── astyle.pri │ │ │ ├── astyle_main.cpp │ │ │ └── astyle_main.h │ │ ├── gdbdebugger/ │ │ │ ├── gdbdebugger.cpp │ │ │ ├── gdbdebugger.h │ │ │ └── gdbdebugger.pri │ │ ├── qscintilla/ │ │ │ ├── ChangeLog │ │ │ ├── LICENSE │ │ │ ├── NEWS │ │ │ ├── Qt4Qt5/ │ │ │ │ ├── InputMethod.cpp │ │ │ │ ├── ListBoxQt.cpp │ │ │ │ ├── ListBoxQt.h │ │ │ │ ├── MacPasteboardMime.cpp │ │ │ │ ├── PlatQt.cpp │ │ │ │ ├── Qsci/ │ │ │ │ │ ├── qsciabstractapis.h │ │ │ │ │ ├── qsciapis.h │ │ │ │ │ ├── qscicommand.h │ │ │ │ │ ├── qscicommandset.h │ │ │ │ │ ├── qscidocument.h │ │ │ │ │ ├── qsciglobal.h │ │ │ │ │ ├── qscilexer.h │ │ │ │ │ ├── qscilexeravs.h │ │ │ │ │ ├── qscilexerbash.h │ │ │ │ │ ├── qscilexerbatch.h │ │ │ │ │ ├── qscilexercmake.h │ │ │ │ │ ├── qscilexercoffeescript.h │ │ │ │ │ ├── qscilexercpp.h │ │ │ │ │ ├── qscilexercsharp.h │ │ │ │ │ ├── qscilexercss.h │ │ │ │ │ ├── qscilexercustom.h │ │ │ │ │ ├── qscilexerd.h │ │ │ │ │ ├── qscilexerdiff.h │ │ │ │ │ ├── qscilexerfortran.h │ │ │ │ │ ├── qscilexerfortran77.h │ │ │ │ │ ├── qscilexerhtml.h │ │ │ │ │ ├── qscilexeridl.h │ │ │ │ │ ├── qscilexerjava.h │ │ │ │ │ ├── qscilexerjavascript.h │ │ │ │ │ ├── qscilexerjson.h │ │ │ │ │ ├── qscilexerlua.h │ │ │ │ │ ├── qscilexermakefile.h │ │ │ │ │ ├── qscilexermarkdown.h │ │ │ │ │ ├── qscilexermatlab.h │ │ │ │ │ ├── qscilexeroctave.h │ │ │ │ │ ├── qscilexerpascal.h │ │ │ │ │ ├── qscilexerperl.h │ │ │ │ │ ├── qscilexerpo.h │ │ │ │ │ ├── qscilexerpostscript.h │ │ │ │ │ ├── qscilexerpov.h │ │ │ │ │ ├── qscilexerproperties.h │ │ │ │ │ ├── qscilexerpython.h │ │ │ │ │ ├── qscilexerruby.h │ │ │ │ │ ├── qscilexerspice.h │ │ │ │ │ ├── qscilexersql.h │ │ │ │ │ ├── qscilexertcl.h │ │ │ │ │ ├── qscilexertex.h │ │ │ │ │ ├── qscilexerverilog.h │ │ │ │ │ ├── qscilexervhdl.h │ │ │ │ │ ├── qscilexerxml.h │ │ │ │ │ ├── qscilexeryaml.h │ │ │ │ │ ├── qscimacro.h │ │ │ │ │ ├── qsciprinter.h │ │ │ │ │ ├── qsciscintilla.h │ │ │ │ │ ├── qsciscintillabase.h │ │ │ │ │ ├── qscistyle.h │ │ │ │ │ └── qscistyledtext.h │ │ │ │ ├── SciClasses.cpp │ │ │ │ ├── SciClasses.h │ │ │ │ ├── SciNamespace.h │ │ │ │ ├── ScintillaQt.cpp │ │ │ │ ├── ScintillaQt.h │ │ │ │ ├── features/ │ │ │ │ │ └── qscintilla2.prf │ │ │ │ ├── features_staticlib/ │ │ │ │ │ └── qscintilla2.prf │ │ │ │ ├── qsciabstractapis.cpp │ │ │ │ ├── qsciapis.cpp │ │ │ │ ├── qscicommand.cpp │ │ │ │ ├── qscicommandset.cpp │ │ │ │ ├── qscidocument.cpp │ │ │ │ ├── qscilexer.cpp │ │ │ │ ├── qscilexeravs.cpp │ │ │ │ ├── qscilexerbash.cpp │ │ │ │ ├── qscilexerbatch.cpp │ │ │ │ ├── qscilexercmake.cpp │ │ │ │ ├── qscilexercoffeescript.cpp │ │ │ │ ├── qscilexercpp.cpp │ │ │ │ ├── qscilexercsharp.cpp │ │ │ │ ├── qscilexercss.cpp │ │ │ │ ├── qscilexercustom.cpp │ │ │ │ ├── qscilexerd.cpp │ │ │ │ ├── qscilexerdiff.cpp │ │ │ │ ├── qscilexerfortran.cpp │ │ │ │ ├── qscilexerfortran77.cpp │ │ │ │ ├── qscilexerhtml.cpp │ │ │ │ ├── qscilexeridl.cpp │ │ │ │ ├── qscilexerjava.cpp │ │ │ │ ├── qscilexerjavascript.cpp │ │ │ │ ├── qscilexerjson.cpp │ │ │ │ ├── qscilexerlua.cpp │ │ │ │ ├── qscilexermakefile.cpp │ │ │ │ ├── qscilexermarkdown.cpp │ │ │ │ ├── qscilexermatlab.cpp │ │ │ │ ├── qscilexeroctave.cpp │ │ │ │ ├── qscilexerpascal.cpp │ │ │ │ ├── qscilexerperl.cpp │ │ │ │ ├── qscilexerpo.cpp │ │ │ │ ├── qscilexerpostscript.cpp │ │ │ │ ├── qscilexerpov.cpp │ │ │ │ ├── qscilexerproperties.cpp │ │ │ │ ├── qscilexerpython.cpp │ │ │ │ ├── qscilexerruby.cpp │ │ │ │ ├── qscilexerspice.cpp │ │ │ │ ├── qscilexersql.cpp │ │ │ │ ├── qscilexertcl.cpp │ │ │ │ ├── qscilexertex.cpp │ │ │ │ ├── qscilexerverilog.cpp │ │ │ │ ├── qscilexervhdl.cpp │ │ │ │ ├── qscilexerxml.cpp │ │ │ │ ├── qscilexeryaml.cpp │ │ │ │ ├── qscimacro.cpp │ │ │ │ ├── qscintilla.pro │ │ │ │ ├── qscintilla_cs.ts │ │ │ │ ├── qscintilla_de.ts │ │ │ │ ├── qscintilla_es.ts │ │ │ │ ├── qscintilla_fr.ts │ │ │ │ ├── qscintilla_pt_br.ts │ │ │ │ ├── qsciprinter.cpp │ │ │ │ ├── qsciscintilla.cpp │ │ │ │ ├── qsciscintillabase.cpp │ │ │ │ ├── qscistyle.cpp │ │ │ │ └── qscistyledtext.cpp │ │ │ ├── README │ │ │ ├── include/ │ │ │ │ ├── ILexer.h │ │ │ │ ├── License.txt │ │ │ │ ├── Platform.h │ │ │ │ ├── SciLexer.h │ │ │ │ ├── Sci_Position.h │ │ │ │ ├── Scintilla.h │ │ │ │ ├── Scintilla.iface │ │ │ │ └── ScintillaWidget.h │ │ │ ├── lexers/ │ │ │ │ ├── LexA68k.cpp │ │ │ │ ├── LexAPDL.cpp │ │ │ │ ├── LexASY.cpp │ │ │ │ ├── LexAU3.cpp │ │ │ │ ├── LexAVE.cpp │ │ │ │ ├── LexAVS.cpp │ │ │ │ ├── LexAbaqus.cpp │ │ │ │ ├── LexAda.cpp │ │ │ │ ├── LexAsm.cpp │ │ │ │ ├── LexAsn1.cpp │ │ │ │ ├── LexBaan.cpp │ │ │ │ ├── LexBash.cpp │ │ │ │ ├── LexBasic.cpp │ │ │ │ ├── LexBatch.cpp │ │ │ │ ├── LexBibTeX.cpp │ │ │ │ ├── LexBullant.cpp │ │ │ │ ├── LexCLW.cpp │ │ │ │ ├── LexCOBOL.cpp │ │ │ │ ├── LexCPP.cpp │ │ │ │ ├── LexCSS.cpp │ │ │ │ ├── LexCaml.cpp │ │ │ │ ├── LexCmake.cpp │ │ │ │ ├── LexCoffeeScript.cpp │ │ │ │ ├── LexConf.cpp │ │ │ │ ├── LexCrontab.cpp │ │ │ │ ├── LexCsound.cpp │ │ │ │ ├── LexD.cpp │ │ │ │ ├── LexDMAP.cpp │ │ │ │ ├── LexDMIS.cpp │ │ │ │ ├── LexDiff.cpp │ │ │ │ ├── LexECL.cpp │ │ │ │ ├── LexEDIFACT.cpp │ │ │ │ ├── LexEScript.cpp │ │ │ │ ├── LexEiffel.cpp │ │ │ │ ├── LexErlang.cpp │ │ │ │ ├── LexErrorList.cpp │ │ │ │ ├── LexFlagship.cpp │ │ │ │ ├── LexForth.cpp │ │ │ │ ├── LexFortran.cpp │ │ │ │ ├── LexGAP.cpp │ │ │ │ ├── LexGui4Cli.cpp │ │ │ │ ├── LexHTML.cpp │ │ │ │ ├── LexHaskell.cpp │ │ │ │ ├── LexHex.cpp │ │ │ │ ├── LexInno.cpp │ │ │ │ ├── LexJSON.cpp │ │ │ │ ├── LexKVIrc.cpp │ │ │ │ ├── LexKix.cpp │ │ │ │ ├── LexLaTeX.cpp │ │ │ │ ├── LexLisp.cpp │ │ │ │ ├── LexLout.cpp │ │ │ │ ├── LexLua.cpp │ │ │ │ ├── LexMMIXAL.cpp │ │ │ │ ├── LexMPT.cpp │ │ │ │ ├── LexMSSQL.cpp │ │ │ │ ├── LexMagik.cpp │ │ │ │ ├── LexMake.cpp │ │ │ │ ├── LexMarkdown.cpp │ │ │ │ ├── LexMatlab.cpp │ │ │ │ ├── LexMetapost.cpp │ │ │ │ ├── LexModula.cpp │ │ │ │ ├── LexMySQL.cpp │ │ │ │ ├── LexNimrod.cpp │ │ │ │ ├── LexNsis.cpp │ │ │ │ ├── LexNull.cpp │ │ │ │ ├── LexOScript.cpp │ │ │ │ ├── LexOpal.cpp │ │ │ │ ├── LexPB.cpp │ │ │ │ ├── LexPLM.cpp │ │ │ │ ├── LexPO.cpp │ │ │ │ ├── LexPOV.cpp │ │ │ │ ├── LexPS.cpp │ │ │ │ ├── LexPascal.cpp │ │ │ │ ├── LexPerl.cpp │ │ │ │ ├── LexPowerPro.cpp │ │ │ │ ├── LexPowerShell.cpp │ │ │ │ ├── LexProgress.cpp │ │ │ │ ├── LexProps.cpp │ │ │ │ ├── LexPython.cpp │ │ │ │ ├── LexR.cpp │ │ │ │ ├── LexRebol.cpp │ │ │ │ ├── LexRegistry.cpp │ │ │ │ ├── LexRuby.cpp │ │ │ │ ├── LexRust.cpp │ │ │ │ ├── LexSML.cpp │ │ │ │ ├── LexSQL.cpp │ │ │ │ ├── LexSTTXT.cpp │ │ │ │ ├── LexScriptol.cpp │ │ │ │ ├── LexSmalltalk.cpp │ │ │ │ ├── LexSorcus.cpp │ │ │ │ ├── LexSpecman.cpp │ │ │ │ ├── LexSpice.cpp │ │ │ │ ├── LexTACL.cpp │ │ │ │ ├── LexTADS3.cpp │ │ │ │ ├── LexTAL.cpp │ │ │ │ ├── LexTCL.cpp │ │ │ │ ├── LexTCMD.cpp │ │ │ │ ├── LexTeX.cpp │ │ │ │ ├── LexTxt2tags.cpp │ │ │ │ ├── LexVB.cpp │ │ │ │ ├── LexVHDL.cpp │ │ │ │ ├── LexVerilog.cpp │ │ │ │ ├── LexVisualProlog.cpp │ │ │ │ ├── LexYAML.cpp │ │ │ │ └── License.txt │ │ │ ├── lexlib/ │ │ │ │ ├── Accessor.cpp │ │ │ │ ├── Accessor.h │ │ │ │ ├── CharacterCategory.cpp │ │ │ │ ├── CharacterCategory.h │ │ │ │ ├── CharacterSet.cpp │ │ │ │ ├── CharacterSet.h │ │ │ │ ├── LexAccessor.h │ │ │ │ ├── LexerBase.cpp │ │ │ │ ├── LexerBase.h │ │ │ │ ├── LexerModule.cpp │ │ │ │ ├── LexerModule.h │ │ │ │ ├── LexerNoExceptions.cpp │ │ │ │ ├── LexerNoExceptions.h │ │ │ │ ├── LexerSimple.cpp │ │ │ │ ├── LexerSimple.h │ │ │ │ ├── License.txt │ │ │ │ ├── OptionSet.h │ │ │ │ ├── PropSetSimple.cpp │ │ │ │ ├── PropSetSimple.h │ │ │ │ ├── SparseState.h │ │ │ │ ├── StringCopy.h │ │ │ │ ├── StyleContext.cpp │ │ │ │ ├── StyleContext.h │ │ │ │ ├── SubStyles.h │ │ │ │ ├── WordList.cpp │ │ │ │ └── WordList.h │ │ │ ├── qscintilla.pri │ │ │ └── src/ │ │ │ ├── AutoComplete.cpp │ │ │ ├── AutoComplete.h │ │ │ ├── CallTip.cpp │ │ │ ├── CallTip.h │ │ │ ├── CaseConvert.cpp │ │ │ ├── CaseConvert.h │ │ │ ├── CaseFolder.cpp │ │ │ ├── CaseFolder.h │ │ │ ├── Catalogue.cpp │ │ │ ├── Catalogue.h │ │ │ ├── CellBuffer.cpp │ │ │ ├── CellBuffer.h │ │ │ ├── CharClassify.cpp │ │ │ ├── CharClassify.h │ │ │ ├── ContractionState.cpp │ │ │ ├── ContractionState.h │ │ │ ├── Decoration.cpp │ │ │ ├── Decoration.h │ │ │ ├── Document.cpp │ │ │ ├── Document.h │ │ │ ├── EditModel.cpp │ │ │ ├── EditModel.h │ │ │ ├── EditView.cpp │ │ │ ├── EditView.h │ │ │ ├── Editor.cpp │ │ │ ├── Editor.h │ │ │ ├── ExternalLexer.cpp │ │ │ ├── ExternalLexer.h │ │ │ ├── FontQuality.h │ │ │ ├── Indicator.cpp │ │ │ ├── Indicator.h │ │ │ ├── KeyMap.cpp │ │ │ ├── KeyMap.h │ │ │ ├── License.txt │ │ │ ├── LineMarker.cpp │ │ │ ├── LineMarker.h │ │ │ ├── MarginView.cpp │ │ │ ├── MarginView.h │ │ │ ├── Partitioning.h │ │ │ ├── PerLine.cpp │ │ │ ├── PerLine.h │ │ │ ├── Position.h │ │ │ ├── PositionCache.cpp │ │ │ ├── PositionCache.h │ │ │ ├── RESearch.cpp │ │ │ ├── RESearch.h │ │ │ ├── RunStyles.cpp │ │ │ ├── RunStyles.h │ │ │ ├── SciTE.properties │ │ │ ├── ScintillaBase.cpp │ │ │ ├── ScintillaBase.h │ │ │ ├── Selection.cpp │ │ │ ├── Selection.h │ │ │ ├── SparseVector.h │ │ │ ├── SplitVector.h │ │ │ ├── Style.cpp │ │ │ ├── Style.h │ │ │ ├── UniConversion.cpp │ │ │ ├── UniConversion.h │ │ │ ├── UnicodeFromUTF8.h │ │ │ ├── ViewStyle.cpp │ │ │ ├── ViewStyle.h │ │ │ ├── XPM.cpp │ │ │ └── XPM.h │ │ └── qtc_gdbmi/ │ │ ├── gdbmi.cpp │ │ ├── gdbmi.h │ │ └── qtc_gdbmi.pri │ ├── aboutdialog.cpp │ ├── aboutdialog.h │ ├── aboutdialog.ui │ ├── appconfig.cpp │ ├── appconfig.h │ ├── bannerwidget.cpp │ ├── bannerwidget.h │ ├── bannerwidget.ui │ ├── clangcodecontext.cpp │ ├── clangcodecontext.h │ ├── codeeditor.cpp │ ├── codeeditor.h │ ├── codetemplate.cpp │ ├── codetemplate.h │ ├── combodocumentview.cpp │ ├── combodocumentview.h │ ├── componentitemwidget.cpp │ ├── componentitemwidget.h │ ├── componentitemwidget.ui │ ├── componentsdialog.cpp │ ├── componentsdialog.h │ ├── componentsdialog.ui │ ├── configdialog.cpp │ ├── configdialog.h │ ├── configdialog.ui │ ├── debugui.cpp │ ├── debugui.h │ ├── debugui.ui │ ├── dialogconfigworkspace.cpp │ ├── dialogconfigworkspace.h │ ├── dialogconfigworkspace.ui │ ├── documentarea.cpp │ ├── documentarea.h │ ├── editorwidget.ui │ ├── embedded-ide.desktop │ ├── etags.cpp │ ├── etags.h │ ├── filedownloader.cpp │ ├── filedownloader.h │ ├── filepropertiesdialog.cpp │ ├── filepropertiesdialog.h │ ├── filepropertiesdialog.ui │ ├── findinfilesdialog.cpp │ ├── findinfilesdialog.h │ ├── findinfilesdialog.ui │ ├── findlineedit.cpp │ ├── findlineedit.h │ ├── formfindreplace.cpp │ ├── formfindreplace.h │ ├── formfindreplace.ui │ ├── gdbstartdialog.cpp │ ├── gdbstartdialog.h │ ├── gdbstartdialog.ui │ ├── i18n/ │ │ ├── es.ts │ │ └── zh.ts │ ├── ide.pro │ ├── loggerwidget.cpp │ ├── loggerwidget.h │ ├── main.cpp │ ├── mainmenuwidget.cpp │ ├── mainmenuwidget.h │ ├── mainmenuwidget.ui │ ├── mainwindow.cpp │ ├── mainwindow.h │ ├── mainwindow.ui │ ├── make2compilationdb.sh │ ├── makefileinfo.cpp │ ├── makefileinfo.h │ ├── mapviewer.cpp │ ├── mapviewer.h │ ├── mapviewer.ui │ ├── passwordpromtdialog.cpp │ ├── passwordpromtdialog.h │ ├── passwordpromtdialog.ui │ ├── projectexporter.cpp │ ├── projectexporter.h │ ├── projecticonprovider.cpp │ ├── projecticonprovider.h │ ├── projectnewdialog.cpp │ ├── projectnewdialog.h │ ├── projectnewdialog.ui │ ├── projectview.cpp │ ├── projectview.h │ ├── projectview.ui │ ├── projetfromtemplate.cpp │ ├── projetfromtemplate.h │ ├── qsvtextoperationswidget.cpp │ ├── qsvtextoperationswidget.h │ ├── qtdialog/ │ │ ├── main.cpp │ │ └── qtdialog.pro │ ├── replaceform.ui │ ├── resources/ │ │ ├── project-filters.txt │ │ ├── reference-code.c │ │ ├── resources.qrc │ │ ├── style.css │ │ ├── styles/ │ │ │ ├── Bespin.xml │ │ │ ├── Black board.xml │ │ │ ├── Choco.xml │ │ │ ├── Deep Black.xml │ │ │ ├── Default.xml │ │ │ ├── Hello Kitty.xml │ │ │ ├── HotFudgeSundae.xml │ │ │ ├── Material-Dark.xml │ │ │ ├── Mono Industrial.xml │ │ │ ├── Monokai.xml │ │ │ ├── MossyLawn.xml │ │ │ ├── Navajo.xml │ │ │ ├── Obsidian.xml │ │ │ ├── Plastic Code Wrap.xml │ │ │ ├── Ruby Blue.xml │ │ │ ├── Solarized-light.xml │ │ │ ├── Solarized.xml │ │ │ ├── Twilight.xml │ │ │ ├── Vibrant Ink.xml │ │ │ ├── Zenburn.xml │ │ │ ├── khaki.xml │ │ │ └── vim Dark Blue.xml │ │ └── templates/ │ │ ├── empty.template │ │ ├── gcc-exec.template │ │ ├── lpcopen-picociaa.template │ │ ├── lpcopen.template │ │ └── sAPI-Project.template │ ├── searchform.ui │ ├── skeleton/ │ │ ├── bin/ │ │ │ ├── ftdi_rules.sh │ │ │ └── qt.conf │ │ ├── desktop-integration.sh │ │ ├── embedded-ide.hardconf │ │ ├── embedded-ide.sh │ │ ├── embedded-ide.sh.wrapper │ │ └── ftdi-tools.sh │ ├── taglist.cpp │ ├── taglist.h │ ├── targetupdatediscover.cpp │ ├── targetupdatediscover.h │ ├── templatedownloader.cpp │ ├── templatedownloader.h │ ├── templatesdownloadselector.cpp │ ├── templatesdownloadselector.h │ ├── templatesdownloadselector.ui │ ├── toolmanager.cpp │ ├── toolmanager.h │ ├── toolmanager.ui │ ├── version.cpp │ └── version.h ├── qtshdialog/ │ ├── 3rdpart/ │ │ └── QJsonModel/ │ │ ├── LICENSE │ │ ├── QJsonModel.pri │ │ ├── QJsonModel.pro │ │ ├── README.md │ │ ├── main.cpp │ │ ├── qjsonmodel.cpp │ │ ├── qjsonmodel.h │ │ └── qjsonmodel.py │ ├── main.cpp │ └── qtshdialog.pro └── socketwaiter/ ├── .gitignore ├── LICENSE ├── README.md ├── main.cpp └── socketwaiter.pro ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.o *.d *.elf *.moc moc_*.cpp ui_*.h *.pro.user ================================================ FILE: .travis.yml ================================================ language: cpp sudo: required dist: xenial os: linux env: global: - DISPLAY=:99 - MXE_TRIPLE=i686-w64-mingw32.shared before_install: - chmod a+x ./ci/tests-environment.sh - ./ci/tests-environment.sh script: - chmod a+x ./ci/tests-ci.sh - ./ci/tests-ci.sh after_success: - wget -c https://github.com/probonopd/uploadtool/raw/master/upload.sh # quick fix for issue 223 - if [ "$TRAVIS_TAG" != "$TRAVIS_BRANCH" ] && [ "$TRAVIS_BRANCH" != "master" ]; then export TRAVIS_EVENT_TYPE=pull_request; fi - bash ./upload.sh ./Embedded_IDE-*.AppImage ./Embedded_IDE-*.zip ./Embedded_IDE-*.tar.bz2 branches: except: - # Do not build tags that we create when we upload to GitHub Releases - /^(?i:continuous)$/ ================================================ FILE: 3rdpart/astyle/ASBeautifier.cpp ================================================ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ASBeautifier.cpp * * Copyright (C) 2014 by Jim Pattee * * * This file is a part of Artistic Style - an indentation and * reformatting tool for C, C++, C# and Java source files. * * * Artistic Style is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Artistic Style is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Artistic Style. If not, see . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "astyle.h" #include namespace astyle { // this must be global static int g_preprocessorCppExternCBracket; /** * ASBeautifier's constructor * This constructor is called only once for each source file. * The cloned ASBeautifier objects are created with the copy constructor. */ ASBeautifier::ASBeautifier() { g_preprocessorCppExternCBracket = 0; waitingBeautifierStack = NULL; activeBeautifierStack = NULL; waitingBeautifierStackLengthStack = NULL; activeBeautifierStackLengthStack = NULL; headerStack = NULL; tempStacks = NULL; blockParenDepthStack = NULL; blockStatementStack = NULL; parenStatementStack = NULL; bracketBlockStateStack = NULL; inStatementIndentStack = NULL; inStatementIndentStackSizeStack = NULL; parenIndentStack = NULL; preprocIndentStack = NULL; sourceIterator = NULL; isModeManuallySet = false; shouldForceTabIndentation = false; setSpaceIndentation(4); setMinConditionalIndentOption(MINCOND_TWO); setMaxInStatementIndentLength(40); classInitializerIndents = 1; tabLength = 0; setClassIndent(false); setModifierIndent(false); setSwitchIndent(false); setCaseIndent(false); setBlockIndent(false); setBracketIndent(false); setBracketIndentVtk(false); setNamespaceIndent(false); setLabelIndent(false); setEmptyLineFill(false); setCStyle(); setPreprocDefineIndent(false); setPreprocConditionalIndent(false); setAlignMethodColon(false); // initialize ASBeautifier member vectors beautifierFileType = 9; // reset to an invalid type headers = new vector; nonParenHeaders = new vector; assignmentOperators = new vector; nonAssignmentOperators = new vector; preBlockStatements = new vector; preCommandHeaders = new vector; indentableHeaders = new vector; } /** * ASBeautifier's copy constructor * Copy the vector objects to vectors in the new ASBeautifier * object so the new object can be destroyed without deleting * the vector objects in the copied vector. * This is the reason a copy constructor is needed. * * Must explicitly call the base class copy constructor. */ ASBeautifier::ASBeautifier(const ASBeautifier &other) : ASBase(other) { // these don't need to copy the stack waitingBeautifierStack = NULL; activeBeautifierStack = NULL; waitingBeautifierStackLengthStack = NULL; activeBeautifierStackLengthStack = NULL; // vector '=' operator performs a DEEP copy of all elements in the vector headerStack = new vector; *headerStack = *other.headerStack; tempStacks = copyTempStacks(other); blockParenDepthStack = new vector; *blockParenDepthStack = *other.blockParenDepthStack; blockStatementStack = new vector; *blockStatementStack = *other.blockStatementStack; parenStatementStack = new vector; *parenStatementStack = *other.parenStatementStack; bracketBlockStateStack = new vector; *bracketBlockStateStack = *other.bracketBlockStateStack; inStatementIndentStack = new vector; *inStatementIndentStack = *other.inStatementIndentStack; inStatementIndentStackSizeStack = new vector; *inStatementIndentStackSizeStack = *other.inStatementIndentStackSizeStack; parenIndentStack = new vector; *parenIndentStack = *other.parenIndentStack; preprocIndentStack = new vector >; *preprocIndentStack = *other.preprocIndentStack; // Copy the pointers to vectors. // This is ok because the original ASBeautifier object // is not deleted until end of job. beautifierFileType = other.beautifierFileType; headers = other.headers; nonParenHeaders = other.nonParenHeaders; assignmentOperators = other.assignmentOperators; nonAssignmentOperators = other.nonAssignmentOperators; preBlockStatements = other.preBlockStatements; preCommandHeaders = other.preCommandHeaders; indentableHeaders = other.indentableHeaders; // protected variables // variables set by ASFormatter // must also be updated in activeBeautifierStack inLineNumber = other.inLineNumber; horstmannIndentInStatement = other.horstmannIndentInStatement; nonInStatementBracket = other.nonInStatementBracket; lineCommentNoBeautify = other.lineCommentNoBeautify; isElseHeaderIndent = other.isElseHeaderIndent; isCaseHeaderCommentIndent = other.isCaseHeaderCommentIndent; isNonInStatementArray = other.isNonInStatementArray; isSharpAccessor = other.isSharpAccessor; isSharpDelegate = other.isSharpDelegate; isInExternC = other.isInExternC; isInBeautifySQL = other.isInBeautifySQL; isInIndentableStruct = other.isInIndentableStruct; isInIndentablePreproc = other.isInIndentablePreproc; // private variables sourceIterator = other.sourceIterator; currentHeader = other.currentHeader; previousLastLineHeader = other.previousLastLineHeader; probationHeader = other.probationHeader; lastLineHeader = other.lastLineHeader; indentString = other.indentString; verbatimDelimiter = other.verbatimDelimiter; isInQuote = other.isInQuote; isInVerbatimQuote = other.isInVerbatimQuote; haveLineContinuationChar = other.haveLineContinuationChar; isInAsm = other.isInAsm; isInAsmOneLine = other.isInAsmOneLine; isInAsmBlock = other.isInAsmBlock; isInComment = other.isInComment; isInPreprocessorComment = other.isInPreprocessorComment; isInHorstmannComment = other.isInHorstmannComment; isInCase = other.isInCase; isInQuestion = other.isInQuestion; isInStatement = other.isInStatement; isInHeader = other.isInHeader; isInTemplate = other.isInTemplate; isInDefine = other.isInDefine; isInDefineDefinition = other.isInDefineDefinition; classIndent = other.classIndent; isIndentModeOff = other.isIndentModeOff; isInClassHeader = other.isInClassHeader; isInClassHeaderTab = other.isInClassHeaderTab; isInClassInitializer = other.isInClassInitializer; isInClass = other.isInClass; isInObjCMethodDefinition = other.isInObjCMethodDefinition; isImmediatelyPostObjCMethodDefinition = other.isImmediatelyPostObjCMethodDefinition; isInIndentablePreprocBlock = other.isInIndentablePreprocBlock; isInObjCInterface = other.isInObjCInterface; isInEnum = other.isInEnum; isInEnumTypeID = other.isInEnumTypeID; isInLet = other.isInLet; modifierIndent = other.modifierIndent; switchIndent = other.switchIndent; caseIndent = other.caseIndent; namespaceIndent = other.namespaceIndent; bracketIndent = other.bracketIndent; bracketIndentVtk = other.bracketIndentVtk; blockIndent = other.blockIndent; labelIndent = other.labelIndent; isInConditional = other.isInConditional; isModeManuallySet = other.isModeManuallySet; shouldForceTabIndentation = other.shouldForceTabIndentation; emptyLineFill = other.emptyLineFill; lineOpensWithLineComment = other.lineOpensWithLineComment; lineOpensWithComment = other.lineOpensWithComment; lineStartsInComment = other.lineStartsInComment; backslashEndsPrevLine = other.backslashEndsPrevLine; blockCommentNoIndent = other.blockCommentNoIndent; blockCommentNoBeautify = other.blockCommentNoBeautify; previousLineProbationTab = other.previousLineProbationTab; lineBeginsWithOpenBracket = other.lineBeginsWithOpenBracket; lineBeginsWithCloseBracket = other.lineBeginsWithCloseBracket; lineBeginsWithComma = other.lineBeginsWithComma; lineIsCommentOnly = other.lineIsCommentOnly; lineIsLineCommentOnly = other.lineIsLineCommentOnly; shouldIndentBrackettedLine = other.shouldIndentBrackettedLine; isInSwitch = other.isInSwitch; foundPreCommandHeader = other.foundPreCommandHeader; foundPreCommandMacro = other.foundPreCommandMacro; shouldAlignMethodColon = other.shouldAlignMethodColon; shouldIndentPreprocDefine = other.shouldIndentPreprocDefine; shouldIndentPreprocConditional = other.shouldIndentPreprocConditional; indentCount = other.indentCount; spaceIndentCount = other.spaceIndentCount; spaceIndentObjCMethodDefinition = other.spaceIndentObjCMethodDefinition; colonIndentObjCMethodDefinition = other.colonIndentObjCMethodDefinition; lineOpeningBlocksNum = other.lineOpeningBlocksNum; lineClosingBlocksNum = other.lineClosingBlocksNum; fileType = other.fileType; minConditionalOption = other.minConditionalOption; minConditionalIndent = other.minConditionalIndent; parenDepth = other.parenDepth; indentLength = other.indentLength; tabLength = other.tabLength; blockTabCount = other.blockTabCount; maxInStatementIndent = other.maxInStatementIndent; classInitializerIndents = other.classInitializerIndents; templateDepth = other.templateDepth; squareBracketCount = other.squareBracketCount; prevFinalLineSpaceIndentCount = other.prevFinalLineSpaceIndentCount; prevFinalLineIndentCount = other.prevFinalLineIndentCount; defineIndentCount = other.defineIndentCount; preprocBlockIndent = other.preprocBlockIndent; quoteChar = other.quoteChar; prevNonSpaceCh = other.prevNonSpaceCh; currentNonSpaceCh = other.currentNonSpaceCh; currentNonLegalCh = other.currentNonLegalCh; prevNonLegalCh = other.prevNonLegalCh; } /** * ASBeautifier's destructor */ ASBeautifier::~ASBeautifier() { deleteBeautifierContainer(waitingBeautifierStack); deleteBeautifierContainer(activeBeautifierStack); deleteContainer(waitingBeautifierStackLengthStack); deleteContainer(activeBeautifierStackLengthStack); deleteContainer(headerStack); deleteTempStacksContainer(tempStacks); deleteContainer(blockParenDepthStack); deleteContainer(blockStatementStack); deleteContainer(parenStatementStack); deleteContainer(bracketBlockStateStack); deleteContainer(inStatementIndentStack); deleteContainer(inStatementIndentStackSizeStack); deleteContainer(parenIndentStack); deleteContainer(preprocIndentStack); } /** * initialize the ASBeautifier. * * This init() should be called every time a ABeautifier object is to start * beautifying a NEW source file. * It is called only when a new ASFormatter object is created. * init() receives a pointer to a ASSourceIterator object that will be * used to iterate through the source code. * * @param iter a pointer to the ASSourceIterator or ASStreamIterator object. */ void ASBeautifier::init(ASSourceIterator* iter) { sourceIterator = iter; initVectors(); ASBase::init(getFileType()); initContainer(waitingBeautifierStack, new vector); initContainer(activeBeautifierStack, new vector); initContainer(waitingBeautifierStackLengthStack, new vector); initContainer(activeBeautifierStackLengthStack, new vector); initContainer(headerStack, new vector); initTempStacksContainer(tempStacks, new vector*>); tempStacks->push_back(new vector); initContainer(blockParenDepthStack, new vector); initContainer(blockStatementStack, new vector); initContainer(parenStatementStack, new vector); initContainer(bracketBlockStateStack, new vector); bracketBlockStateStack->push_back(true); initContainer(inStatementIndentStack, new vector); initContainer(inStatementIndentStackSizeStack, new vector); inStatementIndentStackSizeStack->push_back(0); initContainer(parenIndentStack, new vector); initContainer(preprocIndentStack, new vector >); previousLastLineHeader = NULL; currentHeader = NULL; isInQuote = false; isInVerbatimQuote = false; haveLineContinuationChar = false; isInAsm = false; isInAsmOneLine = false; isInAsmBlock = false; isInComment = false; isInPreprocessorComment = false; isInHorstmannComment = false; isInStatement = false; isInCase = false; isInQuestion = false; isIndentModeOff = false; isInClassHeader = false; isInClassHeaderTab = false; isInClassInitializer = false; isInClass = false; isInObjCMethodDefinition = false; isImmediatelyPostObjCMethodDefinition = false; isInIndentablePreprocBlock = false; isInObjCInterface = false; isInEnum = false; isInEnumTypeID = false; isInLet = false; isInHeader = false; isInTemplate = false; isInConditional = false; indentCount = 0; spaceIndentCount = 0; spaceIndentObjCMethodDefinition = 0; colonIndentObjCMethodDefinition = 0; lineOpeningBlocksNum = 0; lineClosingBlocksNum = 0; templateDepth = 0; squareBracketCount = 0; parenDepth = 0; blockTabCount = 0; prevFinalLineSpaceIndentCount = 0; prevFinalLineIndentCount = 0; defineIndentCount = 0; preprocBlockIndent = 0; prevNonSpaceCh = '{'; currentNonSpaceCh = '{'; prevNonLegalCh = '{'; currentNonLegalCh = '{'; quoteChar = ' '; probationHeader = NULL; lastLineHeader = NULL; backslashEndsPrevLine = false; lineOpensWithLineComment = false; lineOpensWithComment = false; lineStartsInComment = false; isInDefine = false; isInDefineDefinition = false; lineCommentNoBeautify = false; isElseHeaderIndent = false; isCaseHeaderCommentIndent = false; blockCommentNoIndent = false; blockCommentNoBeautify = false; previousLineProbationTab = false; lineBeginsWithOpenBracket = false; lineBeginsWithCloseBracket = false; lineBeginsWithComma = false; lineIsCommentOnly = false; lineIsLineCommentOnly = false; shouldIndentBrackettedLine = true; isInSwitch = false; foundPreCommandHeader = false; foundPreCommandMacro = false; isNonInStatementArray = false; isSharpAccessor = false; isSharpDelegate = false; isInExternC = false; isInBeautifySQL = false; isInIndentableStruct = false; isInIndentablePreproc = false; inLineNumber = 0; horstmannIndentInStatement = 0; nonInStatementBracket = 0; } /* * initialize the vectors */ void ASBeautifier::initVectors() { if (fileType == beautifierFileType) // don't build unless necessary return; beautifierFileType = fileType; headers->clear(); nonParenHeaders->clear(); assignmentOperators->clear(); nonAssignmentOperators->clear(); preBlockStatements->clear(); preCommandHeaders->clear(); indentableHeaders->clear(); ASResource::buildHeaders(headers, fileType, true); ASResource::buildNonParenHeaders(nonParenHeaders, fileType, true); ASResource::buildAssignmentOperators(assignmentOperators); ASResource::buildNonAssignmentOperators(nonAssignmentOperators); ASResource::buildPreBlockStatements(preBlockStatements, fileType); ASResource::buildPreCommandHeaders(preCommandHeaders, fileType); ASResource::buildIndentableHeaders(indentableHeaders); } /** * set indentation style to C/C++. */ void ASBeautifier::setCStyle() { fileType = C_TYPE; } /** * set indentation style to Java. */ void ASBeautifier::setJavaStyle() { fileType = JAVA_TYPE; } /** * set indentation style to C#. */ void ASBeautifier::setSharpStyle() { fileType = SHARP_TYPE; } /** * set mode manually set flag */ void ASBeautifier::setModeManuallySet(bool state) { isModeManuallySet = state; } /** * set tabLength equal to indentLength. * This is done when tabLength is not explicitly set by * "indent=force-tab-x" * */ void ASBeautifier::setDefaultTabLength() { tabLength = indentLength; } /** * indent using a different tab setting for indent=force-tab * * @param length number of spaces per tab. */ void ASBeautifier::setForceTabXIndentation(int length) { // set tabLength instead of indentLength indentString = "\t"; tabLength = length; shouldForceTabIndentation = true; } /** * indent using one tab per indentation */ void ASBeautifier::setTabIndentation(int length, bool forceTabs) { indentString = "\t"; indentLength = length; shouldForceTabIndentation = forceTabs; } /** * indent using a number of spaces per indentation. * * @param length number of spaces per indent. */ void ASBeautifier::setSpaceIndentation(int length) { indentString = string(length, ' '); indentLength = length; } /** * set the maximum indentation between two lines in a multi-line statement. * * @param max maximum indentation length. */ void ASBeautifier::setMaxInStatementIndentLength(int max) { maxInStatementIndent = max; } /** * set the minimum conditional indentation option. * * @param min minimal indentation option. */ void ASBeautifier::setMinConditionalIndentOption(int min) { minConditionalOption = min; } /** * set minConditionalIndent from the minConditionalOption. */ void ASBeautifier::setMinConditionalIndentLength() { if (minConditionalOption == MINCOND_ZERO) minConditionalIndent = 0; else if (minConditionalOption == MINCOND_ONE) minConditionalIndent = indentLength; else if (minConditionalOption == MINCOND_ONEHALF) minConditionalIndent = indentLength / 2; // minConditionalOption = INDENT_TWO else minConditionalIndent = indentLength * 2; } /** * set the state of the bracket indent option. If true, brackets will * be indented one additional indent. * * @param state state of option. */ void ASBeautifier::setBracketIndent(bool state) { bracketIndent = state; } /** * set the state of the bracket indent VTK option. If true, brackets will * be indented one additional indent, except for the opening bracket. * * @param state state of option. */ void ASBeautifier::setBracketIndentVtk(bool state) { // need to set both of these setBracketIndent(state); bracketIndentVtk = state; } /** * set the state of the block indentation option. If true, entire blocks * will be indented one additional indent, similar to the GNU indent style. * * @param state state of option. */ void ASBeautifier::setBlockIndent(bool state) { blockIndent = state; } /** * set the state of the class indentation option. If true, C++ class * definitions will be indented one additional indent. * * @param state state of option. */ void ASBeautifier::setClassIndent(bool state) { classIndent = state; } /** * set the state of the modifier indentation option. If true, C++ class * access modifiers will be indented one-half an indent. * * @param state state of option. */ void ASBeautifier::setModifierIndent(bool state) { modifierIndent = state; } /** * set the state of the switch indentation option. If true, blocks of 'switch' * statements will be indented one additional indent. * * @param state state of option. */ void ASBeautifier::setSwitchIndent(bool state) { switchIndent = state; } /** * set the state of the case indentation option. If true, lines of 'case' * statements will be indented one additional indent. * * @param state state of option. */ void ASBeautifier::setCaseIndent(bool state) { caseIndent = state; } /** * set the state of the namespace indentation option. * If true, blocks of 'namespace' statements will be indented one * additional indent. Otherwise, NO indentation will be added. * * @param state state of option. */ void ASBeautifier::setNamespaceIndent(bool state) { namespaceIndent = state; } /** * set the state of the label indentation option. * If true, labels will be indented one indent LESS than the * current indentation level. * If false, labels will be flushed to the left with NO * indent at all. * * @param state state of option. */ void ASBeautifier::setLabelIndent(bool state) { labelIndent = state; } /** * set the state of the preprocessor indentation option. * If true, multi-line #define statements will be indented. * * @param state state of option. */ void ASBeautifier::setPreprocDefineIndent(bool state) { shouldIndentPreprocDefine = state; } void ASBeautifier::setPreprocConditionalIndent(bool state) { shouldIndentPreprocConditional = state; } /** * set the state of the empty line fill option. * If true, empty lines will be filled with the whitespace. * of their previous lines. * If false, these lines will remain empty. * * @param state state of option. */ void ASBeautifier::setEmptyLineFill(bool state) { emptyLineFill = state; } void ASBeautifier::setAlignMethodColon(bool state) { shouldAlignMethodColon = state; } /** * get the file type. */ int ASBeautifier::getFileType() const { return fileType; } /** * get the number of spaces per indent * * @return value of indentLength option. */ int ASBeautifier::getIndentLength(void) const { return indentLength; } /** * get the char used for indentation, space or tab * * @return the char used for indentation. */ string ASBeautifier::getIndentString(void) const { return indentString; } /** * get mode manually set flag */ bool ASBeautifier::getModeManuallySet() const { return isModeManuallySet; } /** * get the state of the force tab indentation option. * * @return state of force tab indentation. */ bool ASBeautifier::getForceTabIndentation(void) const { return shouldForceTabIndentation; } /** * get the state of the block indentation option. * * @return state of blockIndent option. */ bool ASBeautifier::getBlockIndent(void) const { return blockIndent; } /** * get the state of the bracket indentation option. * * @return state of bracketIndent option. */ bool ASBeautifier::getBracketIndent(void) const { return bracketIndent; } /** * Get the state of the namespace indentation option. If true, blocks * of the 'namespace' statement will be indented one additional indent. * * @return state of namespaceIndent option. */ bool ASBeautifier::getNamespaceIndent(void) const { return namespaceIndent; } /** * Get the state of the class indentation option. If true, blocks of * the 'class' statement will be indented one additional indent. * * @return state of classIndent option. */ bool ASBeautifier::getClassIndent(void) const { return classIndent; } /** * Get the state of the class access modifier indentation option. * If true, the class access modifiers will be indented one-half indent. * * @return state of modifierIndent option. */ bool ASBeautifier::getModifierIndent(void) const { return modifierIndent; } /** * get the state of the switch indentation option. If true, blocks of * the 'switch' statement will be indented one additional indent. * * @return state of switchIndent option. */ bool ASBeautifier::getSwitchIndent(void) const { return switchIndent; } /** * get the state of the case indentation option. If true, lines of 'case' * statements will be indented one additional indent. * * @return state of caseIndent option. */ bool ASBeautifier::getCaseIndent(void) const { return caseIndent; } /** * get the state of the empty line fill option. * If true, empty lines will be filled with the whitespace. * of their previous lines. * If false, these lines will remain empty. * * @return state of emptyLineFill option. */ bool ASBeautifier::getEmptyLineFill(void) const { return emptyLineFill; } /** * get the state of the preprocessor indentation option. * If true, preprocessor "define" lines will be indented. * If false, preprocessor "define" lines will be unchanged. * * @return state of shouldIndentPreprocDefine option. */ bool ASBeautifier::getPreprocDefineIndent(void) const { return shouldIndentPreprocDefine; } /** * get the length of the tab indentation option. * * @return length of tab indent option. */ int ASBeautifier::getTabLength(void) const { return tabLength; } /** * beautify a line of source code. * every line of source code in a source code file should be sent * one after the other to the beautify method. * * @return the indented line. * @param originalLine the original unindented line. */ string ASBeautifier::beautify(const string &originalLine) { string line; bool isInQuoteContinuation = isInVerbatimQuote | haveLineContinuationChar; currentHeader = NULL; lastLineHeader = NULL; blockCommentNoBeautify = blockCommentNoIndent; isInClass = false; isInSwitch = false; lineBeginsWithOpenBracket = false; lineBeginsWithCloseBracket = false; lineBeginsWithComma = false; lineIsCommentOnly = false; lineIsLineCommentOnly = false; shouldIndentBrackettedLine = true; isInAsmOneLine = false; lineOpensWithLineComment = false; lineOpensWithComment = false; lineStartsInComment = isInComment; previousLineProbationTab = false; haveLineContinuationChar = false; lineOpeningBlocksNum = 0; lineClosingBlocksNum = 0; if (isImmediatelyPostObjCMethodDefinition) clearObjCMethodDefinitionAlignment(); // handle and remove white spaces around the line: // If not in comment, first find out size of white space before line, // so that possible comments starting in the line continue in // relation to the preliminary white-space. if (isInQuoteContinuation) { // trim a single space added by ASFormatter, otherwise leave it alone if (!(originalLine.length() == 1 && originalLine[0] == ' ')) line = originalLine; } else if (isInComment || isInBeautifySQL) { // trim the end of comment and SQL lines line = originalLine; size_t trimEnd = line.find_last_not_of(" \t"); if (trimEnd == string::npos) trimEnd = 0; else trimEnd++; if (trimEnd < line.length()) line.erase(trimEnd); // does a bracket open the line size_t firstChar = line.find_first_not_of(" \t"); if (firstChar != string::npos) { if (line[firstChar] == '{') lineBeginsWithOpenBracket = true; else if (line[firstChar] == '}') lineBeginsWithCloseBracket = true; else if (line[firstChar] == ',') lineBeginsWithComma = true; } } else { line = trim(originalLine); if (line.length() > 0) { if (line[0] == '{') lineBeginsWithOpenBracket = true; else if (line[0] == '}') lineBeginsWithCloseBracket = true; else if (line[0] == ',') lineBeginsWithComma = true; else if (line.compare(0, 2, "//") == 0) lineIsLineCommentOnly = true; else if (line.compare(0, 2, "/*") == 0) { if (line.find("*/", 2) != string::npos) lineIsCommentOnly = true; } } isInHorstmannComment = false; size_t j = line.find_first_not_of(" \t{"); if (j != string::npos && line.compare(j, 2, "//") == 0) lineOpensWithLineComment = true; if (j != string::npos && line.compare(j, 2, "/*") == 0) { lineOpensWithComment = true; size_t k = line.find_first_not_of(" \t"); if (k != string::npos && line.compare(k, 1, "{") == 0) isInHorstmannComment = true; } } // When indent is OFF the lines must still be processed by ASBeautifier. // Otherwise the lines immediately following may not be indented correctly. if ((lineIsLineCommentOnly || lineIsCommentOnly) && line.find("*INDENT-OFF*", 0) != string::npos) isIndentModeOff = true; if (line.length() == 0) { if (backslashEndsPrevLine) { backslashEndsPrevLine = false; isInDefine = false; isInDefineDefinition = false; } if (emptyLineFill && !isInQuoteContinuation) { if (isInIndentablePreprocBlock) return preLineWS(preprocBlockIndent, 0); else if (!headerStack->empty() || isInEnum) return preLineWS(prevFinalLineIndentCount, prevFinalLineSpaceIndentCount); // must fall thru here } else return line; } // handle preprocessor commands if (isInIndentablePreprocBlock && line.length() > 0 && line[0] != '#') { string indentedLine; if (isInClassHeaderTab || isInClassInitializer) { // parsing is turned off in ASFormatter by indent-off // the originalLine will probably never be returned here indentedLine = preLineWS(prevFinalLineIndentCount, prevFinalLineSpaceIndentCount) + line; return getIndentedLineReturn(indentedLine, originalLine); } else { indentedLine = preLineWS(preprocBlockIndent, 0) + line; return getIndentedLineReturn(indentedLine, originalLine); } } if (!isInComment && !isInQuoteContinuation && line.length() > 0 && ((line[0] == '#' && !isIndentedPreprocessor(line, 0)) || backslashEndsPrevLine)) { if (line[0] == '#' && !isInDefine) { string preproc = extractPreprocessorStatement(line); processPreprocessor(preproc, line); if (isInIndentablePreprocBlock || isInIndentablePreproc) { string indentedLine; if ((preproc.length() >= 2 && preproc.substr(0, 2) == "if")) // #if, #ifdef, #ifndef { indentedLine = preLineWS(preprocBlockIndent, 0) + line; preprocBlockIndent += 1; isInIndentablePreprocBlock = true; } else if (preproc == "else" || preproc == "elif") { indentedLine = preLineWS(preprocBlockIndent - 1, 0) + line; } else if (preproc == "endif") { preprocBlockIndent -= 1; indentedLine = preLineWS(preprocBlockIndent, 0) + line; if (preprocBlockIndent == 0) isInIndentablePreprocBlock = false; } else indentedLine = preLineWS(preprocBlockIndent, 0) + line; return getIndentedLineReturn(indentedLine, originalLine); } if (shouldIndentPreprocConditional && preproc.length() > 0) { string indentedLine; if (preproc.length() >= 2 && preproc.substr(0, 2) == "if") // #if, #ifdef, #ifndef { pair entry; // indentCount, spaceIndentCount if (!isInDefine && activeBeautifierStack != NULL && !activeBeautifierStack->empty()) entry = activeBeautifierStack->back()->computePreprocessorIndent(); else entry = computePreprocessorIndent(); preprocIndentStack->push_back(entry); indentedLine = preLineWS(preprocIndentStack->back().first, preprocIndentStack->back().second) + line; return getIndentedLineReturn(indentedLine, originalLine); } else if (preproc == "else" || preproc == "elif") { if (preprocIndentStack->size() > 0) // if no entry don't indent { indentedLine = preLineWS(preprocIndentStack->back().first, preprocIndentStack->back().second) + line; return getIndentedLineReturn(indentedLine, originalLine); } } else if (preproc == "endif") { if (preprocIndentStack->size() > 0) // if no entry don't indent { indentedLine = preLineWS(preprocIndentStack->back().first, preprocIndentStack->back().second) + line; preprocIndentStack->pop_back(); return getIndentedLineReturn(indentedLine, originalLine); } } } } // check if the last char is a backslash if (line.length() > 0) backslashEndsPrevLine = (line[line.length() - 1] == '\\'); // comments within the definition line can be continued without the backslash if (isInPreprocessorUnterminatedComment(line)) backslashEndsPrevLine = true; // check if this line ends a multi-line #define // if so, use the #define's cloned beautifier for the line's indentation // and then remove it from the active beautifier stack and delete it. if (!backslashEndsPrevLine && isInDefineDefinition && !isInDefine) { ASBeautifier* defineBeautifier; isInDefineDefinition = false; defineBeautifier = activeBeautifierStack->back(); activeBeautifierStack->pop_back(); string indentedLine = defineBeautifier->beautify(line); delete defineBeautifier; return getIndentedLineReturn(indentedLine, originalLine); } // unless this is a multi-line #define, return this precompiler line as is. if (!isInDefine && !isInDefineDefinition) return originalLine; } // if there exists any worker beautifier in the activeBeautifierStack, // then use it instead of me to indent the current line. // variables set by ASFormatter must be updated. if (!isInDefine && activeBeautifierStack != NULL && !activeBeautifierStack->empty()) { activeBeautifierStack->back()->inLineNumber = inLineNumber; activeBeautifierStack->back()->horstmannIndentInStatement = horstmannIndentInStatement; activeBeautifierStack->back()->nonInStatementBracket = nonInStatementBracket; activeBeautifierStack->back()->lineCommentNoBeautify = lineCommentNoBeautify; activeBeautifierStack->back()->isElseHeaderIndent = isElseHeaderIndent; activeBeautifierStack->back()->isCaseHeaderCommentIndent = isCaseHeaderCommentIndent; activeBeautifierStack->back()->isNonInStatementArray = isNonInStatementArray; activeBeautifierStack->back()->isSharpAccessor = isSharpAccessor; activeBeautifierStack->back()->isSharpDelegate = isSharpDelegate; activeBeautifierStack->back()->isInExternC = isInExternC; activeBeautifierStack->back()->isInBeautifySQL = isInBeautifySQL; activeBeautifierStack->back()->isInIndentableStruct = isInIndentableStruct; activeBeautifierStack->back()->isInIndentablePreproc = isInIndentablePreproc; // must return originalLine not the trimmed line return activeBeautifierStack->back()->beautify(originalLine); } // Flag an indented header in case this line is a one-line block. // The header in the header stack will be deleted by a one-line block. bool isInExtraHeaderIndent = false; if (!headerStack->empty() && lineBeginsWithOpenBracket && (headerStack->back() != &AS_OPEN_BRACKET || probationHeader != NULL)) isInExtraHeaderIndent = true; size_t iPrelim = headerStack->size(); // calculate preliminary indentation based on headerStack and data from past lines computePreliminaryIndentation(); // parse characters in the current line. parseCurrentLine(line); // handle special cases of indentation adjustParsedLineIndentation(iPrelim, isInExtraHeaderIndent); // Objective-C continuation line if (isInObjCMethodDefinition) { // register indent for Objective-C continuation line if (line.length() > 0 && (line[0] == '-' || line[0] == '+')) { if (shouldAlignMethodColon) { colonIndentObjCMethodDefinition = line.find(':'); } else if (inStatementIndentStack->empty() || inStatementIndentStack->back() == 0) { inStatementIndentStack->push_back(indentLength); isInStatement = true; } } // set indent for last definition line else if (!lineBeginsWithOpenBracket) { if (shouldAlignMethodColon) spaceIndentCount = computeObjCColonAlignment(line, colonIndentObjCMethodDefinition); else if (inStatementIndentStack->empty()) spaceIndentCount = spaceIndentObjCMethodDefinition; } } if (isInDefine) { if (line.length() > 0 && line[0] == '#') { // the 'define' does not have to be attached to the '#' string preproc = trim(line.substr(1)); if (preproc.compare(0, 6, "define") == 0) { if (!inStatementIndentStack->empty() && inStatementIndentStack->back() > 0) { defineIndentCount = indentCount; } else { defineIndentCount = indentCount - 1; --indentCount; } } } indentCount -= defineIndentCount; } if (indentCount < 0) indentCount = 0; if (lineCommentNoBeautify || blockCommentNoBeautify || isInQuoteContinuation) indentCount = spaceIndentCount = 0; // finally, insert indentations into beginning of line string indentedLine = preLineWS(indentCount, spaceIndentCount) + line; indentedLine = getIndentedLineReturn(indentedLine, originalLine); prevFinalLineSpaceIndentCount = spaceIndentCount; prevFinalLineIndentCount = indentCount; if (lastLineHeader != NULL) previousLastLineHeader = lastLineHeader; if ((lineIsLineCommentOnly || lineIsCommentOnly) && line.find("*INDENT-ON*", 0) != string::npos) isIndentModeOff = false; return indentedLine; } string &ASBeautifier::getIndentedLineReturn(string &newLine, const string &originalLine) const { if (isIndentModeOff) return const_cast(originalLine); return newLine; } string ASBeautifier::preLineWS(int lineIndentCount, int lineSpaceIndentCount) const { if (shouldForceTabIndentation) { if (tabLength != indentLength) { // adjust for different tab length int indentCountOrig = lineIndentCount; int spaceIndentCountOrig = lineSpaceIndentCount; lineIndentCount = ((indentCountOrig * indentLength) + spaceIndentCountOrig) / tabLength; lineSpaceIndentCount = ((indentCountOrig * indentLength) + spaceIndentCountOrig) % tabLength; } else { lineIndentCount += lineSpaceIndentCount / indentLength; lineSpaceIndentCount = lineSpaceIndentCount % indentLength; } } string ws; for (int i = 0; i < lineIndentCount; i++) ws += indentString; while ((lineSpaceIndentCount--) > 0) ws += string(" "); return ws; } /** * register an in-statement indent. */ void ASBeautifier::registerInStatementIndent(const string &line, int i, int spaceTabCount_, int tabIncrementIn, int minIndent, bool updateParenStack) { int inStatementIndent; int remainingCharNum = line.length() - i; int nextNonWSChar = getNextProgramCharDistance(line, i); // if indent is around the last char in the line, indent instead one indent from the previous indent if (nextNonWSChar == remainingCharNum) { int previousIndent = spaceTabCount_; if (!inStatementIndentStack->empty()) previousIndent = inStatementIndentStack->back(); int currIndent = /*2*/ indentLength + previousIndent; if (currIndent > maxInStatementIndent && line[i] != '{') currIndent = indentLength * 2 + spaceTabCount_; inStatementIndentStack->push_back(currIndent); if (updateParenStack) parenIndentStack->push_back(previousIndent); return; } if (updateParenStack) parenIndentStack->push_back(i + spaceTabCount_ - horstmannIndentInStatement); int tabIncrement = tabIncrementIn; // check for following tabs for (int j = i + 1; j < (i + nextNonWSChar); j++) { if (line[j] == '\t') tabIncrement += convertTabToSpaces(j, tabIncrement); } inStatementIndent = i + nextNonWSChar + spaceTabCount_ + tabIncrement; // check for run-in statement if (i > 0 && line[0] == '{') inStatementIndent -= indentLength; if (inStatementIndent < minIndent) inStatementIndent = minIndent + spaceTabCount_; // this is not done for an in-statement array if (inStatementIndent > maxInStatementIndent && !(prevNonLegalCh == '=' && currentNonLegalCh == '{')) inStatementIndent = indentLength * 2 + spaceTabCount_; if (!inStatementIndentStack->empty() && inStatementIndent < inStatementIndentStack->back()) inStatementIndent = inStatementIndentStack->back(); // the block opener is not indented for a NonInStatementArray if (isNonInStatementArray && !isInEnum && !bracketBlockStateStack->empty() && bracketBlockStateStack->back()) inStatementIndent = 0; inStatementIndentStack->push_back(inStatementIndent); } /** * Register an in-statement indent for a class header or a class initializer colon. */ void ASBeautifier::registerInStatementIndentColon(const string &line, int i, int tabIncrementIn) { assert(line[i] == ':'); assert(isInClassInitializer || isInClassHeaderTab); // register indent at first word after the colon size_t firstChar = line.find_first_not_of(" \t"); if (firstChar == (size_t)i) // firstChar is ':' { size_t firstWord = line.find_first_not_of(" \t", firstChar + 1); if (firstChar != string::npos) { int inStatementIndent = firstWord + spaceIndentCount + tabIncrementIn; inStatementIndentStack->push_back(inStatementIndent); isInStatement = true; } } } /** * Compute indentation for a preprocessor #if statement. * This may be called for the activeBeautiferStack * instead of the active ASBeautifier object. */ pair ASBeautifier::computePreprocessorIndent() { computePreliminaryIndentation(); pair entry(indentCount, spaceIndentCount); if (!headerStack->empty() && entry.first > 0 && (headerStack->back() == &AS_IF || headerStack->back() == &AS_ELSE || headerStack->back() == &AS_FOR || headerStack->back() == &AS_WHILE)) --entry.first; return entry; } /** * get distance to the next non-white space, non-comment character in the line. * if no such character exists, return the length remaining to the end of the line. */ int ASBeautifier::getNextProgramCharDistance(const string &line, int i) const { bool inComment = false; int remainingCharNum = line.length() - i; int charDistance; char ch; for (charDistance = 1; charDistance < remainingCharNum; charDistance++) { ch = line[i + charDistance]; if (inComment) { if (line.compare(i + charDistance, 2, "*/") == 0) { charDistance++; inComment = false; } continue; } else if (isWhiteSpace(ch)) continue; else if (ch == '/') { if (line.compare(i + charDistance, 2, "//") == 0) return remainingCharNum; else if (line.compare(i + charDistance, 2, "/*") == 0) { charDistance++; inComment = true; } } else return charDistance; } return charDistance; } // check if a specific line position contains a header. const string* ASBeautifier::findHeader(const string &line, int i, const vector* possibleHeaders) const { assert(isCharPotentialHeader(line, i)); // check the word size_t maxHeaders = possibleHeaders->size(); for (size_t p = 0; p < maxHeaders; p++) { const string* header = (*possibleHeaders)[p]; const size_t wordEnd = i + header->length(); if (wordEnd > line.length()) continue; int result = (line.compare(i, header->length(), *header)); if (result > 0) continue; if (result < 0) break; // check that this is not part of a longer word if (wordEnd == line.length()) return header; if (isLegalNameChar(line[wordEnd])) continue; const char peekChar = peekNextChar(line, wordEnd - 1); // is not a header if part of a definition if (peekChar == ',' || peekChar == ')') break; // the following accessor definitions are NOT headers // goto default; is NOT a header // default(int) keyword in C# is NOT a header else if ((header == &AS_GET || header == &AS_SET || header == &AS_DEFAULT) && (peekChar == ';' || peekChar == '(' || peekChar == '=')) break; return header; } return NULL; } // check if a specific line position contains an operator. const string* ASBeautifier::findOperator(const string &line, int i, const vector* possibleOperators) const { assert(isCharPotentialOperator(line[i])); // find the operator in the vector // the vector contains the LONGEST operators first // must loop thru the entire vector size_t maxOperators = possibleOperators->size(); for (size_t p = 0; p < maxOperators; p++) { const size_t wordEnd = i + (*(*possibleOperators)[p]).length(); if (wordEnd > line.length()) continue; if (line.compare(i, (*(*possibleOperators)[p]).length(), *(*possibleOperators)[p]) == 0) return (*possibleOperators)[p]; } return NULL; } /** * find the index number of a string element in a container of strings * * @return the index number of element in the container. -1 if element not found. * @param container a vector of strings. * @param element the element to find . */ int ASBeautifier::indexOf(vector &container, const string* element) const { vector::const_iterator where; where = find(container.begin(), container.end(), element); if (where == container.end()) return -1; else return (int) (where - container.begin()); } /** * convert tabs to spaces. * i is the position of the character to convert to spaces. * tabIncrementIn is the increment that must be added for tab indent characters * to get the correct column for the current tab. */ int ASBeautifier::convertTabToSpaces(int i, int tabIncrementIn) const { int tabToSpacesAdjustment = indentLength - 1 - ((tabIncrementIn + i) % indentLength); return tabToSpacesAdjustment; } /** * trim removes the white space surrounding a line. * * @return the trimmed line. * @param str the line to trim. */ string ASBeautifier::trim(const string &str) const { int start = 0; int end = str.length() - 1; while (start < end && isWhiteSpace(str[start])) start++; while (start <= end && isWhiteSpace(str[end])) end--; // don't trim if it ends in a continuation if (end > -1 && str[end] == '\\') end = str.length() - 1; string returnStr(str, start, end + 1 - start); return returnStr; } /** * rtrim removes the white space from the end of a line. * * @return the trimmed line. * @param str the line to trim. */ string ASBeautifier::rtrim(const string &str) const { size_t len = str.length(); size_t end = str.find_last_not_of(" \t"); if (end == string::npos || end == len - 1) return str; string returnStr(str, 0, end + 1); return returnStr; } /** * Copy tempStacks for the copy constructor. * The value of the vectors must also be copied. */ vector*>* ASBeautifier::copyTempStacks(const ASBeautifier &other) const { vector*>* tempStacksNew = new vector*>; vector*>::iterator iter; for (iter = other.tempStacks->begin(); iter != other.tempStacks->end(); ++iter) { vector* newVec = new vector; *newVec = **iter; tempStacksNew->push_back(newVec); } return tempStacksNew; } /** * delete a member vectors to eliminate memory leak reporting */ void ASBeautifier::deleteBeautifierVectors() { beautifierFileType = 9; // reset to an invalid type delete headers; delete nonParenHeaders; delete preBlockStatements; delete preCommandHeaders; delete assignmentOperators; delete nonAssignmentOperators; delete indentableHeaders; } /** * delete a vector object * T is the type of vector * used for all vectors except tempStacks */ template void ASBeautifier::deleteContainer(T &container) { if (container != NULL) { container->clear(); delete (container); container = NULL; } } /** * Delete the ASBeautifier vector object. * This is a vector of pointers to ASBeautifier objects allocated with the 'new' operator. * Therefore the ASBeautifier objects have to be deleted in addition to the * ASBeautifier pointer entries. */ void ASBeautifier::deleteBeautifierContainer(vector* &container) { if (container != NULL) { vector::iterator iter = container->begin(); while (iter < container->end()) { delete *iter; ++iter; } container->clear(); delete (container); container = NULL; } } /** * Delete the tempStacks vector object. * The tempStacks is a vector of pointers to strings allocated with the 'new' operator. * Therefore the strings have to be deleted in addition to the tempStacks entries. */ void ASBeautifier::deleteTempStacksContainer(vector*>* &container) { if (container != NULL) { vector*>::iterator iter = container->begin(); while (iter < container->end()) { delete *iter; ++iter; } container->clear(); delete (container); container = NULL; } } /** * initialize a vector object * T is the type of vector used for all vectors */ template void ASBeautifier::initContainer(T &container, T value) { // since the ASFormatter object is never deleted, // the existing vectors must be deleted before creating new ones if (container != NULL) deleteContainer(container); container = value; } /** * Initialize the tempStacks vector object. * The tempStacks is a vector of pointers to strings allocated with the 'new' operator. * Any residual entries are deleted before the vector is initialized. */ void ASBeautifier::initTempStacksContainer(vector*>* &container, vector*>* value) { if (container != NULL) deleteTempStacksContainer(container); container = value; } /** * Determine if an assignment statement ends with a comma * that is not in a function argument. It ends with a * comma if a comma is the last char on the line. * * @return true if line ends with a comma, otherwise false. */ bool ASBeautifier::statementEndsWithComma(const string &line, int index) const { assert(line[index] == '='); bool isInComment_ = false; bool isInQuote_ = false; int parenCount = 0; size_t lineLength = line.length(); size_t i = 0; char quoteChar_ = ' '; for (i = index + 1; i < lineLength; ++i) { char ch = line[i]; if (isInComment_) { if (line.compare(i, 2, "*/") == 0) { isInComment_ = false; ++i; } continue; } if (ch == '\\') { ++i; continue; } if (isInQuote_) { if (ch == quoteChar_) isInQuote_ = false; continue; } if (ch == '"' || ch == '\'') { isInQuote_ = true; quoteChar_ = ch; continue; } if (line.compare(i, 2, "//") == 0) break; if (line.compare(i, 2, "/*") == 0) { if (isLineEndComment(line, i)) break; else { isInComment_ = true; ++i; continue; } } if (ch == '(') parenCount++; if (ch == ')') parenCount--; } if (isInComment_ || isInQuote_ || parenCount > 0) return false; size_t lastChar = line.find_last_not_of(" \t", i - 1); if (lastChar == string::npos || line[lastChar] != ',') return false; return true; } /** * check if current comment is a line-end comment * * @return is before a line-end comment. */ bool ASBeautifier::isLineEndComment(const string &line, int startPos) const { assert(line.compare(startPos, 2, "/*") == 0); // comment must be closed on this line with nothing after it size_t endNum = line.find("*/", startPos + 2); if (endNum != string::npos) { size_t nextChar = line.find_first_not_of(" \t", endNum + 2); if (nextChar == string::npos) return true; } return false; } /** * get the previous word index for an assignment operator * * @return is the index to the previous word (the in statement indent). */ int ASBeautifier::getInStatementIndentAssign(const string &line, size_t currPos) const { assert(line[currPos] == '='); if (currPos == 0) return 0; // get the last legal word (may be a number) size_t end = line.find_last_not_of(" \t", currPos - 1); if (end == string::npos || !isLegalNameChar(line[end])) return 0; int start; // start of the previous word for (start = end; start > -1; start--) { if (!isLegalNameChar(line[start]) || line[start] == '.') break; } start++; return start; } /** * get the instatement indent for a comma * * @return is the indent to the second word on the line (the in statement indent). */ int ASBeautifier::getInStatementIndentComma(const string &line, size_t currPos) const { assert(line[currPos] == ','); // get first word on a line size_t indent = line.find_first_not_of(" \t"); if (indent == string::npos || !isLegalNameChar(line[indent])) return 0; // bypass first word for (; indent < currPos; indent++) { if (!isLegalNameChar(line[indent])) break; } indent++; if (indent >= currPos || indent < 4) return 0; // point to second word or assignment operator indent = line.find_first_not_of(" \t", indent); if (indent == string::npos || indent >= currPos) return 0; return indent; } /** * get the next word on a line * the argument 'currPos' must point to the current position. * * @return is the next word or an empty string if none found. */ string ASBeautifier::getNextWord(const string &line, size_t currPos) const { size_t lineLength = line.length(); // get the last legal word (may be a number) if (currPos == lineLength - 1) return string(); size_t start = line.find_first_not_of(" \t", currPos + 1); if (start == string::npos || !isLegalNameChar(line[start])) return string(); size_t end; // end of the current word for (end = start + 1; end <= lineLength; end++) { if (!isLegalNameChar(line[end]) || line[end] == '.') break; } return line.substr(start, end - start); } /** * Check if a preprocessor directive is always indented. * C# "region" and "endregion" are always indented. * C/C++ "pragma omp" is always indented. * * @return is true or false. */ bool ASBeautifier::isIndentedPreprocessor(const string &line, size_t currPos) const { assert(line[0] == '#'); string nextWord = getNextWord(line, currPos); if (nextWord == "region" || nextWord == "endregion") return true; // is it #pragma omp if (nextWord == "pragma") { // find pragma size_t start = line.find("pragma"); if (start == string::npos || !isLegalNameChar(line[start])) return false; // bypass pragma for (; start < line.length(); start++) { if (!isLegalNameChar(line[start])) break; } start++; if (start >= line.length()) return false; // point to start of second word start = line.find_first_not_of(" \t", start); if (start == string::npos) return false; // point to end of second word size_t end; for (end = start; end < line.length(); end++) { if (!isLegalNameChar(line[end])) break; } // check for "pragma omp" string word = line.substr(start, end - start); if (word == "omp" || word == "region" || word == "endregion") return true; } return false; } /** * Check if a preprocessor directive is checking for __cplusplus defined. * * @return is true or false. */ bool ASBeautifier::isPreprocessorConditionalCplusplus(const string &line) const { string preproc = trim(line.substr(1)); if (preproc.compare(0, 5, "ifdef") == 0 && getNextWord(preproc, 4) == "__cplusplus") return true; if (preproc.compare(0, 2, "if") == 0) { // check for " #if defined(__cplusplus)" size_t charNum = 2; charNum = preproc.find_first_not_of(" \t", charNum); if (preproc.compare(charNum, 7, "defined") == 0) { charNum += 7; charNum = preproc.find_first_not_of(" \t", charNum); if (preproc.compare(charNum, 1, "(") == 0) { ++charNum; charNum = preproc.find_first_not_of(" \t", charNum); if (preproc.compare(charNum, 11, "__cplusplus") == 0) return true; } } } return false; } /** * Check if a preprocessor definition contains an unterminated comment. * Comments within a preprocessor definition can be continued without the backslash. * * @return is true or false. */ bool ASBeautifier::isInPreprocessorUnterminatedComment(const string &line) { if (!isInPreprocessorComment) { size_t startPos = line.find("/*"); if (startPos == string::npos) return false; } size_t endNum = line.find("*/"); if (endNum != string::npos) { isInPreprocessorComment = false; return false; } isInPreprocessorComment = true; return true; } void ASBeautifier::popLastInStatementIndent() { assert(!inStatementIndentStackSizeStack->empty()); int previousIndentStackSize = inStatementIndentStackSizeStack->back(); if (inStatementIndentStackSizeStack->size() > 1) inStatementIndentStackSizeStack->pop_back(); while (previousIndentStackSize < (int) inStatementIndentStack->size()) inStatementIndentStack->pop_back(); } // for unit testing int ASBeautifier::getBeautifierFileType() const { return beautifierFileType; } /** * Process preprocessor statements and update the beautifier stacks. */ void ASBeautifier::processPreprocessor(const string &preproc, const string &line) { // When finding a multi-lined #define statement, the original beautifier // 1. sets its isInDefineDefinition flag // 2. clones a new beautifier that will be used for the actual indentation // of the #define. This clone is put into the activeBeautifierStack in order // to be called for the actual indentation. // The original beautifier will have isInDefineDefinition = true, isInDefine = false // The cloned beautifier will have isInDefineDefinition = true, isInDefine = true if (shouldIndentPreprocDefine && preproc == "define" && line[line.length() - 1] == '\\') { if (!isInDefineDefinition) { ASBeautifier* defineBeautifier; // this is the original beautifier isInDefineDefinition = true; // push a new beautifier into the active stack // this beautifier will be used for the indentation of this define defineBeautifier = new ASBeautifier(*this); activeBeautifierStack->push_back(defineBeautifier); } else { // the is the cloned beautifier that is in charge of indenting the #define. isInDefine = true; } } else if (preproc.length() >= 2 && preproc.substr(0, 2) == "if") { if (isPreprocessorConditionalCplusplus(line) && !g_preprocessorCppExternCBracket) g_preprocessorCppExternCBracket = 1; // push a new beautifier into the stack waitingBeautifierStackLengthStack->push_back(waitingBeautifierStack->size()); activeBeautifierStackLengthStack->push_back(activeBeautifierStack->size()); if (activeBeautifierStackLengthStack->back() == 0) waitingBeautifierStack->push_back(new ASBeautifier(*this)); else waitingBeautifierStack->push_back(new ASBeautifier(*activeBeautifierStack->back())); } else if (preproc == "else") { if (waitingBeautifierStack && !waitingBeautifierStack->empty()) { // MOVE current waiting beautifier to active stack. activeBeautifierStack->push_back(waitingBeautifierStack->back()); waitingBeautifierStack->pop_back(); } } else if (preproc == "elif") { if (waitingBeautifierStack && !waitingBeautifierStack->empty()) { // append a COPY current waiting beautifier to active stack, WITHOUT deleting the original. activeBeautifierStack->push_back(new ASBeautifier(*(waitingBeautifierStack->back()))); } } else if (preproc == "endif") { int stackLength; ASBeautifier* beautifier; if (waitingBeautifierStackLengthStack != NULL && !waitingBeautifierStackLengthStack->empty()) { stackLength = waitingBeautifierStackLengthStack->back(); waitingBeautifierStackLengthStack->pop_back(); while ((int) waitingBeautifierStack->size() > stackLength) { beautifier = waitingBeautifierStack->back(); waitingBeautifierStack->pop_back(); delete beautifier; } } if (!activeBeautifierStackLengthStack->empty()) { stackLength = activeBeautifierStackLengthStack->back(); activeBeautifierStackLengthStack->pop_back(); while ((int) activeBeautifierStack->size() > stackLength) { beautifier = activeBeautifierStack->back(); activeBeautifierStack->pop_back(); delete beautifier; } } } } // Compute the preliminary indentation based on data in the headerStack // and data from previous lines. // Update the class variable indentCount. void ASBeautifier::computePreliminaryIndentation() { indentCount = 0; spaceIndentCount = 0; isInClassHeaderTab = false; if (isInObjCMethodDefinition && !inStatementIndentStack->empty()) spaceIndentObjCMethodDefinition = inStatementIndentStack->back(); if (!inStatementIndentStack->empty()) spaceIndentCount = inStatementIndentStack->back(); for (size_t i = 0; i < headerStack->size(); i++) { isInClass = false; if (blockIndent) { // do NOT indent opening block for these headers if (!((*headerStack)[i] == &AS_NAMESPACE || (*headerStack)[i] == &AS_CLASS || (*headerStack)[i] == &AS_STRUCT || (*headerStack)[i] == &AS_UNION || (*headerStack)[i] == &AS_INTERFACE || (*headerStack)[i] == &AS_THROWS || (*headerStack)[i] == &AS_STATIC)) ++indentCount; } else if (!(i > 0 && (*headerStack)[i - 1] != &AS_OPEN_BRACKET && (*headerStack)[i] == &AS_OPEN_BRACKET)) ++indentCount; if (!isJavaStyle() && !namespaceIndent && i > 0 && (*headerStack)[i - 1] == &AS_NAMESPACE && (*headerStack)[i] == &AS_OPEN_BRACKET) --indentCount; if (isCStyle() && i >= 1 && (*headerStack)[i - 1] == &AS_CLASS && (*headerStack)[i] == &AS_OPEN_BRACKET) { if (classIndent) ++indentCount; isInClass = true; } // is the switchIndent option is on, indent switch statements an additional indent. else if (switchIndent && i > 1 && (*headerStack)[i - 1] == &AS_SWITCH && (*headerStack)[i] == &AS_OPEN_BRACKET) { ++indentCount; isInSwitch = true; } } // end of for loop if (isInClassHeader) { if (!isJavaStyle()) isInClassHeaderTab = true; if (lineOpensWithLineComment || lineStartsInComment || lineOpensWithComment) { if (!lineBeginsWithOpenBracket) --indentCount; if (!inStatementIndentStack->empty()) spaceIndentCount -= inStatementIndentStack->back(); } else if (blockIndent) { if (!lineBeginsWithOpenBracket) ++indentCount; } } if (isInClassInitializer || isInEnumTypeID) { indentCount += classInitializerIndents; } if (isInEnum && lineBeginsWithComma && !inStatementIndentStack->empty()) { // unregister '=' indent from the previous line inStatementIndentStack->pop_back(); isInStatement = false; spaceIndentCount = 0; } // Objective-C interface continuation line if (isInObjCInterface) ++indentCount; // unindent a class closing bracket... if (!lineStartsInComment && isCStyle() && isInClass && classIndent && headerStack->size() >= 2 && (*headerStack)[headerStack->size() - 2] == &AS_CLASS && (*headerStack)[headerStack->size() - 1] == &AS_OPEN_BRACKET && lineBeginsWithCloseBracket && bracketBlockStateStack->back() == true) --indentCount; // unindent an indented switch closing bracket... else if (!lineStartsInComment && isInSwitch && switchIndent && headerStack->size() >= 2 && (*headerStack)[headerStack->size() - 2] == &AS_SWITCH && (*headerStack)[headerStack->size() - 1] == &AS_OPEN_BRACKET && lineBeginsWithCloseBracket) --indentCount; // handle special case of horstmann comment in an indented class statement if (isInClass && classIndent && isInHorstmannComment && !lineOpensWithComment && headerStack->size() > 1 && (*headerStack)[headerStack->size() - 2] == &AS_CLASS) --indentCount; if (isInConditional) --indentCount; if (g_preprocessorCppExternCBracket >= 4) --indentCount; } void ASBeautifier::adjustParsedLineIndentation(size_t iPrelim, bool isInExtraHeaderIndent) { if (lineStartsInComment) return; // unindent a one-line statement in a header indent if (!blockIndent && lineBeginsWithOpenBracket && headerStack->size() < iPrelim && isInExtraHeaderIndent && (lineOpeningBlocksNum > 0 && lineOpeningBlocksNum <= lineClosingBlocksNum) && shouldIndentBrackettedLine) --indentCount; /* * if '{' doesn't follow an immediately previous '{' in the headerStack * (but rather another header such as "for" or "if", then unindent it * by one indentation relative to its block. */ else if (!blockIndent && lineBeginsWithOpenBracket && !(lineOpeningBlocksNum > 0 && lineOpeningBlocksNum <= lineClosingBlocksNum) && (headerStack->size() > 1 && (*headerStack)[headerStack->size() - 2] != &AS_OPEN_BRACKET) && shouldIndentBrackettedLine) --indentCount; // must check one less in headerStack if more than one header on a line (allow-addins)... else if (headerStack->size() > iPrelim + 1 && !blockIndent && lineBeginsWithOpenBracket && !(lineOpeningBlocksNum > 0 && lineOpeningBlocksNum <= lineClosingBlocksNum) && (headerStack->size() > 2 && (*headerStack)[headerStack->size() - 3] != &AS_OPEN_BRACKET) && shouldIndentBrackettedLine) --indentCount; // unindent a closing bracket... else if (lineBeginsWithCloseBracket && shouldIndentBrackettedLine) --indentCount; // correctly indent one-line-blocks... else if (lineOpeningBlocksNum > 0 && lineOpeningBlocksNum == lineClosingBlocksNum && previousLineProbationTab) --indentCount; if (indentCount < 0) indentCount = 0; // take care of extra bracket indentation option... if (!lineStartsInComment && bracketIndent && shouldIndentBrackettedLine && (lineBeginsWithOpenBracket || lineBeginsWithCloseBracket)) { if (!bracketIndentVtk) ++indentCount; else { // determine if a style VTK bracket is indented bool haveUnindentedBracket = false; for (size_t i = 0; i < headerStack->size(); i++) { if (((*headerStack)[i] == &AS_NAMESPACE || (*headerStack)[i] == &AS_CLASS || (*headerStack)[i] == &AS_STRUCT) && i + 1 < headerStack->size() && (*headerStack)[i + 1] == &AS_OPEN_BRACKET) i++; else if (lineBeginsWithOpenBracket) { // don't double count the current bracket if (i + 1 < headerStack->size() && (*headerStack)[i] == &AS_OPEN_BRACKET) haveUnindentedBracket = true; } else if ((*headerStack)[i] == &AS_OPEN_BRACKET) haveUnindentedBracket = true; } // end of for loop if (haveUnindentedBracket) ++indentCount; } } } /** * Compute indentCount adjustment when in a series of else-if statements * and shouldBreakElseIfs is requested. * It increments by one for each 'else' in the tempStack. */ int ASBeautifier::adjustIndentCountForBreakElseIfComments() const { assert(isElseHeaderIndent && !tempStacks->empty()); int indentCountIncrement = 0; vector* lastTempStack = tempStacks->back(); if (lastTempStack != NULL) { for (size_t i = 0; i < lastTempStack->size(); i++) { if (*lastTempStack->at(i) == AS_ELSE) indentCountIncrement++; } } return indentCountIncrement; } /** * Extract a preprocessor statement without the #. * If a error occurs an empty string is returned. */ string ASBeautifier::extractPreprocessorStatement(const string &line) const { string preproc; size_t start = line.find_first_not_of("#/ \t"); if (start == string::npos) return preproc; size_t end = line.find_first_of("/ \t", start); if (end == string::npos) end = line.length(); preproc = line.substr(start, end - start); return preproc; } /** * Clear the variables used to align the Objective-C method definitions. */ void ASBeautifier::clearObjCMethodDefinitionAlignment() { assert(isImmediatelyPostObjCMethodDefinition); spaceIndentCount = 0; spaceIndentObjCMethodDefinition = 0; colonIndentObjCMethodDefinition = 0; isInObjCMethodDefinition = false; isImmediatelyPostObjCMethodDefinition = false; if (!inStatementIndentStack->empty()) inStatementIndentStack->pop_back(); } /** * Compute the spaceIndentCount necessary to align the current line colon * with the colon position in the argument. * If it cannot be aligned indentLength is returned and a new colon * position is calculated. */ int ASBeautifier::computeObjCColonAlignment(string &line, int colonAlignPosition) const { int colonPosition = line.find(':'); if (colonPosition < 0 || colonPosition > colonAlignPosition) return indentLength; return (colonAlignPosition - colonPosition); } /** * Parse the current line to update indentCount and spaceIndentCount. */ void ASBeautifier::parseCurrentLine(const string &line) { bool isInLineComment = false; bool isInOperator = false; bool isSpecialChar = false; bool haveCaseIndent = false; bool haveAssignmentThisLine = false; bool closingBracketReached = false; bool previousLineProbation = (probationHeader != NULL); char ch = ' '; int tabIncrementIn = 0; for (size_t i = 0; i < line.length(); i++) { ch = line[i]; if (isInBeautifySQL) continue; if (isWhiteSpace(ch)) { if (ch == '\t') tabIncrementIn += convertTabToSpaces(i, tabIncrementIn); continue; } // handle special characters (i.e. backslash+character such as \n, \t, ...) if (isInQuote && !isInVerbatimQuote) { if (isSpecialChar) { isSpecialChar = false; continue; } if (line.compare(i, 2, "\\\\") == 0) { i++; continue; } if (ch == '\\') { if (peekNextChar(line, i) == ' ') // is this '\' at end of line haveLineContinuationChar = true; else isSpecialChar = true; continue; } } else if (isInDefine && ch == '\\') continue; // handle quotes (such as 'x' and "Hello Dolly") if (!(isInComment || isInLineComment) && (ch == '"' || ch == '\'')) { if (!isInQuote) { quoteChar = ch; isInQuote = true; char prevCh = i > 0 ? line[i - 1] : ' '; if (isCStyle() && prevCh == 'R') { int parenPos = line.find('(', i); if (parenPos != -1) { isInVerbatimQuote = true; verbatimDelimiter = line.substr(i + 1, parenPos - i - 1); } } else if (isSharpStyle() && prevCh == '@') isInVerbatimQuote = true; // check for "C" following "extern" else if (g_preprocessorCppExternCBracket == 2 && line.compare(i, 3, "\"C\"") == 0) ++g_preprocessorCppExternCBracket; } else if (isInVerbatimQuote && ch == '"') { if (isCStyle()) { string delim = ')' + verbatimDelimiter; int delimStart = i - delim.length(); if (delimStart > 0 && line.substr(delimStart, delim.length()) == delim) { isInQuote = false; isInVerbatimQuote = false; } } else if (isSharpStyle()) { if (peekNextChar(line, i) == '"') // check consecutive quotes i++; else { isInQuote = false; isInVerbatimQuote = false; } } } else if (quoteChar == ch) { isInQuote = false; isInStatement = true; continue; } } if (isInQuote) continue; // handle comments if (!(isInComment || isInLineComment) && line.compare(i, 2, "//") == 0) { // if there is a 'case' statement after these comments unindent by 1 if (isCaseHeaderCommentIndent) --indentCount; // isElseHeaderIndent is set by ASFormatter if shouldBreakElseIfs is requested // if there is an 'else' after these comments a tempStacks indent is required if (isElseHeaderIndent && lineOpensWithLineComment && !tempStacks->empty()) indentCount += adjustIndentCountForBreakElseIfComments(); isInLineComment = true; i++; continue; } else if (!(isInComment || isInLineComment) && line.compare(i, 2, "/*") == 0) { // if there is a 'case' statement after these comments unindent by 1 if (isCaseHeaderCommentIndent && lineOpensWithComment) --indentCount; // isElseHeaderIndent is set by ASFormatter if shouldBreakElseIfs is requested // if there is an 'else' after these comments a tempStacks indent is required if (isElseHeaderIndent && lineOpensWithComment && !tempStacks->empty()) indentCount += adjustIndentCountForBreakElseIfComments(); isInComment = true; i++; if (!lineOpensWithComment) // does line start with comment? blockCommentNoIndent = true; // if no, cannot indent continuation lines continue; } else if ((isInComment || isInLineComment) && line.compare(i, 2, "*/") == 0) { size_t firstText = line.find_first_not_of(" \t"); // if there is a 'case' statement after these comments unindent by 1 // only if the ending comment is the first entry on the line if (isCaseHeaderCommentIndent && firstText == i) --indentCount; // if this comment close starts the line, must check for else-if indent // isElseHeaderIndent is set by ASFormatter if shouldBreakElseIfs is requested // if there is an 'else' after these comments a tempStacks indent is required if (firstText == i) { if (isElseHeaderIndent && !lineOpensWithComment && !tempStacks->empty()) indentCount += adjustIndentCountForBreakElseIfComments(); } isInComment = false; i++; blockCommentNoIndent = false; // ok to indent next comment continue; } // treat indented preprocessor lines as a line comment else if (line[0] == '#' && isIndentedPreprocessor(line, i)) { isInLineComment = true; } if (isInLineComment) { // bypass rest of the comment up to the comment end while (i + 1 < line.length()) i++; continue; } if (isInComment) { // if there is a 'case' statement after these comments unindent by 1 if (!lineOpensWithComment && isCaseHeaderCommentIndent) --indentCount; // isElseHeaderIndent is set by ASFormatter if shouldBreakElseIfs is requested // if there is an 'else' after these comments a tempStacks indent is required if (!lineOpensWithComment && isElseHeaderIndent && !tempStacks->empty()) indentCount += adjustIndentCountForBreakElseIfComments(); // bypass rest of the comment up to the comment end while (i + 1 < line.length() && line.compare(i + 1, 2, "*/") != 0) i++; continue; } // if we have reached this far then we are NOT in a comment or string of special character... if (probationHeader != NULL) { if ((probationHeader == &AS_STATIC && ch == '{') || (probationHeader == &AS_SYNCHRONIZED && ch == '(')) { // insert the probation header as a new header isInHeader = true; headerStack->push_back(probationHeader); // handle the specific probation header isInConditional = (probationHeader == &AS_SYNCHRONIZED); isInStatement = false; // if the probation comes from the previous line, then indent by 1 tab count. if (previousLineProbation && ch == '{' && !(blockIndent && probationHeader == &AS_STATIC)) { ++indentCount; previousLineProbationTab = true; } previousLineProbation = false; } // dismiss the probation header probationHeader = NULL; } prevNonSpaceCh = currentNonSpaceCh; currentNonSpaceCh = ch; if (!isLegalNameChar(ch) && ch != ',' && ch != ';') { prevNonLegalCh = currentNonLegalCh; currentNonLegalCh = ch; } if (isInHeader) { isInHeader = false; currentHeader = headerStack->back(); } else currentHeader = NULL; if (isCStyle() && isInTemplate && (ch == '<' || ch == '>') && !(line.length() > i + 1 && line.compare(i, 2, ">=") == 0)) { if (ch == '<') { ++templateDepth; inStatementIndentStackSizeStack->push_back(inStatementIndentStack->size()); registerInStatementIndent(line, i, spaceIndentCount, tabIncrementIn, 0, true); } else if (ch == '>') { popLastInStatementIndent(); if (--templateDepth <= 0) { ch = ';'; isInTemplate = false; templateDepth = 0; } } } // handle parentheses if (ch == '(' || ch == '[' || ch == ')' || ch == ']') { if (ch == '(' || ch == '[') { isInOperator = false; // if have a struct header, this is a declaration not a definition if (ch == '(' && !headerStack->empty() && headerStack->back() == &AS_STRUCT) { headerStack->pop_back(); isInClassHeader = false; if (line.find(AS_STRUCT, 0) > i) // if not on this line indentCount -= classInitializerIndents; if (indentCount < 0) indentCount = 0; } if (parenDepth == 0) { parenStatementStack->push_back(isInStatement); isInStatement = true; } parenDepth++; if (ch == '[') ++squareBracketCount; inStatementIndentStackSizeStack->push_back(inStatementIndentStack->size()); if (currentHeader != NULL) registerInStatementIndent(line, i, spaceIndentCount, tabIncrementIn, minConditionalIndent/*indentLength*2*/, true); else registerInStatementIndent(line, i, spaceIndentCount, tabIncrementIn, 0, true); } else if (ch == ')' || ch == ']') { if (ch == ']') --squareBracketCount; if (squareBracketCount < 0) squareBracketCount = 0; foundPreCommandHeader = false; parenDepth--; if (parenDepth == 0) { if (!parenStatementStack->empty()) // in case of unmatched closing parens { isInStatement = parenStatementStack->back(); parenStatementStack->pop_back(); } isInAsm = false; isInConditional = false; } if (!inStatementIndentStackSizeStack->empty()) { popLastInStatementIndent(); if (!parenIndentStack->empty()) { int poppedIndent = parenIndentStack->back(); parenIndentStack->pop_back(); if (i == 0) spaceIndentCount = poppedIndent; } } } continue; } if (ch == '{') { // first, check if '{' is a block-opener or a static-array opener bool isBlockOpener = ((prevNonSpaceCh == '{' && bracketBlockStateStack->back()) || prevNonSpaceCh == '}' || prevNonSpaceCh == ')' || prevNonSpaceCh == ';' || peekNextChar(line, i) == '{' || foundPreCommandHeader || foundPreCommandMacro || isInClassHeader || (isInClassInitializer && !isLegalNameChar(prevNonSpaceCh)) || isNonInStatementArray || isInObjCMethodDefinition || isInObjCInterface || isSharpAccessor || isSharpDelegate || isInExternC || isInAsmBlock || getNextWord(line, i) == AS_NEW || (isInDefine && (prevNonSpaceCh == '(' || isLegalNameChar(prevNonSpaceCh)))); if (isInObjCMethodDefinition) isImmediatelyPostObjCMethodDefinition = true; if (!isBlockOpener && !isInStatement && !isInClassInitializer && !isInEnum) { if (headerStack->empty()) isBlockOpener = true; else if (headerStack->back() == &AS_NAMESPACE || headerStack->back() == &AS_CLASS || headerStack->back() == &AS_INTERFACE || headerStack->back() == &AS_STRUCT || headerStack->back() == &AS_UNION) isBlockOpener = true; } if (!isBlockOpener && currentHeader != NULL) { for (size_t n = 0; n < nonParenHeaders->size(); n++) if (currentHeader == (*nonParenHeaders)[n]) { isBlockOpener = true; break; } } bracketBlockStateStack->push_back(isBlockOpener); if (!isBlockOpener) { inStatementIndentStackSizeStack->push_back(inStatementIndentStack->size()); registerInStatementIndent(line, i, spaceIndentCount, tabIncrementIn, 0, true); parenDepth++; if (i == 0) shouldIndentBrackettedLine = false; isInEnumTypeID = false; continue; } // this bracket is a block opener... ++lineOpeningBlocksNum; if (isInClassInitializer || isInEnumTypeID) { // decrease tab count if bracket is broken if (lineBeginsWithOpenBracket) { indentCount -= classInitializerIndents; // decrease one more if an empty class if (!headerStack->empty() && (*headerStack).back() == &AS_CLASS) { int nextChar = getNextProgramCharDistance(line, i); if ((int) line.length() > nextChar && line[nextChar] == '}') --indentCount; } } } if (isInObjCInterface) { isInObjCInterface = false; if (lineBeginsWithOpenBracket) --indentCount; } if (bracketIndent && !namespaceIndent && !headerStack->empty() && (*headerStack).back() == &AS_NAMESPACE) { shouldIndentBrackettedLine = false; --indentCount; } // an indentable struct is treated like a class in the header stack if (!headerStack->empty() && (*headerStack).back() == &AS_STRUCT && isInIndentableStruct) (*headerStack).back() = &AS_CLASS; blockParenDepthStack->push_back(parenDepth); blockStatementStack->push_back(isInStatement); if (!inStatementIndentStack->empty()) { // completely purge the inStatementIndentStack while (!inStatementIndentStack->empty()) popLastInStatementIndent(); if (isInClassInitializer || isInClassHeaderTab) { if (lineBeginsWithOpenBracket || lineBeginsWithComma) spaceIndentCount = 0; } else spaceIndentCount = 0; } blockTabCount += (isInStatement ? 1 : 0); if (g_preprocessorCppExternCBracket == 3) ++g_preprocessorCppExternCBracket; parenDepth = 0; isInClassHeader = false; isInClassHeaderTab = false; isInClassInitializer = false; isInEnumTypeID = false; isInStatement = false; isInQuestion = false; isInLet = false; foundPreCommandHeader = false; foundPreCommandMacro = false; isInExternC = false; tempStacks->push_back(new vector); headerStack->push_back(&AS_OPEN_BRACKET); lastLineHeader = &AS_OPEN_BRACKET; continue; } // end '{' //check if a header has been reached bool isPotentialHeader = isCharPotentialHeader(line, i); if (isPotentialHeader && !squareBracketCount) { const string* newHeader = findHeader(line, i, headers); // Qt headers may be variables in C++ if (newHeader == &AS_FOREVER || newHeader == &AS_FOREACH) { if (line.find_first_of("=;", i) != string::npos) newHeader = NULL; } if (newHeader != NULL) { // if we reached here, then this is a header... bool isIndentableHeader = true; isInHeader = true; vector* lastTempStack; if (tempStacks->empty()) lastTempStack = NULL; else lastTempStack = tempStacks->back(); // if a new block is opened, push a new stack into tempStacks to hold the // future list of headers in the new block. // take care of the special case: 'else if (...)' if (newHeader == &AS_IF && lastLineHeader == &AS_ELSE) { headerStack->pop_back(); } // take care of 'else' else if (newHeader == &AS_ELSE) { if (lastTempStack != NULL) { int indexOfIf = indexOf(*lastTempStack, &AS_IF); if (indexOfIf != -1) { // recreate the header list in headerStack up to the previous 'if' // from the temporary snapshot stored in lastTempStack. int restackSize = lastTempStack->size() - indexOfIf - 1; for (int r = 0; r < restackSize; r++) { headerStack->push_back(lastTempStack->back()); lastTempStack->pop_back(); } if (!closingBracketReached) indentCount += restackSize; } /* * If the above if is not true, i.e. no 'if' before the 'else', * then nothing beautiful will come out of this... * I should think about inserting an Exception here to notify the caller of this... */ } } // check if 'while' closes a previous 'do' else if (newHeader == &AS_WHILE) { if (lastTempStack != NULL) { int indexOfDo = indexOf(*lastTempStack, &AS_DO); if (indexOfDo != -1) { // recreate the header list in headerStack up to the previous 'do' // from the temporary snapshot stored in lastTempStack. int restackSize = lastTempStack->size() - indexOfDo - 1; for (int r = 0; r < restackSize; r++) { headerStack->push_back(lastTempStack->back()); lastTempStack->pop_back(); } if (!closingBracketReached) indentCount += restackSize; } } } // check if 'catch' closes a previous 'try' or 'catch' else if (newHeader == &AS_CATCH || newHeader == &AS_FINALLY) { if (lastTempStack != NULL) { int indexOfTry = indexOf(*lastTempStack, &AS_TRY); if (indexOfTry == -1) indexOfTry = indexOf(*lastTempStack, &AS_CATCH); if (indexOfTry != -1) { // recreate the header list in headerStack up to the previous 'try' // from the temporary snapshot stored in lastTempStack. int restackSize = lastTempStack->size() - indexOfTry - 1; for (int r = 0; r < restackSize; r++) { headerStack->push_back(lastTempStack->back()); lastTempStack->pop_back(); } if (!closingBracketReached) indentCount += restackSize; } } } else if (newHeader == &AS_CASE) { isInCase = true; if (!haveCaseIndent) { haveCaseIndent = true; if (!lineBeginsWithOpenBracket) --indentCount; } } else if (newHeader == &AS_DEFAULT) { isInCase = true; --indentCount; } else if (newHeader == &AS_STATIC || newHeader == &AS_SYNCHRONIZED) { if (!headerStack->empty() && (headerStack->back() == &AS_STATIC || headerStack->back() == &AS_SYNCHRONIZED)) { isIndentableHeader = false; } else { isIndentableHeader = false; probationHeader = newHeader; } } else if (newHeader == &AS_TEMPLATE) { isInTemplate = true; isIndentableHeader = false; } if (isIndentableHeader) { headerStack->push_back(newHeader); isInStatement = false; if (indexOf(*nonParenHeaders, newHeader) == -1) { isInConditional = true; } lastLineHeader = newHeader; } else isInHeader = false; i += newHeader->length() - 1; continue; } // newHeader != NULL if (findHeader(line, i, preCommandHeaders)) foundPreCommandHeader = true; // Objective-C NSException macros are preCommandHeaders if (isCStyle() && findKeyword(line, i, AS_NS_DURING)) foundPreCommandMacro = true; if (isCStyle() && findKeyword(line, i, AS_NS_HANDLER)) foundPreCommandMacro = true; if (parenDepth == 0 && findKeyword(line, i, AS_ENUM)) isInEnum = true; if (isSharpStyle() && findKeyword(line, i, AS_LET)) isInLet = true; } // isPotentialHeader if (ch == '?') isInQuestion = true; // special handling of colons if (ch == ':') { if (line.length() > i + 1 && line[i + 1] == ':') // look for :: { ++i; continue; } else if (isInQuestion) { // do nothing special } else if (parenDepth > 0) { // found a 'for' loop or an objective-C statement // so do nothing special } else if (isInEnum) { // found an enum with a base-type isInEnumTypeID = true; if (i == 0) indentCount += classInitializerIndents; } else if (isCStyle() && !isInCase && (prevNonSpaceCh == ')' || foundPreCommandHeader)) { // found a 'class' c'tor initializer isInClassInitializer = true; registerInStatementIndentColon(line, i, tabIncrementIn); if (i == 0) indentCount += classInitializerIndents; } else if (isInClassHeader || isInObjCInterface) { // is in a 'class A : public B' definition isInClassHeaderTab = true; registerInStatementIndentColon(line, i, tabIncrementIn); } else if (isInAsm || isInAsmOneLine || isInAsmBlock) { // do nothing special } else if (isDigit(peekNextChar(line, i))) { // found a bit field // so do nothing special } else if (isCStyle() && isInClass && prevNonSpaceCh != ')') { // found a 'private:' or 'public:' inside a class definition --indentCount; if (modifierIndent) spaceIndentCount += (indentLength / 2); } else if (isCStyle() && !isInClass && headerStack->size() >= 2 && (*headerStack)[headerStack->size() - 2] == &AS_CLASS && (*headerStack)[headerStack->size() - 1] == &AS_OPEN_BRACKET) { // found a 'private:' or 'public:' inside a class definition // and on the same line as the class opening bracket // do nothing } else if (isJavaStyle() && lastLineHeader == &AS_FOR) { // found a java for-each statement // so do nothing special } else { currentNonSpaceCh = ';'; // so that brackets after the ':' will appear as block-openers char peekedChar = peekNextChar(line, i); if (isInCase) { isInCase = false; ch = ';'; // from here on, treat char as ';' } else if (isCStyle() || (isSharpStyle() && peekedChar == ';')) { // is in a label (e.g. 'label1:') if (labelIndent) --indentCount; // unindent label by one indent else if (!lineBeginsWithOpenBracket) indentCount = 0; // completely flush indent to left } } } if ((ch == ';' || (parenDepth > 0 && ch == ',')) && !inStatementIndentStackSizeStack->empty()) while ((int) inStatementIndentStackSizeStack->back() + (parenDepth > 0 ? 1 : 0) < (int) inStatementIndentStack->size()) inStatementIndentStack->pop_back(); else if (ch == ',' && isInEnum && isNonInStatementArray && !inStatementIndentStack->empty()) inStatementIndentStack->pop_back(); // handle commas // previous "isInStatement" will be from an assignment operator or class initializer if (ch == ',' && parenDepth == 0 && !isInStatement && !isNonInStatementArray) { // is comma at end of line size_t nextChar = line.find_first_not_of(" \t", i + 1); if (nextChar != string::npos) { if (line.compare(nextChar, 2, "//") == 0 || line.compare(nextChar, 2, "/*") == 0) nextChar = string::npos; } // register indent if (nextChar == string::npos) { // register indent at previous word if (isJavaStyle() && isInClassHeader) { // do nothing for now } // register indent at second word on the line else if (!isInTemplate && !isInClassHeaderTab && !isInClassInitializer) { int prevWord = getInStatementIndentComma(line, i); int inStatementIndent = prevWord + spaceIndentCount + tabIncrementIn; inStatementIndentStack->push_back(inStatementIndent); isInStatement = true; } } } // handle comma first initializers if (ch == ',' && parenDepth == 0 && lineBeginsWithComma && (isInClassInitializer || isInClassHeaderTab)) spaceIndentCount = 0; // handle ends of statements if ((ch == ';' && parenDepth == 0) || ch == '}') { if (ch == '}') { // first check if this '}' closes a previous block, or a static array... if (bracketBlockStateStack->size() > 1) { bool bracketBlockState = bracketBlockStateStack->back(); bracketBlockStateStack->pop_back(); if (!bracketBlockState) { if (!inStatementIndentStackSizeStack->empty()) { // this bracket is a static array popLastInStatementIndent(); parenDepth--; if (i == 0) shouldIndentBrackettedLine = false; if (!parenIndentStack->empty()) { int poppedIndent = parenIndentStack->back(); parenIndentStack->pop_back(); if (i == 0) spaceIndentCount = poppedIndent; } } continue; } } // this bracket is block closer... ++lineClosingBlocksNum; if (!inStatementIndentStackSizeStack->empty()) popLastInStatementIndent(); if (!blockParenDepthStack->empty()) { parenDepth = blockParenDepthStack->back(); blockParenDepthStack->pop_back(); isInStatement = blockStatementStack->back(); blockStatementStack->pop_back(); if (isInStatement) blockTabCount--; } closingBracketReached = true; if (i == 0) spaceIndentCount = 0; isInAsmBlock = false; isInAsm = isInAsmOneLine = isInQuote = false; // close these just in case int headerPlace = indexOf(*headerStack, &AS_OPEN_BRACKET); if (headerPlace != -1) { const string* popped = headerStack->back(); while (popped != &AS_OPEN_BRACKET) { headerStack->pop_back(); popped = headerStack->back(); } headerStack->pop_back(); if (headerStack->empty()) g_preprocessorCppExternCBracket = 0; // do not indent namespace bracket unless namespaces are indented if (!namespaceIndent && !headerStack->empty() && (*headerStack).back() == &AS_NAMESPACE && i == 0) // must be the first bracket on the line shouldIndentBrackettedLine = false; if (!tempStacks->empty()) { vector* temp = tempStacks->back(); tempStacks->pop_back(); delete temp; } } ch = ' '; // needed due to cases such as '}else{', so that headers ('else' in this case) will be identified... } // ch == '}' /* * Create a temporary snapshot of the current block's header-list in the * uppermost inner stack in tempStacks, and clear the headerStack up to * the beginning of the block. * Thus, the next future statement will think it comes one indent past * the block's '{' unless it specifically checks for a companion-header * (such as a previous 'if' for an 'else' header) within the tempStacks, * and recreates the temporary snapshot by manipulating the tempStacks. */ if (!tempStacks->back()->empty()) while (!tempStacks->back()->empty()) tempStacks->back()->pop_back(); while (!headerStack->empty() && headerStack->back() != &AS_OPEN_BRACKET) { tempStacks->back()->push_back(headerStack->back()); headerStack->pop_back(); } if (parenDepth == 0 && ch == ';') isInStatement = false; if (isInObjCMethodDefinition) isImmediatelyPostObjCMethodDefinition = true; previousLastLineHeader = NULL; isInClassHeader = false; // for 'friend' class isInEnum = false; isInQuestion = false; isInObjCInterface = false; foundPreCommandHeader = false; foundPreCommandMacro = false; squareBracketCount = 0; continue; } if (isPotentialHeader) { // check for preBlockStatements in C/C++ ONLY if not within parentheses // (otherwise 'struct XXX' statements would be wrongly interpreted...) if (!isInTemplate && !(isCStyle() && parenDepth > 0)) { const string* newHeader = findHeader(line, i, preBlockStatements); if (newHeader != NULL && !(isCStyle() && newHeader == &AS_CLASS && isInEnum)) // is not 'enum class' { if (!isSharpStyle()) headerStack->push_back(newHeader); // do not need 'where' in the headerStack // do not need second 'class' statement in a row else if (!(newHeader == &AS_WHERE || (newHeader == &AS_CLASS && !headerStack->empty() && headerStack->back() == &AS_CLASS))) headerStack->push_back(newHeader); if (!headerStack->empty()) { if ((*headerStack).back() == &AS_CLASS || (*headerStack).back() == &AS_STRUCT || (*headerStack).back() == &AS_INTERFACE) { isInClassHeader = true; } else if ((*headerStack).back() == &AS_NAMESPACE) { // remove inStatementIndent from namespace if (!inStatementIndentStack->empty()) inStatementIndentStack->pop_back(); isInStatement = false; } } i += newHeader->length() - 1; continue; } } const string* foundIndentableHeader = findHeader(line, i, indentableHeaders); if (foundIndentableHeader != NULL) { // must bypass the header before registering the in statement i += foundIndentableHeader->length() - 1; if (!isInOperator && !isInTemplate && !isNonInStatementArray) { registerInStatementIndent(line, i, spaceIndentCount, tabIncrementIn, 0, false); isInStatement = true; } continue; } if (isCStyle() && findKeyword(line, i, AS_OPERATOR)) isInOperator = true; if (g_preprocessorCppExternCBracket == 1 && findKeyword(line, i, AS_EXTERN)) ++g_preprocessorCppExternCBracket; if (g_preprocessorCppExternCBracket == 3) // extern "C" is not followed by a '{' g_preprocessorCppExternCBracket = 0; // "new" operator is a pointer, not a calculation if (findKeyword(line, i, AS_NEW)) { if (isInStatement && !inStatementIndentStack->empty() && prevNonSpaceCh == '=' ) inStatementIndentStack->back() = 0; } if (isCStyle()) { if (findKeyword(line, i, AS_ASM) || findKeyword(line, i, AS__ASM__)) { isInAsm = true; } else if (findKeyword(line, i, AS_MS_ASM) // microsoft specific || findKeyword(line, i, AS_MS__ASM)) { int index = 4; if (peekNextChar(line, i) == '_') // check for __asm index = 5; char peekedChar = ASBase::peekNextChar(line, i + index); if (peekedChar == '{' || peekedChar == ' ') isInAsmBlock = true; else isInAsmOneLine = true; } } // bypass the entire name for all others string name = getCurrentWord(line, i); i += name.length() - 1; continue; } // Handle Objective-C statements if (ch == '@' && isCharPotentialHeader(line, i + 1)) { string curWord = getCurrentWord(line, i + 1); if (curWord == AS_INTERFACE && headerStack->empty()) { isInObjCInterface = true; string name = '@' + curWord; i += name.length() - 1; continue; } else if (curWord == AS_PUBLIC || curWord == AS_PRIVATE || curWord == AS_PROTECTED) { --indentCount; if (modifierIndent) spaceIndentCount += (indentLength / 2); string name = '@' + curWord; i += name.length() - 1; continue; } else if (curWord == AS_END) { popLastInStatementIndent(); spaceIndentCount = 0; if (isInObjCInterface) --indentCount; isInObjCInterface = false; isInObjCMethodDefinition = false; string name = '@' + curWord; i += name.length() - 1; continue; } } else if ((ch == '-' || ch == '+') && peekNextChar(line, i) == '(' && headerStack->empty() && line.find_first_not_of(" \t") == i) { if (isInObjCInterface) --indentCount; isInObjCInterface = false; isInObjCMethodDefinition = true; continue; } // Handle operators bool isPotentialOperator = isCharPotentialOperator(ch); if (isPotentialOperator) { // Check if an operator has been reached. const string* foundAssignmentOp = findOperator(line, i, assignmentOperators); const string* foundNonAssignmentOp = findOperator(line, i, nonAssignmentOperators); if (foundNonAssignmentOp == &AS_LAMBDA) foundPreCommandHeader = true; if (isInTemplate && foundNonAssignmentOp == &AS_GR_GR) foundNonAssignmentOp = NULL; // Since findHeader's boundary checking was not used above, it is possible // that both an assignment op and a non-assignment op where found, // e.g. '>>' and '>>='. If this is the case, treat the LONGER one as the // found operator. if (foundAssignmentOp != NULL && foundNonAssignmentOp != NULL) { if (foundAssignmentOp->length() < foundNonAssignmentOp->length()) foundAssignmentOp = NULL; else foundNonAssignmentOp = NULL; } if (foundNonAssignmentOp != NULL) { if (foundNonAssignmentOp->length() > 1) i += foundNonAssignmentOp->length() - 1; // For C++ input/output, operator<< and >> should be // aligned, if we are not in a statement already and // also not in the "operator<<(...)" header line if (!isInOperator && inStatementIndentStack->empty() && isCStyle() && (foundNonAssignmentOp == &AS_GR_GR || foundNonAssignmentOp == &AS_LS_LS)) { // this will be true if the line begins with the operator if (i < 2 && spaceIndentCount == 0) spaceIndentCount += 2 * indentLength; // align to the beginning column of the operator registerInStatementIndent(line, i - foundNonAssignmentOp->length(), spaceIndentCount, tabIncrementIn, 0, false); } } else if (foundAssignmentOp != NULL) { foundPreCommandHeader = false; // clears this for array assignments foundPreCommandMacro = false; if (foundAssignmentOp->length() > 1) i += foundAssignmentOp->length() - 1; if (!isInOperator && !isInTemplate && (!isNonInStatementArray || isInEnum)) { // if multiple assignments, align on the previous word if (foundAssignmentOp == &AS_ASSIGN && prevNonSpaceCh != ']' // an array && statementEndsWithComma(line, i)) { if (!haveAssignmentThisLine) // only one assignment indent per line { // register indent at previous word haveAssignmentThisLine = true; int prevWordIndex = getInStatementIndentAssign(line, i); int inStatementIndent = prevWordIndex + spaceIndentCount + tabIncrementIn; inStatementIndentStack->push_back(inStatementIndent); isInStatement = true; } } // don't indent an assignment if 'let' else if (isInLet) { isInLet = false; } else { if (i == 0 && spaceIndentCount == 0) spaceIndentCount += indentLength; registerInStatementIndent(line, i, spaceIndentCount, tabIncrementIn, 0, false); isInStatement = true; } } } } } // end of for loop * end of for loop * end of for loop * end of for loop * end of for loop * } } // end namespace astyle ================================================ FILE: 3rdpart/astyle/ASEnhancer.cpp ================================================ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ASEnhancer.cpp * * Copyright (C) 2014 by Jim Pattee * * * This file is a part of Artistic Style - an indentation and * reformatting tool for C, C++, C# and Java source files. * * * Artistic Style is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Artistic Style is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Artistic Style. If not, see . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "astyle.h" namespace astyle { /** * ASEnhancer constructor */ ASEnhancer::ASEnhancer() { } /** * Destructor of ASEnhancer */ ASEnhancer::~ASEnhancer() { } /** * initialize the ASEnhancer. * * init() is called each time an ASFormatter object is initialized. */ void ASEnhancer::init(int _fileType, int _indentLength, int _tabLength, bool _useTabs, bool _forceTab, bool _namespaceIndent, bool _caseIndent, bool _preprocBlockIndent, bool _preprocDefineIndent, bool _emptyLineFill, vector* >* _indentableMacros) { // formatting variables from ASFormatter and ASBeautifier ASBase::init(_fileType); indentLength = _indentLength; tabLength = _tabLength; useTabs = _useTabs; forceTab = _forceTab; namespaceIndent = _namespaceIndent; caseIndent = _caseIndent; preprocBlockIndent = _preprocBlockIndent; preprocDefineIndent = _preprocDefineIndent; emptyLineFill = _emptyLineFill; indentableMacros = _indentableMacros; quoteChar = '\''; // unindent variables lineNumber = 0; bracketCount = 0; isInComment = false; isInQuote = false; switchDepth = 0; eventPreprocDepth = 0; lookingForCaseBracket = false; unindentNextLine = false; shouldUnindentLine = false; shouldUnindentComment = false; // switch struct and vector sw.switchBracketCount = 0; sw.unindentDepth = 0; sw.unindentCase = false; switchStack.clear(); // other variables nextLineIsEventIndent = false; isInEventTable = false; nextLineIsDeclareIndent = false; isInDeclareSection = false; } /** * additional formatting for line of source code. * every line of source code in a source code file should be sent * one after the other to this function. * indents event tables * unindents the case blocks * * @param line the original formatted line will be updated if necessary. */ void ASEnhancer::enhance(string &line, bool isInNamespace, bool isInPreprocessor, bool isInSQL) { shouldUnindentLine = true; shouldUnindentComment = false; lineNumber++; // check for beginning of event table if (nextLineIsEventIndent) { isInEventTable = true; nextLineIsEventIndent = false; } // check for beginning of SQL declare section if (nextLineIsDeclareIndent) { isInDeclareSection = true; nextLineIsDeclareIndent = false; } if (line.length() == 0 && !isInEventTable && !isInDeclareSection && !emptyLineFill) return; // test for unindent on attached brackets if (unindentNextLine) { sw.unindentDepth++; sw.unindentCase = true; unindentNextLine = false; } // parse characters in the current line parseCurrentLine(line, isInPreprocessor, isInSQL); // check for SQL indentable lines if (isInDeclareSection) { size_t firstText = line.find_first_not_of(" \t"); if (firstText == string::npos || line[firstText] != '#') indentLine(line, 1); } // check for event table indentable lines if (isInEventTable && (eventPreprocDepth == 0 || (namespaceIndent && isInNamespace))) { size_t firstText = line.find_first_not_of(" \t"); if (firstText == string::npos || line[firstText] != '#') indentLine(line, 1); } if (shouldUnindentComment && sw.unindentDepth > 0) unindentLine(line, sw.unindentDepth - 1); else if (shouldUnindentLine && sw.unindentDepth > 0) unindentLine(line, sw.unindentDepth); } /** * convert a force-tab indent to spaces * * @param line a reference to the line that will be converted. */ void ASEnhancer::convertForceTabIndentToSpaces(string &line) const { // replace tab indents with spaces for (size_t i = 0; i < line.length(); i++) { if (!isWhiteSpace(line[i])) break; if (line[i] == '\t') { line.erase(i, 1); line.insert(i, tabLength, ' '); i += tabLength - 1; } } } /** * convert a space indent to force-tab * * @param line a reference to the line that will be converted. */ void ASEnhancer::convertSpaceIndentToForceTab(string &line) const { assert(tabLength > 0); // replace leading spaces with tab indents size_t newSpaceIndentLength = line.find_first_not_of(" \t"); size_t tabCount = newSpaceIndentLength / tabLength; // truncate extra spaces line.erase(0U, tabCount * tabLength); line.insert(0U, tabCount, '\t'); } /** * find the colon following a 'case' statement * * @param line a reference to the line. * @param caseIndex the line index of the case statement. * @return the line index of the colon. */ size_t ASEnhancer::findCaseColon(string &line, size_t caseIndex) const { size_t i = caseIndex; bool isInQuote_ = false; char quoteChar_ = ' '; for (; i < line.length(); i++) { if (isInQuote_) { if (line[i] == '\\') { i++; continue; } else if (line[i] == quoteChar_) // check ending quote { isInQuote_ = false; quoteChar_ = ' '; continue; } else { continue; // must close quote before continuing } } if (line[i] == '\'' || line[i] == '\"') // check opening quote { isInQuote_ = true; quoteChar_ = line[i]; continue; } if (line[i] == ':') { if ((i + 1 < line.length()) && (line[i + 1] == ':')) i++; // bypass scope resolution operator else break; // found it } } return i; } /** * indent a line by a given number of tabsets * by inserting leading whitespace to the line argument. * * @param line a reference to the line to indent. * @param indent the number of tabsets to insert. * @return the number of characters inserted. */ int ASEnhancer::indentLine(string &line, int indent) const { if (line.length() == 0 && !emptyLineFill) return 0; size_t charsToInsert; if (forceTab && indentLength != tabLength) { // replace tab indents with spaces convertForceTabIndentToSpaces(line); // insert the space indents charsToInsert = indent * indentLength; line.insert(0U, charsToInsert, ' '); // replace leading spaces with tab indents convertSpaceIndentToForceTab(line); } else if (useTabs) { charsToInsert = indent; line.insert(0U, charsToInsert, '\t'); } else // spaces { charsToInsert = indent * indentLength; line.insert(0U, charsToInsert, ' '); } return charsToInsert; } /** * check for SQL "BEGIN DECLARE SECTION". * must compare case insensitive and allow any spacing between words. * * @param line a reference to the line to indent. * @param index the current line index. * @return true if a hit. */ bool ASEnhancer::isBeginDeclareSectionSQL(string &line, size_t index) const { string word; size_t hits = 0; size_t i; for (i = index; i < line.length(); i++) { i = line.find_first_not_of(" \t", i); if (i == string::npos) return false; if (line[i] == ';') break; if (!isCharPotentialHeader(line, i)) continue; word = getCurrentWord(line, i); for (size_t j = 0; j < word.length(); j++) word[j] = (char) toupper(word[j]); if (word == "EXEC" || word == "SQL") { i += word.length() - 1; continue; } if (word == "DECLARE" || word == "SECTION") { hits++; i += word.length() - 1; continue; } if (word == "BEGIN") { hits++; i += word.length() - 1; continue; } return false; } if (hits == 3) return true; return false; } /** * check for SQL "END DECLARE SECTION". * must compare case insensitive and allow any spacing between words. * * @param line a reference to the line to indent. * @param index the current line index. * @return true if a hit. */ bool ASEnhancer::isEndDeclareSectionSQL(string &line, size_t index) const { string word; size_t hits = 0; size_t i; for (i = index; i < line.length(); i++) { i = line.find_first_not_of(" \t", i); if (i == string::npos) return false; if (line[i] == ';') break; if (!isCharPotentialHeader(line, i)) continue; word = getCurrentWord(line, i); for (size_t j = 0; j < word.length(); j++) word[j] = (char) toupper(word[j]); if (word == "EXEC" || word == "SQL") { i += word.length() - 1; continue; } if (word == "DECLARE" || word == "SECTION") { hits++; i += word.length() - 1; continue; } if (word == "END") { hits++; i += word.length() - 1; continue; } return false; } if (hits == 3) return true; return false; } /** * check if a one-line bracket has been reached, * i.e. if the currently reached '{' character is closed * with a complimentary '}' elsewhere on the current line, *. * @return false = one-line bracket has not been reached. * true = one-line bracket has been reached. */ bool ASEnhancer::isOneLineBlockReached(string &line, int startChar) const { assert(line[startChar] == '{'); bool isInComment_ = false; bool isInQuote_ = false; int _bracketCount = 1; int lineLength = line.length(); char quoteChar_ = ' '; char ch = ' '; for (int i = startChar + 1; i < lineLength; ++i) { ch = line[i]; if (isInComment_) { if (line.compare(i, 2, "*/") == 0) { isInComment_ = false; ++i; } continue; } if (ch == '\\') { ++i; continue; } if (isInQuote_) { if (ch == quoteChar_) isInQuote_ = false; continue; } if (ch == '"' || ch == '\'') { isInQuote_ = true; quoteChar_ = ch; continue; } if (line.compare(i, 2, "//") == 0) break; if (line.compare(i, 2, "/*") == 0) { isInComment_ = true; ++i; continue; } if (ch == '{') ++_bracketCount; else if (ch == '}') --_bracketCount; if (_bracketCount == 0) return true; } return false; } /** * parse characters in the current line to determine if an indent * or unindent is needed. */ void ASEnhancer::parseCurrentLine(string &line, bool isInPreprocessor, bool isInSQL) { bool isSpecialChar = false; // is a backslash escape character for (size_t i = 0; i < line.length(); i++) { char ch = line[i]; // bypass whitespace if (isWhiteSpace(ch)) continue; // handle special characters (i.e. backslash+character such as \n, \t, ...) if (isSpecialChar) { isSpecialChar = false; continue; } if (!(isInComment) && line.compare(i, 2, "\\\\") == 0) { i++; continue; } if (!(isInComment) && ch == '\\') { isSpecialChar = true; continue; } // handle quotes (such as 'x' and "Hello Dolly") if (!isInComment && (ch == '"' || ch == '\'')) { if (!isInQuote) { quoteChar = ch; isInQuote = true; } else if (quoteChar == ch) { isInQuote = false; continue; } } if (isInQuote) continue; // handle comments if (!(isInComment) && line.compare(i, 2, "//") == 0) { // check for windows line markers if (line.compare(i + 2, 1, "\xf0") > 0) lineNumber--; // unindent if not in case brackets if (line.find_first_not_of(" \t") == i && sw.switchBracketCount == 1 && sw.unindentCase) shouldUnindentComment = true; break; // finished with the line } else if (!(isInComment) && line.compare(i, 2, "/*") == 0) { // unindent if not in case brackets if (sw.switchBracketCount == 1 && sw.unindentCase) shouldUnindentComment = true; isInComment = true; size_t commentEnd = line.find("*/", i); if (commentEnd == string::npos) i = line.length() - 1; else i = commentEnd - 1; continue; } else if ((isInComment) && line.compare(i, 2, "*/") == 0) { // unindent if not in case brackets if (sw.switchBracketCount == 1 && sw.unindentCase) shouldUnindentComment = true; isInComment = false; i++; continue; } if (isInComment) { // unindent if not in case brackets if (sw.switchBracketCount == 1 && sw.unindentCase) shouldUnindentComment = true; size_t commentEnd = line.find("*/", i); if (commentEnd == string::npos) i = line.length() - 1; else i = commentEnd - 1; continue; } // if we have reached this far then we are NOT in a comment or string of special characters if (line[i] == '{') bracketCount++; if (line[i] == '}') bracketCount--; // check for preprocessor within an event table if (isInEventTable && line[i] == '#' && preprocBlockIndent) { string preproc; preproc = line.substr(i + 1); if (preproc.substr(0, 2) == "if") // #if, #ifdef, #ifndef) eventPreprocDepth += 1; if (preproc.substr(0, 5) == "endif" && eventPreprocDepth > 0) eventPreprocDepth -= 1; } bool isPotentialKeyword = isCharPotentialHeader(line, i); // ---------------- wxWidgets and MFC macros ---------------------------------- if (isPotentialKeyword) { for (size_t j = 0; j < indentableMacros->size(); j++) { // 'first' is the beginning macro if (findKeyword(line, i, indentableMacros->at(j)->first)) { nextLineIsEventIndent = true; break; } } for (size_t j = 0; j < indentableMacros->size(); j++) { // 'second' is the ending macro if (findKeyword(line, i, indentableMacros->at(j)->second)) { isInEventTable = false; eventPreprocDepth = 0; break; } } } // ---------------- process SQL ----------------------------------------------- if (isInSQL) { if (isBeginDeclareSectionSQL(line, i)) nextLineIsDeclareIndent = true; if (isEndDeclareSectionSQL(line, i)) isInDeclareSection = false; break; } // ---------------- process switch statements --------------------------------- if (isPotentialKeyword && findKeyword(line, i, "switch")) { switchDepth++; switchStack.push_back(sw); // save current variables sw.switchBracketCount = 0; sw.unindentCase = false; // don't clear case until end of switch i += 5; // bypass switch statement continue; } // just want unindented case statements from this point if (caseIndent || switchDepth == 0 || (isInPreprocessor && !preprocDefineIndent)) { // bypass the entire word if (isPotentialKeyword) { string name = getCurrentWord(line, i); i += name.length() - 1; } continue; } i = processSwitchBlock(line, i); } // end of for loop * end of for loop * end of for loop * end of for loop } /** * process the character at the current index in a switch block. * * @param line a reference to the line to indent. * @param index the current line index. * @return the new line index. */ size_t ASEnhancer::processSwitchBlock(string &line, size_t index) { size_t i = index; bool isPotentialKeyword = isCharPotentialHeader(line, i); if (line[i] == '{') { sw.switchBracketCount++; if (lookingForCaseBracket) // if 1st after case statement { sw.unindentCase = true; // unindenting this case sw.unindentDepth++; lookingForCaseBracket = false; // not looking now } return i; } lookingForCaseBracket = false; // no opening bracket, don't indent if (line[i] == '}') { sw.switchBracketCount--; assert(sw.switchBracketCount <= bracketCount); if (sw.switchBracketCount == 0) // if end of switch statement { int lineUnindent = sw.unindentDepth; if (line.find_first_not_of(" \t") == i && !switchStack.empty()) lineUnindent = switchStack[switchStack.size() - 1].unindentDepth; if (shouldUnindentLine) { if (lineUnindent > 0) i -= unindentLine(line, lineUnindent); shouldUnindentLine = false; } switchDepth--; sw = switchStack.back(); switchStack.pop_back(); } return i; } if (isPotentialKeyword && (findKeyword(line, i, "case") || findKeyword(line, i, "default"))) { if (sw.unindentCase) // if unindented last case { sw.unindentCase = false; // stop unindenting previous case sw.unindentDepth--; } i = findCaseColon(line, i); i++; for (; i < line.length(); i++) // bypass whitespace { if (!isWhiteSpace(line[i])) break; } if (i < line.length()) { if (line[i] == '{') { bracketCount++; sw.switchBracketCount++; if (!isOneLineBlockReached(line, i)) unindentNextLine = true; return i; } } lookingForCaseBracket = true; i--; // need to process this char return i; } if (isPotentialKeyword) { string name = getCurrentWord(line, i); // bypass the entire name i += name.length() - 1; } return i; } /** * unindent a line by a given number of tabsets * by erasing the leading whitespace from the line argument. * * @param line a reference to the line to unindent. * @param unindent the number of tabsets to erase. * @return the number of characters erased. */ int ASEnhancer::unindentLine(string &line, int unindent) const { size_t whitespace = line.find_first_not_of(" \t"); if (whitespace == string::npos) // if line is blank whitespace = line.length(); // must remove padding, if any if (whitespace == 0) return 0; size_t charsToErase = 0; if (forceTab && indentLength != tabLength) { // replace tab indents with spaces convertForceTabIndentToSpaces(line); // remove the space indents size_t spaceIndentLength = line.find_first_not_of(" \t"); charsToErase = unindent * indentLength; if (charsToErase <= spaceIndentLength) line.erase(0, charsToErase); else charsToErase = 0; // replace leading spaces with tab indents convertSpaceIndentToForceTab(line); } else if (useTabs) { charsToErase = unindent; if (charsToErase <= whitespace) line.erase(0, charsToErase); else charsToErase = 0; } else // spaces { charsToErase = unindent * indentLength; if (charsToErase <= whitespace) line.erase(0, charsToErase); else charsToErase = 0; } return charsToErase; } } // end namespace astyle ================================================ FILE: 3rdpart/astyle/ASFormatter.cpp ================================================ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ASFormatter.cpp * * Copyright (C) 2014 by Jim Pattee * * * This file is a part of Artistic Style - an indentation and * reformatting tool for C, C++, C# and Java source files. * * * Artistic Style is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Artistic Style is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Artistic Style. If not, see . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "astyle.h" #include #include namespace astyle { /** * Constructor of ASFormatter */ ASFormatter::ASFormatter() { sourceIterator = NULL; enhancer = new ASEnhancer; preBracketHeaderStack = NULL; bracketTypeStack = NULL; parenStack = NULL; structStack = NULL; questionMarkStack = NULL; lineCommentNoIndent = false; formattingStyle = STYLE_NONE; bracketFormatMode = NONE_MODE; pointerAlignment = PTR_ALIGN_NONE; referenceAlignment = REF_SAME_AS_PTR; objCColonPadMode = COLON_PAD_NO_CHANGE; lineEnd = LINEEND_DEFAULT; maxCodeLength = string::npos; shouldPadOperators = false; shouldPadParensOutside = false; shouldPadFirstParen = false; shouldPadParensInside = false; shouldPadHeader = false; shouldStripCommentPrefix = false; shouldUnPadParens = false; attachClosingBracketMode = false; shouldBreakOneLineBlocks = true; shouldBreakOneLineStatements = true; shouldConvertTabs = false; shouldIndentCol1Comments = false; shouldIndentPreprocBlock = false; shouldCloseTemplates = false; shouldAttachExternC = false; shouldAttachNamespace = false; shouldAttachClass = false; shouldAttachInline = false; shouldBreakBlocks = false; shouldBreakClosingHeaderBlocks = false; shouldBreakClosingHeaderBrackets = false; shouldDeleteEmptyLines = false; shouldBreakElseIfs = false; shouldBreakLineAfterLogical = false; shouldAddBrackets = false; shouldAddOneLineBrackets = false; shouldRemoveBrackets = false; shouldPadMethodColon = false; shouldPadMethodPrefix = false; shouldUnPadMethodPrefix = false; // initialize ASFormatter member vectors formatterFileType = 9; // reset to an invalid type headers = new vector; nonParenHeaders = new vector; preDefinitionHeaders = new vector; preCommandHeaders = new vector; operators = new vector; assignmentOperators = new vector; castOperators = new vector; // initialize ASEnhancer member vectors indentableMacros = new vector* >; } /** * Destructor of ASFormatter */ ASFormatter::~ASFormatter() { // delete ASFormatter stack vectors deleteContainer(preBracketHeaderStack); deleteContainer(bracketTypeStack); deleteContainer(parenStack); deleteContainer(structStack); deleteContainer(questionMarkStack); // delete ASFormatter member vectors formatterFileType = 9; // reset to an invalid type delete headers; delete nonParenHeaders; delete preDefinitionHeaders; delete preCommandHeaders; delete operators; delete assignmentOperators; delete castOperators; // delete ASEnhancer member vectors delete indentableMacros; // delete ASBeautifier member vectors // must be done when the ASFormatter object is deleted (not ASBeautifier) ASBeautifier::deleteBeautifierVectors(); delete enhancer; } /** * initialize the ASFormatter. * * init() should be called every time a ASFormatter object is to start * formatting a NEW source file. * init() receives a pointer to a ASSourceIterator object that will be * used to iterate through the source code. * * @param si a pointer to the ASSourceIterator or ASStreamIterator object. */ void ASFormatter::init(ASSourceIterator* si) { buildLanguageVectors(); fixOptionVariableConflicts(); ASBeautifier::init(si); sourceIterator = si; enhancer->init(getFileType(), getIndentLength(), getTabLength(), getIndentString() == "\t" ? true : false, getForceTabIndentation(), getNamespaceIndent(), getCaseIndent(), shouldIndentPreprocBlock, getPreprocDefineIndent(), getEmptyLineFill(), indentableMacros); initContainer(preBracketHeaderStack, new vector); initContainer(parenStack, new vector); initContainer(structStack, new vector); initContainer(questionMarkStack, new vector); parenStack->push_back(0); // parenStack must contain this default entry initContainer(bracketTypeStack, new vector); bracketTypeStack->push_back(NULL_TYPE); // bracketTypeStack must contain this default entry clearFormattedLineSplitPoints(); currentHeader = NULL; currentLine = ""; readyFormattedLine = ""; formattedLine = ""; verbatimDelimiter = ""; currentChar = ' '; previousChar = ' '; previousCommandChar = ' '; previousNonWSChar = ' '; quoteChar = '"'; preprocBlockEnd = 0; charNum = 0; checksumIn = 0; checksumOut = 0; currentLineFirstBracketNum = string::npos; formattedLineCommentNum = 0; leadingSpaces = 0; previousReadyFormattedLineLength = string::npos; preprocBracketTypeStackSize = 0; spacePadNum = 0; nextLineSpacePadNum = 0; templateDepth = 0; squareBracketCount = 0; horstmannIndentChars = 0; tabIncrementIn = 0; previousBracketType = NULL_TYPE; previousOperator = NULL; isVirgin = true; isInLineComment = false; isInComment = false; isInCommentStartLine = false; noTrimCommentContinuation = false; isInPreprocessor = false; isInPreprocessorBeautify = false; doesLineStartComment = false; lineEndsInCommentOnly = false; lineIsCommentOnly = false; lineIsLineCommentOnly = false; lineIsEmpty = false; isImmediatelyPostCommentOnly = false; isImmediatelyPostEmptyLine = false; isInClassInitializer = false; isInQuote = false; isInVerbatimQuote = false; haveLineContinuationChar = false; isInQuoteContinuation = false; isHeaderInMultiStatementLine = false; isSpecialChar = false; isNonParenHeader = false; foundNamespaceHeader = false; foundClassHeader = false; foundStructHeader = false; foundInterfaceHeader = false; foundPreDefinitionHeader = false; foundPreCommandHeader = false; foundPreCommandMacro = false; foundCastOperator = false; foundQuestionMark = false; isInLineBreak = false; endOfAsmReached = false; endOfCodeReached = false; isFormattingModeOff = false; isInEnum = false; isInExecSQL = false; isInAsm = false; isInAsmOneLine = false; isInAsmBlock = false; isLineReady = false; elseHeaderFollowsComments = false; caseHeaderFollowsComments = false; isPreviousBracketBlockRelated = false; isInPotentialCalculation = false; shouldReparseCurrentChar = false; needHeaderOpeningBracket = false; shouldBreakLineAtNextChar = false; shouldKeepLineUnbroken = false; passedSemicolon = false; passedColon = false; isImmediatelyPostNonInStmt = false; isCharImmediatelyPostNonInStmt = false; isInTemplate = false; isImmediatelyPostComment = false; isImmediatelyPostLineComment = false; isImmediatelyPostEmptyBlock = false; isImmediatelyPostPreprocessor = false; isImmediatelyPostReturn = false; isImmediatelyPostThrow = false; isImmediatelyPostOperator = false; isImmediatelyPostTemplate = false; isImmediatelyPostPointerOrReference = false; isCharImmediatelyPostReturn = false; isCharImmediatelyPostThrow = false; isCharImmediatelyPostOperator = false; isCharImmediatelyPostComment = false; isPreviousCharPostComment = false; isCharImmediatelyPostLineComment = false; isCharImmediatelyPostOpenBlock = false; isCharImmediatelyPostCloseBlock = false; isCharImmediatelyPostTemplate = false; isCharImmediatelyPostPointerOrReference = false; isInObjCMethodDefinition = false; isInObjCInterface = false; isInObjCSelector = false; breakCurrentOneLineBlock = false; shouldRemoveNextClosingBracket = false; isInHorstmannRunIn = false; currentLineBeginsWithBracket = false; isPrependPostBlockEmptyLineRequested = false; isAppendPostBlockEmptyLineRequested = false; isIndentableProprocessor = false; isIndentableProprocessorBlock = false; prependEmptyLine = false; appendOpeningBracket = false; foundClosingHeader = false; isImmediatelyPostHeader = false; isInHeader = false; isInCase = false; isFirstPreprocConditional = false; processedFirstConditional = false; isJavaStaticConstructor = false; } /** * build vectors for each programing language * depending on the file extension. */ void ASFormatter::buildLanguageVectors() { if (getFileType() == formatterFileType) // don't build unless necessary return; formatterFileType = getFileType(); headers->clear(); nonParenHeaders->clear(); preDefinitionHeaders->clear(); preCommandHeaders->clear(); operators->clear(); assignmentOperators->clear(); castOperators->clear(); indentableMacros->clear(); // ASEnhancer ASResource::buildHeaders(headers, getFileType()); ASResource::buildNonParenHeaders(nonParenHeaders, getFileType()); ASResource::buildPreDefinitionHeaders(preDefinitionHeaders, getFileType()); ASResource::buildPreCommandHeaders(preCommandHeaders, getFileType()); ASResource::buildOperators(operators, getFileType()); ASResource::buildAssignmentOperators(assignmentOperators); ASResource::buildCastOperators(castOperators); ASResource::buildIndentableMacros(indentableMacros); //ASEnhancer } /** * set the variables for each predefined style. * this will override any previous settings. */ void ASFormatter::fixOptionVariableConflicts() { if (formattingStyle == STYLE_ALLMAN) { setBracketFormatMode(BREAK_MODE); } else if (formattingStyle == STYLE_JAVA) { setBracketFormatMode(ATTACH_MODE); } else if (formattingStyle == STYLE_KR) { setBracketFormatMode(LINUX_MODE); } else if (formattingStyle == STYLE_STROUSTRUP) { setBracketFormatMode(STROUSTRUP_MODE); } else if (formattingStyle == STYLE_WHITESMITH) { setBracketFormatMode(BREAK_MODE); setBracketIndent(true); setClassIndent(true); // avoid hanging indent with access modifiers setSwitchIndent(true); // avoid hanging indent with case statements } else if (formattingStyle == STYLE_VTK) { // the unindented class bracket does NOT cause a hanging indent like Whitesmith setBracketFormatMode(BREAK_MODE); setBracketIndentVtk(true); // sets both bracketIndent and bracketIndentVtk setSwitchIndent(true); // avoid hanging indent with case statements } else if (formattingStyle == STYLE_BANNER) { // attached brackets can have hanging indents with the closing bracket setBracketFormatMode(ATTACH_MODE); setBracketIndent(true); setClassIndent(true); // avoid hanging indent with access modifiers setSwitchIndent(true); // avoid hanging indent with case statements } else if (formattingStyle == STYLE_GNU) { setBracketFormatMode(BREAK_MODE); setBlockIndent(true); } else if (formattingStyle == STYLE_LINUX) { setBracketFormatMode(LINUX_MODE); // always for Linux style setMinConditionalIndentOption(MINCOND_ONEHALF); } else if (formattingStyle == STYLE_HORSTMANN) { setBracketFormatMode(RUN_IN_MODE); setSwitchIndent(true); } else if (formattingStyle == STYLE_1TBS) { setBracketFormatMode(LINUX_MODE); setAddBracketsMode(true); setRemoveBracketsMode(false); } else if (formattingStyle == STYLE_GOOGLE) { setBracketFormatMode(ATTACH_MODE); setModifierIndent(true); setClassIndent(false); } else if (formattingStyle == STYLE_PICO) { setBracketFormatMode(RUN_IN_MODE); setAttachClosingBracketMode(true); setSwitchIndent(true); setBreakOneLineBlocksMode(false); setSingleStatementsMode(false); // add-brackets won't work for pico, but it could be fixed if necessary // both options should be set to true if (shouldAddBrackets) shouldAddOneLineBrackets = true; } else if (formattingStyle == STYLE_LISP) { setBracketFormatMode(ATTACH_MODE); setAttachClosingBracketMode(true); setSingleStatementsMode(false); // add-one-line-brackets won't work for lisp // only shouldAddBrackets should be set to true if (shouldAddOneLineBrackets) { shouldAddBrackets = true; shouldAddOneLineBrackets = false; } } setMinConditionalIndentLength(); // if not set by indent=force-tab-x set equal to indentLength if (!getTabLength()) setDefaultTabLength(); // add-one-line-brackets implies keep-one-line-blocks if (shouldAddOneLineBrackets) setBreakOneLineBlocksMode(false); // don't allow add-brackets and remove-brackets if (shouldAddBrackets || shouldAddOneLineBrackets) setRemoveBracketsMode(false); // don't allow indent-classes and indent-modifiers if (getClassIndent()) setModifierIndent(false); } /** * get the next formatted line. * * @return formatted line. */ string ASFormatter::nextLine() { const string* newHeader; bool isInVirginLine = isVirgin; isCharImmediatelyPostComment = false; isPreviousCharPostComment = false; isCharImmediatelyPostLineComment = false; isCharImmediatelyPostOpenBlock = false; isCharImmediatelyPostCloseBlock = false; isCharImmediatelyPostTemplate = false; while (!isLineReady) { if (shouldReparseCurrentChar) shouldReparseCurrentChar = false; else if (!getNextChar()) { breakLine(); continue; } else // stuff to do when reading a new character... { // make sure that a virgin '{' at the beginning of the file will be treated as a block... if (isInVirginLine && currentChar == '{' && currentLineBeginsWithBracket && previousCommandChar == ' ') previousCommandChar = '{'; if (isInClassInitializer && isBracketType(bracketTypeStack->back(), COMMAND_TYPE)) isInClassInitializer = false; if (isInHorstmannRunIn) isInLineBreak = false; if (!isWhiteSpace(currentChar)) isInHorstmannRunIn = false; isPreviousCharPostComment = isCharImmediatelyPostComment; isCharImmediatelyPostComment = false; isCharImmediatelyPostTemplate = false; isCharImmediatelyPostReturn = false; isCharImmediatelyPostThrow = false; isCharImmediatelyPostOperator = false; isCharImmediatelyPostPointerOrReference = false; isCharImmediatelyPostOpenBlock = false; isCharImmediatelyPostCloseBlock = false; } if ((lineIsLineCommentOnly || lineIsCommentOnly) && currentLine.find("*INDENT-ON*", charNum) != string::npos && isFormattingModeOff) { isFormattingModeOff = false; breakLine(); formattedLine = currentLine; charNum = (int) currentLine.length() - 1; continue; } if (isFormattingModeOff) { breakLine(); formattedLine = currentLine; charNum = (int) currentLine.length() - 1; continue; } if ((lineIsLineCommentOnly || lineIsCommentOnly) && currentLine.find("*INDENT-OFF*", charNum) != string::npos) { isFormattingModeOff = true; if (isInLineBreak) // is true if not the first line breakLine(); formattedLine = currentLine; charNum = (int)currentLine.length() - 1; continue; } if (shouldBreakLineAtNextChar) { if (isWhiteSpace(currentChar) && !lineIsEmpty) continue; isInLineBreak = true; shouldBreakLineAtNextChar = false; } if (isInExecSQL && !passedSemicolon) { if (currentChar == ';') passedSemicolon = true; appendCurrentChar(); continue; } if (isInLineComment) { formatLineCommentBody(); continue; } else if (isInComment) { formatCommentBody(); continue; } else if (isInQuote) { formatQuoteBody(); continue; } // not in quote or comment or line comment if (isSequenceReached("//")) { formatLineCommentOpener(); testForTimeToSplitFormattedLine(); continue; } else if (isSequenceReached("/*")) { formatCommentOpener(); testForTimeToSplitFormattedLine(); continue; } else if (currentChar == '"' || currentChar == '\'') { formatQuoteOpener(); testForTimeToSplitFormattedLine(); continue; } // treat these preprocessor statements as a line comment else if (currentChar == '#' && currentLine.find_first_not_of(" \t") == (size_t) charNum) { string preproc = trim(currentLine.c_str() + charNum + 1); if (preproc.length() > 0 && isCharPotentialHeader(preproc, 0) && (findKeyword(preproc, 0, "region") || findKeyword(preproc, 0, "endregion") || findKeyword(preproc, 0, "error") || findKeyword(preproc, 0, "warning") || findKeyword(preproc, 0, "line"))) { currentLine = rtrim(currentLine); // trim the end only // check for horstmann run-in if (formattedLine.length() > 0 && formattedLine[0] == '{') { isInLineBreak = true; isInHorstmannRunIn = false; } if (previousCommandChar == '}') currentHeader = NULL; isInLineComment = true; appendCurrentChar(); continue; } } if (isInPreprocessor) { appendCurrentChar(); continue; } if (isInTemplate && shouldCloseTemplates) { if (previousCommandChar == '<' && isWhiteSpace(currentChar)) continue; if (isWhiteSpace(currentChar) && peekNextChar() == '>') continue; } if (shouldRemoveNextClosingBracket && currentChar == '}') { currentLine[charNum] = currentChar = ' '; shouldRemoveNextClosingBracket = false; assert(adjustChecksumIn(-'}')); // if the line is empty, delete it if (currentLine.find_first_not_of(" \t")) continue; } // handle white space - needed to simplify the rest. if (isWhiteSpace(currentChar)) { appendCurrentChar(); continue; } /* not in MIDDLE of quote or comment or SQL or white-space of any type ... */ // check if in preprocessor // ** isInPreprocessor will be automatically reset at the beginning // of a new line in getnextChar() if (currentChar == '#') { isInPreprocessor = true; // check for horstmann run-in if (formattedLine.length() > 0 && formattedLine[0] == '{') { isInLineBreak = true; isInHorstmannRunIn = false; } processPreprocessor(); // if top level it is potentially indentable if (shouldIndentPreprocBlock && (isBracketType(bracketTypeStack->back(), NULL_TYPE) || isBracketType(bracketTypeStack->back(), NAMESPACE_TYPE)) && !foundClassHeader && !isInClassInitializer && sourceIterator->tellg() > preprocBlockEnd) { // indent the #if preprocessor blocks string preproc = ASBeautifier::extractPreprocessorStatement(currentLine); if (preproc.length() >= 2 && preproc.substr(0, 2) == "if") // #if, #ifdef, #ifndef { if (isImmediatelyPostPreprocessor) breakLine(); isIndentableProprocessorBlock = isIndentablePreprocessorBlock(currentLine, charNum); isIndentableProprocessor = isIndentableProprocessorBlock; } } if (isIndentableProprocessorBlock && charNum < (int) currentLine.length() - 1 && isWhiteSpace(currentLine[charNum + 1])) { size_t nextText = currentLine.find_first_not_of(" \t", charNum + 1); if (nextText != string::npos) currentLine.erase(charNum + 1, nextText - charNum - 1); } if (isIndentableProprocessorBlock && sourceIterator->tellg() >= preprocBlockEnd) isIndentableProprocessorBlock = false; // need to fall thru here to reset the variables } /* not in preprocessor ... */ if (isImmediatelyPostComment) { caseHeaderFollowsComments = false; isImmediatelyPostComment = false; isCharImmediatelyPostComment = true; } if (isImmediatelyPostLineComment) { caseHeaderFollowsComments = false; isImmediatelyPostLineComment = false; isCharImmediatelyPostLineComment = true; } if (isImmediatelyPostReturn) { isImmediatelyPostReturn = false; isCharImmediatelyPostReturn = true; } if (isImmediatelyPostThrow) { isImmediatelyPostThrow = false; isCharImmediatelyPostThrow = true; } if (isImmediatelyPostOperator) { isImmediatelyPostOperator = false; isCharImmediatelyPostOperator = true; } if (isImmediatelyPostTemplate) { isImmediatelyPostTemplate = false; isCharImmediatelyPostTemplate = true; } if (isImmediatelyPostPointerOrReference) { isImmediatelyPostPointerOrReference = false; isCharImmediatelyPostPointerOrReference = true; } // reset isImmediatelyPostHeader information if (isImmediatelyPostHeader) { // should brackets be added if (currentChar != '{' && shouldAddBrackets) { bool bracketsAdded = addBracketsToStatement(); if (bracketsAdded && !shouldAddOneLineBrackets) { size_t firstText = currentLine.find_first_not_of(" \t"); assert(firstText != string::npos); if ((int) firstText == charNum) breakCurrentOneLineBlock = true; } } // should brackets be removed else if (currentChar == '{' && shouldRemoveBrackets) { bool bracketsRemoved = removeBracketsFromStatement(); if (bracketsRemoved) { shouldRemoveNextClosingBracket = true; if (isBeforeAnyLineEndComment(charNum)) spacePadNum--; else if (shouldBreakOneLineBlocks || (currentLineBeginsWithBracket && currentLine.find_first_not_of(" \t") != string::npos)) shouldBreakLineAtNextChar = true; continue; } } // break 'else-if' if shouldBreakElseIfs is requested if (shouldBreakElseIfs && currentHeader == &AS_ELSE && isOkToBreakBlock(bracketTypeStack->back()) && !isBeforeAnyComment() && (shouldBreakOneLineStatements || !isHeaderInMultiStatementLine)) { string nextText = peekNextText(currentLine.substr(charNum)); if (nextText.length() > 0 && isCharPotentialHeader(nextText, 0) && ASBeautifier::findHeader(nextText, 0, headers) == &AS_IF) { isInLineBreak = true; } } isImmediatelyPostHeader = false; } if (passedSemicolon) // need to break the formattedLine { passedSemicolon = false; if (parenStack->back() == 0 && !isCharImmediatelyPostComment && currentChar != ';') // allow ;; { // does a one-line block have ending comments? if (isBracketType(bracketTypeStack->back(), SINGLE_LINE_TYPE)) { size_t blockEnd = currentLine.rfind(AS_CLOSE_BRACKET); assert(blockEnd != string::npos); // move ending comments to this formattedLine if (isBeforeAnyLineEndComment(blockEnd)) { size_t commentStart = currentLine.find_first_not_of(" \t", blockEnd + 1); assert(commentStart != string::npos); assert((currentLine.compare(commentStart, 2, "//") == 0) || (currentLine.compare(commentStart, 2, "/*") == 0)); size_t commentLength = currentLine.length() - commentStart; formattedLine.append(getIndentLength() - 1, ' '); formattedLine.append(currentLine, commentStart, commentLength); currentLine.erase(commentStart, commentLength); testForTimeToSplitFormattedLine(); } } isInExecSQL = false; shouldReparseCurrentChar = true; if (formattedLine.find_first_not_of(" \t") != string::npos) isInLineBreak = true; if (needHeaderOpeningBracket) { isCharImmediatelyPostCloseBlock = true; needHeaderOpeningBracket = false; } continue; } } if (passedColon) { passedColon = false; if (parenStack->back() == 0 && !isBeforeAnyComment() && (formattedLine.find_first_not_of(" \t") != string::npos)) { shouldReparseCurrentChar = true; isInLineBreak = true; continue; } } // Check if in template declaration, e.g. foo or foo if (!isInTemplate && currentChar == '<') { checkIfTemplateOpener(); } // handle parens if (currentChar == '(' || currentChar == '[' || (isInTemplate && currentChar == '<')) { questionMarkStack->push_back(foundQuestionMark); foundQuestionMark = false; parenStack->back()++; if (currentChar == '[') ++squareBracketCount; } else if (currentChar == ')' || currentChar == ']' || (isInTemplate && currentChar == '>')) { foundPreCommandHeader = false; parenStack->back()--; // this can happen in preprocessor directives if (parenStack->back() < 0) parenStack->back() = 0; if (!questionMarkStack->empty()) { foundQuestionMark = questionMarkStack->back(); questionMarkStack->pop_back(); } if (isInTemplate && currentChar == '>') { templateDepth--; if (templateDepth == 0) { isInTemplate = false; isImmediatelyPostTemplate = true; } } // check if this parenthesis closes a header, e.g. if (...), while (...) if (isInHeader && parenStack->back() == 0) { isInHeader = false; isImmediatelyPostHeader = true; foundQuestionMark = false; } if (currentChar == ']') { --squareBracketCount; if (squareBracketCount < 0) squareBracketCount = 0; } if (currentChar == ')') { foundCastOperator = false; if (parenStack->back() == 0) endOfAsmReached = true; } } // handle brackets if (currentChar == '{' || currentChar == '}') { // if appendOpeningBracket this was already done for the original bracket if (currentChar == '{' && !appendOpeningBracket) { BracketType newBracketType = getBracketType(); foundNamespaceHeader = false; foundClassHeader = false; foundStructHeader = false; foundInterfaceHeader = false; foundPreDefinitionHeader = false; foundPreCommandHeader = false; foundPreCommandMacro = false; isInPotentialCalculation = false; isInObjCMethodDefinition = false; isInObjCInterface = false; isInEnum = false; isJavaStaticConstructor = false; isCharImmediatelyPostNonInStmt = false; needHeaderOpeningBracket = false; shouldKeepLineUnbroken = false; isPreviousBracketBlockRelated = !isBracketType(newBracketType, ARRAY_TYPE); bracketTypeStack->push_back(newBracketType); preBracketHeaderStack->push_back(currentHeader); currentHeader = NULL; structStack->push_back(isInIndentableStruct); if (isBracketType(newBracketType, STRUCT_TYPE) && isCStyle()) isInIndentableStruct = isStructAccessModified(currentLine, charNum); else isInIndentableStruct = false; } // this must be done before the bracketTypeStack is popped BracketType bracketType = bracketTypeStack->back(); bool isOpeningArrayBracket = (isBracketType(bracketType, ARRAY_TYPE) && bracketTypeStack->size() >= 2 && !isBracketType((*bracketTypeStack)[bracketTypeStack->size() - 2], ARRAY_TYPE) ); if (currentChar == '}') { // if a request has been made to append a post block empty line, // but the block exists immediately before a closing bracket, // then there is no need for the post block empty line. isAppendPostBlockEmptyLineRequested = false; breakCurrentOneLineBlock = false; if (isInAsm) endOfAsmReached = true; isInAsmOneLine = isInQuote = false; shouldKeepLineUnbroken = false; squareBracketCount = 0; if (bracketTypeStack->size() > 1) { previousBracketType = bracketTypeStack->back(); bracketTypeStack->pop_back(); isPreviousBracketBlockRelated = !isBracketType(bracketType, ARRAY_TYPE); } else { previousBracketType = NULL_TYPE; isPreviousBracketBlockRelated = false; } if (!preBracketHeaderStack->empty()) { currentHeader = preBracketHeaderStack->back(); preBracketHeaderStack->pop_back(); } else currentHeader = NULL; if (!structStack->empty()) { isInIndentableStruct = structStack->back(); structStack->pop_back(); } else isInIndentableStruct = false; if (isNonInStatementArray && (!isBracketType(bracketTypeStack->back(), ARRAY_TYPE) // check previous bracket || peekNextChar() == ';')) // check for "};" added V2.01 isImmediatelyPostNonInStmt = true; } // format brackets appendOpeningBracket = false; if (isBracketType(bracketType, ARRAY_TYPE)) { formatArrayBrackets(bracketType, isOpeningArrayBracket); } else { if (currentChar == '{') formatOpeningBracket(bracketType); else formatClosingBracket(bracketType); } continue; } if ((((previousCommandChar == '{' && isPreviousBracketBlockRelated) || ((previousCommandChar == '}' && !isImmediatelyPostEmptyBlock && isPreviousBracketBlockRelated && !isPreviousCharPostComment // Fixes wrongly appended newlines after '}' immediately after comments && peekNextChar() != ' ' && !isBracketType(previousBracketType, DEFINITION_TYPE)) && !isBracketType(bracketTypeStack->back(), DEFINITION_TYPE))) && isOkToBreakBlock(bracketTypeStack->back())) // check for array || (previousCommandChar == '{' // added 9/30/2010 && isBracketType(bracketTypeStack->back(), ARRAY_TYPE) && !isBracketType(bracketTypeStack->back(), SINGLE_LINE_TYPE) && isNonInStatementArray)) { isCharImmediatelyPostOpenBlock = (previousCommandChar == '{'); isCharImmediatelyPostCloseBlock = (previousCommandChar == '}'); if (isCharImmediatelyPostOpenBlock && !isCharImmediatelyPostComment && !isCharImmediatelyPostLineComment) { previousCommandChar = ' '; if (bracketFormatMode == NONE_MODE) { if (shouldBreakOneLineBlocks && isBracketType(bracketTypeStack->back(), SINGLE_LINE_TYPE)) isInLineBreak = true; else if (currentLineBeginsWithBracket) formatRunIn(); else breakLine(); } else if (bracketFormatMode == RUN_IN_MODE && currentChar != '#') formatRunIn(); else isInLineBreak = true; } else if (isCharImmediatelyPostCloseBlock && shouldBreakOneLineStatements && (isLegalNameChar(currentChar) && currentChar != '.') && !isCharImmediatelyPostComment) { previousCommandChar = ' '; isInLineBreak = true; } } // reset block handling flags isImmediatelyPostEmptyBlock = false; // look for headers bool isPotentialHeader = isCharPotentialHeader(currentLine, charNum); if (isPotentialHeader && !isInTemplate && !squareBracketCount) { isNonParenHeader = false; foundClosingHeader = false; newHeader = findHeader(headers); // Qt headers may be variables in C++ if (newHeader == &AS_FOREVER || newHeader == &AS_FOREACH) { if (currentLine.find_first_of("=;", charNum) != string::npos) newHeader = NULL; } if (newHeader != NULL) { const string* previousHeader; // recognize closing headers of do..while, if..else, try..catch..finally if ((newHeader == &AS_ELSE && currentHeader == &AS_IF) || (newHeader == &AS_WHILE && currentHeader == &AS_DO) || (newHeader == &AS_CATCH && currentHeader == &AS_TRY) || (newHeader == &AS_CATCH && currentHeader == &AS_CATCH) || (newHeader == &AS_FINALLY && currentHeader == &AS_TRY) || (newHeader == &AS_FINALLY && currentHeader == &AS_CATCH) || (newHeader == &_AS_FINALLY && currentHeader == &_AS_TRY) || (newHeader == &_AS_EXCEPT && currentHeader == &_AS_TRY) || (newHeader == &AS_SET && currentHeader == &AS_GET) || (newHeader == &AS_REMOVE && currentHeader == &AS_ADD)) foundClosingHeader = true; previousHeader = currentHeader; currentHeader = newHeader; needHeaderOpeningBracket = true; // is the previous statement on the same line? if ((previousNonWSChar == ';' || previousNonWSChar == ':') && !isInLineBreak && isOkToBreakBlock(bracketTypeStack->back())) { // if breaking lines, break the line at the header // except for multiple 'case' statements on a line if (maxCodeLength != string::npos && previousHeader != &AS_CASE) isInLineBreak = true; else isHeaderInMultiStatementLine = true; } if (foundClosingHeader && previousNonWSChar == '}') { if (isOkToBreakBlock(bracketTypeStack->back())) isLineBreakBeforeClosingHeader(); // get the adjustment for a comment following the closing header if (isInLineBreak) nextLineSpacePadNum = getNextLineCommentAdjustment(); else spacePadNum = getCurrentLineCommentAdjustment(); } // check if the found header is non-paren header isNonParenHeader = findHeader(nonParenHeaders) != NULL; // join 'else if' statements if (currentHeader == &AS_IF && previousHeader == &AS_ELSE && isInLineBreak && !shouldBreakElseIfs && !isCharImmediatelyPostLineComment) { // 'else' must be last thing on the line size_t start = formattedLine.length() >= 6 ? formattedLine.length() - 6 : 0; if (formattedLine.find(AS_ELSE, start) != string::npos) { appendSpacePad(); isInLineBreak = false; } } appendSequence(*currentHeader); goForward(currentHeader->length() - 1); // if a paren-header is found add a space after it, if needed // this checks currentLine, appendSpacePad() checks formattedLine // in 'case' and C# 'catch' can be either a paren or non-paren header if (shouldPadHeader && (!isNonParenHeader || (currentHeader == &AS_CASE && peekNextChar() == '(') || (currentHeader == &AS_CATCH && peekNextChar() == '(')) && charNum < (int) currentLine.length() - 1 && !isWhiteSpace(currentLine[charNum + 1])) appendSpacePad(); // Signal that a header has been reached // *** But treat a closing while() (as in do...while) // as if it were NOT a header since a closing while() // should never have a block after it! if (currentHeader != &AS_CASE && currentHeader != &AS_DEFAULT && !(foundClosingHeader && currentHeader == &AS_WHILE)) { isInHeader = true; // in C# 'catch' and 'delegate' can be a paren or non-paren header if (isNonParenHeader && !isSharpStyleWithParen(currentHeader)) { isImmediatelyPostHeader = true; isInHeader = false; } } if (shouldBreakBlocks && isOkToBreakBlock(bracketTypeStack->back()) && !isHeaderInMultiStatementLine) { if (previousHeader == NULL && !foundClosingHeader && !isCharImmediatelyPostOpenBlock && !isImmediatelyPostCommentOnly) { isPrependPostBlockEmptyLineRequested = true; } if (currentHeader == &AS_ELSE || currentHeader == &AS_CATCH || currentHeader == &AS_FINALLY || foundClosingHeader) { isPrependPostBlockEmptyLineRequested = false; } if (shouldBreakClosingHeaderBlocks && isCharImmediatelyPostCloseBlock && !isImmediatelyPostCommentOnly && currentHeader != &AS_WHILE) // closing do-while block { isPrependPostBlockEmptyLineRequested = true; } } if (currentHeader == &AS_CASE || currentHeader == &AS_DEFAULT) isInCase = true; continue; } else if ((newHeader = findHeader(preDefinitionHeaders)) != NULL && parenStack->back() == 0 && !isInEnum) // not C++11 enum class { if (newHeader == &AS_NAMESPACE) foundNamespaceHeader = true; if (newHeader == &AS_CLASS) foundClassHeader = true; if (newHeader == &AS_STRUCT) foundStructHeader = true; if (newHeader == &AS_INTERFACE) foundInterfaceHeader = true; foundPreDefinitionHeader = true; appendSequence(*newHeader); goForward(newHeader->length() - 1); continue; } else if ((newHeader = findHeader(preCommandHeaders)) != NULL) { // a 'const' variable is not a preCommandHeader if (previousNonWSChar != ';' && previousNonWSChar != '{' && getPreviousWord(currentLine, charNum) != AS_STATIC) foundPreCommandHeader = true; } else if ((newHeader = findHeader(castOperators)) != NULL) { foundCastOperator = true; appendSequence(*newHeader); goForward(newHeader->length() - 1); continue; } } // (isPotentialHeader && !isInTemplate) if (isInLineBreak) // OK to break line here { breakLine(); if (isInVirginLine) // adjust for the first line { lineCommentNoBeautify = lineCommentNoIndent; lineCommentNoIndent = false; if (isImmediatelyPostPreprocessor) { isInIndentablePreproc = isIndentableProprocessor; isIndentableProprocessor = false; } } } if (previousNonWSChar == '}' || currentChar == ';') { if (currentChar == ';') { squareBracketCount = 0; if (((shouldBreakOneLineStatements || isBracketType(bracketTypeStack->back(), SINGLE_LINE_TYPE)) && isOkToBreakBlock(bracketTypeStack->back())) && !(attachClosingBracketMode && peekNextChar() == '}')) { passedSemicolon = true; } // append post block empty line for unbracketed header if (shouldBreakBlocks && currentHeader != NULL && currentHeader != &AS_CASE && currentHeader != &AS_DEFAULT && !isHeaderInMultiStatementLine && parenStack->back() == 0) { isAppendPostBlockEmptyLineRequested = true; } } if (currentChar != ';' || (needHeaderOpeningBracket && parenStack->back() == 0)) currentHeader = NULL; resetEndOfStatement(); } if (currentChar == ':' && previousChar != ':' // not part of '::' && peekNextChar() != ':') // not part of '::' { if (isInCase) { isInCase = false; if (shouldBreakOneLineStatements) passedColon = true; } else if (isCStyle() // for C/C++ only && isOkToBreakBlock(bracketTypeStack->back()) && shouldBreakOneLineStatements && !foundQuestionMark // not in a ?: sequence && !foundPreDefinitionHeader // not in a definition block (e.g. class foo : public bar && previousCommandChar != ')' // not immediately after closing paren of a method header, e.g. ASFormatter::ASFormatter(...) : ASBeautifier(...) && !foundPreCommandHeader // not after a 'noexcept' && !squareBracketCount // not in objC method call && !isInObjCMethodDefinition // not objC '-' or '+' method && !isInObjCInterface // not objC @interface && !isInObjCSelector // not objC @selector && !isDigit(peekNextChar()) // not a bit field && !isInEnum // not an enum with a base type && !isInAsm // not in extended assembler && !isInAsmOneLine // not in extended assembler && !isInAsmBlock) // not in extended assembler { passedColon = true; } if (isCStyle() && shouldPadMethodColon && (squareBracketCount > 0 || isInObjCMethodDefinition || isInObjCSelector) && !foundQuestionMark) // not in a ?: sequence padObjCMethodColon(); if (isInObjCInterface) { appendSpacePad(); if ((int) currentLine.length() > charNum + 1 && !isWhiteSpace(currentLine[charNum + 1])) currentLine.insert(charNum + 1, " "); } if (isClassInitializer()) isInClassInitializer = true; } if (currentChar == '?') foundQuestionMark = true; if (isPotentialHeader && !isInTemplate) { if (findKeyword(currentLine, charNum, AS_NEW)) isInPotentialCalculation = false; if (findKeyword(currentLine, charNum, AS_RETURN)) { isInPotentialCalculation = true; // return is the same as an = sign isImmediatelyPostReturn = true; } if (findKeyword(currentLine, charNum, AS_OPERATOR)) isImmediatelyPostOperator = true; if (findKeyword(currentLine, charNum, AS_ENUM)) { size_t firstNum = currentLine.find_first_of("(){},/"); if (firstNum == string::npos || currentLine[firstNum] == '{' || currentLine[firstNum] == '/') isInEnum = true; } if (isCStyle() && findKeyword(currentLine, charNum, AS_THROW) && previousCommandChar != ')' && !foundPreCommandHeader) // 'const' throw() isImmediatelyPostThrow = true; if (isCStyle() && findKeyword(currentLine, charNum, AS_EXTERN) && isExternC()) isInExternC = true; // Objective-C NSException macros are preCommandHeaders if (isCStyle() && findKeyword(currentLine, charNum, AS_NS_DURING)) foundPreCommandMacro = true; if (isCStyle() && findKeyword(currentLine, charNum, AS_NS_HANDLER)) foundPreCommandMacro = true; if (isCStyle() && isExecSQL(currentLine, charNum)) isInExecSQL = true; if (isCStyle()) { if (findKeyword(currentLine, charNum, AS_ASM) || findKeyword(currentLine, charNum, AS__ASM__)) { isInAsm = true; } else if (findKeyword(currentLine, charNum, AS_MS_ASM) // microsoft specific || findKeyword(currentLine, charNum, AS_MS__ASM)) { int index = 4; if (peekNextChar() == '_') // check for __asm index = 5; char peekedChar = ASBase::peekNextChar(currentLine, charNum + index); if (peekedChar == '{' || peekedChar == ' ') isInAsmBlock = true; else isInAsmOneLine = true; } } if (isJavaStyle() && (findKeyword(currentLine, charNum, AS_STATIC) && isNextCharOpeningBracket(charNum + 6))) isJavaStaticConstructor = true; if (isSharpStyle() && (findKeyword(currentLine, charNum, AS_DELEGATE) || findKeyword(currentLine, charNum, AS_UNCHECKED))) isSharpDelegate = true; // append the entire name string name = getCurrentWord(currentLine, charNum); // must pad the 'and' and 'or' operators if required if (name == "and" || name == "or") { if (shouldPadOperators && previousNonWSChar != ':') { appendSpacePad(); appendOperator(name); goForward(name.length() - 1); if (!isBeforeAnyComment() && !(currentLine.compare(charNum + 1, 1, AS_SEMICOLON) == 0) && !(currentLine.compare(charNum + 1, 2, AS_SCOPE_RESOLUTION) == 0)) appendSpaceAfter(); } else { appendOperator(name); goForward(name.length() - 1); } } else { appendSequence(name); goForward(name.length() - 1); } continue; } // (isPotentialHeader && !isInTemplate) // determine if this is an Objective-C statement if (currentChar == '@' && isCharPotentialHeader(currentLine, charNum + 1) && findKeyword(currentLine, charNum + 1, AS_INTERFACE) && isBracketType(bracketTypeStack->back(), NULL_TYPE)) { isInObjCInterface = true; string name = '@' + AS_INTERFACE; appendSequence(name); goForward(name.length() - 1); continue; } else if (currentChar == '@' && isCharPotentialHeader(currentLine, charNum + 1) && findKeyword(currentLine, charNum + 1, AS_SELECTOR)) { isInObjCSelector = true; string name = '@' + AS_SELECTOR; appendSequence(name); goForward(name.length() - 1); continue; } else if ((currentChar == '-' || currentChar == '+') && peekNextChar() == '(' && isBracketType(bracketTypeStack->back(), NULL_TYPE) && !isInPotentialCalculation) { isInObjCMethodDefinition = true; isInObjCInterface = false; appendCurrentChar(); if (shouldPadMethodPrefix || shouldUnPadMethodPrefix) { size_t i = currentLine.find_first_not_of(" \t", charNum + 1); if (i != string::npos) goForward(i - charNum - 1); if (shouldPadMethodPrefix) appendSpaceAfter(); } continue; } // determine if this is a potential calculation bool isPotentialOperator = isCharPotentialOperator(currentChar); newHeader = NULL; if (isPotentialOperator) { newHeader = findOperator(operators); // check for Java ? wildcard if (newHeader == &AS_GCC_MIN_ASSIGN && isJavaStyle() && isInTemplate) newHeader = NULL; if (newHeader != NULL) { if (newHeader == &AS_LAMBDA) foundPreCommandHeader = true; // correct mistake of two >> closing a template if (isInTemplate && (newHeader == &AS_GR_GR || newHeader == &AS_GR_GR_GR)) newHeader = &AS_GR; if (!isInPotentialCalculation) { // must determine if newHeader is an assignment operator // do NOT use findOperator!!! if (find(assignmentOperators->begin(), assignmentOperators->end(), newHeader) != assignmentOperators->end()) { foundPreCommandHeader = false; char peekedChar = peekNextChar(); isInPotentialCalculation = !(newHeader == &AS_EQUAL && peekedChar == '*') && !(newHeader == &AS_EQUAL && peekedChar == '&') && !isCharImmediatelyPostOperator; } } } } // process pointers and references // check newHeader to eliminate things like '&&' sequence if (!isJavaStyle() && (newHeader == &AS_MULT || newHeader == &AS_BIT_AND || newHeader == &AS_BIT_XOR || newHeader == &AS_AND) && isPointerOrReference()) { if (!isDereferenceOrAddressOf() && !isOperatorPaddingDisabled()) formatPointerOrReference(); else { appendOperator(*newHeader); goForward(newHeader->length() - 1); } isImmediatelyPostPointerOrReference = true; continue; } if (shouldPadOperators && newHeader != NULL && !isOperatorPaddingDisabled()) { padOperators(newHeader); continue; } // pad commas and semi-colons if (currentChar == ';' || (currentChar == ',' && shouldPadOperators)) { char nextChar = ' '; if (charNum + 1 < (int) currentLine.length()) nextChar = currentLine[charNum + 1]; if (!isWhiteSpace(nextChar) && nextChar != '}' && nextChar != ')' && nextChar != ']' && nextChar != '>' && nextChar != ';' && !isBeforeAnyComment() /* && !(isBracketType(bracketTypeStack->back(), ARRAY_TYPE)) */ ) { appendCurrentChar(); appendSpaceAfter(); continue; } } // do NOT use 'continue' after this, it must do padParens if necessary if (currentChar == '(' && shouldPadHeader && (isCharImmediatelyPostReturn || isCharImmediatelyPostThrow)) appendSpacePad(); if ((currentChar == '(' || currentChar == ')') && (shouldPadParensOutside || shouldPadParensInside || shouldUnPadParens || shouldPadFirstParen)) { padParens(); continue; } // bypass the entire operator if (newHeader != NULL) { appendOperator(*newHeader); goForward(newHeader->length() - 1); continue; } appendCurrentChar(); } // end of while loop * end of while loop * end of while loop * end of while loop // return a beautified (i.e. correctly indented) line. string beautifiedLine; size_t readyFormattedLineLength = trim(readyFormattedLine).length(); bool isInNamespace = isBracketType(bracketTypeStack->back(), NAMESPACE_TYPE); if (prependEmptyLine // prepend a blank line before this formatted line && readyFormattedLineLength > 0 && previousReadyFormattedLineLength > 0) { isLineReady = true; // signal a waiting readyFormattedLine beautifiedLine = beautify(""); previousReadyFormattedLineLength = 0; // call the enhancer for new empty lines enhancer->enhance(beautifiedLine, isInNamespace, isInPreprocessorBeautify, isInBeautifySQL); } else // format the current formatted line { isLineReady = false; horstmannIndentInStatement = horstmannIndentChars; beautifiedLine = beautify(readyFormattedLine); previousReadyFormattedLineLength = readyFormattedLineLength; // the enhancer is not called for no-indent line comments if (!lineCommentNoBeautify && !isFormattingModeOff) enhancer->enhance(beautifiedLine, isInNamespace, isInPreprocessorBeautify, isInBeautifySQL); horstmannIndentChars = 0; lineCommentNoBeautify = lineCommentNoIndent; lineCommentNoIndent = false; isInIndentablePreproc = isIndentableProprocessor; isIndentableProprocessor = false; isElseHeaderIndent = elseHeaderFollowsComments; isCaseHeaderCommentIndent = caseHeaderFollowsComments; if (isCharImmediatelyPostNonInStmt) { isNonInStatementArray = false; isCharImmediatelyPostNonInStmt = false; } isInPreprocessorBeautify = isInPreprocessor; // used by ASEnhancer isInBeautifySQL = isInExecSQL; // used by ASEnhancer } prependEmptyLine = false; assert(computeChecksumOut(beautifiedLine)); return beautifiedLine; } /** * check if there are any indented lines ready to be read by nextLine() * * @return are there any indented lines ready? */ bool ASFormatter::hasMoreLines() const { return !endOfCodeReached; } /** * comparison function for BracketType enum */ bool ASFormatter::isBracketType(BracketType a, BracketType b) const { if (a == NULL_TYPE || b == NULL_TYPE) return (a == b); return ((a & b) == b); } /** * set the formatting style. * * @param style the formatting style. */ void ASFormatter::setFormattingStyle(FormatStyle style) { formattingStyle = style; } /** * set the add brackets mode. * options: * true brackets added to headers for single line statements. * false brackets NOT added to headers for single line statements. * * @param state the add brackets state. */ void ASFormatter::setAddBracketsMode(bool state) { shouldAddBrackets = state; } /** * set the add one line brackets mode. * options: * true one line brackets added to headers for single line statements. * false one line brackets NOT added to headers for single line statements. * * @param state the add one line brackets state. */ void ASFormatter::setAddOneLineBracketsMode(bool state) { shouldAddBrackets = state; shouldAddOneLineBrackets = state; } /** * set the remove brackets mode. * options: * true brackets removed from headers for single line statements. * false brackets NOT removed from headers for single line statements. * * @param state the remove brackets state. */ void ASFormatter::setRemoveBracketsMode(bool state) { shouldRemoveBrackets = state; } /** * set the bracket formatting mode. * options: * * @param mode the bracket formatting mode. */ void ASFormatter::setBracketFormatMode(BracketMode mode) { bracketFormatMode = mode; } /** * set 'break after' mode for maximum code length * * @param state the 'break after' mode. */ void ASFormatter::setBreakAfterMode(bool state) { shouldBreakLineAfterLogical = state; } /** * set closing header bracket breaking mode * options: * true brackets just before closing headers (e.g. 'else', 'catch') * will be broken, even if standard brackets are attached. * false closing header brackets will be treated as standard brackets. * * @param state the closing header bracket breaking mode. */ void ASFormatter::setBreakClosingHeaderBracketsMode(bool state) { shouldBreakClosingHeaderBrackets = state; } /** * set 'else if()' breaking mode * options: * true 'else' headers will be broken from their succeeding 'if' headers. * false 'else' headers will be attached to their succeeding 'if' headers. * * @param state the 'else if()' breaking mode. */ void ASFormatter::setBreakElseIfsMode(bool state) { shouldBreakElseIfs = state; } /** * set maximum code length * * @param max the maximum code length. */ void ASFormatter::setMaxCodeLength(int max) { maxCodeLength = max; } /** * set operator padding mode. * options: * true statement operators will be padded with spaces around them. * false statement operators will not be padded. * * @param state the padding mode. */ void ASFormatter::setOperatorPaddingMode(bool state) { shouldPadOperators = state; } /** * set parenthesis outside padding mode. * options: * true statement parentheses will be padded with spaces around them. * false statement parentheses will not be padded. * * @param state the padding mode. */ void ASFormatter::setParensOutsidePaddingMode(bool state) { shouldPadParensOutside = state; } /** * set parenthesis inside padding mode. * options: * true statement parenthesis will be padded with spaces around them. * false statement parenthesis will not be padded. * * @param state the padding mode. */ void ASFormatter::setParensInsidePaddingMode(bool state) { shouldPadParensInside = state; } /** * set padding mode before one or more open parentheses. * options: * true first open parenthesis will be padded with a space before. * false first open parenthesis will not be padded. * * @param state the padding mode. */ void ASFormatter::setParensFirstPaddingMode(bool state) { shouldPadFirstParen = state; } /** * set header padding mode. * options: * true headers will be padded with spaces around them. * false headers will not be padded. * * @param state the padding mode. */ void ASFormatter::setParensHeaderPaddingMode(bool state) { shouldPadHeader = state; } /** * set parenthesis unpadding mode. * options: * true statement parenthesis will be unpadded with spaces removed around them. * false statement parenthesis will not be unpadded. * * @param state the padding mode. */ void ASFormatter::setParensUnPaddingMode(bool state) { shouldUnPadParens = state; } /** * set the state of the preprocessor indentation option. * If true, #ifdef blocks at level 0 will be indented. * * @param state state of option. */ void ASFormatter::setPreprocBlockIndent(bool state) { shouldIndentPreprocBlock = state; } /** * Set strip comment prefix mode. * options: * true strip leading '*' in a comment. * false leading '*' in a comment will be left unchanged. * * @param state the strip comment prefix mode. */ void ASFormatter::setStripCommentPrefix(bool state) { shouldStripCommentPrefix = state; } /** * set objective-c '-' or '+' class prefix padding mode. * options: * true class prefix will be padded a spaces after them. * false class prefix will be left unchanged. * * @param state the padding mode. */ void ASFormatter::setMethodPrefixPaddingMode(bool state) { shouldPadMethodPrefix = state; } /** * set objective-c '-' or '+' class prefix unpadding mode. * options: * true class prefix will be unpadded with spaces after them removed. * false class prefix will left unchanged. * * @param state the unpadding mode. */ void ASFormatter::setMethodPrefixUnPaddingMode(bool state) { shouldUnPadMethodPrefix = state; } /** * set objective-c method colon padding mode. * * @param mode objective-c colon padding mode. */ void ASFormatter::setObjCColonPaddingMode(ObjCColonPad mode) { shouldPadMethodColon = true; objCColonPadMode = mode; } /** * set option to attach closing brackets * * @param state true = attach, false = don't attach. */ void ASFormatter::setAttachClosingBracketMode(bool state) { attachClosingBracketMode = state; } /** * set option to attach class brackets * * @param state true = attach, false = use style default. */ void ASFormatter::setAttachClass(bool state) { shouldAttachClass = state; } /** * set option to attach extern "C" brackets * * @param state true = attach, false = use style default. */ void ASFormatter::setAttachExternC(bool state) { shouldAttachExternC = state; } /** * set option to attach namespace brackets * * @param state true = attach, false = use style default. */ void ASFormatter::setAttachNamespace(bool state) { shouldAttachNamespace = state; } /** * set option to attach inline brackets * * @param state true = attach, false = use style default. */ void ASFormatter::setAttachInline(bool state) { shouldAttachInline = state; } /** * set option to break/not break one-line blocks * * @param state true = break, false = don't break. */ void ASFormatter::setBreakOneLineBlocksMode(bool state) { shouldBreakOneLineBlocks = state; } void ASFormatter::setCloseTemplatesMode(bool state) { shouldCloseTemplates = state; } /** * set option to break/not break lines consisting of multiple statements. * * @param state true = break, false = don't break. */ void ASFormatter::setSingleStatementsMode(bool state) { shouldBreakOneLineStatements = state; } /** * set option to convert tabs to spaces. * * @param state true = convert, false = don't convert. */ void ASFormatter::setTabSpaceConversionMode(bool state) { shouldConvertTabs = state; } /** * set option to indent comments in column 1. * * @param state true = indent, false = don't indent. */ void ASFormatter::setIndentCol1CommentsMode(bool state) { shouldIndentCol1Comments = state; } /** * set option to force all line ends to a particular style. * * @param fmt format enum value */ void ASFormatter::setLineEndFormat(LineEndFormat fmt) { lineEnd = fmt; } /** * set option to break unrelated blocks of code with empty lines. * * @param state true = convert, false = don't convert. */ void ASFormatter::setBreakBlocksMode(bool state) { shouldBreakBlocks = state; } /** * set option to break closing header blocks of code (such as 'else', 'catch', ...) with empty lines. * * @param state true = convert, false = don't convert. */ void ASFormatter::setBreakClosingHeaderBlocksMode(bool state) { shouldBreakClosingHeaderBlocks = state; } /** * set option to delete empty lines. * * @param state true = delete, false = don't delete. */ void ASFormatter::setDeleteEmptyLinesMode(bool state) { shouldDeleteEmptyLines = state; } /** * set the pointer alignment. * * @param alignment the pointer alignment. */ void ASFormatter::setPointerAlignment(PointerAlign alignment) { pointerAlignment = alignment; } void ASFormatter::setReferenceAlignment(ReferenceAlign alignment) { referenceAlignment = alignment; } /** * jump over several characters. * * @param i the number of characters to jump over. */ void ASFormatter::goForward(int i) { while (--i >= 0) getNextChar(); } /** * peek at the next unread character. * * @return the next unread character. */ char ASFormatter::peekNextChar() const { char ch = ' '; size_t peekNum = currentLine.find_first_not_of(" \t", charNum + 1); if (peekNum == string::npos) return ch; ch = currentLine[peekNum]; return ch; } /** * check if current placement is before a comment * * @return is before a comment. */ bool ASFormatter::isBeforeComment() const { bool foundComment = false; size_t peekNum = currentLine.find_first_not_of(" \t", charNum + 1); if (peekNum == string::npos) return foundComment; foundComment = (currentLine.compare(peekNum, 2, "/*") == 0); return foundComment; } /** * check if current placement is before a comment or line-comment * * @return is before a comment or line-comment. */ bool ASFormatter::isBeforeAnyComment() const { bool foundComment = false; size_t peekNum = currentLine.find_first_not_of(" \t", charNum + 1); if (peekNum == string::npos) return foundComment; foundComment = (currentLine.compare(peekNum, 2, "/*") == 0 || currentLine.compare(peekNum, 2, "//") == 0); return foundComment; } /** * check if current placement is before a comment or line-comment * if a block comment it must be at the end of the line * * @return is before a comment or line-comment. */ bool ASFormatter::isBeforeAnyLineEndComment(int startPos) const { bool foundLineEndComment = false; size_t peekNum = currentLine.find_first_not_of(" \t", startPos + 1); if (peekNum != string::npos) { if (currentLine.compare(peekNum, 2, "//") == 0) foundLineEndComment = true; else if (currentLine.compare(peekNum, 2, "/*") == 0) { // comment must be closed on this line with nothing after it size_t endNum = currentLine.find("*/", peekNum + 2); if (endNum != string::npos) { size_t nextChar = currentLine.find_first_not_of(" \t", endNum + 2); if (nextChar == string::npos) foundLineEndComment = true; } } } return foundLineEndComment; } /** * check if current placement is before a comment followed by a line-comment * * @return is before a multiple line-end comment. */ bool ASFormatter::isBeforeMultipleLineEndComments(int startPos) const { bool foundMultipleLineEndComment = false; size_t peekNum = currentLine.find_first_not_of(" \t", startPos + 1); if (peekNum != string::npos) { if (currentLine.compare(peekNum, 2, "/*") == 0) { // comment must be closed on this line with nothing after it size_t endNum = currentLine.find("*/", peekNum + 2); if (endNum != string::npos) { size_t nextChar = currentLine.find_first_not_of(" \t", endNum + 2); if (nextChar != string::npos && currentLine.compare(nextChar, 2, "//") == 0) foundMultipleLineEndComment = true; } } } return foundMultipleLineEndComment; } /** * get the next character, increasing the current placement in the process. * the new character is inserted into the variable currentChar. * * @return whether succeeded to receive the new character. */ bool ASFormatter::getNextChar() { isInLineBreak = false; previousChar = currentChar; if (!isWhiteSpace(currentChar)) { previousNonWSChar = currentChar; if (!isInComment && !isInLineComment && !isInQuote && !isImmediatelyPostComment && !isImmediatelyPostLineComment && !isInPreprocessor && !isSequenceReached("/*") && !isSequenceReached("//")) previousCommandChar = currentChar; } if (charNum + 1 < (int) currentLine.length() && (!isWhiteSpace(peekNextChar()) || isInComment || isInLineComment)) { currentChar = currentLine[++charNum]; if (currentChar == '\t' && shouldConvertTabs) convertTabToSpaces(); return true; } // end of line has been reached return getNextLine(); } /** * get the next line of input, increasing the current placement in the process. * * @param emptyLineWasDeleted an empty line was deleted. * @return whether succeeded in reading the next line. */ bool ASFormatter::getNextLine(bool emptyLineWasDeleted /*false*/) { if (sourceIterator->hasMoreLines()) { if (appendOpeningBracket) currentLine = "{"; // append bracket that was removed from the previous line else { currentLine = sourceIterator->nextLine(emptyLineWasDeleted); assert(computeChecksumIn(currentLine)); } // reset variables for new line inLineNumber++; if (endOfAsmReached) endOfAsmReached = isInAsmBlock = isInAsm = false; shouldKeepLineUnbroken = false; isInCommentStartLine = false; isInCase = false; isInAsmOneLine = false; isHeaderInMultiStatementLine = false; isInQuoteContinuation = isInVerbatimQuote | haveLineContinuationChar; haveLineContinuationChar = false; isImmediatelyPostEmptyLine = lineIsEmpty; previousChar = ' '; if (currentLine.length() == 0) currentLine = string(" "); // a null is inserted if this is not done // unless reading in the first line of the file, break a new line. if (!isVirgin) isInLineBreak = true; else isVirgin = false; if (isImmediatelyPostNonInStmt) { isCharImmediatelyPostNonInStmt = true; isImmediatelyPostNonInStmt = false; } // check if is in preprocessor before line trimming // a blank line after a \ will remove the flag isImmediatelyPostPreprocessor = isInPreprocessor; if (!isInComment && (previousNonWSChar != '\\' || isEmptyLine(currentLine))) isInPreprocessor = false; if (passedSemicolon) isInExecSQL = false; initNewLine(); currentChar = currentLine[charNum]; if (isInHorstmannRunIn && previousNonWSChar == '{' && !isInComment) isInLineBreak = false; isInHorstmannRunIn = false; if (currentChar == '\t' && shouldConvertTabs) convertTabToSpaces(); // check for an empty line inside a command bracket. // if yes then read the next line (calls getNextLine recursively). // must be after initNewLine. if (shouldDeleteEmptyLines && lineIsEmpty && isBracketType((*bracketTypeStack)[bracketTypeStack->size() - 1], COMMAND_TYPE)) { if (!shouldBreakBlocks || previousNonWSChar == '{' || !commentAndHeaderFollows()) { isInPreprocessor = isImmediatelyPostPreprocessor; // restore lineIsEmpty = false; return getNextLine(true); } } return true; } else { endOfCodeReached = true; return false; } } /** * jump over the leading white space in the current line, * IF the line does not begin a comment or is in a preprocessor definition. */ void ASFormatter::initNewLine() { size_t len = currentLine.length(); size_t tabSize = getTabLength(); charNum = 0; // don't trim these if (isInQuoteContinuation || (isInPreprocessor && !getPreprocDefineIndent())) return; // SQL continuation lines must be adjusted so the leading spaces // is equivalent to the opening EXEC SQL if (isInExecSQL) { // replace leading tabs with spaces // so that continuation indent will be spaces size_t tabCount_ = 0; size_t i; for (i = 0; i < currentLine.length(); i++) { if (!isWhiteSpace(currentLine[i])) // stop at first text break; if (currentLine[i] == '\t') { size_t numSpaces = tabSize - ((tabCount_ + i) % tabSize); currentLine.replace(i, 1, numSpaces, ' '); tabCount_++; i += tabSize - 1; } } // this will correct the format if EXEC SQL is not a hanging indent trimContinuationLine(); return; } // comment continuation lines must be adjusted so the leading spaces // is equivalent to the opening comment if (isInComment) { if (noTrimCommentContinuation) leadingSpaces = tabIncrementIn = 0; trimContinuationLine(); return; } // compute leading spaces isImmediatelyPostCommentOnly = lineIsLineCommentOnly || lineEndsInCommentOnly; lineIsCommentOnly = false; lineIsLineCommentOnly = false; lineEndsInCommentOnly = false; doesLineStartComment = false; currentLineBeginsWithBracket = false; lineIsEmpty = false; currentLineFirstBracketNum = string::npos; tabIncrementIn = 0; // bypass whitespace at the start of a line // preprocessor tabs are replaced later in the program for (charNum = 0; isWhiteSpace(currentLine[charNum]) && charNum + 1 < (int) len; charNum++) { if (currentLine[charNum] == '\t' && !isInPreprocessor) tabIncrementIn += tabSize - 1 - ((tabIncrementIn + charNum) % tabSize); } leadingSpaces = charNum + tabIncrementIn; if (isSequenceReached("/*")) { doesLineStartComment = true; if ((int) currentLine.length() > charNum + 2 && currentLine.find("*/", charNum + 2) != string::npos) lineIsCommentOnly = true; } else if (isSequenceReached("//")) { lineIsLineCommentOnly = true; } else if (isSequenceReached("{")) { currentLineBeginsWithBracket = true; currentLineFirstBracketNum = charNum; size_t firstText = currentLine.find_first_not_of(" \t", charNum + 1); if (firstText != string::npos) { if (currentLine.compare(firstText, 2, "//") == 0) lineIsLineCommentOnly = true; else if (currentLine.compare(firstText, 2, "/*") == 0 || isExecSQL(currentLine, firstText)) { // get the extra adjustment size_t j; for (j = charNum + 1; j < firstText && isWhiteSpace(currentLine[j]); j++) { if (currentLine[j] == '\t') tabIncrementIn += tabSize - 1 - ((tabIncrementIn + j) % tabSize); } leadingSpaces = j + tabIncrementIn; if (currentLine.compare(firstText, 2, "/*") == 0) doesLineStartComment = true; } } } else if (isWhiteSpace(currentLine[charNum]) && !(charNum + 1 < (int) currentLine.length())) { lineIsEmpty = true; } // do not trim indented preprocessor define (except for comment continuation lines) if (isInPreprocessor) { if (!doesLineStartComment) leadingSpaces = 0; charNum = 0; } } /** * Append a character to the current formatted line. * The formattedLine split points are updated. * * @param ch the character to append. * @param canBreakLine if true, a registered line-break */ void ASFormatter::appendChar(char ch, bool canBreakLine) { if (canBreakLine && isInLineBreak) breakLine(); formattedLine.append(1, ch); isImmediatelyPostCommentOnly = false; if (maxCodeLength != string::npos) { // These compares reduce the frequency of function calls. if (isOkToSplitFormattedLine()) updateFormattedLineSplitPoints(ch); if (formattedLine.length() > maxCodeLength) testForTimeToSplitFormattedLine(); } } /** * Append a string sequence to the current formatted line. * The formattedLine split points are NOT updated. * But the formattedLine is checked for time to split. * * @param sequence the sequence to append. * @param canBreakLine if true, a registered line-break */ void ASFormatter::appendSequence(const string &sequence, bool canBreakLine) { if (canBreakLine && isInLineBreak) breakLine(); formattedLine.append(sequence); if (formattedLine.length() > maxCodeLength) testForTimeToSplitFormattedLine(); } /** * Append an operator sequence to the current formatted line. * The formattedLine split points are updated. * * @param sequence the sequence to append. * @param canBreakLine if true, a registered line-break */ void ASFormatter::appendOperator(const string &sequence, bool canBreakLine) { if (canBreakLine && isInLineBreak) breakLine(); formattedLine.append(sequence); if (maxCodeLength != string::npos) { // These compares reduce the frequency of function calls. if (isOkToSplitFormattedLine()) updateFormattedLineSplitPointsOperator(sequence); if (formattedLine.length() > maxCodeLength) testForTimeToSplitFormattedLine(); } } /** * append a space to the current formattedline, UNLESS the * last character is already a white-space character. */ void ASFormatter::appendSpacePad() { int len = formattedLine.length(); if (len > 0 && !isWhiteSpace(formattedLine[len - 1])) { formattedLine.append(1, ' '); spacePadNum++; if (maxCodeLength != string::npos) { // These compares reduce the frequency of function calls. if (isOkToSplitFormattedLine()) updateFormattedLineSplitPoints(' '); if (formattedLine.length() > maxCodeLength) testForTimeToSplitFormattedLine(); } } } /** * append a space to the current formattedline, UNLESS the * next character is already a white-space character. */ void ASFormatter::appendSpaceAfter() { int len = currentLine.length(); if (charNum + 1 < len && !isWhiteSpace(currentLine[charNum + 1])) { formattedLine.append(1, ' '); spacePadNum++; if (maxCodeLength != string::npos) { // These compares reduce the frequency of function calls. if (isOkToSplitFormattedLine()) updateFormattedLineSplitPoints(' '); if (formattedLine.length() > maxCodeLength) testForTimeToSplitFormattedLine(); } } } /** * register a line break for the formatted line. */ void ASFormatter::breakLine(bool isSplitLine /*false*/) { isLineReady = true; isInLineBreak = false; spacePadNum = nextLineSpacePadNum; nextLineSpacePadNum = 0; readyFormattedLine = formattedLine; formattedLine.erase(); // queue an empty line prepend request if one exists prependEmptyLine = isPrependPostBlockEmptyLineRequested; if (!isSplitLine) { formattedLineCommentNum = string::npos; clearFormattedLineSplitPoints(); if (isAppendPostBlockEmptyLineRequested) { isAppendPostBlockEmptyLineRequested = false; isPrependPostBlockEmptyLineRequested = true; } else isPrependPostBlockEmptyLineRequested = false; } } /** * check if the currently reached open-bracket (i.e. '{') * opens a: * - a definition type block (such as a class or namespace), * - a command block (such as a method block) * - a static array * this method takes for granted that the current character * is an opening bracket. * * @return the type of the opened block. */ BracketType ASFormatter::getBracketType() { assert(currentChar == '{'); BracketType returnVal; if ((previousNonWSChar == '=' || isBracketType(bracketTypeStack->back(), ARRAY_TYPE)) && previousCommandChar != ')') returnVal = ARRAY_TYPE; else if (foundPreDefinitionHeader && previousCommandChar != ')') { returnVal = DEFINITION_TYPE; if (foundNamespaceHeader) returnVal = (BracketType)(returnVal | NAMESPACE_TYPE); else if (foundClassHeader) returnVal = (BracketType)(returnVal | CLASS_TYPE); else if (foundStructHeader) returnVal = (BracketType)(returnVal | STRUCT_TYPE); else if (foundInterfaceHeader) returnVal = (BracketType)(returnVal | INTERFACE_TYPE); } else if (isInEnum) { returnVal = (BracketType)(ARRAY_TYPE | ENUM_TYPE); } else { bool isCommandType = (foundPreCommandHeader || foundPreCommandMacro || (currentHeader != NULL && isNonParenHeader) || (previousCommandChar == ')') || (previousCommandChar == ':' && !foundQuestionMark) || (previousCommandChar == ';') || ((previousCommandChar == '{' || previousCommandChar == '}') && isPreviousBracketBlockRelated) || (isInClassInitializer && (!isLegalNameChar(previousNonWSChar) || foundPreCommandHeader)) || isInObjCMethodDefinition || isInObjCInterface || isJavaStaticConstructor || isSharpDelegate); // C# methods containing 'get', 'set', 'add', and 'remove' do NOT end with parens if (!isCommandType && isSharpStyle() && isNextWordSharpNonParenHeader(charNum + 1)) { isCommandType = true; isSharpAccessor = true; } if (isInExternC) returnVal = (isCommandType ? COMMAND_TYPE : EXTERN_TYPE); else returnVal = (isCommandType ? COMMAND_TYPE : ARRAY_TYPE); } int foundOneLineBlock = isOneLineBlockReached(currentLine, charNum); // this assumes each array definition is on a single line // (foundOneLineBlock == 2) is a one line block followed by a comma if (foundOneLineBlock == 2 && returnVal == COMMAND_TYPE) returnVal = ARRAY_TYPE; if (foundOneLineBlock > 0) // found one line block returnVal = (BracketType)(returnVal | SINGLE_LINE_TYPE); if (isBracketType(returnVal, ARRAY_TYPE)) { if (isNonInStatementArrayBracket()) { returnVal = (BracketType)(returnVal | ARRAY_NIS_TYPE); isNonInStatementArray = true; isImmediatelyPostNonInStmt = false; // in case of "},{" nonInStatementBracket = formattedLine.length() - 1; } if (isUniformInitializerBracket()) returnVal = (BracketType)(returnVal | INIT_TYPE); } return returnVal; } /** * check if a colon is a class initializer separator * * @return whether it is a class initializer separator */ bool ASFormatter::isClassInitializer() const { assert(currentLine[charNum] == ':'); assert(previousChar != ':' && peekNextChar() != ':'); // not part of '::' // this should be similar to ASBeautifier::parseCurrentLine() bool foundClassInitializer = false; if (foundQuestionMark) { // do nothing special } else if (parenStack->back() > 0) { // found a 'for' loop or an objective-C statement // so do nothing special } else if (isInEnum) { // found an enum with a base-type } else if (isCStyle() && !isInCase && (previousCommandChar == ')' || foundPreCommandHeader)) { // found a 'class' c'tor initializer foundClassInitializer = true; } return foundClassInitializer; } /** * check if a line is empty * * @return whether line is empty */ bool ASFormatter::isEmptyLine(const string &line) const { return line.find_first_not_of(" \t") == string::npos; } /** * Check if the following text is "C" as in extern "C". * * @return whether the statement is extern "C" */ bool ASFormatter::isExternC() const { // charNum should be at 'extern' assert(!isWhiteSpace(currentLine[charNum])); size_t startQuote = currentLine.find_first_of(" \t\"", charNum); if (startQuote == string::npos) return false; startQuote = currentLine.find_first_not_of(" \t", startQuote); if (startQuote == string::npos) return false; if (currentLine.compare(startQuote, 3, "\"C\"") != 0) return false; return true; } /** * Check if the currently reached '*', '&' or '^' character is * a pointer-or-reference symbol, or another operator. * A pointer dereference (*) or an "address of" character (&) * counts as a pointer or reference because it is not an * arithmetic operator. * * @return whether current character is a reference-or-pointer */ bool ASFormatter::isPointerOrReference() const { assert(currentChar == '*' || currentChar == '&' || currentChar == '^'); if (isJavaStyle()) return false; if (isCharImmediatelyPostOperator) return false; // get the last legal word (may be a number) string lastWord = getPreviousWord(currentLine, charNum); if (lastWord.empty()) lastWord = " "; // check for preceding or following numeric values string nextText = peekNextText(currentLine.substr(charNum + 1)); if (nextText.length() == 0) nextText = " "; char nextChar = nextText[0]; if (isDigit(lastWord[0]) || isDigit(nextChar) || nextChar == '!' || nextChar == '~') return false; // check for multiply then a dereference (a * *b) if (currentChar == '*' && charNum < (int) currentLine.length() - 1 && isWhiteSpace(currentLine[charNum + 1]) && nextChar == '*') return false; if ((foundCastOperator && nextChar == '>') || isPointerOrReferenceVariable(lastWord)) return true; if (isInClassInitializer && previousNonWSChar != '(' && previousNonWSChar != '{' && previousCommandChar != ',' && nextChar != ')' && nextChar != '}') return false; //check for rvalue reference if (currentChar == '&' && nextChar == '&') { string followingText = peekNextText(currentLine.substr(charNum + 2)); if (followingText.length() > 0 && followingText[0] == ')') return true; if (currentHeader != NULL || isInPotentialCalculation) return false; if (parenStack->back() > 0 && isBracketType(bracketTypeStack->back(), COMMAND_TYPE)) return false; return true; } if (nextChar == '*' || previousNonWSChar == '=' || previousNonWSChar == '(' || previousNonWSChar == '[' || isCharImmediatelyPostReturn || isInTemplate || isCharImmediatelyPostTemplate || currentHeader == &AS_CATCH || currentHeader == &AS_FOREACH || currentHeader == &AS_QFOREACH) return true; if (isBracketType(bracketTypeStack->back(), ARRAY_TYPE) && isLegalNameChar(lastWord[0]) && isLegalNameChar(nextChar) && previousNonWSChar != ')') { if (isArrayOperator()) return false; } // checks on operators in parens if (parenStack->back() > 0 && isLegalNameChar(lastWord[0]) && isLegalNameChar(nextChar)) { // if followed by an assignment it is a pointer or reference // if followed by semicolon it is a pointer or reference in range-based for const string* followingOperator = getFollowingOperator(); if (followingOperator && followingOperator != &AS_MULT && followingOperator != &AS_BIT_AND) { if (followingOperator == &AS_ASSIGN || followingOperator == &AS_COLON) return true; else return false; } if (isBracketType(bracketTypeStack->back(), COMMAND_TYPE) || squareBracketCount > 0) return false; else return true; } // checks on operators in parens with following '(' if (parenStack->back() > 0 && nextChar == '(' && previousNonWSChar != ',' && previousNonWSChar != '(' && previousNonWSChar != '!' && previousNonWSChar != '&' && previousNonWSChar != '*' && previousNonWSChar != '|') return false; if (nextChar == '-' || nextChar == '+') { size_t nextNum = currentLine.find_first_not_of(" \t", charNum + 1); if (nextNum != string::npos) { if (currentLine.compare(nextNum, 2, "++") != 0 && currentLine.compare(nextNum, 2, "--") != 0) return false; } } bool isPR = (!isInPotentialCalculation || (!isLegalNameChar(previousNonWSChar) && !(previousNonWSChar == ')' && nextChar == '(') && !(previousNonWSChar == ')' && currentChar == '*' && !isImmediatelyPostCast()) && previousNonWSChar != ']') || (!isWhiteSpace(nextChar) && nextChar != '-' && nextChar != '(' && nextChar != '[' && !isLegalNameChar(nextChar)) ); return isPR; } /** * Check if the currently reached '*' or '&' character is * a dereferenced pointer or "address of" symbol. * NOTE: this MUST be a pointer or reference as determined by * the function isPointerOrReference(). * * @return whether current character is a dereference or address of */ bool ASFormatter::isDereferenceOrAddressOf() const { assert(currentChar == '*' || currentChar == '&' || currentChar == '^'); if (isCharImmediatelyPostTemplate) return false; if (previousNonWSChar == '=' || previousNonWSChar == ',' || previousNonWSChar == '.' || previousNonWSChar == '{' || previousNonWSChar == '>' || previousNonWSChar == '<' || previousNonWSChar == '?' || isCharImmediatelyPostLineComment || isCharImmediatelyPostComment || isCharImmediatelyPostReturn) return true; char nextChar = peekNextChar(); if (currentChar == '*' && nextChar == '*') { if (previousNonWSChar == '(') return true; if ((int) currentLine.length() < charNum + 2) return true; return false; } if (currentChar == '&' && nextChar == '&') { if (previousNonWSChar == '(' || isInTemplate) return true; if ((int) currentLine.length() < charNum + 2) return true; return false; } // check first char on the line if (charNum == (int) currentLine.find_first_not_of(" \t") && (isBracketType(bracketTypeStack->back(), COMMAND_TYPE) || parenStack->back() != 0)) return true; string nextText = peekNextText(currentLine.substr(charNum + 1)); if (nextText.length() > 0) { if (nextText[0] == ')' || nextText[0] == '>' || nextText[0] == ',' || nextText[0] == '=') return false; if (nextText[0] == ';') return true; } // check for reference to a pointer *& (cannot have &*) if ((currentChar == '*' && nextChar == '&') || (previousNonWSChar == '*' && currentChar == '&')) return false; if (!isBracketType(bracketTypeStack->back(), COMMAND_TYPE) && parenStack->back() == 0) return false; string lastWord = getPreviousWord(currentLine, charNum); if (lastWord == "else" || lastWord == "delete") return true; if (isPointerOrReferenceVariable(lastWord)) return false; bool isDA = (!(isLegalNameChar(previousNonWSChar) || previousNonWSChar == '>') || (nextText.length() > 0 && !isLegalNameChar(nextText[0]) && nextText[0] != '/') || (ispunct((unsigned char)previousNonWSChar) && previousNonWSChar != '.') || isCharImmediatelyPostReturn); return isDA; } /** * Check if the currently reached '*' or '&' character is * centered with one space on each side. * Only spaces are checked, not tabs. * If true then a space will be deleted on the output. * * @return whether current character is centered. */ bool ASFormatter::isPointerOrReferenceCentered() const { assert(currentLine[charNum] == '*' || currentLine[charNum] == '&' || currentLine[charNum] == '^'); int prNum = charNum; int lineLength = (int) currentLine.length(); // check for end of line if (peekNextChar() == ' ') return false; // check space before if (prNum < 1 || currentLine[prNum - 1] != ' ') return false; // check no space before that if (prNum < 2 || currentLine[prNum - 2] == ' ') return false; // check for ** or && if (prNum + 1 < lineLength && (currentLine[prNum + 1] == '*' || currentLine[prNum + 1] == '&')) prNum++; // check space after if (prNum + 1 <= lineLength && currentLine[prNum + 1] != ' ') return false; // check no space after that if (prNum + 2 < lineLength && currentLine[prNum + 2] == ' ') return false; return true; } /** * Check if a word is a pointer or reference variable type. * * @return whether word is a pointer or reference variable. */ bool ASFormatter::isPointerOrReferenceVariable(string &word) const { if (word == "char" || word == "int" || word == "void" || (word.length() >= 6 // check end of word for _t && word.compare(word.length() - 2, 2, "_t") == 0) || word == "INT" || word == "VOID") return true; return false; } /** * check if the currently reached '+' or '-' character is a unary operator * this method takes for granted that the current character * is a '+' or '-'. * * @return whether the current '+' or '-' is a unary operator. */ bool ASFormatter::isUnaryOperator() const { assert(currentChar == '+' || currentChar == '-'); return ((isCharImmediatelyPostReturn || !isLegalNameChar(previousCommandChar)) && previousCommandChar != '.' && previousCommandChar != '\"' && previousCommandChar != '\'' && previousCommandChar != ')' && previousCommandChar != ']'); } /** * check if the currently reached comment is in a 'switch' statement * * @return whether the current '+' or '-' is in an exponent. */ bool ASFormatter::isInSwitchStatement() const { assert(isInLineComment || isInComment); if (preBracketHeaderStack->size() > 0) for (size_t i = 1; i < preBracketHeaderStack->size(); i++) if (preBracketHeaderStack->at(i) == &AS_SWITCH) return true; return false; } /** * check if the currently reached '+' or '-' character is * part of an exponent, i.e. 0.2E-5. * * @return whether the current '+' or '-' is in an exponent. */ bool ASFormatter::isInExponent() const { assert(currentChar == '+' || currentChar == '-'); int formattedLineLength = formattedLine.length(); if (formattedLineLength >= 2) { char prevPrevFormattedChar = formattedLine[formattedLineLength - 2]; char prevFormattedChar = formattedLine[formattedLineLength - 1]; return ((prevFormattedChar == 'e' || prevFormattedChar == 'E') && (prevPrevFormattedChar == '.' || isDigit(prevPrevFormattedChar))); } else return false; } /** * check if an array bracket should NOT have an in-statement indent * * @return the array is non in-statement */ bool ASFormatter::isNonInStatementArrayBracket() const { bool returnVal = false; char nextChar = peekNextChar(); // if this opening bracket begins the line there will be no inStatement indent if (currentLineBeginsWithBracket && charNum == (int) currentLineFirstBracketNum && nextChar != '}') returnVal = true; // if an opening bracket ends the line there will be no inStatement indent if (isWhiteSpace(nextChar) || isBeforeAnyLineEndComment(charNum) || nextChar == '{') returnVal = true; // Java "new Type [] {...}" IS an inStatement indent if (isJavaStyle() && previousNonWSChar == ']') returnVal = false; return returnVal; } /** * check if a one-line bracket has been reached, * i.e. if the currently reached '{' character is closed * with a complimentary '}' elsewhere on the current line, *. * @return 0 = one-line bracket has not been reached. * 1 = one-line bracket has been reached. * 2 = one-line bracket has been reached and is followed by a comma. */ int ASFormatter::isOneLineBlockReached(string &line, int startChar) const { assert(line[startChar] == '{'); bool isInComment_ = false; bool isInQuote_ = false; int bracketCount = 1; int lineLength = line.length(); char quoteChar_ = ' '; char ch = ' '; char prevCh = ' '; for (int i = startChar + 1; i < lineLength; ++i) { ch = line[i]; if (isInComment_) { if (line.compare(i, 2, "*/") == 0) { isInComment_ = false; ++i; } continue; } if (ch == '\\') { ++i; continue; } if (isInQuote_) { if (ch == quoteChar_) isInQuote_ = false; continue; } if (ch == '"' || ch == '\'') { isInQuote_ = true; quoteChar_ = ch; continue; } if (line.compare(i, 2, "//") == 0) break; if (line.compare(i, 2, "/*") == 0) { isInComment_ = true; ++i; continue; } if (ch == '{') ++bracketCount; else if (ch == '}') --bracketCount; if (bracketCount == 0) { // is this an array? if (parenStack->back() == 0 && prevCh != '}') { size_t peekNum = line.find_first_not_of(" \t", i + 1); if (peekNum != string::npos && line[peekNum] == ',') return 2; } return 1; } if (!isWhiteSpace(ch)) prevCh = ch; } return 0; } /** * peek at the next word to determine if it is a C# non-paren header. * will look ahead in the input file if necessary. * * @param startChar position on currentLine to start the search * @return true if the next word is get or set. */ bool ASFormatter::isNextWordSharpNonParenHeader(int startChar) const { // look ahead to find the next non-comment text string nextText = peekNextText(currentLine.substr(startChar)); if (nextText.length() == 0) return false; if (nextText[0] == '[') return true; if (!isCharPotentialHeader(nextText, 0)) return false; if (findKeyword(nextText, 0, AS_GET) || findKeyword(nextText, 0, AS_SET) || findKeyword(nextText, 0, AS_ADD) || findKeyword(nextText, 0, AS_REMOVE)) return true; return false; } /** * peek at the next char to determine if it is an opening bracket. * will look ahead in the input file if necessary. * this determines a java static constructor. * * @param startChar position on currentLine to start the search * @return true if the next word is an opening bracket. */ bool ASFormatter::isNextCharOpeningBracket(int startChar) const { bool retVal = false; string nextText = peekNextText(currentLine.substr(startChar)); if (nextText.length() > 0 && nextText.compare(0, 1, "{") == 0) retVal = true; return retVal; } /** * Check if operator and, pointer, and reference padding is disabled. * Disabling is done thru a NOPAD tag in an ending comment. * * @return true if the formatting on this line is disabled. */ bool ASFormatter::isOperatorPaddingDisabled() const { size_t commentStart = currentLine.find("//", charNum); if (commentStart == string::npos) { commentStart = currentLine.find("/*", charNum); // comment must end on this line if (commentStart != string::npos) { size_t commentEnd = currentLine.find("*/", commentStart + 2); if (commentEnd == string::npos) commentStart = string::npos; } } if (commentStart == string::npos) return false; size_t noPadStart = currentLine.find("*NOPAD*", commentStart); if (noPadStart == string::npos) return false; return true; } /** * Determine if an opening array-type bracket should have a leading space pad. * This is to identify C++11 uniform initializers. */ bool ASFormatter::isUniformInitializerBracket() const { if (isCStyle() && !isInEnum && !isImmediatelyPostPreprocessor) { if (isInClassInitializer || isLegalNameChar(previousNonWSChar)) return true; } return false; } /** * get the next non-whitespace substring on following lines, bypassing all comments. * * @param firstLine the first line to check * @return the next non-whitespace substring. */ string ASFormatter::peekNextText(const string &firstLine, bool endOnEmptyLine /*false*/, bool shouldReset /*false*/) const { bool isFirstLine = true; bool needReset = shouldReset; string nextLine_ = firstLine; size_t firstChar = string::npos; // find the first non-blank text, bypassing all comments. bool isInComment_ = false; while (sourceIterator->hasMoreLines() || isFirstLine) { if (isFirstLine) isFirstLine = false; else { nextLine_ = sourceIterator->peekNextLine(); needReset = true; } firstChar = nextLine_.find_first_not_of(" \t"); if (firstChar == string::npos) { if (endOnEmptyLine && !isInComment_) break; continue; } if (nextLine_.compare(firstChar, 2, "/*") == 0) { firstChar += 2; isInComment_ = true; } if (isInComment_) { firstChar = nextLine_.find("*/", firstChar); if (firstChar == string::npos) continue; firstChar += 2; isInComment_ = false; firstChar = nextLine_.find_first_not_of(" \t", firstChar); if (firstChar == string::npos) continue; } if (nextLine_.compare(firstChar, 2, "//") == 0) continue; // found the next text break; } if (firstChar == string::npos) nextLine_ = ""; else nextLine_ = nextLine_.substr(firstChar); if (needReset) sourceIterator->peekReset(); return nextLine_; } /** * adjust comment position because of adding or deleting spaces * the spaces are added or deleted to formattedLine * spacePadNum contains the adjustment */ void ASFormatter::adjustComments(void) { assert(spacePadNum != 0); assert(currentLine.compare(charNum, 2, "//") == 0 || currentLine.compare(charNum, 2, "/*") == 0); // block comment must be closed on this line with nothing after it if (currentLine.compare(charNum, 2, "/*") == 0) { size_t endNum = currentLine.find("*/", charNum + 2); if (endNum == string::npos) return; if (currentLine.find_first_not_of(" \t", endNum + 2) != string::npos) return; } size_t len = formattedLine.length(); // don't adjust a tab if (formattedLine[len - 1] == '\t') return; // if spaces were removed, need to add spaces before the comment if (spacePadNum < 0) { int adjust = -spacePadNum; // make the number positive formattedLine.append(adjust, ' '); } // if spaces were added, need to delete extra spaces before the comment // if cannot be done put the comment one space after the last text else if (spacePadNum > 0) { int adjust = spacePadNum; size_t lastText = formattedLine.find_last_not_of(' '); if (lastText != string::npos && lastText < len - adjust - 1) formattedLine.resize(len - adjust); else if (len > lastText + 2) formattedLine.resize(lastText + 2); else if (len < lastText + 2) formattedLine.append(len - lastText, ' '); } } /** * append the current bracket inside the end of line comments * currentChar contains the bracket, it will be appended to formattedLine * formattedLineCommentNum is the comment location on formattedLine */ void ASFormatter::appendCharInsideComments(void) { if (formattedLineCommentNum == string::npos) // does the comment start on the previous line? { appendCurrentChar(); // don't attach return; } assert(formattedLine.compare(formattedLineCommentNum, 2, "//") == 0 || formattedLine.compare(formattedLineCommentNum, 2, "/*") == 0); // find the previous non space char size_t end = formattedLineCommentNum; size_t beg = formattedLine.find_last_not_of(" \t", end - 1); if (beg == string::npos) { appendCurrentChar(); // don't attach return; } beg++; // insert the bracket if (end - beg < 3) // is there room to insert? formattedLine.insert(beg, 3 - end + beg, ' '); if (formattedLine[beg] == '\t') // don't pad with a tab formattedLine.insert(beg, 1, ' '); formattedLine[beg + 1] = currentChar; testForTimeToSplitFormattedLine(); if (isBeforeComment()) breakLine(); else if (isCharImmediatelyPostLineComment) shouldBreakLineAtNextChar = true; return; } /** * add or remove space padding to operators * the operators and necessary padding will be appended to formattedLine * the calling function should have a continue statement after calling this method * * @param newOperator the operator to be padded */ void ASFormatter::padOperators(const string* newOperator) { assert(shouldPadOperators); assert(newOperator != NULL); bool shouldPad = (newOperator != &AS_SCOPE_RESOLUTION && newOperator != &AS_PLUS_PLUS && newOperator != &AS_MINUS_MINUS && newOperator != &AS_NOT && newOperator != &AS_BIT_NOT && newOperator != &AS_ARROW && !(newOperator == &AS_COLON && !foundQuestionMark // objC methods && (isInObjCMethodDefinition || isInObjCInterface || isInObjCSelector || squareBracketCount)) && !(newOperator == &AS_MINUS && isInExponent()) && !((newOperator == &AS_PLUS || newOperator == &AS_MINUS) // check for unary plus or minus && (previousNonWSChar == '(' || previousNonWSChar == '[' || previousNonWSChar == '=' || previousNonWSChar == ',')) && !(newOperator == &AS_PLUS && isInExponent()) && !isCharImmediatelyPostOperator //? // commented out in release 2.05.1 - doesn't seem to do anything??? //x && !((newOperator == &AS_MULT || newOperator == &AS_BIT_AND || newOperator == &AS_AND) //x && isPointerOrReference()) && !(newOperator == &AS_MULT && (previousNonWSChar == '.' || previousNonWSChar == '>')) // check for -> && !((isInTemplate || isImmediatelyPostTemplate) && (newOperator == &AS_LS || newOperator == &AS_GR)) && !(newOperator == &AS_GCC_MIN_ASSIGN && ASBase::peekNextChar(currentLine, charNum + 1) == '>') && !(newOperator == &AS_GR && previousNonWSChar == '?') && !(newOperator == &AS_QUESTION // check for Java wildcard && (previousNonWSChar == '<' || ASBase::peekNextChar(currentLine, charNum) == '>' || ASBase::peekNextChar(currentLine, charNum) == '.')) && !isInCase && !isInAsm && !isInAsmOneLine && !isInAsmBlock ); // pad before operator if (shouldPad && !(newOperator == &AS_COLON && (!foundQuestionMark && !isInEnum) && currentHeader != &AS_FOR) && !(newOperator == &AS_QUESTION && isSharpStyle() // check for C# nullable type (e.g. int?) && currentLine.find(':', charNum + 1) == string::npos) ) appendSpacePad(); appendOperator(*newOperator); goForward(newOperator->length() - 1); currentChar = (*newOperator)[newOperator->length() - 1]; // pad after operator // but do not pad after a '-' that is a unary-minus. if (shouldPad && !isBeforeAnyComment() && !(newOperator == &AS_PLUS && isUnaryOperator()) && !(newOperator == &AS_MINUS && isUnaryOperator()) && !(currentLine.compare(charNum + 1, 1, AS_SEMICOLON) == 0) && !(currentLine.compare(charNum + 1, 2, AS_SCOPE_RESOLUTION) == 0) && !(peekNextChar() == ',') && !(newOperator == &AS_QUESTION && isSharpStyle() // check for C# nullable type (e.g. int?) && peekNextChar() == '[') ) appendSpaceAfter(); previousOperator = newOperator; return; } /** * format pointer or reference * currentChar contains the pointer or reference * the symbol and necessary padding will be appended to formattedLine * the calling function should have a continue statement after calling this method * * NOTE: Do NOT use appendCurrentChar() in this method. The line should not be * broken once the calculation starts. */ void ASFormatter::formatPointerOrReference(void) { assert(currentChar == '*' || currentChar == '&' || currentChar == '^'); assert(!isJavaStyle()); int pa = pointerAlignment; int ra = referenceAlignment; int itemAlignment = (currentChar == '*' || currentChar == '^') ? pa : ((ra == REF_SAME_AS_PTR) ? pa : ra); // check for ** and && char peekedChar = peekNextChar(); if ((currentChar == '*' && peekedChar == '*') || (currentChar == '&' && peekedChar == '&')) { size_t nextChar = currentLine.find_first_not_of(" \t", charNum + 2); if (nextChar == string::npos) peekedChar = ' '; else peekedChar = currentLine[nextChar]; } // check for cast if (peekedChar == ')' || peekedChar == '>' || peekedChar == ',') { formatPointerOrReferenceCast(); return; } // check for a padded space and remove it if (charNum > 0 && !isWhiteSpace(currentLine[charNum - 1]) && formattedLine.length() > 0 && isWhiteSpace(formattedLine[formattedLine.length() - 1])) { formattedLine.erase(formattedLine.length() - 1); spacePadNum--; } if (itemAlignment == PTR_ALIGN_TYPE) { formatPointerOrReferenceToType(); } else if (itemAlignment == PTR_ALIGN_MIDDLE) { formatPointerOrReferenceToMiddle(); } else if (itemAlignment == PTR_ALIGN_NAME) { formatPointerOrReferenceToName(); } else // pointerAlignment == PTR_ALIGN_NONE { formattedLine.append(1, currentChar); } } /** * format pointer or reference with align to type */ void ASFormatter::formatPointerOrReferenceToType() { assert(currentChar == '*' || currentChar == '&' || currentChar == '^'); assert(!isJavaStyle()); // do this before bumping charNum bool isOldPRCentered = isPointerOrReferenceCentered(); size_t prevCh = formattedLine.find_last_not_of(" \t"); if (prevCh == string::npos) prevCh = 0; if (formattedLine.length() == 0 || prevCh == formattedLine.length() - 1) formattedLine.append(1, currentChar); else { // exchange * or & with character following the type // this may not work every time with a tab character string charSave = formattedLine.substr(prevCh + 1, 1); formattedLine[prevCh + 1] = currentChar; formattedLine.append(charSave); } if (isSequenceReached("**") || isSequenceReached("&&")) { if (formattedLine.length() == 1) formattedLine.append(1, currentChar); else formattedLine.insert(prevCh + 2, 1, currentChar); goForward(1); } // if no space after then add one if (charNum < (int) currentLine.length() - 1 && !isWhiteSpace(currentLine[charNum + 1]) && currentLine[charNum + 1] != ')') appendSpacePad(); // if old pointer or reference is centered, remove a space if (isOldPRCentered && isWhiteSpace(formattedLine[formattedLine.length() - 1])) { formattedLine.erase(formattedLine.length() - 1, 1); spacePadNum--; } // update the formattedLine split point if (maxCodeLength != string::npos) { size_t index = formattedLine.length() - 1; if (isWhiteSpace(formattedLine[index])) { updateFormattedLineSplitPointsPointerOrReference(index); testForTimeToSplitFormattedLine(); } } } /** * format pointer or reference with align in the middle */ void ASFormatter::formatPointerOrReferenceToMiddle() { assert(currentChar == '*' || currentChar == '&' || currentChar == '^'); assert(!isJavaStyle()); // compute current whitespace before size_t wsBefore = currentLine.find_last_not_of(" \t", charNum - 1); if (wsBefore == string::npos) wsBefore = 0; else wsBefore = charNum - wsBefore - 1; string sequenceToInsert(1, currentChar); if (isSequenceReached("**")) { sequenceToInsert = "**"; goForward(1); } else if (isSequenceReached("&&")) { sequenceToInsert = "&&"; goForward(1); } // if reference to a pointer check for conflicting alignment else if (currentChar == '*' && peekNextChar() == '&' && (referenceAlignment == REF_ALIGN_TYPE || referenceAlignment == REF_ALIGN_MIDDLE || referenceAlignment == REF_SAME_AS_PTR)) { sequenceToInsert = "*&"; goForward(1); for (size_t i = charNum; i < currentLine.length() - 1 && isWhiteSpace(currentLine[i]); i++) goForward(1); } // if a comment follows don't align, just space pad if (isBeforeAnyComment()) { appendSpacePad(); formattedLine.append(sequenceToInsert); appendSpaceAfter(); return; } // do this before goForward() bool isAfterScopeResolution = previousNonWSChar == ':'; size_t charNumSave = charNum; // if this is the last thing on the line if (currentLine.find_first_not_of(" \t", charNum + 1) == string::npos) { if (wsBefore == 0 && !isAfterScopeResolution) formattedLine.append(1, ' '); formattedLine.append(sequenceToInsert); return; } // goForward() to convert tabs to spaces, if necessary, // and move following characters to preceding characters // this may not work every time with tab characters for (size_t i = charNum + 1; i < currentLine.length() && isWhiteSpace(currentLine[i]); i++) { goForward(1); if (formattedLine.length() > 0) formattedLine.append(1, currentLine[i]); else spacePadNum--; } // find space padding after size_t wsAfter = currentLine.find_first_not_of(" \t", charNumSave + 1); if (wsAfter == string::npos || isBeforeAnyComment()) wsAfter = 0; else wsAfter = wsAfter - charNumSave - 1; // don't pad before scope resolution operator, but pad after if (isAfterScopeResolution) { size_t lastText = formattedLine.find_last_not_of(" \t"); formattedLine.insert(lastText + 1, sequenceToInsert); appendSpacePad(); } else if (formattedLine.length() > 0) { // whitespace should be at least 2 chars to center if (wsBefore + wsAfter < 2) { size_t charsToAppend = (2 - (wsBefore + wsAfter)); formattedLine.append(charsToAppend, ' '); spacePadNum += charsToAppend; if (wsBefore == 0) wsBefore++; if (wsAfter == 0) wsAfter++; } // insert the pointer or reference char size_t padAfter = (wsBefore + wsAfter) / 2; size_t index = formattedLine.length() - padAfter; formattedLine.insert(index, sequenceToInsert); } else // formattedLine.length() == 0 { formattedLine.append(sequenceToInsert); if (wsAfter == 0) wsAfter++; formattedLine.append(wsAfter, ' '); spacePadNum += wsAfter; } // update the formattedLine split point after the pointer if (maxCodeLength != string::npos && formattedLine.length() > 0) { size_t index = formattedLine.find_last_not_of(" \t"); if (index != string::npos && (index < formattedLine.length() - 1)) { index++; updateFormattedLineSplitPointsPointerOrReference(index); testForTimeToSplitFormattedLine(); } } } /** * format pointer or reference with align to name */ void ASFormatter::formatPointerOrReferenceToName() { assert(currentChar == '*' || currentChar == '&' || currentChar == '^'); assert(!isJavaStyle()); // do this before bumping charNum bool isOldPRCentered = isPointerOrReferenceCentered(); size_t startNum = formattedLine.find_last_not_of(" \t"); if (startNum == string::npos) startNum = 0; string sequenceToInsert(1, currentChar); if (isSequenceReached("**")) { sequenceToInsert = "**"; goForward(1); } else if (isSequenceReached("&&")) { sequenceToInsert = "&&"; goForward(1); } // if reference to a pointer align both to name else if (currentChar == '*' && peekNextChar() == '&') { sequenceToInsert = "*&"; goForward(1); for (size_t i = charNum; i < currentLine.length() - 1 && isWhiteSpace(currentLine[i]); i++) goForward(1); } char peekedChar = peekNextChar(); bool isAfterScopeResolution = previousNonWSChar == ':'; // check for :: // if this is not the last thing on the line if (!isBeforeAnyComment() && (int) currentLine.find_first_not_of(" \t", charNum + 1) > charNum) { // goForward() to convert tabs to spaces, if necessary, // and move following characters to preceding characters // this may not work every time with tab characters for (size_t i = charNum + 1; i < currentLine.length() && isWhiteSpace(currentLine[i]); i++) { // if a padded paren follows don't move if (shouldPadParensOutside && peekedChar == '(' && !isOldPRCentered) { // empty parens don't count size_t start = currentLine.find_first_not_of("( \t", charNum + 1); if (start != string::npos && currentLine[start] != ')') break; } goForward(1); if (formattedLine.length() > 0) formattedLine.append(1, currentLine[i]); else spacePadNum--; } } // don't pad before scope resolution operator if (isAfterScopeResolution) { size_t lastText = formattedLine.find_last_not_of(" \t"); if (lastText != string::npos && lastText + 1 < formattedLine.length()) formattedLine.erase(lastText + 1); } // if no space before * then add one else if (formattedLine.length() > 0 && (formattedLine.length() <= startNum + 1 || !isWhiteSpace(formattedLine[startNum + 1]))) { formattedLine.insert(startNum + 1, 1, ' '); spacePadNum++; } appendSequence(sequenceToInsert, false); // if old pointer or reference is centered, remove a space if (isOldPRCentered && formattedLine.length() > startNum + 1 && isWhiteSpace(formattedLine[startNum + 1]) && !isBeforeAnyComment()) { formattedLine.erase(startNum + 1, 1); spacePadNum--; } // don't convert to *= or &= if (peekedChar == '=') { appendSpaceAfter(); // if more than one space before, delete one if (formattedLine.length() > startNum && isWhiteSpace(formattedLine[startNum + 1]) && isWhiteSpace(formattedLine[startNum + 2])) { formattedLine.erase(startNum + 1, 1); spacePadNum--; } } // update the formattedLine split point if (maxCodeLength != string::npos) { size_t index = formattedLine.find_last_of(" \t"); if (index != string::npos && index < formattedLine.length() - 1 && (formattedLine[index + 1] == '*' || formattedLine[index + 1] == '&' || formattedLine[index + 1] == '^')) { updateFormattedLineSplitPointsPointerOrReference(index); testForTimeToSplitFormattedLine(); } } } /** * format pointer or reference cast * currentChar contains the pointer or reference * NOTE: the pointers and references in function definitions * are processed as a cast (e.g. void foo(void*, void*)) * is processed here. */ void ASFormatter::formatPointerOrReferenceCast(void) { assert(currentChar == '*' || currentChar == '&' || currentChar == '^'); assert(!isJavaStyle()); int pa = pointerAlignment; int ra = referenceAlignment; int itemAlignment = (currentChar == '*' || currentChar == '^') ? pa : ((ra == REF_SAME_AS_PTR) ? pa : ra); string sequenceToInsert(1, currentChar); if (isSequenceReached("**") || isSequenceReached("&&")) { goForward(1); sequenceToInsert.append(1, currentLine[charNum]); } if (itemAlignment == PTR_ALIGN_NONE) { appendSequence(sequenceToInsert, false); return; } // remove preceding whitespace char prevCh = ' '; size_t prevNum = formattedLine.find_last_not_of(" \t"); if (prevNum != string::npos) { prevCh = formattedLine[prevNum]; if (prevNum + 1 < formattedLine.length() && isWhiteSpace(formattedLine[prevNum + 1]) && prevCh != '(') { spacePadNum -= (formattedLine.length() - 1 - prevNum); formattedLine.erase(prevNum + 1); } } bool isAfterScopeResolution = previousNonWSChar == ':'; if ((itemAlignment == PTR_ALIGN_MIDDLE || itemAlignment == PTR_ALIGN_NAME) && !isAfterScopeResolution && prevCh != '(') { appendSpacePad(); // in this case appendSpacePad may or may not update the split point if (maxCodeLength != string::npos && formattedLine.length() > 0) updateFormattedLineSplitPointsPointerOrReference(formattedLine.length() - 1); appendSequence(sequenceToInsert, false); } else appendSequence(sequenceToInsert, false); // remove trailing whitespace if comma follows char nextChar = peekNextChar(); if (nextChar == ',') { while (isWhiteSpace(currentLine[charNum + 1])) { goForward(1); spacePadNum--; } } } /** * add or remove space padding to parens * currentChar contains the paren * the parens and necessary padding will be appended to formattedLine * the calling function should have a continue statement after calling this method */ void ASFormatter::padParens(void) { assert(shouldPadParensOutside || shouldPadParensInside || shouldUnPadParens || shouldPadFirstParen); assert(currentChar == '(' || currentChar == ')'); int spacesOutsideToDelete = 0; int spacesInsideToDelete = 0; if (currentChar == '(') { spacesOutsideToDelete = formattedLine.length() - 1; spacesInsideToDelete = 0; // compute spaces outside the opening paren to delete if (shouldUnPadParens) { char lastChar = ' '; bool prevIsParenHeader = false; size_t i = formattedLine.find_last_not_of(" \t"); if (i != string::npos) { // if last char is a bracket the previous whitespace is an indent if (formattedLine[i] == '{') spacesOutsideToDelete = 0; else if (isCharImmediatelyPostPointerOrReference) spacesOutsideToDelete = 0; else { spacesOutsideToDelete -= i; lastChar = formattedLine[i]; // if previous word is a header, it will be a paren header string prevWord = getPreviousWord(formattedLine, formattedLine.length()); const string* prevWordH = NULL; if (shouldPadHeader && prevWord.length() > 0 && isCharPotentialHeader(prevWord, 0)) prevWordH = ASBeautifier::findHeader(prevWord, 0, headers); if (prevWordH != NULL) prevIsParenHeader = true; else if (prevWord == "return") // don't unpad prevIsParenHeader = true; else if (isCStyle() && prevWord == "throw" && shouldPadHeader) // don't unpad prevIsParenHeader = true; else if (prevWord == "and" || prevWord == "or") // don't unpad prevIsParenHeader = true; // don't unpad variables else if (prevWord == "bool" || prevWord == "int" || prevWord == "void" || prevWord == "void*" || prevWord == "char" || prevWord == "long" || prevWord == "double" || prevWord == "float" || (prevWord.length() >= 4 // check end of word for _t && prevWord.compare(prevWord.length() - 2, 2, "_t") == 0) || prevWord == "Int32" || prevWord == "UInt32" || prevWord == "Int64" || prevWord == "UInt64" || prevWord == "BOOL" || prevWord == "DWORD" || prevWord == "HWND" || prevWord == "INT" || prevWord == "LPSTR" || prevWord == "VOID" || prevWord == "LPVOID" ) { prevIsParenHeader = true; } } } // do not unpad operators, but leave them if already padded if (shouldPadParensOutside || prevIsParenHeader) spacesOutsideToDelete--; else if (lastChar == '|' // check for || || lastChar == '&' // check for && || lastChar == ',' || (lastChar == '(' && shouldPadParensInside) || (lastChar == '>' && !foundCastOperator) || lastChar == '<' || lastChar == '?' || lastChar == ':' || lastChar == ';' || lastChar == '=' || lastChar == '+' || lastChar == '-' || lastChar == '*' || lastChar == '/' || lastChar == '%' || lastChar == '^' ) spacesOutsideToDelete--; if (spacesOutsideToDelete > 0) { formattedLine.erase(i + 1, spacesOutsideToDelete); spacePadNum -= spacesOutsideToDelete; } } // pad open paren outside char peekedCharOutside = peekNextChar(); if (shouldPadFirstParen && previousChar != '(' && peekedCharOutside != ')') appendSpacePad(); else if (shouldPadParensOutside) { if (!(currentChar == '(' && peekedCharOutside == ')')) appendSpacePad(); } appendCurrentChar(); // unpad open paren inside if (shouldUnPadParens) { size_t j = currentLine.find_first_not_of(" \t", charNum + 1); if (j != string::npos) spacesInsideToDelete = j - charNum - 1; if (shouldPadParensInside) spacesInsideToDelete--; if (spacesInsideToDelete > 0) { currentLine.erase(charNum + 1, spacesInsideToDelete); spacePadNum -= spacesInsideToDelete; } // convert tab to space if requested if (shouldConvertTabs && (int) currentLine.length() > charNum + 1 && currentLine[charNum + 1] == '\t') currentLine[charNum + 1] = ' '; } // pad open paren inside char peekedCharInside = peekNextChar(); if (shouldPadParensInside) if (!(currentChar == '(' && peekedCharInside == ')')) appendSpaceAfter(); } else if (currentChar == ')') { // unpad close paren inside if (shouldUnPadParens) { spacesInsideToDelete = formattedLine.length(); size_t i = formattedLine.find_last_not_of(" \t"); if (i != string::npos) spacesInsideToDelete = formattedLine.length() - 1 - i; if (shouldPadParensInside) spacesInsideToDelete--; if (spacesInsideToDelete > 0) { formattedLine.erase(i + 1, spacesInsideToDelete); spacePadNum -= spacesInsideToDelete; } } // pad close paren inside if (shouldPadParensInside) if (!(previousChar == '(' && currentChar == ')')) appendSpacePad(); appendCurrentChar(); // unpad close paren outside // close parens outside are left unchanged if (shouldUnPadParens) { //spacesOutsideToDelete = 0; //size_t j = currentLine.find_first_not_of(" \t", charNum + 1); //if (j != string::npos) // spacesOutsideToDelete = j - charNum - 1; //if (shouldPadParensOutside) // spacesOutsideToDelete--; //if (spacesOutsideToDelete > 0) //{ // currentLine.erase(charNum + 1, spacesOutsideToDelete); // spacePadNum -= spacesOutsideToDelete; //} } // pad close paren outside char peekedCharOutside = peekNextChar(); if (shouldPadParensOutside) if (peekedCharOutside != ';' && peekedCharOutside != ',' && peekedCharOutside != '.' && peekedCharOutside != '+' // check for ++ && peekedCharOutside != '-' // check for -- && peekedCharOutside != ']') appendSpaceAfter(); } return; } /** * format opening bracket as attached or broken * currentChar contains the bracket * the brackets will be appended to the current formattedLine or a new formattedLine as necessary * the calling function should have a continue statement after calling this method * * @param bracketType the type of bracket to be formatted. */ void ASFormatter::formatOpeningBracket(BracketType bracketType) { assert(!isBracketType(bracketType, ARRAY_TYPE)); assert(currentChar == '{'); parenStack->push_back(0); bool breakBracket = isCurrentBracketBroken(); if (breakBracket) { if (isBeforeAnyComment() && isOkToBreakBlock(bracketType)) { // if comment is at line end leave the comment on this line if (isBeforeAnyLineEndComment(charNum) && !currentLineBeginsWithBracket) { currentChar = ' '; // remove bracket from current line if (parenStack->size() > 1) parenStack->pop_back(); currentLine[charNum] = currentChar; appendOpeningBracket = true; // append bracket to following line } // else put comment after the bracket else if (!isBeforeMultipleLineEndComments(charNum)) breakLine(); } else if (!isBracketType(bracketType, SINGLE_LINE_TYPE)) breakLine(); else if (shouldBreakOneLineBlocks && peekNextChar() != '}') breakLine(); else if (!isInLineBreak) appendSpacePad(); appendCurrentChar(); // should a following comment break from the bracket? // must break the line AFTER the bracket if (isBeforeComment() && formattedLine.length() > 0 && formattedLine[0] == '{' && isOkToBreakBlock(bracketType) && (bracketFormatMode == BREAK_MODE || bracketFormatMode == LINUX_MODE || bracketFormatMode == STROUSTRUP_MODE)) { shouldBreakLineAtNextChar = true; } } else // attach bracket { // are there comments before the bracket? if (isCharImmediatelyPostComment || isCharImmediatelyPostLineComment) { if (isOkToBreakBlock(bracketType) && !(isCharImmediatelyPostComment && isCharImmediatelyPostLineComment) // don't attach if two comments on the line && !isImmediatelyPostPreprocessor // && peekNextChar() != '}' // don't attach { } // removed release 2.03 && previousCommandChar != '{' // don't attach { { && previousCommandChar != '}' // don't attach } { && previousCommandChar != ';') // don't attach ; { { appendCharInsideComments(); } else { appendCurrentChar(); // don't attach } } else if (previousCommandChar == '{' || (previousCommandChar == '}' && !isInClassInitializer) || previousCommandChar == ';') // '}' , ';' chars added for proper handling of '{' immediately after a '}' or ';' { appendCurrentChar(); // don't attach } else { // if a blank line precedes this don't attach if (isEmptyLine(formattedLine)) appendCurrentChar(); // don't attach else if (isOkToBreakBlock(bracketType) && !(isImmediatelyPostPreprocessor && currentLineBeginsWithBracket)) { if (peekNextChar() != '}') { appendSpacePad(); appendCurrentChar(false); // OK to attach testForTimeToSplitFormattedLine(); // line length will have changed // should a following comment attach with the bracket? // insert spaces to reposition the comment if (isBeforeComment() && !isBeforeMultipleLineEndComments(charNum) && (!isBeforeAnyLineEndComment(charNum) || currentLineBeginsWithBracket)) { shouldBreakLineAtNextChar = true; currentLine.insert(charNum + 1, charNum + 1, ' '); } else if (!isBeforeAnyComment()) // added in release 2.03 { shouldBreakLineAtNextChar = true; } } else { if (currentLineBeginsWithBracket && charNum == (int) currentLineFirstBracketNum) { appendSpacePad(); appendCurrentChar(false); // attach shouldBreakLineAtNextChar = true; } else { appendSpacePad(); appendCurrentChar(); // don't attach } } } else { if (!isInLineBreak) appendSpacePad(); appendCurrentChar(); // don't attach } } } } /** * format closing bracket * currentChar contains the bracket * the calling function should have a continue statement after calling this method * * @param bracketType the type of the opening bracket for this closing bracket. */ void ASFormatter::formatClosingBracket(BracketType bracketType) { assert(!isBracketType(bracketType, ARRAY_TYPE)); assert(currentChar == '}'); // parenStack must contain one entry if (parenStack->size() > 1) parenStack->pop_back(); // mark state of immediately after empty block // this state will be used for locating brackets that appear immediately AFTER an empty block (e.g. '{} \n}'). if (previousCommandChar == '{') isImmediatelyPostEmptyBlock = true; if (attachClosingBracketMode) { // for now, namespaces and classes will be attached. Uncomment the lines below to break. if ((isEmptyLine(formattedLine) // if a blank line precedes this || isCharImmediatelyPostLineComment || isCharImmediatelyPostComment || (isImmediatelyPostPreprocessor && (int) currentLine.find_first_not_of(" \t") == charNum) // || (isBracketType(bracketType, CLASS_TYPE) && isOkToBreakBlock(bracketType) && previousNonWSChar != '{') // || (isBracketType(bracketType, NAMESPACE_TYPE) && isOkToBreakBlock(bracketType) && previousNonWSChar != '{') ) && (!isBracketType(bracketType, SINGLE_LINE_TYPE) || isOkToBreakBlock(bracketType))) { breakLine(); appendCurrentChar(); // don't attach } else { if (previousNonWSChar != '{' && (!isBracketType(bracketType, SINGLE_LINE_TYPE) || isOkToBreakBlock(bracketType))) appendSpacePad(); appendCurrentChar(false); // attach } } else if ((!(previousCommandChar == '{' && isPreviousBracketBlockRelated)) // this '}' does not close an empty block && isOkToBreakBlock(bracketType)) // astyle is allowed to break one line blocks { breakLine(); appendCurrentChar(); } else { appendCurrentChar(); } // if a declaration follows a definition, space pad if (isLegalNameChar(peekNextChar())) appendSpaceAfter(); if (shouldBreakBlocks && currentHeader != NULL && !isHeaderInMultiStatementLine && parenStack->back() == 0) { if (currentHeader == &AS_CASE || currentHeader == &AS_DEFAULT) { // do not yet insert a line if "break" statement is outside the brackets string nextText = peekNextText(currentLine.substr(charNum + 1)); if (nextText.length() > 0 && nextText.substr(0, 5) != "break") isAppendPostBlockEmptyLineRequested = true; } else isAppendPostBlockEmptyLineRequested = true; } } /** * format array brackets as attached or broken * determine if the brackets can have an inStatement indent * currentChar contains the bracket * the brackets will be appended to the current formattedLine or a new formattedLine as necessary * the calling function should have a continue statement after calling this method * * @param bracketType the type of bracket to be formatted, must be an ARRAY_TYPE. * @param isOpeningArrayBracket indicates if this is the opening bracket for the array block. */ void ASFormatter::formatArrayBrackets(BracketType bracketType, bool isOpeningArrayBracket) { assert(isBracketType(bracketType, ARRAY_TYPE)); assert(currentChar == '{' || currentChar == '}'); if (currentChar == '{') { // is this the first opening bracket in the array? if (isOpeningArrayBracket) { if (bracketFormatMode == ATTACH_MODE || bracketFormatMode == LINUX_MODE || bracketFormatMode == STROUSTRUP_MODE) { // don't attach to a preprocessor directive or '\' line if ((isImmediatelyPostPreprocessor || (formattedLine.length() > 0 && formattedLine[formattedLine.length() - 1] == '\\')) && currentLineBeginsWithBracket) { isInLineBreak = true; appendCurrentChar(); // don't attach } else if (isCharImmediatelyPostComment) { // TODO: attach bracket to line-end comment appendCurrentChar(); // don't attach } else if (isCharImmediatelyPostLineComment && !isBracketType(bracketType, SINGLE_LINE_TYPE)) { appendCharInsideComments(); } else { // if a blank line precedes this don't attach if (isEmptyLine(formattedLine)) appendCurrentChar(); // don't attach else { // if bracket is broken or not an assignment if (currentLineBeginsWithBracket && !isBracketType(bracketType, SINGLE_LINE_TYPE)) { appendSpacePad(); appendCurrentChar(false); // OK to attach // TODO: debug the following line testForTimeToSplitFormattedLine(); // line length will have changed if (currentLineBeginsWithBracket && (int) currentLineFirstBracketNum == charNum) shouldBreakLineAtNextChar = true; } else { if (previousNonWSChar != '(') { // don't space pad C++11 uniform initialization if (!isBracketType(bracketType, INIT_TYPE)) appendSpacePad(); } appendCurrentChar(); } } } } else if (bracketFormatMode == BREAK_MODE) { if (isWhiteSpace(peekNextChar())) breakLine(); else if (isBeforeAnyComment()) { // do not break unless comment is at line end if (isBeforeAnyLineEndComment(charNum) && !currentLineBeginsWithBracket) { currentChar = ' '; // remove bracket from current line appendOpeningBracket = true; // append bracket to following line } } if (!isInLineBreak && previousNonWSChar != '(') { // don't space pad C++11 uniform initialization if (!isBracketType(bracketType, INIT_TYPE)) appendSpacePad(); } appendCurrentChar(); if (currentLineBeginsWithBracket && (int) currentLineFirstBracketNum == charNum && !isBracketType(bracketType, SINGLE_LINE_TYPE)) shouldBreakLineAtNextChar = true; } else if (bracketFormatMode == RUN_IN_MODE) { if (isWhiteSpace(peekNextChar())) breakLine(); else if (isBeforeAnyComment()) { // do not break unless comment is at line end if (isBeforeAnyLineEndComment(charNum) && !currentLineBeginsWithBracket) { currentChar = ' '; // remove bracket from current line appendOpeningBracket = true; // append bracket to following line } } if (!isInLineBreak && previousNonWSChar != '(') { // don't space pad C++11 uniform initialization if (!isBracketType(bracketType, INIT_TYPE)) appendSpacePad(); } appendCurrentChar(); } else if (bracketFormatMode == NONE_MODE) { if (currentLineBeginsWithBracket && charNum == (int) currentLineFirstBracketNum) { appendCurrentChar(); // don't attach } else { if (previousNonWSChar != '(') { // don't space pad C++11 uniform initialization if (!isBracketType(bracketType, INIT_TYPE)) appendSpacePad(); } appendCurrentChar(false); // OK to attach } } } else // not the first opening bracket { if (bracketFormatMode == RUN_IN_MODE) { if (previousNonWSChar == '{' && bracketTypeStack->size() > 2 && !isBracketType((*bracketTypeStack)[bracketTypeStack->size() - 2], SINGLE_LINE_TYPE)) formatArrayRunIn(); } else if (!isInLineBreak && !isWhiteSpace(peekNextChar()) && previousNonWSChar == '{' && bracketTypeStack->size() > 2 && !isBracketType((*bracketTypeStack)[bracketTypeStack->size() - 2], SINGLE_LINE_TYPE)) formatArrayRunIn(); appendCurrentChar(); } } else if (currentChar == '}') { if (attachClosingBracketMode) { if (isEmptyLine(formattedLine) // if a blank line precedes this || isImmediatelyPostPreprocessor || isCharImmediatelyPostLineComment || isCharImmediatelyPostComment) appendCurrentChar(); // don't attach else { appendSpacePad(); appendCurrentChar(false); // attach } } else { // does this close the first opening bracket in the array? // must check if the block is still a single line because of anonymous statements if (!isBracketType(bracketType, INIT_TYPE) && (!isBracketType(bracketType, SINGLE_LINE_TYPE) || formattedLine.find('{') == string::npos)) breakLine(); appendCurrentChar(); } // if a declaration follows an enum definition, space pad char peekedChar = peekNextChar(); if (isLegalNameChar(peekedChar) || peekedChar == '[') appendSpaceAfter(); } } /** * determine if a run-in can be attached. * if it can insert the indents in formattedLine and reset the current line break. */ void ASFormatter::formatRunIn() { assert(bracketFormatMode == RUN_IN_MODE || bracketFormatMode == NONE_MODE); // keep one line blocks returns true without indenting the run-in if (!isOkToBreakBlock(bracketTypeStack->back())) return; // true; // make sure the line begins with a bracket size_t lastText = formattedLine.find_last_not_of(" \t"); if (lastText == string::npos || formattedLine[lastText] != '{') return; // false; // make sure the bracket is broken if (formattedLine.find_first_not_of(" \t{") != string::npos) return; // false; if (isBracketType(bracketTypeStack->back(), NAMESPACE_TYPE)) return; // false; bool extraIndent = false; bool extraHalfIndent = false; isInLineBreak = true; // cannot attach a class modifier without indent-classes if (isCStyle() && isCharPotentialHeader(currentLine, charNum) && (isBracketType(bracketTypeStack->back(), CLASS_TYPE) || (isBracketType(bracketTypeStack->back(), STRUCT_TYPE) && isInIndentableStruct))) { if (findKeyword(currentLine, charNum, AS_PUBLIC) || findKeyword(currentLine, charNum, AS_PRIVATE) || findKeyword(currentLine, charNum, AS_PROTECTED)) { if (getModifierIndent()) extraHalfIndent = true; else if (!getClassIndent()) return; // false; } else if (getClassIndent()) extraIndent = true; } // cannot attach a 'case' statement without indent-switches if (!getSwitchIndent() && isCharPotentialHeader(currentLine, charNum) && (findKeyword(currentLine, charNum, AS_CASE) || findKeyword(currentLine, charNum, AS_DEFAULT))) return; // false; // extra indent for switch statements if (getSwitchIndent() && !preBracketHeaderStack->empty() && preBracketHeaderStack->back() == &AS_SWITCH && ((isLegalNameChar(currentChar) && !findKeyword(currentLine, charNum, AS_CASE)))) extraIndent = true; isInLineBreak = false; // remove for extra whitespace if (formattedLine.length() > lastText + 1 && formattedLine.find_first_not_of(" \t", lastText + 1) == string::npos) formattedLine.erase(lastText + 1); if (extraHalfIndent) { int indentLength_ = getIndentLength(); horstmannIndentChars = indentLength_ / 2; formattedLine.append(horstmannIndentChars - 1, ' '); } else if (getForceTabIndentation() && getIndentLength() != getTabLength()) { // insert the space indents string indent; int indentLength_ = getIndentLength(); int tabLength_ = getTabLength(); indent.append(indentLength_, ' '); if (extraIndent) indent.append(indentLength_, ' '); // replace spaces indents with tab indents size_t tabCount = indent.length() / tabLength_; // truncate extra spaces indent.erase(0U, tabCount * tabLength_); indent.insert(0U, tabCount, '\t'); horstmannIndentChars = indentLength_; if (indent[0] == ' ') // allow for bracket indent.erase(0, 1); formattedLine.append(indent); } else if (getIndentString() == "\t") { appendChar('\t', false); horstmannIndentChars = 2; // one for { and one for tab if (extraIndent) { appendChar('\t', false); horstmannIndentChars++; } } else // spaces { int indentLength_ = getIndentLength(); formattedLine.append(indentLength_ - 1, ' '); horstmannIndentChars = indentLength_; if (extraIndent) { formattedLine.append(indentLength_, ' '); horstmannIndentChars += indentLength_; } } isInHorstmannRunIn = true; } /** * remove whitespace and add indentation for an array run-in. */ void ASFormatter::formatArrayRunIn() { assert(isBracketType(bracketTypeStack->back(), ARRAY_TYPE)); // make sure the bracket is broken if (formattedLine.find_first_not_of(" \t{") != string::npos) return; size_t lastText = formattedLine.find_last_not_of(" \t"); if (lastText == string::npos || formattedLine[lastText] != '{') return; // check for extra whitespace if (formattedLine.length() > lastText + 1 && formattedLine.find_first_not_of(" \t", lastText + 1) == string::npos) formattedLine.erase(lastText + 1); if (getIndentString() == "\t") { appendChar('\t', false); horstmannIndentChars = 2; // one for { and one for tab } else { int indent = getIndentLength(); formattedLine.append(indent - 1, ' '); horstmannIndentChars = indent; } isInHorstmannRunIn = true; isInLineBreak = false; } /** * delete a bracketTypeStack vector object * BracketTypeStack did not work with the DeleteContainer template */ void ASFormatter::deleteContainer(vector* &container) { if (container != NULL) { container->clear(); delete (container); container = NULL; } } /** * delete a vector object * T is the type of vector * used for all vectors except bracketTypeStack */ template void ASFormatter::deleteContainer(T &container) { if (container != NULL) { container->clear(); delete (container); container = NULL; } } /** * initialize a BracketType vector object * BracketType did not work with the DeleteContainer template */ void ASFormatter::initContainer(vector* &container, vector* value) { if (container != NULL) deleteContainer(container); container = value; } /** * initialize a vector object * T is the type of vector * used for all vectors except bracketTypeStack */ template void ASFormatter::initContainer(T &container, T value) { // since the ASFormatter object is never deleted, // the existing vectors must be deleted before creating new ones if (container != NULL) deleteContainer(container); container = value; } /** * convert a tab to spaces. * charNum points to the current character to convert to spaces. * tabIncrementIn is the increment that must be added for tab indent characters * to get the correct column for the current tab. * replaces the tab in currentLine with the required number of spaces. * replaces the value of currentChar. */ void ASFormatter::convertTabToSpaces() { assert(currentLine[charNum] == '\t'); // do NOT replace if in quotes if (isInQuote || isInQuoteContinuation) return; size_t tabSize = getTabLength(); size_t numSpaces = tabSize - ((tabIncrementIn + charNum) % tabSize); currentLine.replace(charNum, 1, numSpaces, ' '); currentChar = currentLine[charNum]; } /** * is it ok to break this block? */ bool ASFormatter::isOkToBreakBlock(BracketType bracketType) const { // Actually, there should not be an ARRAY_TYPE bracket here. // But this will avoid breaking a one line block when there is. // Otherwise they will be formatted differently on consecutive runs. if (isBracketType(bracketType, ARRAY_TYPE) && isBracketType(bracketType, SINGLE_LINE_TYPE)) return false; if (!isBracketType(bracketType, SINGLE_LINE_TYPE) || shouldBreakOneLineBlocks || breakCurrentOneLineBlock) return true; return false; } /** * check if a sharp header is a paren or non-paren header */ bool ASFormatter::isSharpStyleWithParen(const string* header) const { if (isSharpStyle() && peekNextChar() == '(' && (header == &AS_CATCH || header == &AS_DELEGATE)) return true; return false; } /** * Check for a following header when a comment is reached. * firstLine must contain the start of the comment. * return value is a pointer to the header or NULL. */ const string* ASFormatter::checkForHeaderFollowingComment(const string &firstLine) const { assert(isInComment || isInLineComment); assert(shouldBreakElseIfs || shouldBreakBlocks || isInSwitchStatement()); // look ahead to find the next non-comment text bool endOnEmptyLine = (currentHeader == NULL); if (isInSwitchStatement()) endOnEmptyLine = false; string nextText = peekNextText(firstLine, endOnEmptyLine); if (nextText.length() == 0 || !isCharPotentialHeader(nextText, 0)) return NULL; return ASBeautifier::findHeader(nextText, 0, headers); } /** * process preprocessor statements. * charNum should be the index of the #. * * delete bracketTypeStack entries added by #if if a #else is found. * prevents double entries in the bracketTypeStack. */ void ASFormatter::processPreprocessor() { assert(currentChar == '#'); const size_t preproc = currentLine.find_first_not_of(" \t", charNum + 1); if (preproc == string::npos) return; if (currentLine.compare(preproc, 2, "if") == 0) { preprocBracketTypeStackSize = bracketTypeStack->size(); } else if (currentLine.compare(preproc, 4, "else") == 0) { // delete stack entries added in #if // should be replaced by #else if (preprocBracketTypeStackSize > 0) { int addedPreproc = bracketTypeStack->size() - preprocBracketTypeStackSize; for (int i = 0; i < addedPreproc; i++) bracketTypeStack->pop_back(); } } } /** * determine if the next line starts a comment * and a header follows the comment or comments. */ bool ASFormatter::commentAndHeaderFollows() { // called ONLY IF shouldDeleteEmptyLines and shouldBreakBlocks are TRUE. assert(shouldDeleteEmptyLines && shouldBreakBlocks); // is the next line a comment if (!sourceIterator->hasMoreLines()) return false; string nextLine_ = sourceIterator->peekNextLine(); size_t firstChar = nextLine_.find_first_not_of(" \t"); if (firstChar == string::npos || !(nextLine_.compare(firstChar, 2, "//") == 0 || nextLine_.compare(firstChar, 2, "/*") == 0)) { sourceIterator->peekReset(); return false; } // find the next non-comment text, and reset string nextText = peekNextText(nextLine_, false, true); if (nextText.length() == 0 || !isCharPotentialHeader(nextText, 0)) return false; const string* newHeader = ASBeautifier::findHeader(nextText, 0, headers); if (newHeader == NULL) return false; // if a closing header, reset break unless break is requested if (isClosingHeader(newHeader) && !shouldBreakClosingHeaderBlocks) { isAppendPostBlockEmptyLineRequested = false; return false; } return true; } /** * determine if a bracket should be attached or broken * uses brackets in the bracketTypeStack * the last bracket in the bracketTypeStack is the one being formatted * returns true if the bracket should be broken */ bool ASFormatter::isCurrentBracketBroken() const { assert(bracketTypeStack->size() > 1); bool breakBracket = false; size_t stackEnd = bracketTypeStack->size() - 1; // check bracket modifiers if (shouldAttachExternC && isBracketType((*bracketTypeStack)[stackEnd], EXTERN_TYPE)) { return false; } if (shouldAttachNamespace && isBracketType((*bracketTypeStack)[stackEnd], NAMESPACE_TYPE)) { return false; } else if (shouldAttachClass && (isBracketType((*bracketTypeStack)[stackEnd], CLASS_TYPE) || isBracketType((*bracketTypeStack)[stackEnd], INTERFACE_TYPE))) { return false; } else if (shouldAttachInline && isCStyle() // for C++ only && bracketFormatMode != RUN_IN_MODE && isBracketType((*bracketTypeStack)[stackEnd], COMMAND_TYPE)) { size_t i; for (i = 1; i < bracketTypeStack->size(); i++) if (isBracketType((*bracketTypeStack)[i], CLASS_TYPE) || isBracketType((*bracketTypeStack)[i], STRUCT_TYPE)) return false; } // check brackets if (isBracketType((*bracketTypeStack)[stackEnd], EXTERN_TYPE)) { if (currentLineBeginsWithBracket || bracketFormatMode == RUN_IN_MODE) breakBracket = true; } else if (bracketFormatMode == NONE_MODE) { if (currentLineBeginsWithBracket && (int) currentLineFirstBracketNum == charNum) breakBracket = true; } else if (bracketFormatMode == BREAK_MODE || bracketFormatMode == RUN_IN_MODE) { breakBracket = true; } else if (bracketFormatMode == LINUX_MODE || bracketFormatMode == STROUSTRUP_MODE) { // break a namespace, class, or interface if Linux if (isBracketType((*bracketTypeStack)[stackEnd], NAMESPACE_TYPE) || isBracketType((*bracketTypeStack)[stackEnd], CLASS_TYPE) || isBracketType((*bracketTypeStack)[stackEnd], INTERFACE_TYPE)) { if (bracketFormatMode == LINUX_MODE) breakBracket = true; } // break the first bracket if a function else if (isBracketType((*bracketTypeStack)[stackEnd], COMMAND_TYPE)) { if (stackEnd == 1) { breakBracket = true; } else if (stackEnd > 1) { // break the first bracket after these if a function if (isBracketType((*bracketTypeStack)[stackEnd - 1], NAMESPACE_TYPE) || isBracketType((*bracketTypeStack)[stackEnd - 1], CLASS_TYPE) || isBracketType((*bracketTypeStack)[stackEnd - 1], ARRAY_TYPE) || isBracketType((*bracketTypeStack)[stackEnd - 1], STRUCT_TYPE) || isBracketType((*bracketTypeStack)[stackEnd - 1], EXTERN_TYPE)) { breakBracket = true; } } } } return breakBracket; } /** * format comment body * the calling function should have a continue statement after calling this method */ void ASFormatter::formatCommentBody() { assert(isInComment); // append the comment while (charNum < (int) currentLine.length()) { currentChar = currentLine[charNum]; if (currentLine.compare(charNum, 2, "*/") == 0) { formatCommentCloser(); break; } if (currentChar == '\t' && shouldConvertTabs) convertTabToSpaces(); appendCurrentChar(); ++charNum; } if (shouldStripCommentPrefix) stripCommentPrefix(); } /** * format a comment opener * the comment opener will be appended to the current formattedLine or a new formattedLine as necessary * the calling function should have a continue statement after calling this method */ void ASFormatter::formatCommentOpener() { assert(isSequenceReached("/*")); isInComment = isInCommentStartLine = true; isImmediatelyPostLineComment = false; if (previousNonWSChar == '}') resetEndOfStatement(); // Check for a following header. // For speed do not check multiple comment lines more than once. // For speed do not check shouldBreakBlocks if previous line is empty, a comment, or a '{'. const string* followingHeader = NULL; if ((doesLineStartComment && !isImmediatelyPostCommentOnly && isBracketType(bracketTypeStack->back(), COMMAND_TYPE)) && (shouldBreakElseIfs || isInSwitchStatement() || (shouldBreakBlocks && !isImmediatelyPostEmptyLine && previousCommandChar != '{'))) followingHeader = checkForHeaderFollowingComment(currentLine.substr(charNum)); if (spacePadNum != 0 && !isInLineBreak) adjustComments(); formattedLineCommentNum = formattedLine.length(); // must be done BEFORE appendSequence if (previousCommandChar == '{' && !isImmediatelyPostComment && !isImmediatelyPostLineComment) { if (bracketFormatMode == NONE_MODE) { // should a run-in statement be attached? if (currentLineBeginsWithBracket) formatRunIn(); } else if (bracketFormatMode == ATTACH_MODE) { // if the bracket was not attached? if (formattedLine.length() > 0 && formattedLine[0] == '{' && !isBracketType(bracketTypeStack->back(), SINGLE_LINE_TYPE)) isInLineBreak = true; } else if (bracketFormatMode == RUN_IN_MODE) { // should a run-in statement be attached? if (formattedLine.length() > 0 && formattedLine[0] == '{') formatRunIn(); } } else if (!doesLineStartComment) noTrimCommentContinuation = true; // ASBeautifier needs to know the following statements if (shouldBreakElseIfs && followingHeader == &AS_ELSE) elseHeaderFollowsComments = true; if (followingHeader == &AS_CASE || followingHeader == &AS_DEFAULT) caseHeaderFollowsComments = true; // appendSequence will write the previous line appendSequence(AS_OPEN_COMMENT); goForward(1); // must be done AFTER appendSequence // Break before the comment if a header follows the line comment. // But not break if previous line is empty, a comment, or a '{'. if (shouldBreakBlocks && followingHeader != NULL && !isImmediatelyPostEmptyLine && previousCommandChar != '{') { if (isClosingHeader(followingHeader)) { if (!shouldBreakClosingHeaderBlocks) isPrependPostBlockEmptyLineRequested = false; } // if an opening header, break before the comment else isPrependPostBlockEmptyLineRequested = true; } if (previousCommandChar == '}') currentHeader = NULL; } /** * format a comment closer * the comment closer will be appended to the current formattedLine */ void ASFormatter::formatCommentCloser() { isInComment = false; noTrimCommentContinuation = false; isImmediatelyPostComment = true; appendSequence(AS_CLOSE_COMMENT); goForward(1); if (doesLineStartComment && (currentLine.find_first_not_of(" \t", charNum + 1) == string::npos)) lineEndsInCommentOnly = true; if (peekNextChar() == '}' && previousCommandChar != ';' && !isBracketType(bracketTypeStack->back(), ARRAY_TYPE) && !isInPreprocessor && isOkToBreakBlock(bracketTypeStack->back())) { isInLineBreak = true; shouldBreakLineAtNextChar = true; } } /** * format a line comment body * the calling function should have a continue statement after calling this method */ void ASFormatter::formatLineCommentBody() { assert(isInLineComment); // append the comment while (charNum < (int) currentLine.length()) // && !isLineReady // commented out in release 2.04, unnecessary { currentChar = currentLine[charNum]; if (currentChar == '\t' && shouldConvertTabs) convertTabToSpaces(); appendCurrentChar(); ++charNum; } // explicitly break a line when a line comment's end is found. if (charNum == (int) currentLine.length()) { isInLineBreak = true; isInLineComment = false; isImmediatelyPostLineComment = true; currentChar = 0; //make sure it is a neutral char. } } /** * format a line comment opener * the line comment opener will be appended to the current formattedLine or a new formattedLine as necessary * the calling function should have a continue statement after calling this method */ void ASFormatter::formatLineCommentOpener() { assert(isSequenceReached("//")); if ((int) currentLine.length() > charNum + 2 && currentLine[charNum + 2] == '\xf2') // check for windows line marker isAppendPostBlockEmptyLineRequested = false; isInLineComment = true; isCharImmediatelyPostComment = false; if (previousNonWSChar == '}') resetEndOfStatement(); // Check for a following header. // For speed do not check multiple comment lines more than once. // For speed do not check shouldBreakBlocks if previous line is empty, a comment, or a '{'. const string* followingHeader = NULL; if ((lineIsLineCommentOnly && !isImmediatelyPostCommentOnly && isBracketType(bracketTypeStack->back(), COMMAND_TYPE)) && (shouldBreakElseIfs || isInSwitchStatement() || (shouldBreakBlocks && !isImmediatelyPostEmptyLine && previousCommandChar != '{'))) followingHeader = checkForHeaderFollowingComment(currentLine.substr(charNum)); // do not indent if in column 1 or 2 // or in a namespace before the opening bracket if ((!shouldIndentCol1Comments && !lineCommentNoIndent) || foundNamespaceHeader) { if (charNum == 0) lineCommentNoIndent = true; else if (charNum == 1 && currentLine[0] == ' ') lineCommentNoIndent = true; } // move comment if spaces were added or deleted if (lineCommentNoIndent == false && spacePadNum != 0 && !isInLineBreak) adjustComments(); formattedLineCommentNum = formattedLine.length(); // must be done BEFORE appendSequence // check for run-in statement if (previousCommandChar == '{' && !isImmediatelyPostComment && !isImmediatelyPostLineComment) { if (bracketFormatMode == NONE_MODE) { if (currentLineBeginsWithBracket) formatRunIn(); } else if (bracketFormatMode == RUN_IN_MODE) { if (!lineCommentNoIndent) formatRunIn(); else isInLineBreak = true; } else if (bracketFormatMode == BREAK_MODE) { if (formattedLine.length() > 0 && formattedLine[0] == '{') isInLineBreak = true; } else { if (currentLineBeginsWithBracket) isInLineBreak = true; } } // ASBeautifier needs to know the following statements if (shouldBreakElseIfs && followingHeader == &AS_ELSE) elseHeaderFollowsComments = true; if (followingHeader == &AS_CASE || followingHeader == &AS_DEFAULT) caseHeaderFollowsComments = true; // appendSequence will write the previous line appendSequence(AS_OPEN_LINE_COMMENT); goForward(1); // must be done AFTER appendSequence // Break before the comment if a header follows the line comment. // But do not break if previous line is empty, a comment, or a '{'. if (shouldBreakBlocks && followingHeader != NULL && !isImmediatelyPostEmptyLine && previousCommandChar != '{') { if (isClosingHeader(followingHeader)) { if (!shouldBreakClosingHeaderBlocks) isPrependPostBlockEmptyLineRequested = false; } // if an opening header, break before the comment else isPrependPostBlockEmptyLineRequested = true; } if (previousCommandChar == '}') currentHeader = NULL; // if tabbed input don't convert the immediately following tabs to spaces if (getIndentString() == "\t" && lineCommentNoIndent) { while (charNum + 1 < (int) currentLine.length() && currentLine[charNum + 1] == '\t') { currentChar = currentLine[++charNum]; appendCurrentChar(); } } // explicitly break a line when a line comment's end is found. if (charNum + 1 == (int) currentLine.length()) { isInLineBreak = true; isInLineComment = false; isImmediatelyPostLineComment = true; currentChar = 0; //make sure it is a neutral char. } } /** * format quote body * the calling function should have a continue statement after calling this method */ void ASFormatter::formatQuoteBody() { assert(isInQuote); if (isSpecialChar) { isSpecialChar = false; } else if (currentChar == '\\' && !isInVerbatimQuote) { if (peekNextChar() == ' ') // is this '\' at end of line haveLineContinuationChar = true; else isSpecialChar = true; } else if (isInVerbatimQuote && currentChar == '"') { if (isCStyle()) { string delim = ')' + verbatimDelimiter; int delimStart = charNum - delim.length(); if (delimStart > 0 && currentLine.substr(delimStart, delim.length()) == delim) { isInQuote = false; isInVerbatimQuote = false; } } else if (isSharpStyle()) { if (peekNextChar() == '"') // check consecutive quotes { appendSequence("\"\""); goForward(1); return; } else { isInQuote = false; isInVerbatimQuote = false; } } } else if (quoteChar == currentChar) { isInQuote = false; } appendCurrentChar(); // append the text to the ending quoteChar or an escape sequence // tabs in quotes are NOT changed by convert-tabs if (isInQuote && currentChar != '\\') { while (charNum + 1 < (int) currentLine.length() && currentLine[charNum + 1] != quoteChar && currentLine[charNum + 1] != '\\') { currentChar = currentLine[++charNum]; appendCurrentChar(); } } } /** * format a quote opener * the quote opener will be appended to the current formattedLine or a new formattedLine as necessary * the calling function should have a continue statement after calling this method */ void ASFormatter::formatQuoteOpener() { assert(currentChar == '"' || currentChar == '\''); isInQuote = true; quoteChar = currentChar; if (isCStyle() && previousChar == 'R') { int parenPos = currentLine.find('(', charNum); if (parenPos != -1) { isInVerbatimQuote = true; verbatimDelimiter = currentLine.substr(charNum + 1, parenPos - charNum - 1); } } else if (isSharpStyle() && previousChar == '@') isInVerbatimQuote = true; // a quote following a bracket is an array if (previousCommandChar == '{' && !isImmediatelyPostComment && !isImmediatelyPostLineComment && isNonInStatementArray && !isBracketType(bracketTypeStack->back(), SINGLE_LINE_TYPE) && !isWhiteSpace(peekNextChar())) { if (bracketFormatMode == NONE_MODE) { if (currentLineBeginsWithBracket) formatRunIn(); } else if (bracketFormatMode == RUN_IN_MODE) { formatRunIn(); } else if (bracketFormatMode == BREAK_MODE) { if (formattedLine.length() > 0 && formattedLine[0] == '{') isInLineBreak = true; } else { if (currentLineBeginsWithBracket) isInLineBreak = true; } } previousCommandChar = ' '; appendCurrentChar(); } /** * get the next line comment adjustment that results from breaking a closing bracket. * the bracket must be on the same line as the closing header. * i.e "} else" changed to "} else". */ int ASFormatter::getNextLineCommentAdjustment() { assert(foundClosingHeader && previousNonWSChar == '}'); if (charNum < 1) // "else" is in column 1 return 0; size_t lastBracket = currentLine.rfind('}', charNum - 1); if (lastBracket != string::npos) return (lastBracket - charNum); // return a negative number return 0; } // for console build only LineEndFormat ASFormatter::getLineEndFormat() const { return lineEnd; } /** * get the current line comment adjustment that results from attaching * a closing header to a closing bracket. * the bracket must be on the line previous to the closing header. * the adjustment is 2 chars, one for the bracket and one for the space. * i.e "} else" changed to "} else". */ int ASFormatter::getCurrentLineCommentAdjustment() { assert(foundClosingHeader && previousNonWSChar == '}'); if (charNum < 1) return 2; size_t lastBracket = currentLine.rfind('}', charNum - 1); if (lastBracket == string::npos) return 2; return 0; } /** * get the previous word on a line * the argument 'currPos' must point to the current position. * * @return is the previous word or an empty string if none found. */ string ASFormatter::getPreviousWord(const string &line, int currPos) const { // get the last legal word (may be a number) if (currPos == 0) return string(); size_t end = line.find_last_not_of(" \t", currPos - 1); if (end == string::npos || !isLegalNameChar(line[end])) return string(); int start; // start of the previous word for (start = end; start > -1; start--) { if (!isLegalNameChar(line[start]) || line[start] == '.') break; } start++; return (line.substr(start, end - start + 1)); } /** * check if a line break is needed when a closing bracket * is followed by a closing header. * the break depends on the bracketFormatMode and other factors. */ void ASFormatter::isLineBreakBeforeClosingHeader() { assert(foundClosingHeader && previousNonWSChar == '}'); if (bracketFormatMode == BREAK_MODE || bracketFormatMode == RUN_IN_MODE || attachClosingBracketMode) { isInLineBreak = true; } else if (bracketFormatMode == NONE_MODE) { if (shouldBreakClosingHeaderBrackets || getBracketIndent() || getBlockIndent()) { isInLineBreak = true; } else { appendSpacePad(); // is closing bracket broken? size_t i = currentLine.find_first_not_of(" \t"); if (i != string::npos && currentLine[i] == '}') isInLineBreak = false; if (shouldBreakBlocks) isAppendPostBlockEmptyLineRequested = false; } } // bracketFormatMode == ATTACH_MODE, LINUX_MODE, STROUSTRUP_MODE else { if (shouldBreakClosingHeaderBrackets || getBracketIndent() || getBlockIndent()) { isInLineBreak = true; } else { // if a blank line does not precede this // or last line is not a one line block, attach header bool previousLineIsEmpty = isEmptyLine(formattedLine); int previousLineIsOneLineBlock = 0; size_t firstBracket = findNextChar(formattedLine, '{'); if (firstBracket != string::npos) previousLineIsOneLineBlock = isOneLineBlockReached(formattedLine, firstBracket); if (!previousLineIsEmpty && previousLineIsOneLineBlock == 0) { isInLineBreak = false; appendSpacePad(); spacePadNum = 0; // don't count as comment padding } if (shouldBreakBlocks) isAppendPostBlockEmptyLineRequested = false; } } } /** * Add brackets to a single line statement following a header. * Brackets are not added if the proper conditions are not met. * Brackets are added to the currentLine. */ bool ASFormatter::addBracketsToStatement() { assert(isImmediatelyPostHeader); if (currentHeader != &AS_IF && currentHeader != &AS_ELSE && currentHeader != &AS_FOR && currentHeader != &AS_WHILE && currentHeader != &AS_DO && currentHeader != &AS_FOREACH && currentHeader != &AS_QFOREACH && currentHeader != &AS_QFOREVER && currentHeader != &AS_FOREVER) return false; if (currentHeader == &AS_WHILE && foundClosingHeader) // do-while return false; // do not bracket an empty statement if (currentChar == ';') return false; // do not add if a header follows if (isCharPotentialHeader(currentLine, charNum)) if (findHeader(headers) != NULL) return false; // find the next semi-colon size_t nextSemiColon = charNum; if (currentChar != ';') nextSemiColon = findNextChar(currentLine, ';', charNum + 1); if (nextSemiColon == string::npos) return false; // add closing bracket before changing the line length if (nextSemiColon == currentLine.length() - 1) currentLine.append(" }"); else currentLine.insert(nextSemiColon + 1, " }"); // add opening bracket currentLine.insert(charNum, "{ "); assert(computeChecksumIn("{}")); currentChar = '{'; // remove extra spaces if (!shouldAddOneLineBrackets) { size_t lastText = formattedLine.find_last_not_of(" \t"); if ((formattedLine.length() - 1) - lastText > 1) formattedLine.erase(lastText + 1); } return true; } /** * Remove brackets from a single line statement following a header. * Brackets are not removed if the proper conditions are not met. * The first bracket is replaced by a space. */ bool ASFormatter::removeBracketsFromStatement() { assert(isImmediatelyPostHeader); assert(currentChar == '{'); if (currentHeader != &AS_IF && currentHeader != &AS_ELSE && currentHeader != &AS_FOR && currentHeader != &AS_WHILE && currentHeader != &AS_FOREACH) return false; if (currentHeader == &AS_WHILE && foundClosingHeader) // do-while return false; bool isFirstLine = true; bool needReset = false; string nextLine_; // leave nextLine_ empty if end of line comment follows if (!isBeforeAnyLineEndComment(charNum) || currentLineBeginsWithBracket) nextLine_ = currentLine.substr(charNum + 1); size_t nextChar = 0; // find the first non-blank text while (sourceIterator->hasMoreLines() || isFirstLine) { if (isFirstLine) isFirstLine = false; else { nextLine_ = sourceIterator->peekNextLine(); nextChar = 0; needReset = true; } nextChar = nextLine_.find_first_not_of(" \t", nextChar); if (nextChar != string::npos) break; } // don't remove if comments or a header follow the bracket if ((nextLine_.compare(nextChar, 2, "/*") == 0) || (nextLine_.compare(nextChar, 2, "//") == 0) || (isCharPotentialHeader(nextLine_, nextChar) && ASBeautifier::findHeader(nextLine_, nextChar, headers) != NULL)) { if (needReset) sourceIterator->peekReset(); return false; } // find the next semi-colon size_t nextSemiColon = nextChar; if (nextLine_[nextChar] != ';') nextSemiColon = findNextChar(nextLine_, ';', nextChar + 1); if (nextSemiColon == string::npos) { if (needReset) sourceIterator->peekReset(); return false; } // find the closing bracket isFirstLine = true; nextChar = nextSemiColon + 1; while (sourceIterator->hasMoreLines() || isFirstLine) { if (isFirstLine) isFirstLine = false; else { nextLine_ = sourceIterator->peekNextLine(); nextChar = 0; needReset = true; } nextChar = nextLine_.find_first_not_of(" \t", nextChar); if (nextChar != string::npos) break; } if (nextLine_.length() == 0 || nextLine_[nextChar] != '}') { if (needReset) sourceIterator->peekReset(); return false; } // remove opening bracket currentLine[charNum] = currentChar = ' '; assert(adjustChecksumIn(-'{')); if (needReset) sourceIterator->peekReset(); return true; } /** * Find the next character that is not in quotes or a comment. * * @param line the line to be searched. * @param searchChar the char to find. * @param searchStart the start position on the line (default is 0). * @return the position on the line or string::npos if not found. */ size_t ASFormatter::findNextChar(string &line, char searchChar, int searchStart /*0*/) { // find the next searchChar size_t i; for (i = searchStart; i < line.length(); i++) { if (line.compare(i, 2, "//") == 0) return string::npos; if (line.compare(i, 2, "/*") == 0) { size_t endComment = line.find("*/", i + 2); if (endComment == string::npos) return string::npos; i = endComment + 2; if (i >= line.length()) return string::npos; } if (line[i] == '\'' || line[i] == '\"') { char quote = line[i]; while (i < line.length()) { size_t endQuote = line.find(quote, i + 1); if (endQuote == string::npos) return string::npos; i = endQuote; if (line[endQuote - 1] != '\\') // check for '\"' break; if (line[endQuote - 2] == '\\') // check for '\\' break; } } if (line[i] == searchChar) break; // for now don't process C# 'delegate' brackets // do this last in case the search char is a '{' if (line[i] == '{') return string::npos; } if (i >= line.length()) // didn't find searchChar return string::npos; return i; } /** * Look ahead in the file to see if a struct has access modifiers. * * @param firstLine a reference to the line to indent. * @param index the current line index. * @return true if the struct has access modifiers. */ bool ASFormatter::isStructAccessModified(string &firstLine, size_t index) const { assert(firstLine[index] == '{'); assert(isCStyle()); bool isFirstLine = true; bool needReset = false; size_t bracketCount = 1; string nextLine_ = firstLine.substr(index + 1); // find the first non-blank text, bypassing all comments and quotes. bool isInComment_ = false; bool isInQuote_ = false; char quoteChar_ = ' '; while (sourceIterator->hasMoreLines() || isFirstLine) { if (isFirstLine) isFirstLine = false; else { nextLine_ = sourceIterator->peekNextLine(); needReset = true; } // parse the line for (size_t i = 0; i < nextLine_.length(); i++) { if (isWhiteSpace(nextLine_[i])) continue; if (nextLine_.compare(i, 2, "/*") == 0) isInComment_ = true; if (isInComment_) { if (nextLine_.compare(i, 2, "*/") == 0) { isInComment_ = false; ++i; } continue; } if (nextLine_[i] == '\\') { ++i; continue; } if (isInQuote_) { if (nextLine_[i] == quoteChar_) isInQuote_ = false; continue; } if (nextLine_[i] == '"' || nextLine_[i] == '\'') { isInQuote_ = true; quoteChar_ = nextLine_[i]; continue; } if (nextLine_.compare(i, 2, "//") == 0) { i = nextLine_.length(); continue; } // handle brackets if (nextLine_[i] == '{') ++bracketCount; if (nextLine_[i] == '}') --bracketCount; if (bracketCount == 0) { if (needReset) sourceIterator->peekReset(); return false; } // check for access modifiers if (isCharPotentialHeader(nextLine_, i)) { if (findKeyword(nextLine_, i, AS_PUBLIC) || findKeyword(nextLine_, i, AS_PRIVATE) || findKeyword(nextLine_, i, AS_PROTECTED)) { if (needReset) sourceIterator->peekReset(); return true; } string name = getCurrentWord(nextLine_, i); i += name.length() - 1; } } // end of for loop } // end of while loop if (needReset) sourceIterator->peekReset(); return false; } /** * Look ahead in the file to see if a preprocessor block is indentable. * * @param firstLine a reference to the line to indent. * @param index the current line index. * @return true if the block is indentable. */ bool ASFormatter::isIndentablePreprocessorBlock(string &firstLine, size_t index) { assert(firstLine[index] == '#'); bool isFirstLine = true; bool needReset = false; bool isInIndentableBlock = false; bool blockContainsBrackets = false; bool blockContainsDefineContinuation = false; bool isInClassConstructor = false; int numBlockIndents = 0; int lineParenCount = 0; string nextLine_ = firstLine.substr(index); // find end of the block, bypassing all comments and quotes. bool isInComment_ = false; bool isInQuote_ = false; char quoteChar_ = ' '; while (sourceIterator->hasMoreLines() || isFirstLine) { if (isFirstLine) isFirstLine = false; else { nextLine_ = sourceIterator->peekNextLine(); needReset = true; } // parse the line for (size_t i = 0; i < nextLine_.length(); i++) { if (isWhiteSpace(nextLine_[i])) continue; if (nextLine_.compare(i, 2, "/*") == 0) isInComment_ = true; if (isInComment_) { if (nextLine_.compare(i, 2, "*/") == 0) { isInComment_ = false; ++i; } continue; } if (nextLine_[i] == '\\') { ++i; continue; } if (isInQuote_) { if (nextLine_[i] == quoteChar_) isInQuote_ = false; continue; } if (nextLine_[i] == '"' || nextLine_[i] == '\'') { isInQuote_ = true; quoteChar_ = nextLine_[i]; continue; } if (nextLine_.compare(i, 2, "//") == 0) { i = nextLine_.length(); continue; } // handle preprocessor statement if (nextLine_[i] == '#') { string preproc = ASBeautifier::extractPreprocessorStatement(nextLine_); if (preproc.length() >= 2 && preproc.substr(0, 2) == "if") // #if, #ifdef, #ifndef { numBlockIndents += 1; isInIndentableBlock = true; // flag first preprocessor conditional for header include guard check if (!processedFirstConditional) { processedFirstConditional = true; isFirstPreprocConditional = true; } } else if (preproc == "endif") { if (numBlockIndents > 0) numBlockIndents -= 1; // must exit BOTH loops if (numBlockIndents == 0) goto EndOfWhileLoop; } else if (preproc == "define" && nextLine_[nextLine_.length() - 1] == '\\') { blockContainsDefineContinuation = true; } i = nextLine_.length(); continue; } // handle exceptions if (nextLine_[i] == '{' || nextLine_[i] == '}') blockContainsBrackets = true; else if (nextLine_[i] == '(') ++lineParenCount; else if (nextLine_[i] == ')') --lineParenCount; else if (nextLine_[i] == ':') { // check for '::' if (nextLine_.length() > i && nextLine_[i + 1] == ':') ++i; else isInClassConstructor = true; } // bypass unnecessary parsing - must exit BOTH loops if (blockContainsBrackets || isInClassConstructor || blockContainsDefineContinuation) goto EndOfWhileLoop; } // end of for loop, end of line if (lineParenCount != 0) break; } // end of while loop EndOfWhileLoop: preprocBlockEnd = sourceIterator->tellg(); if (preprocBlockEnd < 0) preprocBlockEnd = sourceIterator->getStreamLength(); if (blockContainsBrackets || isInClassConstructor || blockContainsDefineContinuation || lineParenCount != 0 || numBlockIndents != 0) isInIndentableBlock = false; // find next executable instruction // this WILL RESET the get pointer string nextText = peekNextText("", false, needReset); // bypass header include guards, with an exception for small test files if (isFirstPreprocConditional) { isFirstPreprocConditional = false; if (nextText.empty() && sourceIterator->getStreamLength() > 250) { isInIndentableBlock = false; preprocBlockEnd = 0; } } // this allows preprocessor blocks within this block to be indented if (!isInIndentableBlock) preprocBlockEnd = 0; // peekReset() is done by previous peekNextText() return isInIndentableBlock; } /** * Check to see if this is an EXEC SQL statement. * * @param line a reference to the line to indent. * @param index the current line index. * @return true if the statement is EXEC SQL. */ bool ASFormatter::isExecSQL(string &line, size_t index) const { if (line[index] != 'e' && line[index] != 'E') // quick check to reject most return false; string word; if (isCharPotentialHeader(line, index)) word = getCurrentWord(line, index); for (size_t i = 0; i < word.length(); i++) word[i] = (char) toupper(word[i]); if (word != "EXEC") return false; size_t index2 = index + word.length(); index2 = line.find_first_not_of(" \t", index2); if (index2 == string::npos) return false; word.erase(); if (isCharPotentialHeader(line, index2)) word = getCurrentWord(line, index2); for (size_t i = 0; i < word.length(); i++) word[i] = (char) toupper(word[i]); if (word != "SQL") return false; return true; } /** * The continuation lines must be adjusted so the leading spaces * is equivalent to the text on the opening line. * * Updates currentLine and charNum. */ void ASFormatter::trimContinuationLine() { size_t len = currentLine.length(); size_t tabSize = getTabLength(); charNum = 0; if (leadingSpaces > 0 && len > 0) { size_t i; size_t continuationIncrementIn = 0; for (i = 0; (i < len) && (i + continuationIncrementIn < leadingSpaces); i++) { if (!isWhiteSpace(currentLine[i])) // don't delete any text { if (i < continuationIncrementIn) leadingSpaces = i + tabIncrementIn; continuationIncrementIn = tabIncrementIn; break; } if (currentLine[i] == '\t') continuationIncrementIn += tabSize - 1 - ((continuationIncrementIn + i) % tabSize); } if ((int) continuationIncrementIn == tabIncrementIn) charNum = i; else { // build a new line with the equivalent leading chars string newLine; int leadingChars = 0; if ((int) leadingSpaces > tabIncrementIn) leadingChars = leadingSpaces - tabIncrementIn; newLine.append(leadingChars, ' '); newLine.append(currentLine, i, len - i); currentLine = newLine; charNum = leadingChars; if (currentLine.length() == 0) currentLine = string(" "); // a null is inserted if this is not done } if (i >= len) charNum = 0; } return; } /** * Determine if a header is a closing header * * @return true if the header is a closing header. */ bool ASFormatter::isClosingHeader(const string* header) const { return (header == &AS_ELSE || header == &AS_CATCH || header == &AS_FINALLY); } /** * Determine if a * following a closing paren is immediately. * after a cast. If so it is a deference and not a multiply. * e.g. "(int*) *ptr" is a deference. */ bool ASFormatter::isImmediatelyPostCast() const { assert(previousNonWSChar == ')' && currentChar == '*'); // find preceding closing paren on currentLine or readyFormattedLine string line; // currentLine or readyFormattedLine size_t paren = currentLine.rfind(")", charNum); if (paren != string::npos) line = currentLine; // if not on currentLine it must be on the previous line else { line = readyFormattedLine; paren = line.rfind(")"); if (paren == string::npos) return false; } if (paren == 0) return false; // find character preceding the closing paren size_t lastChar = line.find_last_not_of(" \t", paren - 1); if (lastChar == string::npos) return false; // check for pointer cast if (line[lastChar] == '*') return true; return false; } /** * Determine if a < is a template definition or instantiation. * Sets the class variables isInTemplate and templateDepth. */ void ASFormatter::checkIfTemplateOpener() { assert(!isInTemplate && currentChar == '<'); // find first char after the '<' operators size_t firstChar = currentLine.find_first_not_of("< \t", charNum); if (firstChar == string::npos || currentLine[firstChar] == '=') { // this is not a template -> leave... isInTemplate = false; return; } bool isFirstLine = true; bool needReset = false; int parenDepth_ = 0; int maxTemplateDepth = 0; templateDepth = 0; string nextLine_ = currentLine.substr(charNum); // find the angle brackets, bypassing all comments and quotes. bool isInComment_ = false; bool isInQuote_ = false; char quoteChar_ = ' '; while (sourceIterator->hasMoreLines() || isFirstLine) { if (isFirstLine) isFirstLine = false; else { nextLine_ = sourceIterator->peekNextLine(); needReset = true; } // parse the line for (size_t i = 0; i < nextLine_.length(); i++) { char currentChar_ = nextLine_[i]; if (isWhiteSpace(currentChar_)) continue; if (nextLine_.compare(i, 2, "/*") == 0) isInComment_ = true; if (isInComment_) { if (nextLine_.compare(i, 2, "*/") == 0) { isInComment_ = false; ++i; } continue; } if (currentChar_ == '\\') { ++i; continue; } if (isInQuote_) { if (currentChar_ == quoteChar_) isInQuote_ = false; continue; } if (currentChar_ == '"' || currentChar_ == '\'') { isInQuote_ = true; quoteChar_ = currentChar_; continue; } if (nextLine_.compare(i, 2, "//") == 0) { i = nextLine_.length(); continue; } // not in a comment or quote if (currentChar_ == '<') { ++templateDepth; ++maxTemplateDepth; continue; } else if (currentChar_ == '>') { --templateDepth; if (templateDepth == 0) { if (parenDepth_ == 0) { // this is a template! isInTemplate = true; templateDepth = maxTemplateDepth; } goto exitFromSearch; } continue; } else if (currentChar_ == '(' || currentChar_ == ')') { if (currentChar_ == '(') ++parenDepth_; else --parenDepth_; if (parenDepth_ >= 0) continue; // this is not a template -> leave... isInTemplate = false; goto exitFromSearch; } else if (nextLine_.compare(i, 2, AS_AND) == 0 || nextLine_.compare(i, 2, AS_OR) == 0) { // this is not a template -> leave... isInTemplate = false; goto exitFromSearch; } else if (currentChar_ == ',' // comma, e.g. A || currentChar_ == '&' // reference, e.g. A || currentChar_ == '*' // pointer, e.g. A || currentChar_ == '^' // C++/CLI managed pointer, e.g. A || currentChar_ == ':' // ::, e.g. std::string || currentChar_ == '=' // assign e.g. default parameter || currentChar_ == '[' // [] e.g. string[] || currentChar_ == ']' // [] e.g. string[] || currentChar_ == '(' // (...) e.g. function definition || currentChar_ == ')' // (...) e.g. function definition || (isJavaStyle() && currentChar_ == '?') // Java wildcard ) { continue; } else if (!isLegalNameChar(currentChar_)) { // this is not a template -> leave... isInTemplate = false; templateDepth = 0; goto exitFromSearch; } string name = getCurrentWord(nextLine_, i); i += name.length() - 1; } // end of for loop } // end of while loop // goto needed to exit from two loops exitFromSearch: if (needReset) sourceIterator->peekReset(); } void ASFormatter::updateFormattedLineSplitPoints(char appendedChar) { assert(maxCodeLength != string::npos); assert(formattedLine.length() > 0); if (!isOkToSplitFormattedLine()) return; char nextChar = peekNextChar(); // don't split before an end of line comment if (nextChar == '/') return; // don't split before or after a bracket if (appendedChar == '{' || appendedChar == '}' || previousNonWSChar == '{' || previousNonWSChar == '}' || nextChar == '{' || nextChar == '}' || currentChar == '{' || currentChar == '}') // currentChar tests for an appended bracket return; // don't split before or after a block paren if (appendedChar == '[' || appendedChar == ']' || previousNonWSChar == '[' || nextChar == '[' || nextChar == ']') return; if (isWhiteSpace(appendedChar)) { if (nextChar != ')' // space before a closing paren && nextChar != '(' // space before an opening paren && nextChar != '/' // space before a comment && nextChar != ':' // space before a colon && currentChar != ')' // appended space before and after a closing paren && currentChar != '(' // appended space before and after a opening paren && previousNonWSChar != '(' // decided at the '(' // don't break before a pointer or reference aligned to type && !(nextChar == '*' && !isCharPotentialOperator(previousNonWSChar) && pointerAlignment == PTR_ALIGN_TYPE) && !(nextChar == '&' && !isCharPotentialOperator(previousNonWSChar) && (referenceAlignment == REF_ALIGN_TYPE || (referenceAlignment == REF_SAME_AS_PTR && pointerAlignment == PTR_ALIGN_TYPE))) ) { if (formattedLine.length() - 1 <= maxCodeLength) maxWhiteSpace = formattedLine.length() - 1; else maxWhiteSpacePending = formattedLine.length() - 1; } } // unpadded closing parens may split after the paren (counts as whitespace) else if (appendedChar == ')') { if (nextChar != ')' && nextChar != ' ' && nextChar != ';' && nextChar != ',' && nextChar != '.' && !(nextChar == '-' && pointerSymbolFollows())) // check for -> { if (formattedLine.length() <= maxCodeLength) maxWhiteSpace = formattedLine.length(); else maxWhiteSpacePending = formattedLine.length(); } } // unpadded commas may split after the comma else if (appendedChar == ',') { if (formattedLine.length() <= maxCodeLength) maxComma = formattedLine.length(); else maxCommaPending = formattedLine.length(); } else if (appendedChar == '(') { if (nextChar != ')' && nextChar != '(' && nextChar != '"' && nextChar != '\'') { // if follows an operator break before size_t parenNum; if (isCharPotentialOperator(previousNonWSChar)) parenNum = formattedLine.length() - 1 ; else parenNum = formattedLine.length(); if (formattedLine.length() <= maxCodeLength) maxParen = parenNum; else maxParenPending = parenNum; } } else if (appendedChar == ';') { if (nextChar != ' ' && nextChar != '}' && nextChar != '/') // check for following comment { if (formattedLine.length() <= maxCodeLength) maxSemi = formattedLine.length(); else maxSemiPending = formattedLine.length(); } } } void ASFormatter::updateFormattedLineSplitPointsOperator(const string &sequence) { assert(maxCodeLength != string::npos); assert(formattedLine.length() > 0); if (!isOkToSplitFormattedLine()) return; char nextChar = peekNextChar(); // don't split before an end of line comment if (nextChar == '/') return; // check for logical conditional if (sequence == "||" || sequence == "&&" || sequence == "or" || sequence == "and") { if (shouldBreakLineAfterLogical) { if (formattedLine.length() <= maxCodeLength) maxAndOr = formattedLine.length(); else maxAndOrPending = formattedLine.length(); } else { // adjust for leading space in the sequence size_t sequenceLength = sequence.length(); if (formattedLine.length() > sequenceLength && isWhiteSpace(formattedLine[formattedLine.length() - sequenceLength - 1])) sequenceLength++; if (formattedLine.length() - sequenceLength <= maxCodeLength) maxAndOr = formattedLine.length() - sequenceLength; else maxAndOrPending = formattedLine.length() - sequenceLength; } } // comparison operators will split after the operator (counts as whitespace) else if (sequence == "==" || sequence == "!=" || sequence == ">=" || sequence == "<=") { if (formattedLine.length() <= maxCodeLength) maxWhiteSpace = formattedLine.length(); else maxWhiteSpacePending = formattedLine.length(); } // unpadded operators that will split BEFORE the operator (counts as whitespace) else if (sequence == "+" || sequence == "-" || sequence == "?") { if (charNum > 0 && (isLegalNameChar(currentLine[charNum - 1]) || currentLine[charNum - 1] == ')' || currentLine[charNum - 1] == ']' || currentLine[charNum - 1] == '\"')) { if (formattedLine.length() - 1 <= maxCodeLength) maxWhiteSpace = formattedLine.length() - 1; else maxWhiteSpacePending = formattedLine.length() - 1; } } // unpadded operators that will USUALLY split AFTER the operator (counts as whitespace) else if (sequence == "=" || sequence == ":") { // split BEFORE if the line is too long // do NOT use <= here, must allow for a bracket attached to an array size_t splitPoint = 0; if (formattedLine.length() < maxCodeLength) splitPoint = formattedLine.length(); else splitPoint = formattedLine.length() - 1; // padded or unpadded arrays if (previousNonWSChar == ']') { if (formattedLine.length() - 1 <= maxCodeLength) maxWhiteSpace = splitPoint; else maxWhiteSpacePending = splitPoint; } else if (charNum > 0 && (isLegalNameChar(currentLine[charNum - 1]) || currentLine[charNum - 1] == ')' || currentLine[charNum - 1] == ']')) { if (formattedLine.length() <= maxCodeLength) maxWhiteSpace = splitPoint; else maxWhiteSpacePending = splitPoint; } } } /** * Update the split point when a pointer or reference is formatted. * The argument is the maximum index of the last whitespace character. */ void ASFormatter::updateFormattedLineSplitPointsPointerOrReference(size_t index) { assert(maxCodeLength != string::npos); assert(formattedLine.length() > 0); assert(index < formattedLine.length()); if (!isOkToSplitFormattedLine()) return; if (index < maxWhiteSpace) // just in case return; if (index <= maxCodeLength) maxWhiteSpace = index; else maxWhiteSpacePending = index; } bool ASFormatter::isOkToSplitFormattedLine() { assert(maxCodeLength != string::npos); // Is it OK to split the line? if (shouldKeepLineUnbroken || isInLineComment || isInComment || isInQuote || isInCase || isInPreprocessor || isInExecSQL || isInAsm || isInAsmOneLine || isInAsmBlock || isInTemplate) return false; if (!isOkToBreakBlock(bracketTypeStack->back()) && currentChar != '{') { shouldKeepLineUnbroken = true; clearFormattedLineSplitPoints(); return false; } else if (isBracketType(bracketTypeStack->back(), ARRAY_TYPE)) { shouldKeepLineUnbroken = true; if (!isBracketType(bracketTypeStack->back(), ARRAY_NIS_TYPE)) clearFormattedLineSplitPoints(); return false; } return true; } /* This is called if the option maxCodeLength is set. */ void ASFormatter::testForTimeToSplitFormattedLine() { // DO NOT ASSERT maxCodeLength HERE // should the line be split if (formattedLine.length() > maxCodeLength && !isLineReady) { size_t splitPoint = findFormattedLineSplitPoint(); if (splitPoint > 0 && splitPoint < formattedLine.length()) { string splitLine = formattedLine.substr(splitPoint); formattedLine = formattedLine.substr(0, splitPoint); breakLine(true); formattedLine = splitLine; // if break-blocks is requested and this is a one-line statement string nextWord = ASBeautifier::getNextWord(currentLine, charNum - 1); if (isAppendPostBlockEmptyLineRequested && (nextWord == "break" || nextWord == "continue")) { isAppendPostBlockEmptyLineRequested = false; isPrependPostBlockEmptyLineRequested = true; } else isPrependPostBlockEmptyLineRequested = false; // adjust max split points maxAndOr = (maxAndOr > splitPoint) ? (maxAndOr - splitPoint) : 0; maxSemi = (maxSemi > splitPoint) ? (maxSemi - splitPoint) : 0; maxComma = (maxComma > splitPoint) ? (maxComma - splitPoint) : 0; maxParen = (maxParen > splitPoint) ? (maxParen - splitPoint) : 0; maxWhiteSpace = (maxWhiteSpace > splitPoint) ? (maxWhiteSpace - splitPoint) : 0; if (maxSemiPending > 0) { maxSemi = (maxSemiPending > splitPoint) ? (maxSemiPending - splitPoint) : 0; maxSemiPending = 0; } if (maxAndOrPending > 0) { maxAndOr = (maxAndOrPending > splitPoint) ? (maxAndOrPending - splitPoint) : 0; maxAndOrPending = 0; } if (maxCommaPending > 0) { maxComma = (maxCommaPending > splitPoint) ? (maxCommaPending - splitPoint) : 0; maxCommaPending = 0; } if (maxParenPending > 0) { maxParen = (maxParenPending > splitPoint) ? (maxParenPending - splitPoint) : 0; maxParenPending = 0; } if (maxWhiteSpacePending > 0) { maxWhiteSpace = (maxWhiteSpacePending > splitPoint) ? (maxWhiteSpacePending - splitPoint) : 0; maxWhiteSpacePending = 0; } // don't allow an empty formatted line size_t firstText = formattedLine.find_first_not_of(" \t"); if (firstText == string::npos && formattedLine.length() > 0) { formattedLine.erase(); clearFormattedLineSplitPoints(); if (isWhiteSpace(currentChar)) for (size_t i = charNum + 1; i < currentLine.length() && isWhiteSpace(currentLine[i]); i++) goForward(1); } else if (firstText > 0) { formattedLine.erase(0, firstText); maxSemi = (maxSemi > firstText) ? (maxSemi - firstText) : 0; maxAndOr = (maxAndOr > firstText) ? (maxAndOr - firstText) : 0; maxComma = (maxComma > firstText) ? (maxComma - firstText) : 0; maxParen = (maxParen > firstText) ? (maxParen - firstText) : 0; maxWhiteSpace = (maxWhiteSpace > firstText) ? (maxWhiteSpace - firstText) : 0; } // reset formattedLineCommentNum if (formattedLineCommentNum != string::npos) { formattedLineCommentNum = formattedLine.find("//"); if (formattedLineCommentNum == string::npos) formattedLineCommentNum = formattedLine.find("/*"); } } } } size_t ASFormatter::findFormattedLineSplitPoint() const { assert(maxCodeLength != string::npos); // determine where to split size_t minCodeLength = 10; size_t splitPoint = 0; splitPoint = maxSemi; if (maxAndOr >= minCodeLength) splitPoint = maxAndOr; if (splitPoint < minCodeLength) { splitPoint = maxWhiteSpace; // use maxParen instead if it is long enough if (maxParen > splitPoint || maxParen >= maxCodeLength * .7) splitPoint = maxParen; // use maxComma instead if it is long enough // increasing the multiplier causes more splits at whitespace if (maxComma > splitPoint || maxComma >= maxCodeLength * .3) splitPoint = maxComma; } // replace split point with first available break point if (splitPoint < minCodeLength) { splitPoint = string::npos; if (maxSemiPending > 0 && maxSemiPending < splitPoint) splitPoint = maxSemiPending; if (maxAndOrPending > 0 && maxAndOrPending < splitPoint) splitPoint = maxAndOrPending; if (maxCommaPending > 0 && maxCommaPending < splitPoint) splitPoint = maxCommaPending; if (maxParenPending > 0 && maxParenPending < splitPoint) splitPoint = maxParenPending; if (maxWhiteSpacePending > 0 && maxWhiteSpacePending < splitPoint) splitPoint = maxWhiteSpacePending; if (splitPoint == string::npos) splitPoint = 0; } // if remaining line after split is too long else if (formattedLine.length() - splitPoint > maxCodeLength) { // if end of the currentLine, find a new split point size_t newCharNum; if (isCharPotentialHeader(currentLine, charNum)) newCharNum = getCurrentWord(currentLine, charNum).length() + charNum; else newCharNum = charNum + 2; if (newCharNum + 1 > currentLine.length()) { // don't move splitPoint from before a conditional to after if (maxWhiteSpace > splitPoint + 3) splitPoint = maxWhiteSpace; if (maxParen > splitPoint) splitPoint = maxParen; } } return splitPoint; } void ASFormatter::clearFormattedLineSplitPoints() { maxSemi = 0; maxAndOr = 0; maxComma = 0; maxParen = 0; maxWhiteSpace = 0; maxSemiPending = 0; maxAndOrPending = 0; maxCommaPending = 0; maxParenPending = 0; maxWhiteSpacePending = 0; } /** * Check if a pointer symbol (->) follows on the currentLine. */ bool ASFormatter::pointerSymbolFollows() const { size_t peekNum = currentLine.find_first_not_of(" \t", charNum + 1); if (peekNum == string::npos || currentLine.compare(peekNum, 2, "->") != 0) return false; return true; } /** * Compute the input checksum. * This is called as an assert so it for is debug config only */ bool ASFormatter::computeChecksumIn(const string ¤tLine_) { for (size_t i = 0; i < currentLine_.length(); i++) if (!isWhiteSpace(currentLine_[i])) checksumIn += currentLine_[i]; return true; } /** * Adjust the input checksum for deleted chars. * This is called as an assert so it for is debug config only */ bool ASFormatter::adjustChecksumIn(int adjustment) { checksumIn += adjustment; return true; } /** * get the value of checksumIn for unit testing * * @return checksumIn. */ size_t ASFormatter::getChecksumIn() const { return checksumIn; } /** * Compute the output checksum. * This is called as an assert so it is for debug config only */ bool ASFormatter::computeChecksumOut(const string &beautifiedLine) { for (size_t i = 0; i < beautifiedLine.length(); i++) if (!isWhiteSpace(beautifiedLine[i])) checksumOut += beautifiedLine[i]; return true; } /** * Return isLineReady for the final check at end of file. */ bool ASFormatter::getIsLineReady() const { return isLineReady; } /** * get the value of checksumOut for unit testing * * @return checksumOut. */ size_t ASFormatter::getChecksumOut() const { return checksumOut; } /** * Return the difference in checksums. * If zero all is okay. */ int ASFormatter::getChecksumDiff() const { return checksumOut - checksumIn; } // for unit testing int ASFormatter::getFormatterFileType() const { return formatterFileType; } // Check if an operator follows the next word. // The next word must be a legal name. const string* ASFormatter::getFollowingOperator() const { // find next word size_t nextNum = currentLine.find_first_not_of(" \t", charNum + 1); if (nextNum == string::npos) return NULL; if (!isLegalNameChar(currentLine[nextNum])) return NULL; // bypass next word and following spaces while (nextNum < currentLine.length()) { if (!isLegalNameChar(currentLine[nextNum]) && !isWhiteSpace(currentLine[nextNum])) break; nextNum++; } if (nextNum >= currentLine.length() || !isCharPotentialOperator(currentLine[nextNum]) || currentLine[nextNum] == '/') // comment return NULL; const string* newOperator = ASBeautifier::findOperator(currentLine, nextNum, operators); return newOperator; } // Check following data to determine if the current character is an array operator. bool ASFormatter::isArrayOperator() const { assert(currentChar == '*' || currentChar == '&' || currentChar == '^'); assert(isBracketType(bracketTypeStack->back(), ARRAY_TYPE)); // find next word size_t nextNum = currentLine.find_first_not_of(" \t", charNum + 1); if (nextNum == string::npos) return false; if (!isLegalNameChar(currentLine[nextNum])) return false; // bypass next word and following spaces while (nextNum < currentLine.length()) { if (!isLegalNameChar(currentLine[nextNum]) && !isWhiteSpace(currentLine[nextNum])) break; nextNum++; } // check for characters that indicate an operator if (currentLine[nextNum] == ',' || currentLine[nextNum] == '}' || currentLine[nextNum] == ')' || currentLine[nextNum] == '(') return true; return false; } // Reset the flags that indicate various statement information. void ASFormatter::resetEndOfStatement() { foundQuestionMark = false; foundNamespaceHeader = false; foundClassHeader = false; foundStructHeader = false; foundInterfaceHeader = false; foundPreDefinitionHeader = false; foundPreCommandHeader = false; foundPreCommandMacro = false; foundCastOperator = false; isInPotentialCalculation = false; isSharpAccessor = false; isSharpDelegate = false; isInObjCMethodDefinition = false; isInObjCInterface = false; isInObjCSelector = false; isInEnum = false; isInExternC = false; elseHeaderFollowsComments = false; nonInStatementBracket = 0; while (!questionMarkStack->empty()) questionMarkStack->pop_back(); } // pad an Objective-C method colon void ASFormatter::padObjCMethodColon() { assert(currentChar == ':'); char nextChar = peekNextChar(); if (objCColonPadMode == COLON_PAD_NONE || objCColonPadMode == COLON_PAD_AFTER || nextChar == ')') { // remove spaces before for (int i = formattedLine.length() - 1; (i > -1) && isWhiteSpace(formattedLine[i]); i--) formattedLine.erase(i); } else { // pad space before for (int i = formattedLine.length() - 1; (i > 0) && isWhiteSpace(formattedLine[i]); i--) if (isWhiteSpace(formattedLine[i - 1])) formattedLine.erase(i); appendSpacePad(); } if (objCColonPadMode == COLON_PAD_NONE || objCColonPadMode == COLON_PAD_BEFORE || nextChar == ')') { // remove spaces after // do not need to bump i since a char is erased size_t i = charNum + 1; while ((i < currentLine.length()) && isWhiteSpace(currentLine[i])) currentLine.erase(i, 1); } else { // pad space after // do not need to bump i since a char is erased size_t i = charNum + 1; while ((i + 1 < currentLine.length()) && isWhiteSpace(currentLine[i])) currentLine.erase(i, 1); if (((int) currentLine.length() > charNum + 1) && !isWhiteSpace(currentLine[charNum + 1])) currentLine.insert(charNum + 1, " "); } } // Remove the leading '*' from a comment line and indent to the next tab. void ASFormatter::stripCommentPrefix() { int firstChar = formattedLine.find_first_not_of(" \t"); if (firstChar < 0) return; if (isInCommentStartLine) { // comment opener must begin the line if (formattedLine.compare(firstChar, 2, "/*") != 0) return; int commentOpener = firstChar; // ignore single line comments int commentEnd = formattedLine.find("*/", firstChar + 2); if (commentEnd != -1) return; // first char after the comment opener must be at least one indent int followingText = formattedLine.find_first_not_of(" \t", commentOpener + 2); if (followingText < 0) return; if (formattedLine[followingText] == '*' || formattedLine[followingText] == '!') followingText = formattedLine.find_first_not_of(" \t", followingText + 1); if (followingText < 0) return; if (formattedLine[followingText] == '*') return; int indentLen = getIndentLength(); int followingTextIndent = followingText - commentOpener; if (followingTextIndent < indentLen) { string stringToInsert(indentLen - followingTextIndent, ' '); formattedLine.insert(followingText, stringToInsert); } return; } // comment body including the closer else if (formattedLine[firstChar] == '*') { if (formattedLine.compare(firstChar, 2, "*/") == 0) { // line starts with an end comment formattedLine = "*/"; } else { // build a new line with one indent int secondChar = formattedLine.find_first_not_of(" \t", firstChar + 1); if (secondChar < 0) { adjustChecksumIn(-'*'); formattedLine.erase(); return; } if (formattedLine[secondChar] == '*') return; // replace the leading '*' int indentLen = getIndentLength(); adjustChecksumIn(-'*'); // second char must be at least one indent if (formattedLine.substr(0, secondChar).find('\t') != string::npos) { formattedLine.erase(firstChar, 1); } else { int spacesToInsert = 0; if (secondChar >= indentLen) spacesToInsert = secondChar; else spacesToInsert = indentLen; formattedLine = string(spacesToInsert, ' ') + formattedLine.substr(secondChar); } // remove a trailing '*' int lastChar = formattedLine.find_last_not_of(" \t"); if (lastChar > -1 && formattedLine[lastChar] == '*') { adjustChecksumIn(-'*'); formattedLine[lastChar] = ' '; } } } else { // first char not a '*' // first char must be at least one indent if (formattedLine.substr(0, firstChar).find('\t') == string::npos) { int indentLen = getIndentLength(); if (firstChar < indentLen) { string stringToInsert(indentLen, ' '); formattedLine = stringToInsert + formattedLine.substr(firstChar); } } } } } // end namespace astyle ================================================ FILE: 3rdpart/astyle/ASLocalizer.cpp ================================================ // // FILE ENCODING IS UTF-8 WITHOUT A BOM. // русский 中文(简体) 日本 한국의 // /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ASLocalizer.cpp * * Copyright (C) 2014 by Jim Pattee * * * This file is a part of Artistic Style - an indentation and * reformatting tool for C, C++, C# and Java source files. * * * Artistic Style is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Artistic Style is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Artistic Style. If not, see . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * To add a new language: * * Add a new translation class to ASLocalizer.h. * Add the Add the English-Translation pair to the constructor in ASLocalizer.cpp. * Update the WinLangCode array, if necessary. * Add the language code to the function setTranslationClass(). * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //---------------------------------------------------------------------------- // headers //---------------------------------------------------------------------------- #include "ASLocalizer.h" #ifdef _WIN32 #include #endif #ifdef __DMC__ #include // digital mars doesn't have these const size_t SUBLANG_CHINESE_MACAU = 5; const size_t LANG_HINDI = 57; #endif #ifdef __VMS #define __USE_STD_IOSTREAM 1 #include #else #include #endif #include #include #include #include #ifdef _MSC_VER #pragma warning(disable: 4996) // secure version deprecation warnings // #pragma warning(disable: 4267) // 64 bit signed/unsigned loss of data #endif #ifdef __BORLANDC__ #pragma warn -8104 // Local Static with constructor dangerous for multi-threaded apps #endif #ifdef __INTEL_COMPILER #pragma warning(disable: 383) // value copied to temporary, reference to temporary used #pragma warning(disable: 981) // operands are evaluated in unspecified order #endif namespace astyle { #ifndef ASTYLE_LIB //---------------------------------------------------------------------------- // ASLocalizer class methods. //---------------------------------------------------------------------------- ASLocalizer::ASLocalizer() // Set the locale information. { // set language default values to english (ascii) // this will be used if a locale or a language cannot be found m_localeName = "UNKNOWN"; m_langID = "en"; m_lcid = 0; m_subLangID.clear(); m_translation = NULL; // Not all compilers support the C++ function locale::global(locale("")); // For testing on Windows change the "Region and Language" settings or use AppLocale. // For testing on Linux change the LANG environment variable: LANG=fr_FR.UTF-8. // setlocale() will use the LANG environment variable on Linux. char* localeName = setlocale(LC_ALL, ""); if (localeName == NULL) // use the english (ascii) defaults { fprintf(stderr, "\n%s\n\n", "Cannot set native locale, reverting to English"); setTranslationClass(); return; } // set the class variables #ifdef _WIN32 size_t lcid = GetUserDefaultLCID(); setLanguageFromLCID(lcid); #else setLanguageFromName(localeName); #endif } ASLocalizer::~ASLocalizer() // Delete dynamically allocated memory. { delete m_translation; } #ifdef _WIN32 struct WinLangCode { size_t winLang; char canonicalLang[3]; }; static WinLangCode wlc[] = // primary language identifier http://msdn.microsoft.com/en-us/library/aa912554.aspx // sublanguage identifier http://msdn.microsoft.com/en-us/library/aa913256.aspx // language ID http://msdn.microsoft.com/en-us/library/ee797784%28v=cs.20%29.aspx { { LANG_CHINESE, "zh" }, { LANG_DUTCH, "nl" }, { LANG_ENGLISH, "en" }, { LANG_FINNISH, "fi" }, { LANG_FRENCH, "fr" }, { LANG_GERMAN, "de" }, { LANG_HINDI, "hi" }, { LANG_ITALIAN, "it" }, { LANG_JAPANESE, "ja" }, { LANG_KOREAN, "ko" }, { LANG_POLISH, "pl" }, { LANG_PORTUGUESE, "pt" }, { LANG_RUSSIAN, "ru" }, { LANG_SPANISH, "es" }, { LANG_SWEDISH, "sv" }, { LANG_UKRAINIAN, "uk" }, }; void ASLocalizer::setLanguageFromLCID(size_t lcid) // Windows get the language to use from the user locale. // NOTE: GetUserDefaultLocaleName() gets nearly the same name as Linux. // But it needs Windows Vista or higher. // Same with LCIDToLocaleName(). { m_lcid = lcid; m_langID = "en"; // default to english size_t lang = PRIMARYLANGID(LANGIDFROMLCID(m_lcid)); size_t sublang = SUBLANGID(LANGIDFROMLCID(m_lcid)); // find language in the wlc table size_t count = sizeof(wlc) / sizeof(wlc[0]); for (size_t i = 0; i < count; i++) { if (wlc[i].winLang == lang) { m_langID = wlc[i].canonicalLang; break; } } if (m_langID == "zh") { if (sublang == SUBLANG_CHINESE_SIMPLIFIED || sublang == SUBLANG_CHINESE_SINGAPORE) m_subLangID = "CHS"; else m_subLangID = "CHT"; // default } setTranslationClass(); } #endif // _win32 string ASLocalizer::getLanguageID() const // Returns the language ID in m_langID. { return m_langID; } const Translation* ASLocalizer::getTranslationClass() const // Returns the name of the translation class in m_translation. Used for testing. { assert(m_translation); return m_translation; } void ASLocalizer::setLanguageFromName(const char* langID) // Linux set the language to use from the langID. // // the language string has the following form // // lang[_LANG][.encoding][@modifier] // // (see environ(5) in the Open Unix specification) // // where lang is the primary language, LANG is a sublang/territory, // encoding is the charset to use and modifier "allows the user to select // a specific instance of localization data within a single category" // // for example, the following strings are valid: // fr // fr_FR // de_DE.iso88591 // de_DE@euro // de_DE.iso88591@euro { // the constants describing the format of lang_LANG locale string static const size_t LEN_LANG = 2; m_lcid = 0; string langStr = langID; m_langID = langStr.substr(0, LEN_LANG); // need the sublang for chinese if (m_langID == "zh" && langStr[LEN_LANG] == '_') { string subLang = langStr.substr(LEN_LANG + 1, LEN_LANG); if (subLang == "CN" || subLang == "SG") m_subLangID = "CHS"; else m_subLangID = "CHT"; // default } setTranslationClass(); } const char* ASLocalizer::settext(const char* textIn) const // Call the settext class and return the value. { assert(m_translation); const string stringIn = textIn; return m_translation->translate(stringIn).c_str(); } void ASLocalizer::setTranslationClass() // Return the required translation class. // Sets the class variable m_translation from the value of m_langID. // Get the language ID at http://msdn.microsoft.com/en-us/library/ee797784%28v=cs.20%29.aspx { assert(m_langID.length()); // delete previously set (--ascii option) if (m_translation) { delete m_translation; m_translation = NULL; } if (m_langID == "zh" && m_subLangID == "CHS") m_translation = new ChineseSimplified; else if (m_langID == "zh" && m_subLangID == "CHT") m_translation = new ChineseTraditional; else if (m_langID == "nl") m_translation = new Dutch; else if (m_langID == "en") m_translation = new English; else if (m_langID == "fi") m_translation = new Finnish; else if (m_langID == "fr") m_translation = new French; else if (m_langID == "de") m_translation = new German; else if (m_langID == "hi") m_translation = new Hindi; else if (m_langID == "it") m_translation = new Italian; else if (m_langID == "ja") m_translation = new Japanese; else if (m_langID == "ko") m_translation = new Korean; else if (m_langID == "pl") m_translation = new Polish; else if (m_langID == "pt") m_translation = new Portuguese; else if (m_langID == "ru") m_translation = new Russian; else if (m_langID == "es") m_translation = new Spanish; else if (m_langID == "sv") m_translation = new Swedish; else if (m_langID == "uk") m_translation = new Ukrainian; else // default m_translation = new English; } //---------------------------------------------------------------------------- // Translation base class methods. //---------------------------------------------------------------------------- void Translation::addPair(const string &english, const wstring &translated) // Add a string pair to the translation vector. { pair entry(english, translated); m_translation.push_back(entry); } string Translation::convertToMultiByte(const wstring &wideStr) const // Convert wchar_t to a multibyte string using the currently assigned locale. // Return an empty string if an error occurs. { static bool msgDisplayed = false; // get length of the output excluding the NULL and validate the parameters size_t mbLen = wcstombs(NULL, wideStr.c_str(), 0); if (mbLen == string::npos) { if (!msgDisplayed) { fprintf(stderr, "\n%s\n\n", "Cannot convert to multi-byte string, reverting to English"); msgDisplayed = true; } return ""; } // convert the characters char* mbStr = new(nothrow) char[mbLen + 1]; if (mbStr == NULL) { if (!msgDisplayed) { fprintf(stderr, "\n%s\n\n", "Bad memory alloc for multi-byte string, reverting to English"); msgDisplayed = true; } return ""; } wcstombs(mbStr, wideStr.c_str(), mbLen + 1); // return the string string mbTranslation = mbStr; delete [] mbStr; return mbTranslation; } size_t Translation::getTranslationVectorSize() const // Return the translation vector size. Used for testing. { return m_translation.size(); } bool Translation::getWideTranslation(const string &stringIn, wstring &wideOut) const // Get the wide translation string. Used for testing. { for (size_t i = 0; i < m_translation.size(); i++) { if (m_translation[i].first == stringIn) { wideOut = m_translation[i].second; return true; } } // not found wideOut = L""; return false; } string &Translation::translate(const string &stringIn) const // Translate a string. // Return a static string instead of a member variable so the method can have a "const" designation. // This allows "settext" to be called from a "const" method. { static string mbTranslation; mbTranslation.clear(); for (size_t i = 0; i < m_translation.size(); i++) { if (m_translation[i].first == stringIn) { mbTranslation = convertToMultiByte(m_translation[i].second); break; } } // not found, return english if (mbTranslation.empty()) mbTranslation = stringIn; return mbTranslation; } //---------------------------------------------------------------------------- // Translation class methods. // These classes have only a constructor which builds the language vector. //---------------------------------------------------------------------------- ChineseSimplified::ChineseSimplified() // 中文(简体) { addPair("Formatted %s\n", L"格式化 %s\n"); // should align with unchanged addPair("Unchanged %s\n", L"未改变 %s\n"); // should align with formatted addPair("Directory %s\n", L"目录 %s\n"); addPair("Exclude %s\n", L"排除 %s\n"); addPair("Exclude (unmatched) %s\n", L"排除(无匹配项) %s\n"); addPair(" %s formatted %s unchanged ", L" %s 格式化 %s 未改变 "); addPair(" seconds ", L" 秒 "); addPair("%d min %d sec ", L"%d 分 %d 秒 "); addPair("%s lines\n", L"%s 行\n"); addPair("Using default options file %s\n", L"使用默认配置文件 %s\n"); addPair("Opening HTML documentation %s\n", L"打开HTML文档 %s\n"); addPair("Invalid option file options:", L"无效的配置文件选项:"); addPair("Invalid command line options:", L"无效的命令行选项:"); addPair("For help on options type 'astyle -h'", L"输入 'astyle -h' 以获得有关命令行的帮助"); addPair("Cannot open options file", L"无法打开配置文件"); addPair("Cannot open directory", L"无法打开目录"); addPair("Cannot open HTML file %s\n", L"无法打开HTML文件 %s\n"); addPair("Command execute failure", L"执行命令失败"); addPair("Command is not installed", L"未安装命令"); addPair("Missing filename in %s\n", L"在%s缺少文件名\n"); addPair("Recursive option with no wildcard", L"递归选项没有通配符"); addPair("Did you intend quote the filename", L"你打算引用文件名"); addPair("No file to process %s\n", L"没有文件可处理 %s\n"); addPair("Did you intend to use --recursive", L"你打算使用 --recursive"); addPair("Cannot process UTF-32 encoding", L"不能处理UTF-32编码"); addPair("\nArtistic Style has terminated", L"\nArtistic Style 已经终止运行"); } ChineseTraditional::ChineseTraditional() // 中文(繁體) { addPair("Formatted %s\n", L"格式化 %s\n"); // should align with unchanged addPair("Unchanged %s\n", L"未改變 %s\n"); // should align with formatted addPair("Directory %s\n", L"目錄 %s\n"); addPair("Exclude %s\n", L"排除 %s\n"); addPair("Exclude (unmatched) %s\n", L"排除(無匹配項) %s\n"); addPair(" %s formatted %s unchanged ", L" %s 格式化 %s 未改變 "); addPair(" seconds ", L" 秒 "); addPair("%d min %d sec ", L"%d 分 %d 秒 "); addPair("%s lines\n", L"%s 行\n"); addPair("Using default options file %s\n", L"使用默認配置文件 %s\n"); addPair("Opening HTML documentation %s\n", L"打開HTML文檔 %s\n"); addPair("Invalid option file options:", L"無效的配置文件選項:"); addPair("Invalid command line options:", L"無效的命令行選項:"); addPair("For help on options type 'astyle -h'", L"輸入'astyle -h'以獲得有關命令行的幫助:"); addPair("Cannot open options file", L"無法打開配置文件"); addPair("Cannot open directory", L"無法打開目錄"); addPair("Cannot open HTML file %s\n", L"無法打開HTML文件 %s\n"); addPair("Command execute failure", L"執行命令失敗"); addPair("Command is not installed", L"未安裝命令"); addPair("Missing filename in %s\n", L"在%s缺少文件名\n"); addPair("Recursive option with no wildcard", L"遞歸選項沒有通配符"); addPair("Did you intend quote the filename", L"你打算引用文件名"); addPair("No file to process %s\n", L"沒有文件可處理 %s\n"); addPair("Did you intend to use --recursive", L"你打算使用 --recursive"); addPair("Cannot process UTF-32 encoding", L"不能處理UTF-32編碼"); addPair("\nArtistic Style has terminated", L"\nArtistic Style 已經終止運行"); } Dutch::Dutch() // Nederlandse // build the translation vector in the Translation base class { addPair("Formatted %s\n", L"Geformatteerd %s\n"); // should align with unchanged addPair("Unchanged %s\n", L"Onveranderd %s\n"); // should align with formatted addPair("Directory %s\n", L"Directory %s\n"); addPair("Exclude %s\n", L"Uitsluiten %s\n"); addPair("Exclude (unmatched) %s\n", L"Uitgesloten (ongeëvenaarde) %s\n"); addPair(" %s formatted %s unchanged ", L" %s geformatteerd %s onveranderd "); addPair(" seconds ", L" seconden "); addPair("%d min %d sec ", L"%d min %d sec "); addPair("%s lines\n", L"%s lijnen\n"); addPair("Using default options file %s\n", L"Met behulp van standaard opties bestand %s\n"); addPair("Opening HTML documentation %s\n", L"Het openen van HTML-documentatie %s\n"); addPair("Invalid option file options:", L"Ongeldige optie file opties:"); addPair("Invalid command line options:", L"Ongeldige command line opties:"); addPair("For help on options type 'astyle -h'", L"Voor hulp bij 'astyle-h' opties het type"); addPair("Cannot open options file", L"Kan niet worden geopend options bestand"); addPair("Cannot open directory", L"Kan niet open directory"); addPair("Cannot open HTML file %s\n", L"Kan HTML-bestand niet openen %s\n"); addPair("Command execute failure", L"Voeren commando falen"); addPair("Command is not installed", L"Command is niet geïnstalleerd"); addPair("Missing filename in %s\n", L"Ontbrekende bestandsnaam in %s\n"); addPair("Recursive option with no wildcard", L"Recursieve optie met geen wildcard"); addPair("Did you intend quote the filename", L"Heeft u van plan citaat van de bestandsnaam"); addPair("No file to process %s\n", L"Geen bestand te verwerken %s\n"); addPair("Did you intend to use --recursive", L"Hebt u van plan bent te gebruiken --recursive"); addPair("Cannot process UTF-32 encoding", L"Kan niet verwerken UTF-32 codering"); addPair("\nArtistic Style has terminated", L"\nArtistic Style heeft beëindigd"); } English::English() // this class is NOT translated {} Finnish::Finnish() // Suomeksi // build the translation vector in the Translation base class { addPair("Formatted %s\n", L"Muotoiltu %s\n"); // should align with unchanged addPair("Unchanged %s\n", L"Ennallaan %s\n"); // should align with formatted addPair("Directory %s\n", L"Directory %s\n"); addPair("Exclude %s\n", L"Sulkea %s\n"); addPair("Exclude (unmatched) %s\n", L"Sulkea (verraton) %s\n"); addPair(" %s formatted %s unchanged ", L" %s muotoiltu %s ennallaan "); addPair(" seconds ", L" sekuntia "); addPair("%d min %d sec ", L"%d min %d sek "); addPair("%s lines\n", L"%s linjat\n"); addPair("Using default options file %s\n", L"Käyttämällä oletusasetuksia tiedosto %s\n"); addPair("Opening HTML documentation %s\n", L"Avaaminen HTML asiakirjat %s\n"); addPair("Invalid option file options:", L"Virheellinen vaihtoehto tiedosto vaihtoehtoja:"); addPair("Invalid command line options:", L"Virheellinen komentorivin:"); addPair("For help on options type 'astyle -h'", L"Apua vaihtoehdoista tyyppi 'astyle -h'"); addPair("Cannot open options file", L"Ei voi avata vaihtoehtoja tiedostoa"); addPair("Cannot open directory", L"Ei Open Directory"); addPair("Cannot open HTML file %s\n", L"Ei voi avata HTML-tiedoston %s\n"); addPair("Command execute failure", L"Suorita komento vika"); addPair("Command is not installed", L"Komento ei ole asennettu"); addPair("Missing filename in %s\n", L"Puuttuvat tiedostonimi %s\n"); addPair("Recursive option with no wildcard", L"Rekursiivinen vaihtoehto ilman wildcard"); addPair("Did you intend quote the filename", L"Oletko aio lainata tiedostonimi"); addPair("No file to process %s\n", L"Ei tiedostoa käsitellä %s\n"); addPair("Did you intend to use --recursive", L"Oliko aiot käyttää --recursive"); addPair("Cannot process UTF-32 encoding", L"Ei voi käsitellä UTF-32 koodausta"); addPair("\nArtistic Style has terminated", L"\nArtistic Style on päättynyt"); } French::French() // Française // build the translation vector in the Translation base class { addPair("Formatted %s\n", L"Formaté %s\n"); // should align with unchanged addPair("Unchanged %s\n", L"Inchangée %s\n"); // should align with formatted addPair("Directory %s\n", L"Répertoire %s\n"); addPair("Exclude %s\n", L"Exclure %s\n"); addPair("Exclude (unmatched) %s\n", L"Exclure (non appariés) %s\n"); addPair(" %s formatted %s unchanged ", L" %s formaté %s inchangée "); addPair(" seconds ", L" seconde "); addPair("%d min %d sec ", L"%d min %d sec "); addPair("%s lines\n", L"%s lignes\n"); addPair("Using default options file %s\n", L"Options par défaut utilisation du fichier %s\n"); addPair("Opening HTML documentation %s\n", L"Ouverture documentation HTML %s\n"); addPair("Invalid option file options:", L"Options Blancs option du fichier:"); addPair("Invalid command line options:", L"Blancs options ligne de commande:"); addPair("For help on options type 'astyle -h'", L"Pour de l'aide sur les options tapez 'astyle -h'"); addPair("Cannot open options file", L"Impossible d'ouvrir le fichier d'options"); addPair("Cannot open directory", L"Impossible d'ouvrir le répertoire"); addPair("Cannot open HTML file %s\n", L"Impossible d'ouvrir le fichier HTML %s\n"); addPair("Command execute failure", L"Exécuter échec de la commande"); addPair("Command is not installed", L"Commande n'est pas installé"); addPair("Missing filename in %s\n", L"Nom de fichier manquant dans %s\n"); addPair("Recursive option with no wildcard", L"Option récursive sans joker"); addPair("Did you intend quote the filename", L"Avez-vous l'intention de citer le nom de fichier"); addPair("No file to process %s\n", L"Aucun fichier à traiter %s\n"); addPair("Did you intend to use --recursive", L"Avez-vous l'intention d'utiliser --recursive"); addPair("Cannot process UTF-32 encoding", L"Impossible de traiter codage UTF-32"); addPair("\nArtistic Style has terminated", L"\nArtistic Style a mis fin"); } German::German() // Deutsch // build the translation vector in the Translation base class { addPair("Formatted %s\n", L"Formatiert %s\n"); // should align with unchanged addPair("Unchanged %s\n", L"Unverändert %s\n"); // should align with formatted addPair("Directory %s\n", L"Verzeichnis %s\n"); addPair("Exclude %s\n", L"Ausschließen %s\n"); addPair("Exclude (unmatched) %s\n", L"Ausschließen (unerreichte) %s\n"); addPair(" %s formatted %s unchanged ", L" %s formatiert %s unverändert "); addPair(" seconds ", L" sekunden "); addPair("%d min %d sec ", L"%d min %d sek "); addPair("%s lines\n", L"%s linien\n"); addPair("Using default options file %s\n", L"Mit Standard-Optionen Dat %s\n"); addPair("Opening HTML documentation %s\n", L"Öffnen HTML-Dokumentation %s\n"); addPair("Invalid option file options:", L"Ungültige Option Datei-Optionen:"); addPair("Invalid command line options:", L"Ungültige Kommandozeilen-Optionen:"); addPair("For help on options type 'astyle -h'", L"Für Hilfe zu den Optionen geben Sie 'astyle -h'"); addPair("Cannot open options file", L"Kann nicht geöffnet werden Optionsdatei"); addPair("Cannot open directory", L"Kann nicht geöffnet werden Verzeichnis"); addPair("Cannot open HTML file %s\n", L"Kann nicht öffnen HTML-Datei %s\n"); addPair("Command execute failure", L"Execute Befehl Scheitern"); addPair("Command is not installed", L"Befehl ist nicht installiert"); addPair("Missing filename in %s\n", L"Missing in %s Dateiname\n"); addPair("Recursive option with no wildcard", L"Rekursive Option ohne Wildcard"); addPair("Did you intend quote the filename", L"Haben Sie die Absicht Inhalte der Dateiname"); addPair("No file to process %s\n", L"Keine Datei zu verarbeiten %s\n"); addPair("Did you intend to use --recursive", L"Haben Sie verwenden möchten --recursive"); addPair("Cannot process UTF-32 encoding", L"Nicht verarbeiten kann UTF-32 Codierung"); addPair("\nArtistic Style has terminated", L"\nArtistic Style ist beendet"); } Hindi::Hindi() // हिन्दी // build the translation vector in the Translation base class { // NOTE: Scintilla based editors (CodeBlocks) cannot always edit Hindi. // Use Visual Studio instead. addPair("Formatted %s\n", L"स्वरूपित किया %s\n"); // should align with unchanged addPair("Unchanged %s\n", L"अपरिवर्तित %s\n"); // should align with formatted addPair("Directory %s\n", L"निर्देशिका %s\n"); addPair("Exclude %s\n", L"निकालना %s\n"); addPair("Exclude (unmatched) %s\n", L"अपवर्जित (बेजोड़) %s\n"); addPair(" %s formatted %s unchanged ", L" %s स्वरूपित किया %s अपरिवर्तित "); addPair(" seconds ", L" सेकंड "); addPair("%d min %d sec ", L"%d मिनट %d सेकंड "); addPair("%s lines\n", L"%s लाइनों\n"); addPair("Using default options file %s\n", L"डिफ़ॉल्ट विकल्प का उपयोग कर फ़ाइल %s\n"); addPair("Opening HTML documentation %s\n", L"एचटीएमएल प्रलेखन खोलना %s\n"); addPair("Invalid option file options:", L"अवैध विकल्प फ़ाइल विकल्प हैं:"); addPair("Invalid command line options:", L"कमांड लाइन विकल्प अवैध:"); addPair("For help on options type 'astyle -h'", L"विकल्पों पर मदद के लिए प्रकार 'astyle -h'"); addPair("Cannot open options file", L"विकल्प फ़ाइल नहीं खोल सकता है"); addPair("Cannot open directory", L"निर्देशिका नहीं खोल सकता"); addPair("Cannot open HTML file %s\n", L"HTML फ़ाइल नहीं खोल सकता %s\n"); addPair("Command execute failure", L"आदेश विफलता निष्पादित"); addPair("Command is not installed", L"कमान स्थापित नहीं है"); addPair("Missing filename in %s\n", L"लापता में फ़ाइलनाम %s\n"); addPair("Recursive option with no wildcard", L"कोई वाइल्डकार्ड साथ पुनरावर्ती विकल्प"); addPair("Did you intend quote the filename", L"क्या आप बोली फ़ाइलनाम का इरादा"); addPair("No file to process %s\n", L"कोई फ़ाइल %s प्रक्रिया के लिए\n"); addPair("Did you intend to use --recursive", L"क्या आप उपयोग करना चाहते हैं --recursive"); addPair("Cannot process UTF-32 encoding", L"UTF-32 कूटबन्धन प्रक्रिया नहीं कर सकते"); addPair("\nArtistic Style has terminated", L"\nArtistic Style समाप्त किया है"); } Italian::Italian() // Italiano // build the translation vector in the Translation base class { addPair("Formatted %s\n", L"Formattata %s\n"); // should align with unchanged addPair("Unchanged %s\n", L"Immutato %s\n"); // should align with formatted addPair("Directory %s\n", L"Elenco %s\n"); addPair("Exclude %s\n", L"Escludere %s\n"); addPair("Exclude (unmatched) %s\n", L"Escludere (senza pari) %s\n"); addPair(" %s formatted %s unchanged ", L" %s ormattata %s immutato "); addPair(" seconds ", L" secondo "); addPair("%d min %d sec ", L"%d min %d seg "); addPair("%s lines\n", L"%s linee\n"); addPair("Using default options file %s\n", L"Utilizzando file delle opzioni di default %s\n"); addPair("Opening HTML documentation %s\n", L"Apertura di documenti HTML %s\n"); addPair("Invalid option file options:", L"Opzione non valida file delle opzioni:"); addPair("Invalid command line options:", L"Opzioni della riga di comando non valido:"); addPair("For help on options type 'astyle -h'", L"Per informazioni sulle opzioni di tipo 'astyle-h'"); addPair("Cannot open options file", L"Impossibile aprire il file opzioni"); addPair("Cannot open directory", L"Impossibile aprire la directory"); addPair("Cannot open HTML file %s\n", L"Impossibile aprire il file HTML %s\n"); addPair("Command execute failure", L"Esegui fallimento comando"); addPair("Command is not installed", L"Il comando non è installato"); addPair("Missing filename in %s\n", L"Nome del file mancante in %s\n"); addPair("Recursive option with no wildcard", L"Opzione ricorsiva senza jolly"); addPair("Did you intend quote the filename", L"Avete intenzione citare il nome del file"); addPair("No file to process %s\n", L"Nessun file al processo %s\n"); addPair("Did you intend to use --recursive", L"Hai intenzione di utilizzare --recursive"); addPair("Cannot process UTF-32 encoding", L"Non è possibile processo di codifica UTF-32"); addPair("\nArtistic Style has terminated", L"\nArtistic Style ha terminato"); } Japanese::Japanese() // 日本 { addPair("Formatted %s\n", L"フォーマット %s\n"); // should align with unchanged addPair("Unchanged %s\n", L"変更 %s\n"); // should align with formatted addPair("Directory %s\n", L"ディレクトリ %s\n"); addPair("Exclude %s\n", L"除外する %s\n"); addPair("Exclude (unmatched) %s\n", L"除外(マッチせず) %s\n"); addPair(" %s formatted %s unchanged ", L" %sフォーマット %s 変更 "); addPair(" seconds ", L" 秒 "); addPair("%d min %d sec ", L"%d 分 %d 秒 "); addPair("%s lines\n", L"%s の行\n"); addPair("Using default options file %s\n", L"デフォルトの設定ファイルを使用してください %s\n"); addPair("Opening HTML documentation %s\n", L"HTML文書を開く %s\n"); addPair("Invalid option file options:", L"無効なコンフィギュレーションファイルオプション:"); addPair("Invalid command line options:", L"無効なコマンドラインオプション:"); addPair("For help on options type 'astyle -h'", L"コマンドラインについてのヘルプは'astyle- h'を入力してください"); addPair("Cannot open options file", L"コンフィギュレーションファイルを開くことができません"); addPair("Cannot open directory", L"ディレクトリのオープンに失敗しました"); addPair("Cannot open HTML file %s\n", L"HTMLファイルを開くことができません %s\n"); addPair("Command execute failure", L"コマンドの失敗を実行"); addPair("Command is not installed", L"コマンドがインストールされていません"); addPair("Missing filename in %s\n", L"%s はファイル名で欠落しています\n"); addPair("Recursive option with no wildcard", L"再帰的なオプションではワイルドカードではない"); addPair("Did you intend quote the filename", L"あなたは、ファイル名を参照するつもり"); addPair("No file to process %s\n", L"いいえファイルは処理できません %s\n"); addPair("Did you intend to use --recursive", L"あなたが使用する予定 --recursive"); addPair("Cannot process UTF-32 encoding", L"UTF- 32エンコーディングを処理できない"); addPair("\nArtistic Style has terminated", L"\nArtistic Style 実行が終了しました"); } Korean::Korean() // 한국의 { addPair("Formatted %s\n", L"수정됨 %s\n"); // should align with unchanged addPair("Unchanged %s\n", L"변경없음 %s\n"); // should align with formatted addPair("Directory %s\n", L"디렉토리 %s\n"); addPair("Exclude %s\n", L"제외됨 %s\n"); addPair("Exclude (unmatched) %s\n", L"제외 (NO 일치) %s\n"); addPair(" %s formatted %s unchanged ", L" %s 수정됨 %s 변경없음 "); addPair(" seconds ", L" 초 "); addPair("%d min %d sec ", L"%d 분 %d 초 "); addPair("%s lines\n", L"%s 라인\n"); addPair("Using default options file %s\n", L"기본 구성 파일을 사용 %s\n"); addPair("Opening HTML documentation %s\n", L"HTML 문서를 열기 %s\n"); addPair("Invalid option file options:", L"잘못된 구성 파일 옵션 :"); addPair("Invalid command line options:", L"잘못된 명령줄 옵션 :"); addPair("For help on options type 'astyle -h'", L"도움말을 보려면 옵션 유형 'astyle - H'를 사용합니다"); addPair("Cannot open options file", L"구성 파일을 열 수 없습니다"); addPair("Cannot open directory", L"디렉토리를 열지 못했습니다"); addPair("Cannot open HTML file %s\n", L"HTML 파일을 열 수 없습니다 %s\n"); addPair("Command execute failure", L"명령 실패를 실행"); addPair("Command is not installed", L"명령이 설치되어 있지 않습니다"); addPair("Missing filename in %s\n", L"%s 에서 누락된 파일 이름\n"); addPair("Recursive option with no wildcard", L"와일드 카드없이 재귀 옵션"); addPair("Did you intend quote the filename", L"당신은 파일 이름을 인용하고자하나요"); addPair("No file to process %s\n", L"처리할 파일이 없습니다 %s\n"); addPair("Did you intend to use --recursive", L"--recursive 를 사용하고자 하십니까"); addPair("Cannot process UTF-32 encoding", L"UTF-32 인코딩을 처리할 수 없습니다"); addPair("\nArtistic Style has terminated", L"\nArtistic Style를 종료합니다"); } Polish::Polish() // Polski // build the translation vector in the Translation base class { addPair("Formatted %s\n", L"Sformatowany %s\n"); // should align with unchanged addPair("Unchanged %s\n", L"Niezmienione %s\n"); // should align with formatted addPair("Directory %s\n", L"Katalog %s\n"); addPair("Exclude %s\n", L"Wykluczać %s\n"); addPair("Exclude (unmatched) %s\n", L"Wyklucz (niezrównany) %s\n"); addPair(" %s formatted %s unchanged ", L" %s sformatowany %s niezmienione "); addPair(" seconds ", L" sekund "); addPair("%d min %d sec ", L"%d min %d sek "); addPair("%s lines\n", L"%s linii\n"); addPair("Using default options file %s\n", L"Korzystanie z domyślnej opcji %s plik\n"); addPair("Opening HTML documentation %s\n", L"Otwarcie dokumentacji HTML %s\n"); addPair("Invalid option file options:", L"Nieprawidłowy opcji pliku opcji:"); addPair("Invalid command line options:", L"Nieprawidłowe opcje wiersza polecenia:"); addPair("For help on options type 'astyle -h'", L"Aby uzyskać pomoc od rodzaju opcji 'astyle -h'"); addPair("Cannot open options file", L"Nie można otworzyć pliku opcji"); addPair("Cannot open directory", L"Nie można otworzyć katalogu"); addPair("Cannot open HTML file %s\n", L"Nie można otworzyć pliku HTML %s\n"); addPair("Command execute failure", L"Wykonaj polecenia niepowodzenia"); addPair("Command is not installed", L"Polecenie nie jest zainstalowany"); addPair("Missing filename in %s\n", L"Brakuje pliku w %s\n"); addPair("Recursive option with no wildcard", L"Rekurencyjne opcja bez symboli"); addPair("Did you intend quote the filename", L"Czy zamierza Pan podać nazwę pliku"); addPair("No file to process %s\n", L"Brak pliku do procesu %s\n"); addPair("Did you intend to use --recursive", L"Czy masz zamiar używać --recursive"); addPair("Cannot process UTF-32 encoding", L"Nie można procesu kodowania UTF-32"); addPair("\nArtistic Style has terminated", L"\nArtistic Style został zakończony"); } Portuguese::Portuguese() // Português // build the translation vector in the Translation base class { addPair("Formatted %s\n", L"Formatado %s\n"); // should align with unchanged addPair("Unchanged %s\n", L"Inalterado %s\n"); // should align with formatted addPair("Directory %s\n", L"Diretório %s\n"); addPair("Exclude %s\n", L"Excluir %s\n"); addPair("Exclude (unmatched) %s\n", L"Excluir (incomparável) %s\n"); addPair(" %s formatted %s unchanged ", L" %s formatado %s inalterado "); addPair(" seconds ", L" segundo "); addPair("%d min %d sec ", L"%d min %d seg "); addPair("%s lines\n", L"%s linhas\n"); addPair("Using default options file %s\n", L"Usando o arquivo de opções padrão %s\n"); addPair("Opening HTML documentation %s\n", L"Abrindo a documentação HTML %s\n"); addPair("Invalid option file options:", L"Opções de arquivo inválido opção:"); addPair("Invalid command line options:", L"Opções de linha de comando inválida:"); addPair("For help on options type 'astyle -h'", L"Para obter ajuda sobre as opções de tipo 'astyle -h'"); addPair("Cannot open options file", L"Não é possível abrir arquivo de opções"); addPair("Cannot open directory", L"Não é possível abrir diretório"); addPair("Cannot open HTML file %s\n", L"Não é possível abrir arquivo HTML %s\n"); addPair("Command execute failure", L"Executar falha de comando"); addPair("Command is not installed", L"Comando não está instalado"); addPair("Missing filename in %s\n", L"Filename faltando em %s\n"); addPair("Recursive option with no wildcard", L"Opção recursiva sem curinga"); addPair("Did you intend quote the filename", L"Será que você pretende citar o nome do arquivo"); addPair("No file to process %s\n", L"Nenhum arquivo para processar %s\n"); addPair("Did you intend to use --recursive", L"Será que você pretende usar --recursive"); addPair("Cannot process UTF-32 encoding", L"Não pode processar a codificação UTF-32"); addPair("\nArtistic Style has terminated", L"\nArtistic Style terminou"); } Russian::Russian() // русский // build the translation vector in the Translation base class { addPair("Formatted %s\n", L"Форматированный %s\n"); // should align with unchanged addPair("Unchanged %s\n", L"без изменений %s\n"); // should align with formatted addPair("Directory %s\n", L"каталог %s\n"); addPair("Exclude %s\n", L"исключать %s\n"); addPair("Exclude (unmatched) %s\n", L"Исключить (непревзойденный) %s\n"); addPair(" %s formatted %s unchanged ", L" %s Форматированный %s без изменений "); addPair(" seconds ", L" секунды "); addPair("%d min %d sec ", L"%d мин %d сек "); addPair("%s lines\n", L"%s линий\n"); addPair("Using default options file %s\n", L"Использование опции по умолчанию файл %s\n"); addPair("Opening HTML documentation %s\n", L"Открытие HTML документации %s\n"); addPair("Invalid option file options:", L"Недопустимый файл опций опцию:"); addPair("Invalid command line options:", L"Недопустимые параметры командной строки:"); addPair("For help on options type 'astyle -h'", L"Для получения справки по 'astyle -h' опций типа"); addPair("Cannot open options file", L"Не удается открыть файл параметров"); addPair("Cannot open directory", L"Не могу открыть каталог"); addPair("Cannot open HTML file %s\n", L"Не удается открыть файл HTML %s\n"); addPair("Command execute failure", L"Выполнить команду недостаточности"); addPair("Command is not installed", L"Не установлен Команда"); addPair("Missing filename in %s\n", L"Отсутствует имя файла в %s\n"); addPair("Recursive option with no wildcard", L"Рекурсивный вариант без каких-либо шаблона"); addPair("Did you intend quote the filename", L"Вы намерены цитатой файла"); addPair("No file to process %s\n", L"Нет файлов для обработки %s\n"); addPair("Did you intend to use --recursive", L"Неужели вы собираетесь использовать --recursive"); addPair("Cannot process UTF-32 encoding", L"Не удается обработать UTF-32 кодировке"); addPair("\nArtistic Style has terminated", L"\nArtistic Style прекратил"); } Spanish::Spanish() // Español // build the translation vector in the Translation base class { addPair("Formatted %s\n", L"Formato %s\n"); // should align with unchanged addPair("Unchanged %s\n", L"Inalterado %s\n"); // should align with formatted addPair("Directory %s\n", L"Directorio %s\n"); addPair("Exclude %s\n", L"Excluir %s\n"); addPair("Exclude (unmatched) %s\n", L"Excluir (incomparable) %s\n"); addPair(" %s formatted %s unchanged ", L" %s formato %s inalterado "); addPair(" seconds ", L" segundo "); addPair("%d min %d sec ", L"%d min %d seg "); addPair("%s lines\n", L"%s líneas\n"); addPair("Using default options file %s\n", L"Uso de las opciones por defecto del archivo %s\n"); addPair("Opening HTML documentation %s\n", L"Apertura de documentación HTML %s\n"); addPair("Invalid option file options:", L"Opción no válida opciones de archivo:"); addPair("Invalid command line options:", L"No válido opciones de línea de comando:"); addPair("For help on options type 'astyle -h'", L"Para obtener ayuda sobre las opciones tipo 'astyle -h'"); addPair("Cannot open options file", L"No se puede abrir el archivo de opciones"); addPair("Cannot open directory", L"No se puede abrir el directorio"); addPair("Cannot open HTML file %s\n", L"No se puede abrir el archivo HTML %s\n"); addPair("Command execute failure", L"Ejecutar el fracaso de comandos"); addPair("Command is not installed", L"El comando no está instalado"); addPair("Missing filename in %s\n", L"Falta nombre del archivo en %s\n"); addPair("Recursive option with no wildcard", L"Recursiva opción sin comodín"); addPair("Did you intend quote the filename", L"Se tiene la intención de citar el nombre de archivo"); addPair("No file to process %s\n", L"No existe el fichero a procesar %s\n"); addPair("Did you intend to use --recursive", L"Se va a utilizar --recursive"); addPair("Cannot process UTF-32 encoding", L"No se puede procesar la codificación UTF-32"); addPair("\nArtistic Style has terminated", L"\nArtistic Style ha terminado"); } Swedish::Swedish() // Svenska // build the translation vector in the Translation base class { addPair("Formatted %s\n", L"Formaterade %s\n"); // should align with unchanged addPair("Unchanged %s\n", L"Oförändrade %s\n"); // should align with formatted addPair("Directory %s\n", L"Katalog %s\n"); addPair("Exclude %s\n", L"Uteslut %s\n"); addPair("Exclude (unmatched) %s\n", L"Uteslut (oöverträffad) %s\n"); addPair(" %s formatted %s unchanged ", L" %s formaterade %s oförändrade "); addPair(" seconds ", L" sekunder "); addPair("%d min %d sec ", L"%d min %d sek "); addPair("%s lines\n", L"%s linjer\n"); addPair("Using default options file %s\n", L"Använda standardalternativ fil %s\n"); addPair("Opening HTML documentation %s\n", L"Öppna HTML-dokumentation %s\n"); addPair("Invalid option file options:", L"Ogiltigt alternativ fil alternativ:"); addPair("Invalid command line options:", L"Ogiltig kommandoraden alternativ:"); addPair("For help on options type 'astyle -h'", L"För hjälp om alternativ typ 'astyle -h'"); addPair("Cannot open options file", L"Kan inte öppna inställningsfilen"); addPair("Cannot open directory", L"Kan inte öppna katalog"); addPair("Cannot open HTML file %s\n", L"Kan inte öppna HTML-filen %s\n"); addPair("Command execute failure", L"Utför kommando misslyckande"); addPair("Command is not installed", L"Kommandot är inte installerat"); addPair("Missing filename in %s\n", L"Saknade filnamn i %s\n"); addPair("Recursive option with no wildcard", L"Rekursiva alternativ utan jokertecken"); addPair("Did you intend quote the filename", L"Visste du tänker citera filnamnet"); addPair("No file to process %s\n", L"Ingen fil att bearbeta %s\n"); addPair("Did you intend to use --recursive", L"Har du för avsikt att använda --recursive"); addPair("Cannot process UTF-32 encoding", L"Kan inte hantera UTF-32 kodning"); addPair("\nArtistic Style has terminated", L"\nArtistic Style har upphört"); } Ukrainian::Ukrainian() // Український // build the translation vector in the Translation base class { addPair("Formatted %s\n", L"форматований %s\n"); // should align with unchanged addPair("Unchanged %s\n", L"без змін %s\n"); // should align with formatted addPair("Directory %s\n", L"Каталог %s\n"); addPair("Exclude %s\n", L"Виключити %s\n"); addPair("Exclude (unmatched) %s\n", L"Виключити (неперевершений) %s\n"); addPair(" %s formatted %s unchanged ", L" %s відформатований %s без змін "); addPair(" seconds ", L" секунди "); addPair("%d min %d sec ", L"%d хви %d cek "); addPair("%s lines\n", L"%s ліній\n"); addPair("Using default options file %s\n", L"Використання файлів опцій за замовчуванням %s\n"); addPair("Opening HTML documentation %s\n", L"Відкриття HTML документації %s\n"); addPair("Invalid option file options:", L"Неприпустимий файл опцій опцію:"); addPair("Invalid command line options:", L"Неприпустима параметри командного рядка:"); addPair("For help on options type 'astyle -h'", L"Для отримання довідки по 'astyle -h' опцій типу"); addPair("Cannot open options file", L"Не вдається відкрити файл параметрів"); addPair("Cannot open directory", L"Не можу відкрити каталог"); addPair("Cannot open HTML file %s\n", L"Не вдається відкрити файл HTML %s\n"); addPair("Command execute failure", L"Виконати команду недостатності"); addPair("Command is not installed", L"Не встановлений Команда"); addPair("Missing filename in %s\n", L"Відсутня назва файлу в %s\n"); addPair("Recursive option with no wildcard", L"Рекурсивний варіант без будь-яких шаблону"); addPair("Did you intend quote the filename", L"Ви маєте намір цитатою файлу"); addPair("No file to process %s\n", L"Немає файлів для обробки %s\n"); addPair("Did you intend to use --recursive", L"Невже ви збираєтеся використовувати --recursive"); addPair("Cannot process UTF-32 encoding", L"Не вдається обробити UTF-32 кодуванні"); addPair("\nArtistic Style has terminated", L"\nArtistic Style припинив"); } #endif // ASTYLE_LIB } // end of namespace astyle ================================================ FILE: 3rdpart/astyle/ASLocalizer.h ================================================ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ASLocalizer.h * * Copyright (C) 2014 by Jim Pattee * * * This file is a part of Artistic Style - an indentation and * reformatting tool for C, C++, C# and Java source files. * * * Artistic Style is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Artistic Style is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Artistic Style. If not, see . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef ASLOCALIZER_H #define ASLOCALIZER_H #include #include namespace astyle { using namespace std; #ifndef ASTYLE_LIB //----------------------------------------------------------------------------- // ASLocalizer class for console build. // This class encapsulates all language-dependent settings and is a // generalization of the C locale concept. //----------------------------------------------------------------------------- class Translation; class ASLocalizer { public: // functions ASLocalizer(); virtual ~ASLocalizer(); string getLanguageID() const; const Translation* getTranslationClass() const; #ifdef _WIN32 void setLanguageFromLCID(size_t lcid); #endif void setLanguageFromName(const char* langID); const char* settext(const char* textIn) const; private: // functions void setTranslationClass(); private: // variables Translation* m_translation; // pointer to a polymorphic Translation class string m_langID; // language identifier from the locale string m_subLangID; // sub language identifier, if needed string m_localeName; // name of the current locale (Linux only) size_t m_lcid; // LCID of the user locale (Windows only) }; //---------------------------------------------------------------------------- // Translation base class. //---------------------------------------------------------------------------- class Translation // This base class is inherited by the language translation classes. // Polymorphism is used to call the correct language translator. // This class contains the translation vector and settext translation method. // The language vector is built by the language sub classes. // NOTE: This class must have virtual methods for typeid() to work. // typeid() is used by AStyleTestI18n_Localizer.cpp. { public: Translation() {} virtual ~Translation() {} string convertToMultiByte(const wstring &wideStr) const; size_t getTranslationVectorSize() const; bool getWideTranslation(const string &stringIn, wstring &wideOut) const; string &translate(const string &stringIn) const; protected: void addPair(const string &english, const wstring &translated); // variables vector > m_translation; // translation vector }; //---------------------------------------------------------------------------- // Translation classes // One class for each language. // These classes have only a constructor which builds the language vector. //---------------------------------------------------------------------------- class ChineseSimplified : public Translation { public: ChineseSimplified(); }; class ChineseTraditional : public Translation { public: ChineseTraditional(); }; class Dutch : public Translation { public: Dutch(); }; class English : public Translation { public: English(); }; class Finnish : public Translation { public: Finnish(); }; class French : public Translation { public: French(); }; class German : public Translation { public: German(); }; class Hindi : public Translation { public: Hindi(); }; class Italian : public Translation { public: Italian(); }; class Japanese : public Translation { public: Japanese(); }; class Korean : public Translation { public: Korean(); }; class Polish : public Translation { public: Polish(); }; class Portuguese : public Translation { public: Portuguese(); }; class Russian : public Translation { public: Russian(); }; class Spanish : public Translation { public: Spanish(); }; class Swedish : public Translation { public: Swedish(); }; class Ukrainian : public Translation { public: Ukrainian(); }; #endif // ASTYLE_LIB } // namespace astyle #endif // ASLOCALIZER_H ================================================ FILE: 3rdpart/astyle/ASResource.cpp ================================================ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ASResource.cpp * * Copyright (C) 2014 by Jim Pattee * * * This file is a part of Artistic Style - an indentation and * reformatting tool for C, C++, C# and Java source files. * * * Artistic Style is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Artistic Style is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Artistic Style. If not, see . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "astyle.h" #include namespace astyle { const string ASResource::AS_IF = string("if"); const string ASResource::AS_ELSE = string("else"); const string ASResource::AS_FOR = string("for"); const string ASResource::AS_DO = string("do"); const string ASResource::AS_WHILE = string("while"); const string ASResource::AS_SWITCH = string("switch"); const string ASResource::AS_CASE = string("case"); const string ASResource::AS_DEFAULT = string("default"); const string ASResource::AS_CLASS = string("class"); const string ASResource::AS_VOLATILE = string("volatile"); const string ASResource::AS_INTERRUPT = string("interrupt"); const string ASResource::AS_NOEXCEPT = string("noexcept"); const string ASResource::AS_AUTORELEASEPOOL = string("autoreleasepool"); const string ASResource::AS_STRUCT = string("struct"); const string ASResource::AS_UNION = string("union"); const string ASResource::AS_INTERFACE = string("interface"); const string ASResource::AS_NAMESPACE = string("namespace"); const string ASResource::AS_END = string("end"); const string ASResource::AS_SELECTOR = string("selector"); const string ASResource::AS_EXTERN = string("extern"); const string ASResource::AS_ENUM = string("enum"); const string ASResource::AS_PUBLIC = string("public"); const string ASResource::AS_PROTECTED = string("protected"); const string ASResource::AS_PRIVATE = string("private"); const string ASResource::AS_STATIC = string("static"); const string ASResource::AS_SYNCHRONIZED = string("synchronized"); const string ASResource::AS_OPERATOR = string("operator"); const string ASResource::AS_TEMPLATE = string("template"); const string ASResource::AS_TRY = string("try"); const string ASResource::AS_CATCH = string("catch"); const string ASResource::AS_THROW = string("throw"); const string ASResource::AS_FINALLY = string("finally"); const string ASResource::_AS_TRY = string("__try"); const string ASResource::_AS_FINALLY = string("__finally"); const string ASResource::_AS_EXCEPT = string("__except"); const string ASResource::AS_THROWS = string("throws"); const string ASResource::AS_CONST = string("const"); const string ASResource::AS_SEALED = string("sealed"); const string ASResource::AS_OVERRIDE = string("override"); const string ASResource::AS_WHERE = string("where"); const string ASResource::AS_LET = string("let"); const string ASResource::AS_NEW = string("new"); const string ASResource::AS_ASM = string("asm"); const string ASResource::AS__ASM__ = string("__asm__"); const string ASResource::AS_MS_ASM = string("_asm"); const string ASResource::AS_MS__ASM = string("__asm"); const string ASResource::AS_BAR_DEFINE = string("#define"); const string ASResource::AS_BAR_INCLUDE = string("#include"); const string ASResource::AS_BAR_IF = string("#if"); const string ASResource::AS_BAR_EL = string("#el"); const string ASResource::AS_BAR_ENDIF = string("#endif"); const string ASResource::AS_OPEN_BRACKET = string("{"); const string ASResource::AS_CLOSE_BRACKET = string("}"); const string ASResource::AS_OPEN_LINE_COMMENT = string("//"); const string ASResource::AS_OPEN_COMMENT = string("/*"); const string ASResource::AS_CLOSE_COMMENT = string("*/"); const string ASResource::AS_ASSIGN = string("="); const string ASResource::AS_PLUS_ASSIGN = string("+="); const string ASResource::AS_MINUS_ASSIGN = string("-="); const string ASResource::AS_MULT_ASSIGN = string("*="); const string ASResource::AS_DIV_ASSIGN = string("/="); const string ASResource::AS_MOD_ASSIGN = string("%="); const string ASResource::AS_OR_ASSIGN = string("|="); const string ASResource::AS_AND_ASSIGN = string("&="); const string ASResource::AS_XOR_ASSIGN = string("^="); const string ASResource::AS_GR_GR_ASSIGN = string(">>="); const string ASResource::AS_LS_LS_ASSIGN = string("<<="); const string ASResource::AS_GR_GR_GR_ASSIGN = string(">>>="); const string ASResource::AS_LS_LS_LS_ASSIGN = string("<<<="); const string ASResource::AS_GCC_MIN_ASSIGN = string("?"); const string ASResource::AS_RETURN = string("return"); const string ASResource::AS_CIN = string("cin"); const string ASResource::AS_COUT = string("cout"); const string ASResource::AS_CERR = string("cerr"); const string ASResource::AS_EQUAL = string("=="); const string ASResource::AS_PLUS_PLUS = string("++"); const string ASResource::AS_MINUS_MINUS = string("--"); const string ASResource::AS_NOT_EQUAL = string("!="); const string ASResource::AS_GR_EQUAL = string(">="); const string ASResource::AS_GR_GR = string(">>"); const string ASResource::AS_GR_GR_GR = string(">>>"); const string ASResource::AS_LS_EQUAL = string("<="); const string ASResource::AS_LS_LS = string("<<"); const string ASResource::AS_LS_LS_LS = string("<<<"); const string ASResource::AS_QUESTION_QUESTION = string("??"); const string ASResource::AS_LAMBDA = string("=>"); // C# lambda expression arrow const string ASResource::AS_ARROW = string("->"); const string ASResource::AS_AND = string("&&"); const string ASResource::AS_OR = string("||"); const string ASResource::AS_SCOPE_RESOLUTION = string("::"); const string ASResource::AS_PLUS = string("+"); const string ASResource::AS_MINUS = string("-"); const string ASResource::AS_MULT = string("*"); const string ASResource::AS_DIV = string("/"); const string ASResource::AS_MOD = string("%"); const string ASResource::AS_GR = string(">"); const string ASResource::AS_LS = string("<"); const string ASResource::AS_NOT = string("!"); const string ASResource::AS_BIT_OR = string("|"); const string ASResource::AS_BIT_AND = string("&"); const string ASResource::AS_BIT_NOT = string("~"); const string ASResource::AS_BIT_XOR = string("^"); const string ASResource::AS_QUESTION = string("?"); const string ASResource::AS_COLON = string(":"); const string ASResource::AS_COMMA = string(","); const string ASResource::AS_SEMICOLON = string(";"); const string ASResource::AS_QFOREACH = string("Q_FOREACH"); const string ASResource::AS_QFOREVER = string("Q_FOREVER"); const string ASResource::AS_FOREVER = string("forever"); const string ASResource::AS_FOREACH = string("foreach"); const string ASResource::AS_LOCK = string("lock"); const string ASResource::AS_UNSAFE = string("unsafe"); const string ASResource::AS_FIXED = string("fixed"); const string ASResource::AS_GET = string("get"); const string ASResource::AS_SET = string("set"); const string ASResource::AS_ADD = string("add"); const string ASResource::AS_REMOVE = string("remove"); const string ASResource::AS_DELEGATE = string("delegate"); const string ASResource::AS_UNCHECKED = string("unchecked"); const string ASResource::AS_CONST_CAST = string("const_cast"); const string ASResource::AS_DYNAMIC_CAST = string("dynamic_cast"); const string ASResource::AS_REINTERPRET_CAST = string("reinterpret_cast"); const string ASResource::AS_STATIC_CAST = string("static_cast"); const string ASResource::AS_NS_DURING = string("NS_DURING"); const string ASResource::AS_NS_HANDLER = string("NS_HANDLER"); /** * Sort comparison function. * Compares the length of the value of pointers in the vectors. * The LONGEST strings will be first in the vector. * * @param a and b, the string pointers to be compared. */ bool sortOnLength(const string* a, const string* b) { return (*a).length() > (*b).length(); } /** * Sort comparison function. * Compares the value of pointers in the vectors. * * @param a and b, the string pointers to be compared. */ bool sortOnName(const string* a, const string* b) { return *a < *b; } /** * Build the vector of assignment operators. * Used by BOTH ASFormatter.cpp and ASBeautifier.cpp * * @param assignmentOperators a reference to the vector to be built. */ void ASResource::buildAssignmentOperators(vector* assignmentOperators) { assignmentOperators->push_back(&AS_ASSIGN); assignmentOperators->push_back(&AS_PLUS_ASSIGN); assignmentOperators->push_back(&AS_MINUS_ASSIGN); assignmentOperators->push_back(&AS_MULT_ASSIGN); assignmentOperators->push_back(&AS_DIV_ASSIGN); assignmentOperators->push_back(&AS_MOD_ASSIGN); assignmentOperators->push_back(&AS_OR_ASSIGN); assignmentOperators->push_back(&AS_AND_ASSIGN); assignmentOperators->push_back(&AS_XOR_ASSIGN); // Java assignmentOperators->push_back(&AS_GR_GR_GR_ASSIGN); assignmentOperators->push_back(&AS_GR_GR_ASSIGN); assignmentOperators->push_back(&AS_LS_LS_ASSIGN); // Unknown assignmentOperators->push_back(&AS_LS_LS_LS_ASSIGN); sort(assignmentOperators->begin(), assignmentOperators->end(), sortOnLength); } /** * Build the vector of C++ cast operators. * Used by ONLY ASFormatter.cpp * * @param castOperators a reference to the vector to be built. */ void ASResource::buildCastOperators(vector* castOperators) { castOperators->push_back(&AS_CONST_CAST); castOperators->push_back(&AS_DYNAMIC_CAST); castOperators->push_back(&AS_REINTERPRET_CAST); castOperators->push_back(&AS_STATIC_CAST); } /** * Build the vector of header words. * Used by BOTH ASFormatter.cpp and ASBeautifier.cpp * * @param headers a reference to the vector to be built. */ void ASResource::buildHeaders(vector* headers, int fileType, bool beautifier) { headers->push_back(&AS_IF); headers->push_back(&AS_ELSE); headers->push_back(&AS_FOR); headers->push_back(&AS_WHILE); headers->push_back(&AS_DO); headers->push_back(&AS_SWITCH); headers->push_back(&AS_CASE); headers->push_back(&AS_DEFAULT); headers->push_back(&AS_TRY); headers->push_back(&AS_CATCH); headers->push_back(&AS_QFOREACH); // QT headers->push_back(&AS_QFOREVER); // QT headers->push_back(&AS_FOREACH); // QT & C# headers->push_back(&AS_FOREVER); // Qt & Boost if (fileType == C_TYPE) { headers->push_back(&_AS_TRY); // __try headers->push_back(&_AS_FINALLY); // __finally headers->push_back(&_AS_EXCEPT); // __except } if (fileType == JAVA_TYPE) { headers->push_back(&AS_FINALLY); headers->push_back(&AS_SYNCHRONIZED); } if (fileType == SHARP_TYPE) { headers->push_back(&AS_FINALLY); headers->push_back(&AS_LOCK); headers->push_back(&AS_FIXED); headers->push_back(&AS_GET); headers->push_back(&AS_SET); headers->push_back(&AS_ADD); headers->push_back(&AS_REMOVE); } if (beautifier) { if (fileType == C_TYPE) { headers->push_back(&AS_TEMPLATE); } if (fileType == JAVA_TYPE) { headers->push_back(&AS_STATIC); // for static constructor } } sort(headers->begin(), headers->end(), sortOnName); } /** * Build the vector of indentable headers. * Used by ONLY ASBeautifier.cpp * * @param indentableHeaders a reference to the vector to be built. */ void ASResource::buildIndentableHeaders(vector* indentableHeaders) { indentableHeaders->push_back(&AS_RETURN); sort(indentableHeaders->begin(), indentableHeaders->end(), sortOnName); } /** * Build the vector of indentable macros pairs. * Initialized by ASFormatter, used by ONLY ASEnhancer.cpp * * @param indentableMacros a reference to the vector to be built. */ void ASResource::buildIndentableMacros(vector* >* indentableMacros) { // the pairs must be retained in memory static const struct pair macros[] = { // wxWidgets make_pair("BEGIN_EVENT_TABLE", "END_EVENT_TABLE"), make_pair("wxBEGIN_EVENT_TABLE", "wxEND_EVENT_TABLE"), // MFC make_pair("BEGIN_DISPATCH_MAP", "END_DISPATCH_MAP"), make_pair("BEGIN_EVENT_MAP", "END_EVENT_MAP"), make_pair("BEGIN_MESSAGE_MAP", "END_MESSAGE_MAP"), make_pair("BEGIN_PROPPAGEIDS", "END_PROPPAGEIDS"), }; size_t elements = sizeof(macros) / sizeof(macros[0]); for (size_t i = 0; i < elements; i++) indentableMacros->push_back(¯os[i]); } /** * Build the vector of non-assignment operators. * Used by ONLY ASBeautifier.cpp * * @param nonAssignmentOperators a reference to the vector to be built. */ void ASResource::buildNonAssignmentOperators(vector* nonAssignmentOperators) { nonAssignmentOperators->push_back(&AS_EQUAL); nonAssignmentOperators->push_back(&AS_PLUS_PLUS); nonAssignmentOperators->push_back(&AS_MINUS_MINUS); nonAssignmentOperators->push_back(&AS_NOT_EQUAL); nonAssignmentOperators->push_back(&AS_GR_EQUAL); nonAssignmentOperators->push_back(&AS_GR_GR_GR); nonAssignmentOperators->push_back(&AS_GR_GR); nonAssignmentOperators->push_back(&AS_LS_EQUAL); nonAssignmentOperators->push_back(&AS_LS_LS_LS); nonAssignmentOperators->push_back(&AS_LS_LS); nonAssignmentOperators->push_back(&AS_ARROW); nonAssignmentOperators->push_back(&AS_AND); nonAssignmentOperators->push_back(&AS_OR); nonAssignmentOperators->push_back(&AS_LAMBDA); sort(nonAssignmentOperators->begin(), nonAssignmentOperators->end(), sortOnLength); } /** * Build the vector of header non-paren headers. * Used by BOTH ASFormatter.cpp and ASBeautifier.cpp. * NOTE: Non-paren headers should also be included in the headers vector. * * @param nonParenHeaders a reference to the vector to be built. */ void ASResource::buildNonParenHeaders(vector* nonParenHeaders, int fileType, bool beautifier) { nonParenHeaders->push_back(&AS_ELSE); nonParenHeaders->push_back(&AS_DO); nonParenHeaders->push_back(&AS_TRY); nonParenHeaders->push_back(&AS_CATCH); // can be paren or non-paren nonParenHeaders->push_back(&AS_CASE); // can be paren or non-paren nonParenHeaders->push_back(&AS_DEFAULT); nonParenHeaders->push_back(&AS_QFOREVER); // QT nonParenHeaders->push_back(&AS_FOREVER); // Boost if (fileType == C_TYPE) { nonParenHeaders->push_back(&_AS_TRY); // __try nonParenHeaders->push_back(&_AS_FINALLY); // __finally } if (fileType == JAVA_TYPE) { nonParenHeaders->push_back(&AS_FINALLY); } if (fileType == SHARP_TYPE) { nonParenHeaders->push_back(&AS_FINALLY); nonParenHeaders->push_back(&AS_GET); nonParenHeaders->push_back(&AS_SET); nonParenHeaders->push_back(&AS_ADD); nonParenHeaders->push_back(&AS_REMOVE); } if (beautifier) { if (fileType == C_TYPE) { nonParenHeaders->push_back(&AS_TEMPLATE); } if (fileType == JAVA_TYPE) { nonParenHeaders->push_back(&AS_STATIC); } } sort(nonParenHeaders->begin(), nonParenHeaders->end(), sortOnName); } /** * Build the vector of operators. * Used by ONLY ASFormatter.cpp * * @param operators a reference to the vector to be built. */ void ASResource::buildOperators(vector* operators, int fileType) { operators->push_back(&AS_PLUS_ASSIGN); operators->push_back(&AS_MINUS_ASSIGN); operators->push_back(&AS_MULT_ASSIGN); operators->push_back(&AS_DIV_ASSIGN); operators->push_back(&AS_MOD_ASSIGN); operators->push_back(&AS_OR_ASSIGN); operators->push_back(&AS_AND_ASSIGN); operators->push_back(&AS_XOR_ASSIGN); operators->push_back(&AS_EQUAL); operators->push_back(&AS_PLUS_PLUS); operators->push_back(&AS_MINUS_MINUS); operators->push_back(&AS_NOT_EQUAL); operators->push_back(&AS_GR_EQUAL); operators->push_back(&AS_GR_GR_GR_ASSIGN); operators->push_back(&AS_GR_GR_ASSIGN); operators->push_back(&AS_GR_GR_GR); operators->push_back(&AS_GR_GR); operators->push_back(&AS_LS_EQUAL); operators->push_back(&AS_LS_LS_LS_ASSIGN); operators->push_back(&AS_LS_LS_ASSIGN); operators->push_back(&AS_LS_LS_LS); operators->push_back(&AS_LS_LS); operators->push_back(&AS_QUESTION_QUESTION); operators->push_back(&AS_LAMBDA); operators->push_back(&AS_ARROW); operators->push_back(&AS_AND); operators->push_back(&AS_OR); operators->push_back(&AS_SCOPE_RESOLUTION); operators->push_back(&AS_PLUS); operators->push_back(&AS_MINUS); operators->push_back(&AS_MULT); operators->push_back(&AS_DIV); operators->push_back(&AS_MOD); operators->push_back(&AS_QUESTION); operators->push_back(&AS_COLON); operators->push_back(&AS_ASSIGN); operators->push_back(&AS_LS); operators->push_back(&AS_GR); operators->push_back(&AS_NOT); operators->push_back(&AS_BIT_OR); operators->push_back(&AS_BIT_AND); operators->push_back(&AS_BIT_NOT); operators->push_back(&AS_BIT_XOR); if (fileType == C_TYPE) { operators->push_back(&AS_GCC_MIN_ASSIGN); operators->push_back(&AS_GCC_MAX_ASSIGN); } sort(operators->begin(), operators->end(), sortOnLength); } /** * Build the vector of pre-block statements. * Used by ONLY ASBeautifier.cpp * NOTE: Cannot be both a header and a preBlockStatement. * * @param preBlockStatements a reference to the vector to be built. */ void ASResource::buildPreBlockStatements(vector* preBlockStatements, int fileType) { preBlockStatements->push_back(&AS_CLASS); if (fileType == C_TYPE) { preBlockStatements->push_back(&AS_STRUCT); preBlockStatements->push_back(&AS_UNION); preBlockStatements->push_back(&AS_NAMESPACE); } if (fileType == JAVA_TYPE) { preBlockStatements->push_back(&AS_INTERFACE); preBlockStatements->push_back(&AS_THROWS); } if (fileType == SHARP_TYPE) { preBlockStatements->push_back(&AS_INTERFACE); preBlockStatements->push_back(&AS_NAMESPACE); preBlockStatements->push_back(&AS_WHERE); preBlockStatements->push_back(&AS_STRUCT); } sort(preBlockStatements->begin(), preBlockStatements->end(), sortOnName); } /** * Build the vector of pre-command headers. * Used by BOTH ASFormatter.cpp and ASBeautifier.cpp. * NOTE: Cannot be both a header and a preCommandHeader. * * A preCommandHeader is in a function definition between * the closing paren and the opening bracket. * e.g. in "void foo() const {}", "const" is a preCommandHeader. */ void ASResource::buildPreCommandHeaders(vector* preCommandHeaders, int fileType) { if (fileType == C_TYPE) { preCommandHeaders->push_back(&AS_CONST); preCommandHeaders->push_back(&AS_VOLATILE); preCommandHeaders->push_back(&AS_INTERRUPT); preCommandHeaders->push_back(&AS_NOEXCEPT); preCommandHeaders->push_back(&AS_OVERRIDE); preCommandHeaders->push_back(&AS_SEALED); // Visual C only preCommandHeaders->push_back(&AS_AUTORELEASEPOOL); // Obj-C only } if (fileType == JAVA_TYPE) { preCommandHeaders->push_back(&AS_THROWS); } if (fileType == SHARP_TYPE) { preCommandHeaders->push_back(&AS_WHERE); } sort(preCommandHeaders->begin(), preCommandHeaders->end(), sortOnName); } /** * Build the vector of pre-definition headers. * Used by ONLY ASFormatter.cpp * NOTE: Do NOT add 'enum' here. It is an array type bracket. * NOTE: Do NOT add 'extern' here. Do not want an extra indent. * * @param preDefinitionHeaders a reference to the vector to be built. */ void ASResource::buildPreDefinitionHeaders(vector* preDefinitionHeaders, int fileType) { preDefinitionHeaders->push_back(&AS_CLASS); if (fileType == C_TYPE) { preDefinitionHeaders->push_back(&AS_STRUCT); preDefinitionHeaders->push_back(&AS_UNION); preDefinitionHeaders->push_back(&AS_NAMESPACE); } if (fileType == JAVA_TYPE) { preDefinitionHeaders->push_back(&AS_INTERFACE); } if (fileType == SHARP_TYPE) { preDefinitionHeaders->push_back(&AS_STRUCT); preDefinitionHeaders->push_back(&AS_INTERFACE); preDefinitionHeaders->push_back(&AS_NAMESPACE); } sort(preDefinitionHeaders->begin(), preDefinitionHeaders->end(), sortOnName); } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ASBase Functions * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // check if a specific line position contains a keyword. bool ASBase::findKeyword(const string &line, int i, const string &keyword) const { assert(isCharPotentialHeader(line, i)); // check the word const size_t keywordLength = keyword.length(); const size_t wordEnd = i + keywordLength; if (wordEnd > line.length()) return false; if (line.compare(i, keywordLength, keyword) != 0) return false; // check that this is not part of a longer word if (wordEnd == line.length()) return true; if (isLegalNameChar(line[wordEnd])) return false; // is not a keyword if part of a definition const char peekChar = peekNextChar(line, wordEnd - 1); if (peekChar == ',' || peekChar == ')') return false; return true; } // get the current word on a line // index must point to the beginning of the word string ASBase::getCurrentWord(const string &line, size_t index) const { assert(isCharPotentialHeader(line, index)); size_t lineLength = line.length(); size_t i; for (i = index; i < lineLength; i++) { if (!isLegalNameChar(line[i])) break; } return line.substr(index, i - index); } } // end namespace astyle ================================================ FILE: 3rdpart/astyle/astyle.h ================================================ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * astyle.h * Copyright (C) 2014 by Jim Pattee * * * This file is a part of Artistic Style - an indentation and * reformatting tool for C, C++, C# and Java source files. * * * Artistic Style is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Artistic Style is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Artistic Style. If not, see . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef ASTYLE_H #define ASTYLE_H #ifdef __VMS #define __USE_STD_IOSTREAM 1 #include #else #include #endif #include #include // for cout #include #include #ifdef __GNUC__ #include // need both string and string.h for GCC #endif #ifdef _MSC_VER #pragma warning(disable: 4996) // secure version deprecation warnings #pragma warning(disable: 4267) // 64 bit signed/unsigned loss of data #endif #ifdef __BORLANDC__ #pragma warn -8004 // variable is assigned a value that is never used #endif #ifdef __INTEL_COMPILER #pragma warning(disable: 383) // value copied to temporary, reference to temporary used #pragma warning(disable: 981) // operands are evaluated in unspecified order #endif #ifdef __clang__ #pragma clang diagnostic ignored "-Wshorten-64-to-32" #endif namespace astyle { using namespace std; enum FileType { C_TYPE = 0, JAVA_TYPE = 1, SHARP_TYPE = 2 }; /* The enums below are not recognized by 'vectors' in Microsoft Visual C++ V5 when they are part of a namespace!!! Use Visual C++ V6 or higher. */ enum FormatStyle { STYLE_NONE, STYLE_ALLMAN, STYLE_JAVA, STYLE_KR, STYLE_STROUSTRUP, STYLE_WHITESMITH, STYLE_VTK, STYLE_BANNER, STYLE_GNU, STYLE_LINUX, STYLE_HORSTMANN, STYLE_1TBS, STYLE_GOOGLE, STYLE_PICO, STYLE_LISP }; enum BracketMode { NONE_MODE, ATTACH_MODE, BREAK_MODE, LINUX_MODE, STROUSTRUP_MODE, RUN_IN_MODE }; enum BracketType { NULL_TYPE = 0, NAMESPACE_TYPE = 1, // also a DEFINITION_TYPE CLASS_TYPE = 2, // also a DEFINITION_TYPE STRUCT_TYPE = 4, // also a DEFINITION_TYPE INTERFACE_TYPE = 8, // also a DEFINITION_TYPE DEFINITION_TYPE = 16, COMMAND_TYPE = 32, ARRAY_NIS_TYPE = 64, // also an ARRAY_TYPE ENUM_TYPE = 128, // also an ARRAY_TYPE INIT_TYPE = 256, // also an ARRAY_TYPE ARRAY_TYPE = 512, EXTERN_TYPE = 1024, // extern "C", not a command type extern SINGLE_LINE_TYPE = 2048 }; enum MinConditional { MINCOND_ZERO, MINCOND_ONE, MINCOND_TWO, MINCOND_ONEHALF, MINCOND_END }; enum ObjCColonPad { COLON_PAD_NO_CHANGE, COLON_PAD_NONE, COLON_PAD_ALL, COLON_PAD_AFTER, COLON_PAD_BEFORE }; enum PointerAlign { PTR_ALIGN_NONE, PTR_ALIGN_TYPE, PTR_ALIGN_MIDDLE, PTR_ALIGN_NAME }; enum ReferenceAlign { REF_ALIGN_NONE = PTR_ALIGN_NONE, REF_ALIGN_TYPE = PTR_ALIGN_TYPE, REF_ALIGN_MIDDLE = PTR_ALIGN_MIDDLE, REF_ALIGN_NAME = PTR_ALIGN_NAME, REF_SAME_AS_PTR }; enum FileEncoding { ENCODING_8BIT, UTF_16BE, UTF_16LE, // Windows default UTF_32BE, UTF_32LE }; enum LineEndFormat { LINEEND_DEFAULT, // Use line break that matches most of the file LINEEND_WINDOWS, LINEEND_LINUX, LINEEND_MACOLD, LINEEND_CRLF = LINEEND_WINDOWS, LINEEND_LF = LINEEND_LINUX, LINEEND_CR = LINEEND_MACOLD }; //----------------------------------------------------------------------------- // Class ASSourceIterator // A pure virtual class is used by ASFormatter and ASBeautifier instead of // ASStreamIterator. This allows programs using AStyle as a plug-in to define // their own ASStreamIterator. The ASStreamIterator class must inherit // this class. //----------------------------------------------------------------------------- class ASSourceIterator { public: ASSourceIterator() {} virtual ~ASSourceIterator() {} virtual int getStreamLength() const = 0; virtual bool hasMoreLines() const = 0; virtual string nextLine(bool emptyLineWasDeleted = false) = 0; virtual string peekNextLine() = 0; virtual void peekReset() = 0; virtual streamoff tellg() = 0; }; //----------------------------------------------------------------------------- // Class ASResource //----------------------------------------------------------------------------- class ASResource { public: ASResource() {} virtual ~ASResource() {} void buildAssignmentOperators(vector* assignmentOperators); void buildCastOperators(vector* castOperators); void buildHeaders(vector* headers, int fileType, bool beautifier = false); void buildIndentableMacros(vector* >* indentableMacros); void buildIndentableHeaders(vector* indentableHeaders); void buildNonAssignmentOperators(vector* nonAssignmentOperators); void buildNonParenHeaders(vector* nonParenHeaders, int fileType, bool beautifier = false); void buildOperators(vector* operators, int fileType); void buildPreBlockStatements(vector* preBlockStatements, int fileType); void buildPreCommandHeaders(vector* preCommandHeaders, int fileType); void buildPreDefinitionHeaders(vector* preDefinitionHeaders, int fileType); public: static const string AS_IF, AS_ELSE; static const string AS_DO, AS_WHILE; static const string AS_FOR; static const string AS_SWITCH, AS_CASE, AS_DEFAULT; static const string AS_TRY, AS_CATCH, AS_THROW, AS_THROWS, AS_FINALLY; static const string _AS_TRY, _AS_FINALLY, _AS_EXCEPT; static const string AS_PUBLIC, AS_PROTECTED, AS_PRIVATE; static const string AS_CLASS, AS_STRUCT, AS_UNION, AS_INTERFACE, AS_NAMESPACE; static const string AS_END; static const string AS_SELECTOR; static const string AS_EXTERN, AS_ENUM; static const string AS_STATIC, AS_CONST, AS_SEALED, AS_OVERRIDE, AS_VOLATILE, AS_NEW; static const string AS_NOEXCEPT, AS_INTERRUPT, AS_AUTORELEASEPOOL; static const string AS_WHERE, AS_LET, AS_SYNCHRONIZED; static const string AS_OPERATOR, AS_TEMPLATE; static const string AS_OPEN_BRACKET, AS_CLOSE_BRACKET; static const string AS_OPEN_LINE_COMMENT, AS_OPEN_COMMENT, AS_CLOSE_COMMENT; static const string AS_BAR_DEFINE, AS_BAR_INCLUDE, AS_BAR_IF, AS_BAR_EL, AS_BAR_ENDIF; static const string AS_RETURN; static const string AS_CIN, AS_COUT, AS_CERR; static const string AS_ASSIGN, AS_PLUS_ASSIGN, AS_MINUS_ASSIGN, AS_MULT_ASSIGN; static const string AS_DIV_ASSIGN, AS_MOD_ASSIGN, AS_XOR_ASSIGN, AS_OR_ASSIGN, AS_AND_ASSIGN; static const string AS_GR_GR_ASSIGN, AS_LS_LS_ASSIGN, AS_GR_GR_GR_ASSIGN, AS_LS_LS_LS_ASSIGN; static const string AS_GCC_MIN_ASSIGN, AS_GCC_MAX_ASSIGN; static const string AS_EQUAL, AS_PLUS_PLUS, AS_MINUS_MINUS, AS_NOT_EQUAL, AS_GR_EQUAL, AS_GR_GR_GR, AS_GR_GR; static const string AS_LS_EQUAL, AS_LS_LS_LS, AS_LS_LS; static const string AS_QUESTION_QUESTION, AS_LAMBDA; static const string AS_ARROW, AS_AND, AS_OR; static const string AS_SCOPE_RESOLUTION; static const string AS_PLUS, AS_MINUS, AS_MULT, AS_DIV, AS_MOD, AS_GR, AS_LS; static const string AS_NOT, AS_BIT_XOR, AS_BIT_OR, AS_BIT_AND, AS_BIT_NOT; static const string AS_QUESTION, AS_COLON, AS_SEMICOLON, AS_COMMA; static const string AS_ASM, AS__ASM__, AS_MS_ASM, AS_MS__ASM; static const string AS_QFOREACH, AS_QFOREVER, AS_FOREVER; static const string AS_FOREACH, AS_LOCK, AS_UNSAFE, AS_FIXED; static const string AS_GET, AS_SET, AS_ADD, AS_REMOVE; static const string AS_DELEGATE, AS_UNCHECKED; static const string AS_CONST_CAST, AS_DYNAMIC_CAST, AS_REINTERPRET_CAST, AS_STATIC_CAST; static const string AS_NS_DURING, AS_NS_HANDLER; }; // Class ASResource //----------------------------------------------------------------------------- // Class ASBase //----------------------------------------------------------------------------- class ASBase { private: // all variables should be set by the "init" function int baseFileType; // a value from enum FileType protected: ASBase() : baseFileType(C_TYPE) { } virtual ~ASBase() {} // functions definitions are at the end of ASResource.cpp bool findKeyword(const string &line, int i, const string &keyword) const; string getCurrentWord(const string &line, size_t index) const; protected: void init(int fileTypeArg) { baseFileType = fileTypeArg; } bool isCStyle() const { return (baseFileType == C_TYPE); } bool isJavaStyle() const { return (baseFileType == JAVA_TYPE); } bool isSharpStyle() const { return (baseFileType == SHARP_TYPE); } // check if a specific character is a digit // NOTE: Visual C isdigit() gives assert error if char > 256 bool isDigit(char ch) const { return (ch >= '0' && ch <= '9'); } // check if a specific character can be used in a legal variable/method/class name bool isLegalNameChar(char ch) const { if (isWhiteSpace(ch)) return false; if ((unsigned) ch > 127) return false; return (isalnum((unsigned char)ch) || ch == '.' || ch == '_' || (isJavaStyle() && ch == '$') || (isSharpStyle() && ch == '@')); // may be used as a prefix } // check if a specific character can be part of a header bool isCharPotentialHeader(const string &line, size_t i) const { assert(!isWhiteSpace(line[i])); char prevCh = ' '; if (i > 0) prevCh = line[i - 1]; if (!isLegalNameChar(prevCh) && isLegalNameChar(line[i])) return true; return false; } // check if a specific character can be part of an operator bool isCharPotentialOperator(char ch) const { assert(!isWhiteSpace(ch)); if ((unsigned) ch > 127) return false; return (ispunct((unsigned char)ch) && ch != '{' && ch != '}' && ch != '(' && ch != ')' && ch != '[' && ch != ']' && ch != ';' && ch != ',' && ch != '#' && ch != '\\' && ch != '\'' && ch != '\"'); } // check if a specific character is a whitespace character bool isWhiteSpace(char ch) const { return (ch == ' ' || ch == '\t'); } // peek at the next unread character. char peekNextChar(const string &line, int i) const { char ch = ' '; size_t peekNum = line.find_first_not_of(" \t", i + 1); if (peekNum == string::npos) return ch; ch = line[peekNum]; return ch; } }; // Class ASBase //----------------------------------------------------------------------------- // Class ASBeautifier //----------------------------------------------------------------------------- class ASBeautifier : protected ASResource, protected ASBase { public: ASBeautifier(); virtual ~ASBeautifier(); virtual void init(ASSourceIterator* iter); virtual string beautify(const string &line); void setCaseIndent(bool state); void setClassIndent(bool state); void setCStyle(); void setDefaultTabLength(); void setEmptyLineFill(bool state); void setForceTabXIndentation(int length); void setJavaStyle(); void setLabelIndent(bool state); void setMaxInStatementIndentLength(int max); void setMinConditionalIndentOption(int min); void setMinConditionalIndentLength(); void setModeManuallySet(bool state); void setModifierIndent(bool state); void setNamespaceIndent(bool state); void setAlignMethodColon(bool state); void setSharpStyle(); void setSpaceIndentation(int length = 4); void setSwitchIndent(bool state); void setTabIndentation(int length = 4, bool forceTabs = false); void setPreprocDefineIndent(bool state); void setPreprocConditionalIndent(bool state); int getBeautifierFileType() const; int getFileType() const; int getIndentLength(void) const; int getTabLength(void) const; string getIndentString(void) const; string getNextWord(const string &line, size_t currPos) const; bool getBracketIndent(void) const; bool getBlockIndent(void) const; bool getCaseIndent(void) const; bool getClassIndent(void) const; bool getEmptyLineFill(void) const; bool getForceTabIndentation(void) const; bool getModeManuallySet(void) const; bool getModifierIndent(void) const; bool getNamespaceIndent(void) const; bool getPreprocDefineIndent(void) const; bool getSwitchIndent(void) const; protected: void deleteBeautifierVectors(); const string* findHeader(const string &line, int i, const vector* possibleHeaders) const; const string* findOperator(const string &line, int i, const vector* possibleOperators) const; int getNextProgramCharDistance(const string &line, int i) const; int indexOf(vector &container, const string* element) const; void setBlockIndent(bool state); void setBracketIndent(bool state); void setBracketIndentVtk(bool state); string extractPreprocessorStatement(const string &line) const; string trim(const string &str) const; string rtrim(const string &str) const; // variables set by ASFormatter - must be updated in activeBeautifierStack int inLineNumber; int horstmannIndentInStatement; int nonInStatementBracket; bool lineCommentNoBeautify; bool isElseHeaderIndent; bool isCaseHeaderCommentIndent; bool isNonInStatementArray; bool isSharpAccessor; bool isSharpDelegate; bool isInExternC; bool isInBeautifySQL; bool isInIndentableStruct; bool isInIndentablePreproc; private: // functions ASBeautifier(const ASBeautifier ©); ASBeautifier &operator=(ASBeautifier &); // not to be implemented void adjustParsedLineIndentation(size_t iPrelim, bool isInExtraHeaderIndent); void computePreliminaryIndentation(); void parseCurrentLine(const string &line); void popLastInStatementIndent(); void processPreprocessor(const string &preproc, const string &line); void registerInStatementIndent(const string &line, int i, int spaceIndentCount, int tabIncrementIn, int minIndent, bool updateParenStack); void registerInStatementIndentColon(const string &line, int i, int tabIncrementIn); void initVectors(); void initTempStacksContainer(vector*>* &container, vector*>* value); void clearObjCMethodDefinitionAlignment(); void deleteBeautifierContainer(vector* &container); void deleteTempStacksContainer(vector*>* &container); int adjustIndentCountForBreakElseIfComments() const; int computeObjCColonAlignment(string &line, int colonAlignPosition) const; int convertTabToSpaces(int i, int tabIncrementIn) const; int getInStatementIndentAssign(const string &line, size_t currPos) const; int getInStatementIndentComma(const string &line, size_t currPos) const; bool isIndentedPreprocessor(const string &line, size_t currPos) const; bool isLineEndComment(const string &line, int startPos) const; bool isPreprocessorConditionalCplusplus(const string &line) const; bool isInPreprocessorUnterminatedComment(const string &line); bool statementEndsWithComma(const string &line, int index) const; string &getIndentedLineReturn(string &newLine, const string &originalLine) const; string preLineWS(int lineIndentCount, int lineSpaceIndentCount) const; template void deleteContainer(T &container); template void initContainer(T &container, T value); vector*>* copyTempStacks(const ASBeautifier &other) const; pair computePreprocessorIndent(); private: // variables int beautifierFileType; vector* headers; vector* nonParenHeaders; vector* preBlockStatements; vector* preCommandHeaders; vector* assignmentOperators; vector* nonAssignmentOperators; vector* indentableHeaders; vector* waitingBeautifierStack; vector* activeBeautifierStack; vector* waitingBeautifierStackLengthStack; vector* activeBeautifierStackLengthStack; vector* headerStack; vector* >* tempStacks; vector* blockParenDepthStack; vector* blockStatementStack; vector* parenStatementStack; vector* bracketBlockStateStack; vector* inStatementIndentStack; vector* inStatementIndentStackSizeStack; vector* parenIndentStack; vector >* preprocIndentStack; ASSourceIterator* sourceIterator; const string* currentHeader; const string* previousLastLineHeader; const string* probationHeader; const string* lastLineHeader; string indentString; string verbatimDelimiter; bool isInQuote; bool isInVerbatimQuote; bool haveLineContinuationChar; bool isInAsm; bool isInAsmOneLine; bool isInAsmBlock; bool isInComment; bool isInPreprocessorComment; bool isInHorstmannComment; bool isInCase; bool isInQuestion; bool isInStatement; bool isInHeader; bool isInTemplate; bool isInDefine; bool isInDefineDefinition; bool classIndent; bool isIndentModeOff; bool isInClassHeader; // is in a class before the opening bracket bool isInClassHeaderTab; // is in an indentable class header line bool isInClassInitializer; // is in a class after the ':' initializer bool isInClass; // is in a class after the opening bracket bool isInObjCMethodDefinition; bool isImmediatelyPostObjCMethodDefinition; bool isInIndentablePreprocBlock; bool isInObjCInterface; bool isInEnum; bool isInEnumTypeID; bool isInLet; bool modifierIndent; bool switchIndent; bool caseIndent; bool namespaceIndent; bool bracketIndent; bool bracketIndentVtk; bool blockIndent; bool labelIndent; bool shouldIndentPreprocDefine; bool isInConditional; bool isModeManuallySet; bool shouldForceTabIndentation; bool emptyLineFill; bool backslashEndsPrevLine; bool lineOpensWithLineComment; bool lineOpensWithComment; bool lineStartsInComment; bool blockCommentNoIndent; bool blockCommentNoBeautify; bool previousLineProbationTab; bool lineBeginsWithOpenBracket; bool lineBeginsWithCloseBracket; bool lineBeginsWithComma; bool lineIsCommentOnly; bool lineIsLineCommentOnly; bool shouldIndentBrackettedLine; bool isInSwitch; bool foundPreCommandHeader; bool foundPreCommandMacro; bool shouldAlignMethodColon; bool shouldIndentPreprocConditional; int indentCount; int spaceIndentCount; int spaceIndentObjCMethodDefinition; int colonIndentObjCMethodDefinition; int lineOpeningBlocksNum; int lineClosingBlocksNum; int fileType; int minConditionalOption; int minConditionalIndent; int parenDepth; int indentLength; int tabLength; int blockTabCount; int maxInStatementIndent; int classInitializerIndents; int templateDepth; int squareBracketCount; int prevFinalLineSpaceIndentCount; int prevFinalLineIndentCount; int defineIndentCount; int preprocBlockIndent; char quoteChar; char prevNonSpaceCh; char currentNonSpaceCh; char currentNonLegalCh; char prevNonLegalCh; }; // Class ASBeautifier //----------------------------------------------------------------------------- // Class ASEnhancer //----------------------------------------------------------------------------- class ASEnhancer : protected ASBase { public: // functions ASEnhancer(); virtual ~ASEnhancer(); void init(int, int, int, bool, bool, bool, bool, bool, bool, bool, vector* >*); void enhance(string &line, bool isInNamespace, bool isInPreprocessor, bool isInSQL); private: // functions void convertForceTabIndentToSpaces(string &line) const; void convertSpaceIndentToForceTab(string &line) const; size_t findCaseColon(string &line, size_t caseIndex) const; int indentLine(string &line, int indent) const; bool isBeginDeclareSectionSQL(string &line, size_t index) const; bool isEndDeclareSectionSQL(string &line, size_t index) const; bool isOneLineBlockReached(string &line, int startChar) const; void parseCurrentLine(string &line, bool isInPreprocessor, bool isInSQL); size_t processSwitchBlock(string &line, size_t index); int unindentLine(string &line, int unindent) const; private: // options from command line or options file int indentLength; int tabLength; bool useTabs; bool forceTab; bool namespaceIndent; bool caseIndent; bool preprocBlockIndent; bool preprocDefineIndent; bool emptyLineFill; // parsing variables int lineNumber; bool isInQuote; bool isInComment; char quoteChar; // unindent variables int bracketCount; int switchDepth; int eventPreprocDepth; bool lookingForCaseBracket; bool unindentNextLine; bool shouldUnindentLine; bool shouldUnindentComment; // struct used by ParseFormattedLine function // contains variables used to unindent the case blocks struct switchVariables { int switchBracketCount; int unindentDepth; bool unindentCase; }; switchVariables sw; // switch variables struct vector switchStack; // stack vector of switch variables // event table variables bool nextLineIsEventIndent; // begin event table indent is reached bool isInEventTable; // need to indent an event table vector* >* indentableMacros; // SQL variables bool nextLineIsDeclareIndent; // begin declare section indent is reached bool isInDeclareSection; // need to indent a declare section }; // Class ASEnhancer //----------------------------------------------------------------------------- // Class ASFormatter //----------------------------------------------------------------------------- class ASFormatter : public ASBeautifier { public: // functions ASFormatter(); virtual ~ASFormatter(); virtual void init(ASSourceIterator* iter); virtual bool hasMoreLines() const; virtual string nextLine(); LineEndFormat getLineEndFormat() const; bool getIsLineReady() const; void setFormattingStyle(FormatStyle style); void setAddBracketsMode(bool state); void setAddOneLineBracketsMode(bool state); void setRemoveBracketsMode(bool state); void setAttachClass(bool state); void setAttachExternC(bool state); void setAttachNamespace(bool state); void setAttachInline(bool state); void setBracketFormatMode(BracketMode mode); void setBreakAfterMode(bool state); void setBreakClosingHeaderBracketsMode(bool state); void setBreakBlocksMode(bool state); void setBreakClosingHeaderBlocksMode(bool state); void setBreakElseIfsMode(bool state); void setBreakOneLineBlocksMode(bool state); void setMethodPrefixPaddingMode(bool state); void setMethodPrefixUnPaddingMode(bool state); void setCloseTemplatesMode(bool state); void setDeleteEmptyLinesMode(bool state); void setIndentCol1CommentsMode(bool state); void setLineEndFormat(LineEndFormat fmt); void setMaxCodeLength(int max); void setObjCColonPaddingMode(ObjCColonPad mode); void setOperatorPaddingMode(bool mode); void setParensOutsidePaddingMode(bool mode); void setParensFirstPaddingMode(bool mode); void setParensInsidePaddingMode(bool mode); void setParensHeaderPaddingMode(bool mode); void setParensUnPaddingMode(bool state); void setPointerAlignment(PointerAlign alignment); void setPreprocBlockIndent(bool state); void setReferenceAlignment(ReferenceAlign alignment); void setSingleStatementsMode(bool state); void setStripCommentPrefix(bool state); void setTabSpaceConversionMode(bool state); size_t getChecksumIn() const; size_t getChecksumOut() const; int getChecksumDiff() const; int getFormatterFileType() const; private: // functions ASFormatter(const ASFormatter ©); // copy constructor not to be implemented ASFormatter &operator=(ASFormatter &); // assignment operator not to be implemented template void deleteContainer(T &container); template void initContainer(T &container, T value); char peekNextChar() const; BracketType getBracketType(); bool adjustChecksumIn(int adjustment); bool computeChecksumIn(const string ¤tLine_); bool computeChecksumOut(const string &beautifiedLine); bool addBracketsToStatement(); bool removeBracketsFromStatement(); bool commentAndHeaderFollows(); bool getNextChar(); bool getNextLine(bool emptyLineWasDeleted = false); bool isArrayOperator() const; bool isBeforeComment() const; bool isBeforeAnyComment() const; bool isBeforeAnyLineEndComment(int startPos) const; bool isBeforeMultipleLineEndComments(int startPos) const; bool isBracketType(BracketType a, BracketType b) const; bool isClassInitializer() const; bool isClosingHeader(const string* header) const; bool isCurrentBracketBroken() const; bool isDereferenceOrAddressOf() const; bool isExecSQL(string &line, size_t index) const; bool isEmptyLine(const string &line) const; bool isExternC() const; bool isNextWordSharpNonParenHeader(int startChar) const; bool isNonInStatementArrayBracket() const; bool isOkToSplitFormattedLine(); bool isPointerOrReference() const; bool isPointerOrReferenceCentered() const; bool isPointerOrReferenceVariable(string &word) const; bool isSharpStyleWithParen(const string* header) const; bool isStructAccessModified(string &firstLine, size_t index) const; bool isIndentablePreprocessorBlock(string &firstLine, size_t index); bool isUnaryOperator() const; bool isUniformInitializerBracket() const; bool isImmediatelyPostCast() const; bool isInExponent() const; bool isInSwitchStatement() const; bool isNextCharOpeningBracket(int startChar) const; bool isOkToBreakBlock(BracketType bracketType) const; bool isOperatorPaddingDisabled() const; bool pointerSymbolFollows() const; int getCurrentLineCommentAdjustment(); int getNextLineCommentAdjustment(); int isOneLineBlockReached(string &line, int startChar) const; void adjustComments(); void appendChar(char ch, bool canBreakLine); void appendCharInsideComments(); void appendOperator(const string &sequence, bool canBreakLine = true); void appendSequence(const string &sequence, bool canBreakLine = true); void appendSpacePad(); void appendSpaceAfter(); void breakLine(bool isSplitLine = false); void buildLanguageVectors(); void updateFormattedLineSplitPoints(char appendedChar); void updateFormattedLineSplitPointsOperator(const string &sequence); void checkIfTemplateOpener(); void clearFormattedLineSplitPoints(); void convertTabToSpaces(); void deleteContainer(vector* &container); void formatArrayRunIn(); void formatRunIn(); void formatArrayBrackets(BracketType bracketType, bool isOpeningArrayBracket); void formatClosingBracket(BracketType bracketType); void formatCommentBody(); void formatCommentOpener(); void formatCommentCloser(); void formatLineCommentBody(); void formatLineCommentOpener(); void formatOpeningBracket(BracketType bracketType); void formatQuoteBody(); void formatQuoteOpener(); void formatPointerOrReference(); void formatPointerOrReferenceCast(); void formatPointerOrReferenceToMiddle(); void formatPointerOrReferenceToName(); void formatPointerOrReferenceToType(); void fixOptionVariableConflicts(); void goForward(int i); void isLineBreakBeforeClosingHeader(); void initContainer(vector* &container, vector* value); void initNewLine(); void padObjCMethodColon(); void padOperators(const string* newOperator); void padParens(); void processPreprocessor(); void resetEndOfStatement(); void setAttachClosingBracketMode(bool state); void setBreakBlocksVariables(); void stripCommentPrefix(); void testForTimeToSplitFormattedLine(); void trimContinuationLine(); void updateFormattedLineSplitPointsPointerOrReference(size_t index); size_t findFormattedLineSplitPoint() const; size_t findNextChar(string &line, char searchChar, int searchStart = 0); const string* checkForHeaderFollowingComment(const string &firstLine) const; const string* getFollowingOperator() const; string getPreviousWord(const string &line, int currPos) const; string peekNextText(const string &firstLine, bool endOnEmptyLine = false, bool shouldReset = false) const; private: // variables int formatterFileType; vector* headers; vector* nonParenHeaders; vector* preDefinitionHeaders; vector* preCommandHeaders; vector* operators; vector* assignmentOperators; vector* castOperators; vector* >* indentableMacros; // for ASEnhancer ASSourceIterator* sourceIterator; ASEnhancer* enhancer; vector* preBracketHeaderStack; vector* bracketTypeStack; vector* parenStack; vector* structStack; vector* questionMarkStack; string currentLine; string formattedLine; string readyFormattedLine; string verbatimDelimiter; const string* currentHeader; const string* previousOperator; // used ONLY by pad-oper char currentChar; char previousChar; char previousNonWSChar; char previousCommandChar; char quoteChar; streamoff preprocBlockEnd; int charNum; int horstmannIndentChars; int nextLineSpacePadNum; int preprocBracketTypeStackSize; int spacePadNum; int tabIncrementIn; int templateDepth; int squareBracketCount; size_t checksumIn; size_t checksumOut; size_t currentLineFirstBracketNum; // first bracket location on currentLine size_t formattedLineCommentNum; // comment location on formattedLine size_t leadingSpaces; size_t maxCodeLength; // possible split points size_t maxSemi; // probably a 'for' statement size_t maxAndOr; // probably an 'if' statement size_t maxComma; size_t maxParen; size_t maxWhiteSpace; size_t maxSemiPending; size_t maxAndOrPending; size_t maxCommaPending; size_t maxParenPending; size_t maxWhiteSpacePending; size_t previousReadyFormattedLineLength; FormatStyle formattingStyle; BracketMode bracketFormatMode; BracketType previousBracketType; PointerAlign pointerAlignment; ReferenceAlign referenceAlignment; ObjCColonPad objCColonPadMode; LineEndFormat lineEnd; bool isVirgin; bool shouldPadOperators; bool shouldPadParensOutside; bool shouldPadFirstParen; bool shouldPadParensInside; bool shouldPadHeader; bool shouldStripCommentPrefix; bool shouldUnPadParens; bool shouldConvertTabs; bool shouldIndentCol1Comments; bool shouldIndentPreprocBlock; bool shouldCloseTemplates; bool shouldAttachExternC; bool shouldAttachNamespace; bool shouldAttachClass; bool shouldAttachInline; bool isInLineComment; bool isInComment; bool isInCommentStartLine; bool noTrimCommentContinuation; bool isInPreprocessor; bool isInPreprocessorBeautify; bool isInTemplate; bool doesLineStartComment; bool lineEndsInCommentOnly; bool lineIsCommentOnly; bool lineIsLineCommentOnly; bool lineIsEmpty; bool isImmediatelyPostCommentOnly; bool isImmediatelyPostEmptyLine; bool isInClassInitializer; bool isInQuote; bool isInVerbatimQuote; bool haveLineContinuationChar; bool isInQuoteContinuation; bool isHeaderInMultiStatementLine; bool isSpecialChar; bool isNonParenHeader; bool foundQuestionMark; bool foundPreDefinitionHeader; bool foundNamespaceHeader; bool foundClassHeader; bool foundStructHeader; bool foundInterfaceHeader; bool foundPreCommandHeader; bool foundPreCommandMacro; bool foundCastOperator; bool isInLineBreak; bool endOfAsmReached; bool endOfCodeReached; bool lineCommentNoIndent; bool isFormattingModeOff; bool isInEnum; bool isInExecSQL; bool isInAsm; bool isInAsmOneLine; bool isInAsmBlock; bool isLineReady; bool elseHeaderFollowsComments; bool caseHeaderFollowsComments; bool isPreviousBracketBlockRelated; bool isInPotentialCalculation; bool isCharImmediatelyPostComment; bool isPreviousCharPostComment; bool isCharImmediatelyPostLineComment; bool isCharImmediatelyPostOpenBlock; bool isCharImmediatelyPostCloseBlock; bool isCharImmediatelyPostTemplate; bool isCharImmediatelyPostReturn; bool isCharImmediatelyPostThrow; bool isCharImmediatelyPostOperator; bool isCharImmediatelyPostPointerOrReference; bool isInObjCMethodDefinition; bool isInObjCInterface; bool isInObjCSelector; bool breakCurrentOneLineBlock; bool shouldRemoveNextClosingBracket; bool isInHorstmannRunIn; bool currentLineBeginsWithBracket; bool attachClosingBracketMode; bool shouldBreakOneLineBlocks; bool shouldReparseCurrentChar; bool shouldBreakOneLineStatements; bool shouldBreakClosingHeaderBrackets; bool shouldBreakElseIfs; bool shouldBreakLineAfterLogical; bool shouldAddBrackets; bool shouldAddOneLineBrackets; bool shouldRemoveBrackets; bool shouldPadMethodColon; bool shouldPadMethodPrefix; bool shouldUnPadMethodPrefix; bool shouldDeleteEmptyLines; bool needHeaderOpeningBracket; bool shouldBreakLineAtNextChar; bool shouldKeepLineUnbroken; bool passedSemicolon; bool passedColon; bool isImmediatelyPostNonInStmt; bool isCharImmediatelyPostNonInStmt; bool isImmediatelyPostComment; bool isImmediatelyPostLineComment; bool isImmediatelyPostEmptyBlock; bool isImmediatelyPostPreprocessor; bool isImmediatelyPostReturn; bool isImmediatelyPostThrow; bool isImmediatelyPostOperator; bool isImmediatelyPostTemplate; bool isImmediatelyPostPointerOrReference; bool shouldBreakBlocks; bool shouldBreakClosingHeaderBlocks; bool isPrependPostBlockEmptyLineRequested; bool isAppendPostBlockEmptyLineRequested; bool isIndentableProprocessor; bool isIndentableProprocessorBlock; bool prependEmptyLine; bool appendOpeningBracket; bool foundClosingHeader; bool isInHeader; bool isImmediatelyPostHeader; bool isInCase; bool isFirstPreprocConditional; bool processedFirstConditional; bool isJavaStaticConstructor; private: // inline functions // append the CURRENT character (curentChar) to the current formatted line. void appendCurrentChar(bool canBreakLine = true) { appendChar(currentChar, canBreakLine); } // check if a specific sequence exists in the current placement of the current line bool isSequenceReached(const char* sequence) const { return currentLine.compare(charNum, strlen(sequence), sequence) == 0; } // call ASBase::findHeader for the current character const string* findHeader(const vector* headers_) { return ASBeautifier::findHeader(currentLine, charNum, headers_); } // call ASBase::findOperator for the current character const string* findOperator(const vector* headers_) { return ASBeautifier::findOperator(currentLine, charNum, headers_); } }; // Class ASFormatter //----------------------------------------------------------------------------- // astyle namespace global declarations //----------------------------------------------------------------------------- // sort comparison functions for ASResource bool sortOnLength(const string* a, const string* b); bool sortOnName(const string* a, const string* b); } // end of astyle namespace // end of astyle namespace -------------------------------------------------- #endif // closes ASTYLE_H ================================================ FILE: 3rdpart/astyle/astyle.pri ================================================ SOURCES += $$PWD/ASBeautifier.cpp SOURCES += $$PWD/ASEnhancer.cpp SOURCES += $$PWD/ASFormatter.cpp SOURCES += $$PWD/ASLocalizer.cpp SOURCES += $$PWD/ASResource.cpp SOURCES += $$PWD/astyle_main.cpp HEADERS += $$PWD/ASLocalizer.h HEADERS += $$PWD/astyle.h HEADERS += $$PWD/astyle_main.h DEFINES += ASTYLE_LIB INCLUDEPATH += $$PWD ================================================ FILE: 3rdpart/astyle/astyle_main.cpp ================================================ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * astyle_main.cpp * * Copyright (C) 2014 by Jim Pattee * * * This file is a part of Artistic Style - an indentation and * reformatting tool for C, C++, C# and Java source files. * * * Artistic Style is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Artistic Style is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Artistic Style. If not, see . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* AStyle_main source file map. This source file contains several classes. They are arranged as follows. --------------------------------------- namespace astyle { ASStreamIterator methods ASConsole methods // Windows specific // Linux specific ASLibrary methods // Windows specific // Linux specific ASOptions methods Utf8_16 methods } // end of astyle namespace Global Area --------------------------- Java Native Interface functions AStyleMainUtf16 entry point AStyleMain entry point AStyleGetVersion entry point main entry point --------------------------------------- */ #include "astyle_main.h" #include #include #include #include #include // includes for recursive getFileNames() function #ifdef _WIN32 #undef UNICODE // use ASCII windows functions #include #else #include #include #include #ifdef __VMS #include #include #include #include #include #include #endif /* __VMS */ #endif #ifdef __DMC__ #include #endif // turn off MinGW automatic file globbing // this CANNOT be in the astyle namespace #ifndef ASTYLE_LIB int _CRT_glob = 0; #endif //---------------------------------------------------------------------------- // astyle namespace //---------------------------------------------------------------------------- namespace astyle { // console build variables #ifndef ASTYLE_LIB ASConsole* g_console = NULL; // class to encapsulate console variables ostream* _err = &cerr; // direct error messages to cerr #ifdef _WIN32 char g_fileSeparator = '\\'; // Windows file separator bool g_isCaseSensitive = false; // Windows IS case sensitive #else char g_fileSeparator = '/'; // Linux file separator bool g_isCaseSensitive = true; // Linux IS NOT case sensitive #endif // _WIN32 #endif // ASTYLE_LIB // java library build variables #ifdef ASTYLE_JNI JNIEnv* g_env; jobject g_obj; jmethodID g_mid; #endif const char* g_version = "2.05.1"; //----------------------------------------------------------------------------- // ASStreamIterator class // typename will be istringstream for GUI and istream otherwise //----------------------------------------------------------------------------- template ASStreamIterator::ASStreamIterator(T* in) { inStream = in; buffer.reserve(200); eolWindows = 0; eolLinux = 0; eolMacOld = 0; outputEOL[0] = '\0'; peekStart = 0; prevLineDeleted = false; checkForEmptyLine = false; // get length of stream inStream->seekg(0, inStream->end); streamLength = inStream->tellg(); inStream->seekg(0, inStream->beg); } template ASStreamIterator::~ASStreamIterator() { } /** * get the length of the input stream. * streamLength variable is set by the constructor. * * @return length of the input file stream, converted to an int. */ template int ASStreamIterator::getStreamLength() const { return static_cast(streamLength); } /** * read the input stream, delete any end of line characters, * and build a string that contains the input line. * * @return string containing the next input line minus any end of line characters */ template string ASStreamIterator::nextLine(bool emptyLineWasDeleted) { // verify that the current position is correct assert(peekStart == 0); // a deleted line may be replaced if break-blocks is requested // this sets up the compare to check for a replaced empty line if (prevLineDeleted) { prevLineDeleted = false; checkForEmptyLine = true; } if (!emptyLineWasDeleted) prevBuffer = buffer; else prevLineDeleted = true; // read the next record buffer.clear(); char ch; inStream->get(ch); while (!inStream->eof() && ch != '\n' && ch != '\r') { buffer.append(1, ch); inStream->get(ch); } if (inStream->eof()) { return buffer; } int peekCh = inStream->peek(); // find input end-of-line characters if (!inStream->eof()) { if (ch == '\r') // CR+LF is windows otherwise Mac OS 9 { if (peekCh == '\n') { inStream->get(); eolWindows++; } else eolMacOld++; } else // LF is Linux, allow for improbable LF/CR { if (peekCh == '\r') { inStream->get(); eolWindows++; } else eolLinux++; } } else { inStream->clear(); } // set output end of line characters if (eolWindows >= eolLinux) { if (eolWindows >= eolMacOld) strcpy(outputEOL, "\r\n"); // Windows (CR+LF) else strcpy(outputEOL, "\r"); // MacOld (CR) } else if (eolLinux >= eolMacOld) strcpy(outputEOL, "\n"); // Linux (LF) else strcpy(outputEOL, "\r"); // MacOld (CR) return buffer; } // save the current position and get the next line // this can be called for multiple reads // when finished peeking you MUST call peekReset() // call this function from ASFormatter ONLY template string ASStreamIterator::peekNextLine() { assert(hasMoreLines()); string nextLine_; char ch; if (peekStart == 0) peekStart = inStream->tellg(); // read the next record inStream->get(ch); while (!inStream->eof() && ch != '\n' && ch != '\r') { nextLine_.append(1, ch); inStream->get(ch); } if (inStream->eof()) { return nextLine_; } int peekCh = inStream->peek(); // remove end-of-line characters if (!inStream->eof()) { if ((peekCh == '\n' || peekCh == '\r') && peekCh != ch) inStream->get(); } return nextLine_; } // reset current position and EOF for peekNextLine() template void ASStreamIterator::peekReset() { assert(peekStart != 0); inStream->clear(); inStream->seekg(peekStart); peekStart = 0; } // save the last input line after input has reached EOF template void ASStreamIterator::saveLastInputLine() { assert(inStream->eof()); prevBuffer = buffer; } // return position of the get pointer template streamoff ASStreamIterator::tellg() { return inStream->tellg(); } // check for a change in line ends template bool ASStreamIterator::getLineEndChange(int lineEndFormat) const { assert(lineEndFormat == LINEEND_DEFAULT || lineEndFormat == LINEEND_WINDOWS || lineEndFormat == LINEEND_LINUX || lineEndFormat == LINEEND_MACOLD); bool lineEndChange = false; if (lineEndFormat == LINEEND_WINDOWS) lineEndChange = (eolLinux + eolMacOld != 0); else if (lineEndFormat == LINEEND_LINUX) lineEndChange = (eolWindows + eolMacOld != 0); else if (lineEndFormat == LINEEND_MACOLD) lineEndChange = (eolWindows + eolLinux != 0); else { if (eolWindows > 0) lineEndChange = (eolLinux + eolMacOld != 0); else if (eolLinux > 0) lineEndChange = (eolWindows + eolMacOld != 0); else if (eolMacOld > 0) lineEndChange = (eolWindows + eolLinux != 0); } return lineEndChange; } //----------------------------------------------------------------------------- // ASConsole class // main function will be included only in the console build //----------------------------------------------------------------------------- #ifndef ASTYLE_LIB // rewrite a stringstream converting the line ends void ASConsole::convertLineEnds(ostringstream &out, int lineEnd) { assert(lineEnd == LINEEND_WINDOWS || lineEnd == LINEEND_LINUX || lineEnd == LINEEND_MACOLD); const string &inStr = out.str(); // avoids strange looking syntax string outStr; // the converted output int inLength = inStr.length(); for (int pos = 0; pos < inLength; pos++) { if (inStr[pos] == '\r') { if (inStr[pos + 1] == '\n') { // CRLF if (lineEnd == LINEEND_CR) { outStr += inStr[pos]; // Delete the LF pos++; continue; } else if (lineEnd == LINEEND_LF) { outStr += inStr[pos + 1]; // Delete the CR pos++; continue; } else { outStr += inStr[pos]; // Do not change outStr += inStr[pos + 1]; pos++; continue; } } else { // CR if (lineEnd == LINEEND_CRLF) { outStr += inStr[pos]; // Insert the CR outStr += '\n'; // Insert the LF continue; } else if (lineEnd == LINEEND_LF) { outStr += '\n'; // Insert the LF continue; } else { outStr += inStr[pos]; // Do not change continue; } } } else if (inStr[pos] == '\n') { // LF if (lineEnd == LINEEND_CRLF) { outStr += '\r'; // Insert the CR outStr += inStr[pos]; // Insert the LF continue; } else if (lineEnd == LINEEND_CR) { outStr += '\r'; // Insert the CR continue; } else { outStr += inStr[pos]; // Do not change continue; } } else { outStr += inStr[pos]; // Write the current char } } // replace the stream out.str(outStr); } void ASConsole::correctMixedLineEnds(ostringstream &out) { LineEndFormat lineEndFormat = LINEEND_DEFAULT; if (strcmp(outputEOL, "\r\n") == 0) lineEndFormat = LINEEND_WINDOWS; if (strcmp(outputEOL, "\n") == 0) lineEndFormat = LINEEND_LINUX; if (strcmp(outputEOL, "\r") == 0) lineEndFormat = LINEEND_MACOLD; convertLineEnds(out, lineEndFormat); } // check files for 16 or 32 bit encoding // the file must have a Byte Order Mark (BOM) // NOTE: some string functions don't work with NULLs (e.g. length()) FileEncoding ASConsole::detectEncoding(const char* data, size_t dataSize) const { FileEncoding encoding = ENCODING_8BIT; if (dataSize >= 4 && memcmp(data, "\x00\x00\xFE\xFF", 4) == 0) encoding = UTF_32BE; else if (dataSize >= 4 && memcmp(data, "\xFF\xFE\x00\x00", 4) == 0) encoding = UTF_32LE; else if (dataSize >= 2 && memcmp(data, "\xFE\xFF", 2) == 0) encoding = UTF_16BE; else if (dataSize >= 2 && memcmp(data, "\xFF\xFE", 2) == 0) encoding = UTF_16LE; return encoding; } // error exit without a message void ASConsole::error() const { (*_err) << _("\nArtistic Style has terminated") << endl; exit(EXIT_FAILURE); } // error exit with a message void ASConsole::error(const char* why, const char* what) const { (*_err) << why << ' ' << what << endl; error(); } /** * If no files have been given, use cin for input and cout for output. * * This is used to format text for text editors like TextWrangler (Mac). * Do NOT display any console messages when this function is used. */ void ASConsole::formatCinToCout() { // Using cin.tellg() causes problems with both Windows and Linux. // The Windows problem occurs when the input is not Windows line-ends. // The tellg() will be out of sequence with the get() statements. // The Linux cin.tellg() will return -1 (invalid). // Copying the input sequentially to a stringstream before // formatting solves the problem for both. istream* inStream = &cin; stringstream outStream; char ch; inStream->get(ch); while (!inStream->eof()) { outStream.put(ch); inStream->get(ch); } ASStreamIterator streamIterator(&outStream); // Windows pipe or redirection always outputs Windows line-ends. // Linux pipe or redirection will output any line end. LineEndFormat lineEndFormat = formatter.getLineEndFormat(); initializeOutputEOL(lineEndFormat); formatter.init(&streamIterator); while (formatter.hasMoreLines()) { cout << formatter.nextLine(); if (formatter.hasMoreLines()) { setOutputEOL(lineEndFormat, streamIterator.getOutputEOL()); cout << outputEOL; } else { // this can happen if the file if missing a closing bracket and break-blocks is requested if (formatter.getIsLineReady()) { setOutputEOL(lineEndFormat, streamIterator.getOutputEOL()); cout << outputEOL; cout << formatter.nextLine(); } } } cout.flush(); } /** * Open input file, format it, and close the output. * * @param fileName_ The path and name of the file to be processed. */ void ASConsole::formatFile(const string &fileName_) { stringstream in; ostringstream out; FileEncoding encoding = readFile(fileName_, in); // Unless a specific language mode has been set, set the language mode // according to the file's suffix. if (!formatter.getModeManuallySet()) { if (stringEndsWith(fileName_, string(".java"))) formatter.setJavaStyle(); else if (stringEndsWith(fileName_, string(".cs"))) formatter.setSharpStyle(); else formatter.setCStyle(); } // set line end format string nextLine; // next output line filesAreIdentical = true; // input and output files are identical LineEndFormat lineEndFormat = formatter.getLineEndFormat(); initializeOutputEOL(lineEndFormat); // do this AFTER setting the file mode ASStreamIterator streamIterator(&in); formatter.init(&streamIterator); // format the file while (formatter.hasMoreLines()) { nextLine = formatter.nextLine(); out << nextLine; linesOut++; if (formatter.hasMoreLines()) { setOutputEOL(lineEndFormat, streamIterator.getOutputEOL()); out << outputEOL; } else { streamIterator.saveLastInputLine(); // to compare the last input line // this can happen if the file if missing a closing bracket and break-blocks is requested if (formatter.getIsLineReady()) { setOutputEOL(lineEndFormat, streamIterator.getOutputEOL()); out << outputEOL; nextLine = formatter.nextLine(); out << nextLine; linesOut++; streamIterator.saveLastInputLine(); } } if (filesAreIdentical) { if (streamIterator.checkForEmptyLine) { if (nextLine.find_first_not_of(" \t") != string::npos) filesAreIdentical = false; } else if (!streamIterator.compareToInputBuffer(nextLine)) filesAreIdentical = false; streamIterator.checkForEmptyLine = false; } } // correct for mixed line ends if (lineEndsMixed) { correctMixedLineEnds(out); filesAreIdentical = false; } // remove targetDirectory from filename if required by print string displayName; if (hasWildcard) displayName = fileName_.substr(targetDirectory.length() + 1); else displayName = fileName_; // if file has changed, write the new file if (!filesAreIdentical || streamIterator.getLineEndChange(lineEndFormat)) { if (!isDryRun) writeFile(fileName_, encoding, out); printMsg(_("Formatted %s\n"), displayName); filesFormatted++; } else { if (!isFormattedOnly) printMsg(_("Unchanged %s\n"), displayName); filesUnchanged++; } assert(formatter.getChecksumDiff() == 0); } // build a vector of argv options // the program path argv[0] is excluded vector ASConsole::getArgvOptions(int argc, char** argv) const { vector argvOptions; for (int i = 1; i < argc; i++) { argvOptions.push_back(string(argv[i])); } return argvOptions; } // for unit testing vector ASConsole::getExcludeHitsVector() const { return excludeHitsVector; } // for unit testing vector ASConsole::getExcludeVector() const { return excludeVector; } // for unit testing vector ASConsole::getFileName() const { return fileName; } // for unit testing vector ASConsole::getFileNameVector() const { return fileNameVector; } // for unit testing vector ASConsole::getFileOptionsVector() const { return fileOptionsVector; } // for unit testing bool ASConsole::getFilesAreIdentical() const { return filesAreIdentical; } // for unit testing int ASConsole::getFilesFormatted() const { return filesFormatted; } // for unit testing bool ASConsole::getIgnoreExcludeErrors() const { return ignoreExcludeErrors; } // for unit testing bool ASConsole::getIgnoreExcludeErrorsDisplay() const { return ignoreExcludeErrorsDisplay; } // for unit testing bool ASConsole::getIsDryRun() const { return isDryRun; } // for unit testing bool ASConsole::getIsFormattedOnly() const { return isFormattedOnly; } // for unit testing string ASConsole::getLanguageID() const { return localizer.getLanguageID(); } // for unit testing bool ASConsole::getIsQuiet() const { return isQuiet; } // for unit testing bool ASConsole::getIsRecursive() const { return isRecursive; } // for unit testing bool ASConsole::getIsVerbose() const { return isVerbose; } // for unit testing bool ASConsole::getLineEndsMixed() const { return lineEndsMixed; } // for unit testing bool ASConsole::getNoBackup() const { return noBackup; } // for unit testing string ASConsole::getOptionsFileName() const { return optionsFileName; } // for unit testing vector ASConsole::getOptionsVector() const { return optionsVector; } // for unit testing string ASConsole::getOrigSuffix() const { return origSuffix; } // for unit testing bool ASConsole::getPreserveDate() const { return preserveDate; } // for unit testing void ASConsole::setBypassBrowserOpen(bool state) { bypassBrowserOpen = state; } string ASConsole::getParam(const string &arg, const char* op) { return arg.substr(strlen(op)); } // initialize output end of line void ASConsole::initializeOutputEOL(LineEndFormat lineEndFormat) { assert(lineEndFormat == LINEEND_DEFAULT || lineEndFormat == LINEEND_WINDOWS || lineEndFormat == LINEEND_LINUX || lineEndFormat == LINEEND_MACOLD); outputEOL[0] = '\0'; // current line end prevEOL[0] = '\0'; // previous line end lineEndsMixed = false; // output has mixed line ends, LINEEND_DEFAULT only if (lineEndFormat == LINEEND_WINDOWS) strcpy(outputEOL, "\r\n"); else if (lineEndFormat == LINEEND_LINUX) strcpy(outputEOL, "\n"); else if (lineEndFormat == LINEEND_MACOLD) strcpy(outputEOL, "\r"); else outputEOL[0] = '\0'; } FileEncoding ASConsole::readFile(const string &fileName_, stringstream &in) const { const int blockSize = 65536; // 64 KB ifstream fin(fileName_.c_str(), ios::binary); if (!fin) error("Cannot open input file", fileName_.c_str()); char* data = new(nothrow) char[blockSize]; if (!data) error("Cannot allocate memory for input file", fileName_.c_str()); fin.read(data, blockSize); if (fin.bad()) error("Cannot read input file", fileName_.c_str()); size_t dataSize = static_cast(fin.gcount()); FileEncoding encoding = detectEncoding(data, dataSize); if (encoding == UTF_32BE || encoding == UTF_32LE) error(_("Cannot process UTF-32 encoding"), fileName_.c_str()); bool firstBlock = true; bool isBigEndian = (encoding == UTF_16BE); while (dataSize) { if (encoding == UTF_16LE || encoding == UTF_16BE) { // convert utf-16 to utf-8 size_t utf8Size = utf8_16.Utf8LengthFromUtf16(data, dataSize, isBigEndian); char* utf8Out = new(nothrow) char[utf8Size]; if (!utf8Out) error("Cannot allocate memory for utf-8 conversion", fileName_.c_str()); size_t utf8Len = utf8_16.Utf16ToUtf8(data, dataSize, isBigEndian, firstBlock, utf8Out); assert(utf8Len == utf8Size); in << string(utf8Out, utf8Len); delete [] utf8Out; } else in << string(data, dataSize); fin.read(data, blockSize); if (fin.bad()) error("Cannot read input file", fileName_.c_str()); dataSize = static_cast(fin.gcount()); firstBlock = false; } fin.close(); delete [] data; return encoding; } void ASConsole::setIgnoreExcludeErrors(bool state) { ignoreExcludeErrors = state; } void ASConsole::setIgnoreExcludeErrorsAndDisplay(bool state) { ignoreExcludeErrors = state; ignoreExcludeErrorsDisplay = state; } void ASConsole::setIsFormattedOnly(bool state) { isFormattedOnly = state; } void ASConsole::setIsQuiet(bool state) { isQuiet = state; } void ASConsole::setIsRecursive(bool state) { isRecursive = state; } void ASConsole::setIsDryRun(bool state) { isDryRun = state; } void ASConsole::setIsVerbose(bool state) { isVerbose = state; } void ASConsole::setNoBackup(bool state) { noBackup = state; } void ASConsole::setOptionsFileName(string name) { optionsFileName = name; } void ASConsole::setOrigSuffix(string suffix) { origSuffix = suffix; } void ASConsole::setPreserveDate(bool state) { preserveDate = state; } // set outputEOL variable void ASConsole::setOutputEOL(LineEndFormat lineEndFormat, const char* currentEOL) { if (lineEndFormat == LINEEND_DEFAULT) { strcpy(outputEOL, currentEOL); if (strlen(prevEOL) == 0) strcpy(prevEOL, outputEOL); if (strcmp(prevEOL, outputEOL) != 0) { lineEndsMixed = true; filesAreIdentical = false; strcpy(prevEOL, outputEOL); } } else { strcpy(prevEOL, currentEOL); if (strcmp(prevEOL, outputEOL) != 0) filesAreIdentical = false; } } #ifdef _WIN32 // Windows specific /** * WINDOWS function to display the last system error. */ void ASConsole::displayLastError() { LPSTR msgBuf; DWORD lastError = GetLastError(); FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, lastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language (LPSTR) &msgBuf, 0, NULL ); // Display the string. (*_err) << "Error (" << lastError << ") " << msgBuf << endl; // Free the buffer. LocalFree(msgBuf); } /** * WINDOWS function to get the current directory. * NOTE: getenv("CD") does not work for Windows Vista. * The Windows function GetCurrentDirectory is used instead. * * @return The path of the current directory */ string ASConsole::getCurrentDirectory(const string &fileName_) const { char currdir[MAX_PATH]; currdir[0] = '\0'; if (!GetCurrentDirectory(sizeof(currdir), currdir)) error("Cannot find file", fileName_.c_str()); return string(currdir); } /** * WINDOWS function to resolve wildcards and recurse into sub directories. * The fileName vector is filled with the path and names of files to process. * * @param directory The path of the directory to be processed. * @param wildcard The wildcard to be processed (e.g. *.cpp). */ void ASConsole::getFileNames(const string &directory, const string &wildcard) { vector subDirectory; // sub directories of directory WIN32_FIND_DATA findFileData; // for FindFirstFile and FindNextFile // Find the first file in the directory // Find will get at least "." and "..". string firstFile = directory + "\\*"; HANDLE hFind = FindFirstFile(firstFile.c_str(), &findFileData); if (hFind == INVALID_HANDLE_VALUE) { // Error (3) The system cannot find the path specified. // Error (123) The filename, directory name, or volume label syntax is incorrect. // ::FindClose(hFind); before exiting displayLastError(); error(_("Cannot open directory"), directory.c_str()); } // save files and sub directories do { // skip hidden or read only if (findFileData.cFileName[0] == '.' || (findFileData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) || (findFileData.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) continue; // is this a sub directory if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if (!isRecursive) continue; // if a sub directory and recursive, save sub directory string subDirectoryPath = directory + g_fileSeparator + findFileData.cFileName; if (isPathExclued(subDirectoryPath)) printMsg(_("Exclude %s\n"), subDirectoryPath.substr(mainDirectoryLength)); else subDirectory.push_back(subDirectoryPath); continue; } // save the file name string filePathName = directory + g_fileSeparator + findFileData.cFileName; // check exclude before wildcmp to avoid "unmatched exclude" error bool isExcluded = isPathExclued(filePathName); // save file name if wildcard match if (wildcmp(wildcard.c_str(), findFileData.cFileName)) { if (isExcluded) printMsg(_("Exclude %s\n"), filePathName.substr(mainDirectoryLength)); else fileName.push_back(filePathName); } } while (FindNextFile(hFind, &findFileData) != 0); // check for processing error ::FindClose(hFind); DWORD dwError = GetLastError(); if (dwError != ERROR_NO_MORE_FILES) error("Error processing directory", directory.c_str()); // recurse into sub directories // if not doing recursive subDirectory is empty for (unsigned i = 0; i < subDirectory.size(); i++) getFileNames(subDirectory[i], wildcard); return; } /** * WINDOWS function to format a number according to the current locale. * This formats positive integers only, no float. * * @param num The number to be formatted. * @param lcid The LCID of the locale to be used for testing. * @return The formatted number. */ string ASConsole::getNumberFormat(int num, size_t lcid) const { #if defined(_MSC_VER) || defined(__MINGW32__) || defined(__BORLANDC__) || defined(__GNUC__) // Compilers that don't support C++ locales should still support this assert. // The C locale should be set but not the C++. // This function is not necessary if the C++ locale is set. // The locale().name() return value is not portable to all compilers. assert(locale().name() == "C"); #endif // convert num to a string stringstream alphaNum; alphaNum << num; string number = alphaNum.str(); if (useAscii) return number; // format the number using the Windows API if (lcid == 0) lcid = LOCALE_USER_DEFAULT; int outSize = ::GetNumberFormat(lcid, 0, number.c_str(), NULL, NULL, 0); char* outBuf = new(nothrow) char[outSize]; if (outBuf == NULL) return number; ::GetNumberFormat(lcid, 0, number.c_str(), NULL, outBuf, outSize); string formattedNum(outBuf); delete [] outBuf; // remove the decimal int decSize = ::GetLocaleInfo(lcid, LOCALE_SDECIMAL, NULL, 0); char* decBuf = new(nothrow) char[decSize]; if (decBuf == NULL) return number; ::GetLocaleInfo(lcid, LOCALE_SDECIMAL, decBuf, decSize); size_t i = formattedNum.rfind(decBuf); delete [] decBuf; if (i != string::npos) formattedNum.erase(i); if (!formattedNum.length()) formattedNum = "0"; return formattedNum; } /** * WINDOWS function to open a HTML file in the default browser. */ void ASConsole::launchDefaultBrowser(const char* filePathIn /*NULL*/) const { struct stat statbuf; const char* envPaths[] = { "PROGRAMFILES(X86)", "PROGRAMFILES" }; size_t pathsLen = sizeof(envPaths) / sizeof(envPaths[0]); string htmlDefaultPath; for (size_t i = 0; i < pathsLen; i++) { const char* envPath = getenv(envPaths[i]); if (envPath == NULL) continue; htmlDefaultPath = envPath; if (htmlDefaultPath.length() > 0 && htmlDefaultPath[htmlDefaultPath.length() - 1] == g_fileSeparator) htmlDefaultPath.erase(htmlDefaultPath.length() - 1); htmlDefaultPath.append("\\AStyle\\doc"); if (stat(htmlDefaultPath.c_str(), &statbuf) == 0 && statbuf.st_mode & S_IFDIR) break; } htmlDefaultPath.append("\\"); // build file path string htmlFilePath; if (filePathIn == NULL) htmlFilePath = htmlDefaultPath + "astyle.html"; else { if (strpbrk(filePathIn, "\\/") == NULL) htmlFilePath = htmlDefaultPath + filePathIn; else htmlFilePath = filePathIn; } standardizePath(htmlFilePath); if (stat(htmlFilePath.c_str(), &statbuf) != 0 || !(statbuf.st_mode & S_IFREG)) { printf(_("Cannot open HTML file %s\n"), htmlFilePath.c_str()); return; } SHELLEXECUTEINFO sei = { sizeof(sei), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; sei.fMask = SEE_MASK_FLAG_NO_UI; sei.lpVerb = "open"; sei.lpFile = htmlFilePath.c_str(); sei.nShow = SW_SHOWNORMAL; // browser open will be bypassed in test programs printf(_("Opening HTML documentation %s\n"), htmlFilePath.c_str()); if (!bypassBrowserOpen) { int ret = ShellExecuteEx(&sei); if (!ret) error(_("Command execute failure"), htmlFilePath.c_str()); } } #else // Linux specific /** * LINUX function to get the current directory. * This is done if the fileName does not contain a path. * It is probably from an editor sending a single file. * * @param fileName_ The filename is used only for the error message. * @return The path of the current directory */ string ASConsole::getCurrentDirectory(const string &fileName_) const { char* currdir = getenv("PWD"); if (currdir == NULL) error("Cannot find file", fileName_.c_str()); return string(currdir); } /** * LINUX function to resolve wildcards and recurse into sub directories. * The fileName vector is filled with the path and names of files to process. * * @param directory The path of the directory to be processed. * @param wildcard The wildcard to be processed (e.g. *.cpp). */ void ASConsole::getFileNames(const string &directory, const string &wildcard) { struct dirent* entry; // entry from readdir() struct stat statbuf; // entry from stat() vector subDirectory; // sub directories of this directory // errno is defined in and is set for errors in opendir, readdir, or stat errno = 0; DIR* dp = opendir(directory.c_str()); if (dp == NULL) error(_("Cannot open directory"), directory.c_str()); // save the first fileName entry for this recursion const unsigned firstEntry = fileName.size(); // save files and sub directories while ((entry = readdir(dp)) != NULL) { // get file status string entryFilepath = directory + g_fileSeparator + entry->d_name; if (stat(entryFilepath.c_str(), &statbuf) != 0) { if (errno == EOVERFLOW) // file over 2 GB is OK { errno = 0; continue; } perror("errno message"); error("Error getting file status in directory", directory.c_str()); } // skip hidden or read only if (entry->d_name[0] == '.' || !(statbuf.st_mode & S_IWUSR)) continue; // if a sub directory and recursive, save sub directory if (S_ISDIR(statbuf.st_mode) && isRecursive) { if (isPathExclued(entryFilepath)) printMsg(_("Exclude %s\n"), entryFilepath.substr(mainDirectoryLength)); else subDirectory.push_back(entryFilepath); continue; } // if a file, save file name if (S_ISREG(statbuf.st_mode)) { // check exclude before wildcmp to avoid "unmatched exclude" error bool isExcluded = isPathExclued(entryFilepath); // save file name if wildcard match if (wildcmp(wildcard.c_str(), entry->d_name)) { if (isExcluded) printMsg(_("Exclude %s\n"), entryFilepath.substr(mainDirectoryLength)); else fileName.push_back(entryFilepath); } } } if (closedir(dp) != 0) { perror("errno message"); error("Error reading directory", directory.c_str()); } // sort the current entries for fileName if (firstEntry < fileName.size()) sort(&fileName[firstEntry], &fileName[fileName.size()]); // recurse into sub directories // if not doing recursive, subDirectory is empty if (subDirectory.size() > 1) sort(subDirectory.begin(), subDirectory.end()); for (unsigned i = 0; i < subDirectory.size(); i++) { getFileNames(subDirectory[i], wildcard); } return; } /** * LINUX function to get locale information and call getNumberFormat. * This formats positive integers only, no float. * * @param num The number to be formatted. * size_t is for compatibility with the Windows function. * @return The formatted number. */ string ASConsole::getNumberFormat(int num, size_t) const { #if defined(_MSC_VER) || defined(__MINGW32__) || defined(__BORLANDC__) || defined(__GNUC__) // Compilers that don't support C++ locales should still support this assert. // The C locale should be set but not the C++. // This function is not necessary if the C++ locale is set. // The locale().name() return value is not portable to all compilers. assert(locale().name() == "C"); #endif // get the locale info struct lconv* lc; lc = localeconv(); // format the number return getNumberFormat(num, lc->grouping, lc->thousands_sep); } /** * LINUX function to format a number according to the current locale. * This formats positive integers only, no float. * * @param num The number to be formatted. * @param groupingArg The grouping string from the locale. * @param separator The thousands group separator from the locale. * @return The formatted number. */ string ASConsole::getNumberFormat(int num, const char* groupingArg, const char* separator) const { // convert num to a string stringstream alphaNum; alphaNum << num; string number = alphaNum.str(); // format the number from right to left string formattedNum; size_t ig = 0; // grouping index int grouping = groupingArg[ig]; int i = number.length(); // check for no grouping if (grouping == 0) grouping = number.length(); while (i > 0) { // extract a group of numbers string group; if (i < grouping) group = number; else group = number.substr(i - grouping); // update formatted number formattedNum.insert(0, group); i -= grouping; if (i < 0) i = 0; if (i > 0) formattedNum.insert(0, separator); number.erase(i); // update grouping if (groupingArg[ig] != '\0' && groupingArg[ig + 1] != '\0') grouping = groupingArg[++ig]; } return formattedNum; } /** * LINUX function to open a HTML file in the default browser. * Use xdg-open from freedesktop.org cross-desktop compatibility suite xdg-utils. * see http://portland.freedesktop.org/wiki/ * This is installed on most modern distributions. */ void ASConsole::launchDefaultBrowser(const char* filePathIn /*NULL*/) const { struct stat statbuf; string htmlDefaultPath = "/usr/share/doc/astyle/html/"; string htmlDefaultFile = "astyle.html"; // build file path string htmlFilePath; if (filePathIn == NULL) htmlFilePath = htmlDefaultPath + htmlDefaultFile; else { if (strpbrk(filePathIn, "\\/") == NULL) htmlFilePath = htmlDefaultPath + filePathIn; else htmlFilePath = filePathIn; } standardizePath(htmlFilePath); if (stat(htmlFilePath.c_str(), &statbuf) != 0 || !(statbuf.st_mode & S_IFREG)) { printf(_("Cannot open HTML file %s\n"), htmlFilePath.c_str()); return; } // get search paths const char* envPaths = getenv("PATH"); if (envPaths == NULL) envPaths = "?"; size_t envlen = strlen(envPaths); char* paths = new char[envlen + 1]; strcpy(paths, envPaths); // find xdg-open (usually in /usr/bin) // Mac uses open instead #ifdef __APPLE__ const char* FILE_OPEN = "open"; #else const char* FILE_OPEN = "xdg-open"; #endif string searchPath; char* searchDir = strtok(paths, ":"); while (searchDir != NULL) { searchPath = searchDir; if (searchPath.length() > 0 && searchPath[searchPath.length() - 1] != g_fileSeparator) searchPath.append(string(1, g_fileSeparator)); searchPath.append(FILE_OPEN); if (stat(searchPath.c_str(), &statbuf) == 0 && (statbuf.st_mode & S_IFREG)) break; searchDir = strtok(NULL, ":"); } delete[] paths; if (searchDir == NULL) error(_("Command is not installed"), FILE_OPEN); // browser open will be bypassed in test programs printf(_("Opening HTML documentation %s\n"), htmlFilePath.c_str()); if (!bypassBrowserOpen) { execlp(FILE_OPEN, FILE_OPEN, htmlFilePath.c_str(), NULL); // execlp will NOT return if successful error(_("Command execute failure"), FILE_OPEN); } } #endif // _WIN32 // get individual file names from the command-line file path void ASConsole::getFilePaths(string &filePath) { fileName.clear(); targetDirectory = string(); targetFilename = string(); // separate directory and file name size_t separator = filePath.find_last_of(g_fileSeparator); if (separator == string::npos) { // if no directory is present, use the currently active directory targetDirectory = getCurrentDirectory(filePath); targetFilename = filePath; mainDirectoryLength = targetDirectory.length() + 1; // +1 includes trailing separator } else { targetDirectory = filePath.substr(0, separator); targetFilename = filePath.substr(separator + 1); mainDirectoryLength = targetDirectory.length() + 1; // +1 includes trailing separator } if (targetFilename.length() == 0) { fprintf(stderr, _("Missing filename in %s\n"), filePath.c_str()); error(); } // check filename for wildcards hasWildcard = false; if (targetFilename.find_first_of("*?") != string::npos) hasWildcard = true; // clear exclude hits vector for (size_t ix = 0; ix < excludeHitsVector.size(); ix++) excludeHitsVector[ix] = false; // If the filename is not quoted on Linux, bash will replace the // wildcard instead of passing it to the program. if (isRecursive && !hasWildcard) { fprintf(stderr, "%s\n", _("Recursive option with no wildcard")); #ifndef _WIN32 fprintf(stderr, "%s\n", _("Did you intend quote the filename")); #endif error(); } // display directory name for wildcard processing if (hasWildcard) { printSeparatingLine(); printMsg(_("Directory %s\n"), targetDirectory + g_fileSeparator + targetFilename); } // create a vector of paths and file names to process if (hasWildcard || isRecursive) getFileNames(targetDirectory, targetFilename); else { // verify a single file is not a directory (needed on Linux) string entryFilepath = targetDirectory + g_fileSeparator + targetFilename; struct stat statbuf; if (stat(entryFilepath.c_str(), &statbuf) == 0 && (statbuf.st_mode & S_IFREG)) fileName.push_back(entryFilepath); } // check for unprocessed excludes bool excludeErr = false; for (size_t ix = 0; ix < excludeHitsVector.size(); ix++) { if (excludeHitsVector[ix] == false) { excludeErr = true; if (!ignoreExcludeErrorsDisplay) { if (ignoreExcludeErrors) printMsg(_("Exclude (unmatched) %s\n"), excludeVector[ix]); else fprintf(stderr, _("Exclude (unmatched) %s\n"), excludeVector[ix].c_str()); } else { if (!ignoreExcludeErrors) fprintf(stderr, _("Exclude (unmatched) %s\n"), excludeVector[ix].c_str()); } } } if (excludeErr && !ignoreExcludeErrors) { if (hasWildcard && !isRecursive) fprintf(stderr, "%s\n", _("Did you intend to use --recursive")); error(); } // check if files were found (probably an input error if not) if (fileName.empty()) { fprintf(stderr, _("No file to process %s\n"), filePath.c_str()); if (hasWildcard && !isRecursive) fprintf(stderr, "%s\n", _("Did you intend to use --recursive")); error(); } if (hasWildcard) printSeparatingLine(); } bool ASConsole::fileNameVectorIsEmpty() const { return fileNameVector.empty(); } bool ASConsole::isOption(const string &arg, const char* op) { return arg.compare(op) == 0; } bool ASConsole::isOption(const string &arg, const char* a, const char* b) { return (isOption(arg, a) || isOption(arg, b)); } bool ASConsole::isParamOption(const string &arg, const char* option) { bool retVal = arg.compare(0, strlen(option), option) == 0; // if comparing for short option, 2nd char of arg must be numeric if (retVal && strlen(option) == 1 && arg.length() > 1) if (!isdigit((unsigned char)arg[1])) retVal = false; return retVal; } // compare a path to the exclude vector // used for both directories and filenames // updates the g_excludeHitsVector // return true if a match bool ASConsole::isPathExclued(const string &subPath) { bool retVal = false; // read the exclude vector checking for a match for (size_t i = 0; i < excludeVector.size(); i++) { string exclude = excludeVector[i]; if (subPath.length() < exclude.length()) continue; size_t compareStart = subPath.length() - exclude.length(); // subPath compare must start with a directory name if (compareStart > 0) { char lastPathChar = subPath[compareStart - 1]; if (lastPathChar != g_fileSeparator) continue; } string compare = subPath.substr(compareStart); if (!g_isCaseSensitive) { // make it case insensitive for Windows for (size_t j = 0; j < compare.length(); j++) compare[j] = (char)tolower(compare[j]); for (size_t j = 0; j < exclude.length(); j++) exclude[j] = (char)tolower(exclude[j]); } // compare sub directory to exclude data - must check them all if (compare == exclude) { excludeHitsVector[i] = true; retVal = true; break; } } return retVal; } void ASConsole::printHelp() const { cout << endl; cout << " Artistic Style " << g_version << endl; cout << " Maintained by: Jim Pattee\n"; cout << " Original Author: Tal Davidson\n"; cout << endl; cout << "Usage:\n"; cout << "------\n"; cout << " astyle [OPTIONS] File1 File2 File3 [...]\n"; cout << endl; cout << " astyle [OPTIONS] < Original > Beautified\n"; cout << endl; cout << " When indenting a specific file, the resulting indented file RETAINS\n"; cout << " the original file-name. The original pre-indented file is renamed,\n"; cout << " with a suffix of \'.orig\' added to the original filename.\n"; cout << endl; cout << " Wildcards (* and ?) may be used in the filename.\n"; cout << " A \'recursive\' option can process directories recursively.\n"; cout << endl; cout << " By default, astyle is set up to indent with four spaces per indent,\n"; cout << " a maximal indentation of 40 spaces inside continuous statements,\n"; cout << " a minimum indentation of eight spaces inside conditional statements,\n"; cout << " and NO formatting options.\n"; cout << endl; cout << "Options:\n"; cout << "--------\n"; cout << " This program follows the usual GNU command line syntax.\n"; cout << " Long options (starting with '--') must be written one at a time.\n"; cout << " Short options (starting with '-') may be appended together.\n"; cout << " Thus, -bps4 is the same as -b -p -s4.\n"; cout << endl; cout << "Options File:\n"; cout << "-------------\n"; cout << " Artistic Style looks for a default options file in the\n"; cout << " following order:\n"; cout << " 1. The contents of the ARTISTIC_STYLE_OPTIONS environment\n"; cout << " variable if it exists.\n"; cout << " 2. The file called .astylerc in the directory pointed to by the\n"; cout << " HOME environment variable ( i.e. $HOME/.astylerc ).\n"; cout << " 3. The file called astylerc in the directory pointed to by the\n"; cout << " USERPROFILE environment variable (i.e. %USERPROFILE%\\astylerc).\n"; cout << " If a default options file is found, the options in this file will\n"; cout << " be parsed BEFORE the command-line options.\n"; cout << " Long options within the default option file may be written without\n"; cout << " the preliminary '--'.\n"; cout << endl; cout << "Disable Formatting:\n"; cout << "----------------------\n"; cout << " Disable Block\n"; cout << " Blocks of code can be disabled with the comment tags *INDENT-OFF*\n"; cout << " and *INDENT-ON*. It must be contained in a one-line comment.\n"; cout << endl; cout << " Disable Line\n"; cout << " Padding of operators can be disabled on a single line using the\n"; cout << " comment tag *NOPAD*. It must be contained in a line-end comment.\n"; cout << endl; cout << "Bracket Style Options:\n"; cout << "----------------------\n"; cout << " default bracket style\n"; cout << " If no bracket style is requested, the opening brackets will not be\n"; cout << " changed and closing brackets will be broken from the preceding line.\n"; cout << endl; cout << " --style=allman OR --style=bsd OR --style=break OR -A1\n"; cout << " Allman style formatting/indenting.\n"; cout << " Broken brackets.\n"; cout << endl; cout << " --style=java OR --style=attach OR -A2\n"; cout << " Java style formatting/indenting.\n"; cout << " Attached brackets.\n"; cout << endl; cout << " --style=kr OR --style=k&r OR --style=k/r OR -A3\n"; cout << " Kernighan & Ritchie style formatting/indenting.\n"; cout << " Linux brackets.\n"; cout << endl; cout << " --style=stroustrup OR -A4\n"; cout << " Stroustrup style formatting/indenting.\n"; cout << " Stroustrup brackets.\n"; cout << endl; cout << " --style=whitesmith OR -A5\n"; cout << " Whitesmith style formatting/indenting.\n"; cout << " Broken, indented brackets.\n"; cout << " Indented class blocks and switch blocks.\n"; cout << endl; cout << " --style=vtk OR -A15\n"; cout << " VTK style formatting/indenting.\n"; cout << " Broken, indented brackets, except for opening brackets.\n"; cout << endl; cout << " --style=banner OR -A6\n"; cout << " Banner style formatting/indenting.\n"; cout << " Attached, indented brackets.\n"; cout << endl; cout << " --style=gnu OR -A7\n"; cout << " GNU style formatting/indenting.\n"; cout << " Broken brackets, indented blocks.\n"; cout << endl; cout << " --style=linux OR --style=knf OR -A8\n"; cout << " Linux style formatting/indenting.\n"; cout << " Linux brackets, minimum conditional indent is one-half indent.\n"; cout << endl; cout << " --style=horstmann OR -A9\n"; cout << " Horstmann style formatting/indenting.\n"; cout << " Run-in brackets, indented switches.\n"; cout << endl; cout << " --style=1tbs OR --style=otbs OR -A10\n"; cout << " One True Brace Style formatting/indenting.\n"; cout << " Linux brackets, add brackets to all conditionals.\n"; cout << endl; cout << " --style=google OR -A14\n"; cout << " Google style formatting/indenting.\n"; cout << " Attached brackets, indented class modifiers.\n"; cout << endl; cout << " --style=pico OR -A11\n"; cout << " Pico style formatting/indenting.\n"; cout << " Run-in opening brackets and attached closing brackets.\n"; cout << " Uses keep one line blocks and keep one line statements.\n"; cout << endl; cout << " --style=lisp OR -A12\n"; cout << " Lisp style formatting/indenting.\n"; cout << " Attached opening brackets and attached closing brackets.\n"; cout << " Uses keep one line statements.\n"; cout << endl; cout << "Tab Options:\n"; cout << "------------\n"; cout << " default indent option\n"; cout << " If no indentation option is set, the default\n"; cout << " option of 4 spaces per indent will be used.\n"; cout << endl; cout << " --indent=spaces=# OR -s#\n"; cout << " Indent using # spaces per indent. Not specifying #\n"; cout << " will result in a default of 4 spaces per indent.\n"; cout << endl; cout << " --indent=tab OR --indent=tab=# OR -t OR -t#\n"; cout << " Indent using tab characters, assuming that each\n"; cout << " indent is # spaces long. Not specifying # will result\n"; cout << " in a default assumption of 4 spaces per indent.\n"; cout << endl; cout << " --indent=force-tab=# OR -T#\n"; cout << " Indent using tab characters, assuming that each\n"; cout << " indent is # spaces long. Force tabs to be used in areas\n"; cout << " AStyle would prefer to use spaces.\n"; cout << endl; cout << " --indent=force-tab-x=# OR -xT#\n"; cout << " Allows the tab length to be set to a length that is different\n"; cout << " from the indent length. This may cause the indentation to be\n"; cout << " a mix of both spaces and tabs. This option sets the tab length.\n"; cout << endl; cout << "Bracket Modify Options:\n"; cout << "-----------------------\n"; cout << " --attach-namespaces OR -xn\n"; cout << " Attach brackets to a namespace statement.\n"; cout << endl; cout << " --attach-classes OR -xc\n"; cout << " Attach brackets to a class statement.\n"; cout << endl; cout << " --attach-inlines OR -xl\n"; cout << " Attach brackets to class inline function definitions.\n"; cout << endl; cout << " --attach-extern-c OR -xk\n"; cout << " Attach brackets to an extern \"C\" statement.\n"; cout << endl; cout << "Indentation Options:\n"; cout << "--------------------\n"; cout << " --indent-classes OR -C\n"; cout << " Indent 'class' blocks so that the entire block is indented.\n"; cout << endl; cout << " --indent-modifiers OR -xG\n"; cout << " Indent 'class' access modifiers, 'public:', 'protected:' or\n"; cout << " 'private:', one half indent. The rest of the class is not\n"; cout << " indented. \n"; cout << endl; cout << " --indent-switches OR -S\n"; cout << " Indent 'switch' blocks, so that the inner 'case XXX:'\n"; cout << " headers are indented in relation to the switch block.\n"; cout << endl; cout << " --indent-cases OR -K\n"; cout << " Indent case blocks from the 'case XXX:' headers.\n"; cout << " Case statements not enclosed in blocks are NOT indented.\n"; cout << endl; cout << " --indent-namespaces OR -N\n"; cout << " Indent the contents of namespace blocks.\n"; cout << endl; cout << " --indent-labels OR -L\n"; cout << " Indent labels so that they appear one indent less than\n"; cout << " the current indentation level, rather than being\n"; cout << " flushed completely to the left (which is the default).\n"; cout << endl; cout << " --indent-preproc-block OR -xW\n"; cout << " Indent preprocessor blocks at bracket level 0.\n"; cout << " Without this option the preprocessor block is not indented.\n"; cout << endl; cout << " --indent-preproc-cond OR -xw\n"; cout << " Indent preprocessor conditional statements #if/#else/#endif\n"; cout << " to the same level as the source code.\n"; cout << endl; cout << " --indent-preproc-define OR -w\n"; cout << " Indent multi-line preprocessor #define statements.\n"; cout << endl; cout << " --indent-col1-comments OR -Y\n"; cout << " Indent line comments that start in column one.\n"; cout << endl; cout << " --min-conditional-indent=# OR -m#\n"; cout << " Indent a minimal # spaces in a continuous conditional\n"; cout << " belonging to a conditional header.\n"; cout << " The valid values are:\n"; cout << " 0 - no minimal indent.\n"; cout << " 1 - indent at least one additional indent.\n"; cout << " 2 - indent at least two additional indents.\n"; cout << " 3 - indent at least one-half an additional indent.\n"; cout << " The default value is 2, two additional indents.\n"; cout << endl; cout << " --max-instatement-indent=# OR -M#\n"; cout << " Indent a maximal # spaces in a continuous statement,\n"; cout << " relative to the previous line.\n"; cout << " The valid values are 40 thru 120.\n"; cout << " The default value is 40.\n"; cout << endl; cout << "Padding Options:\n"; cout << "----------------\n"; cout << " --break-blocks OR -f\n"; cout << " Insert empty lines around unrelated blocks, labels, classes, ...\n"; cout << endl; cout << " --break-blocks=all OR -F\n"; cout << " Like --break-blocks, except also insert empty lines \n"; cout << " around closing headers (e.g. 'else', 'catch', ...).\n"; cout << endl; cout << " --pad-oper OR -p\n"; cout << " Insert space padding around operators.\n"; cout << endl; cout << " --pad-paren OR -P\n"; cout << " Insert space padding around parenthesis on both the outside\n"; cout << " and the inside.\n"; cout << endl; cout << " --pad-paren-out OR -d\n"; cout << " Insert space padding around parenthesis on the outside only.\n"; cout << endl; cout << " --pad-first-paren-out OR -xd\n"; cout << " Insert space padding around first parenthesis in a series on\n"; cout << " the outside only.\n"; cout << endl; cout << " --pad-paren-in OR -D\n"; cout << " Insert space padding around parenthesis on the inside only.\n"; cout << endl; cout << " --pad-header OR -H\n"; cout << " Insert space padding after paren headers (e.g. 'if', 'for'...).\n"; cout << endl; cout << " --unpad-paren OR -U\n"; cout << " Remove unnecessary space padding around parenthesis. This\n"; cout << " can be used in combination with the 'pad' options above.\n"; cout << endl; cout << " --delete-empty-lines OR -xd\n"; cout << " Delete empty lines within a function or method.\n"; cout << " It will NOT delete lines added by the break-blocks options.\n"; cout << endl; cout << " --fill-empty-lines OR -E\n"; cout << " Fill empty lines with the white space of their\n"; cout << " previous lines.\n"; cout << endl; cout << " --align-pointer=type OR -k1\n"; cout << " --align-pointer=middle OR -k2\n"; cout << " --align-pointer=name OR -k3\n"; cout << " Attach a pointer or reference operator (*, &, or ^) to either\n"; cout << " the operator type (left), middle, or operator name (right).\n"; cout << " To align the reference separately use --align-reference.\n"; cout << endl; cout << " --align-reference=none OR -W0\n"; cout << " --align-reference=type OR -W1\n"; cout << " --align-reference=middle OR -W2\n"; cout << " --align-reference=name OR -W3\n"; cout << " Attach a reference operator (&) to either\n"; cout << " the operator type (left), middle, or operator name (right).\n"; cout << " If not set, follow pointer alignment.\n"; cout << endl; cout << "Formatting Options:\n"; cout << "-------------------\n"; cout << " --break-closing-brackets OR -y\n"; cout << " Break brackets before closing headers (e.g. 'else', 'catch', ...).\n"; cout << " Use with --style=java, --style=kr, --style=stroustrup,\n"; cout << " --style=linux, or --style=1tbs.\n"; cout << endl; cout << " --break-elseifs OR -e\n"; cout << " Break 'else if()' statements into two different lines.\n"; cout << endl; cout << " --add-brackets OR -j\n"; cout << " Add brackets to unbracketed one line conditional statements.\n"; cout << endl; cout << " --add-one-line-brackets OR -J\n"; cout << " Add one line brackets to unbracketed one line conditional\n"; cout << " statements.\n"; cout << endl; cout << " --remove-brackets OR -xj\n"; cout << " Remove brackets from a bracketed one line conditional statements.\n"; cout << endl; cout << " --keep-one-line-blocks OR -O\n"; cout << " Don't break blocks residing completely on one line.\n"; cout << endl; cout << " --keep-one-line-statements OR -o\n"; cout << " Don't break lines containing multiple statements into\n"; cout << " multiple single-statement lines.\n"; cout << endl; cout << " --convert-tabs OR -c\n"; cout << " Convert tabs to the appropriate number of spaces.\n"; cout << endl; cout << " --close-templates OR -xy\n"; cout << " Close ending angle brackets on template definitions.\n"; cout << endl; cout << " --remove-comment-prefix OR -xp\n"; cout << " Remove the leading '*' prefix on multi-line comments and\n"; cout << " indent the comment text one indent.\n"; cout << endl; cout << " --max-code-length=# OR -xC#\n"; cout << " --break-after-logical OR -xL\n"; cout << " max-code-length=# will break the line if it exceeds more than\n"; cout << " # characters. The valid values are 50 thru 200.\n"; cout << " If the line contains logical conditionals they will be placed\n"; cout << " first on the new line. The option break-after-logical will\n"; cout << " cause the logical conditional to be placed last on the\n"; cout << " previous line.\n"; cout << endl; cout << " --mode=c\n"; cout << " Indent a C or C++ source file (this is the default).\n"; cout << endl; cout << " --mode=java\n"; cout << " Indent a Java source file.\n"; cout << endl; cout << " --mode=cs\n"; cout << " Indent a C# source file.\n"; cout << endl; cout << "Objective-C Options:\n"; cout << "--------------------\n"; cout << " --align-method-colon OR -xM\n"; cout << " Align the colons in an Objective-C method definition.\n"; cout << endl; cout << " --pad-method-prefix OR -xQ\n"; cout << " Insert space padding after the '-' or '+' Objective-C\n"; cout << " method prefix.\n"; cout << endl; cout << " --unpad-method-prefix OR -xR\n"; cout << " Remove all space padding after the '-' or '+' Objective-C\n"; cout << " method prefix.\n"; cout << endl; cout << " --pad-method-colon=none OR -xP\n"; cout << " --pad-method-colon=all OR -xP1\n"; cout << " --pad-method-colon=after OR -xP2\n"; cout << " --pad-method-colon=before OR -xP3\n"; cout << " Add or remove space padding before or after the colons in an\n"; cout << " Objective-C method call.\n"; cout << endl; cout << "Other Options:\n"; cout << "--------------\n"; cout << " --suffix=####\n"; cout << " Append the suffix #### instead of '.orig' to original filename.\n"; cout << endl; cout << " --suffix=none OR -n\n"; cout << " Do not retain a backup of the original file.\n"; cout << endl; cout << " --recursive OR -r OR -R\n"; cout << " Process subdirectories recursively.\n"; cout << endl; cout << " --dry-run\n"; cout << " Perform a trial run with no changes made to check for formatting.\n"; cout << endl; cout << " --exclude=####\n"; cout << " Specify a file or directory #### to be excluded from processing.\n"; cout << endl; cout << " --ignore-exclude-errors OR -i\n"; cout << " Allow processing to continue if there are errors in the exclude=####\n"; cout << " options. It will display the unmatched excludes.\n"; cout << endl; cout << " --ignore-exclude-errors-x OR -xi\n"; cout << " Allow processing to continue if there are errors in the exclude=####\n"; cout << " options. It will NOT display the unmatched excludes.\n"; cout << endl; cout << " --errors-to-stdout OR -X\n"; cout << " Print errors and help information to standard-output rather than\n"; cout << " to standard-error.\n"; cout << endl; cout << " --preserve-date OR -Z\n"; cout << " Preserve the original file's date and time modified. The time\n"; cout << " modified will be changed a few micro seconds to force a compile.\n"; cout << endl; cout << " --verbose OR -v\n"; cout << " Verbose mode. Extra informational messages will be displayed.\n"; cout << endl; cout << " --formatted OR -Q\n"; cout << " Formatted display mode. Display only the files that have been\n"; cout << " formatted.\n"; cout << endl; cout << " --quiet OR -q\n"; cout << " Quiet mode. Suppress all output except error messages.\n"; cout << endl; cout << " --lineend=windows OR -z1\n"; cout << " --lineend=linux OR -z2\n"; cout << " --lineend=macold OR -z3\n"; cout << " Force use of the specified line end style. Valid options\n"; cout << " are windows (CRLF), linux (LF), and macold (CR).\n"; cout << endl; cout << "Command Line Only:\n"; cout << "------------------\n"; cout << " --options=####\n"; cout << " Specify an options file #### to read and use.\n"; cout << endl; cout << " --options=none\n"; cout << " Disable the default options file.\n"; cout << " Only the command-line parameters will be used.\n"; cout << endl; cout << " --ascii OR -I\n"; cout << " The displayed output will be ascii characters only.\n"; cout << endl; cout << " --version OR -V\n"; cout << " Print version number.\n"; cout << endl; cout << " --help OR -h OR -?\n"; cout << " Print this help message.\n"; cout << endl; cout << " --html OR -!\n"; cout << " Open the HTML help file \"astyle.html\" in the default browser.\n"; cout << " The documentation must be installed in the standard install path.\n"; cout << endl; cout << " --html=####\n"; cout << " Open a HTML help file in the default browser using the file path\n"; cout << " ####. The path may include a directory path and a file name, or a\n"; cout << " file name only. Paths containing spaces must be enclosed in quotes.\n"; cout << endl; cout << endl; } /** * Process files in the fileNameVector. */ void ASConsole::processFiles() { if (isVerbose) printVerboseHeader(); clock_t startTime = clock(); // start time of file formatting // loop thru input fileNameVector and process the files for (size_t i = 0; i < fileNameVector.size(); i++) { getFilePaths(fileNameVector[i]); // loop thru fileName vector formatting the files for (size_t j = 0; j < fileName.size(); j++) formatFile(fileName[j]); } // files are processed, display stats if (isVerbose) printVerboseStats(startTime); } // process options from the command line and options file // build the vectors fileNameVector, excludeVector, optionsVector, and fileOptionsVector void ASConsole::processOptions(vector &argvOptions) { string arg; bool ok = true; bool shouldParseOptionsFile = true; // get command line options for (size_t i = 0; i < argvOptions.size(); i++) { arg = argvOptions[i]; if ( isOption(arg, "-I" ) || isOption(arg, "--ascii") ) { useAscii = true; setlocale(LC_ALL, "C"); // use English decimal indicator localizer.setLanguageFromName("en"); } else if ( isOption(arg, "--options=none") ) { shouldParseOptionsFile = false; } else if ( isParamOption(arg, "--options=") ) { optionsFileName = getParam(arg, "--options="); optionsFileRequired = true; if (optionsFileName.compare("") == 0) setOptionsFileName(" "); } else if ( isOption(arg, "-h") || isOption(arg, "--help") || isOption(arg, "-?") ) { printHelp(); exit(EXIT_SUCCESS); } else if ( isOption(arg, "-!") || isOption(arg, "--html") ) { launchDefaultBrowser(); exit(EXIT_SUCCESS); } else if ( isParamOption(arg, "--html=") ) { string htmlFilePath = getParam(arg, "--html="); launchDefaultBrowser(htmlFilePath.c_str()); exit(EXIT_SUCCESS); } else if ( isOption(arg, "-V" ) || isOption(arg, "--version") ) { printf("Artistic Style Version %s\n", g_version); exit(EXIT_SUCCESS); } else if (arg[0] == '-') { optionsVector.push_back(arg); } else // file-name { standardizePath(arg); fileNameVector.push_back(arg); } } // get options file path and name if (shouldParseOptionsFile) { if (optionsFileName.compare("") == 0) { char* env = getenv("ARTISTIC_STYLE_OPTIONS"); if (env != NULL) setOptionsFileName(env); } if (optionsFileName.compare("") == 0) { char* env = getenv("HOME"); if (env != NULL) setOptionsFileName(string(env) + "/.astylerc"); } if (optionsFileName.compare("") == 0) { char* env = getenv("USERPROFILE"); if (env != NULL) setOptionsFileName(string(env) + "/astylerc"); } if (optionsFileName.compare("") != 0) standardizePath(optionsFileName); } // create the options file vector and parse the options for errors ASOptions options(formatter); if (optionsFileName.compare("") != 0) { ifstream optionsIn(optionsFileName.c_str()); if (optionsIn) { options.importOptions(optionsIn, fileOptionsVector); ok = options.parseOptions(fileOptionsVector, string(_("Invalid option file options:"))); } else { if (optionsFileRequired) error(_("Cannot open options file"), optionsFileName.c_str()); optionsFileName.clear(); } optionsIn.close(); } if (!ok) { (*_err) << options.getOptionErrors() << endl; (*_err) << _("For help on options type 'astyle -h'") << endl; error(); } // parse the command line options vector for errors ok = options.parseOptions(optionsVector, string(_("Invalid command line options:"))); if (!ok) { (*_err) << options.getOptionErrors() << endl; (*_err) << _("For help on options type 'astyle -h'") << endl; error(); } } // remove a file and check for an error void ASConsole::removeFile(const char* fileName_, const char* errMsg) const { if (remove(fileName_)) { if (errno == ENOENT) // no file is OK errno = 0; if (errno) { perror("errno message"); error(errMsg, fileName_); } } } // rename a file and check for an error void ASConsole::renameFile(const char* oldFileName, const char* newFileName, const char* errMsg) const { int result = rename(oldFileName, newFileName); if (result != 0) { // if file still exists the remove needs more time - retry if (errno == EEXIST) { errno = 0; waitForRemove(newFileName); result = rename(oldFileName, newFileName); } if (result != 0) { perror("errno message"); error(errMsg, oldFileName); } } } // make sure file separators are correct type (Windows or Linux) // remove ending file separator // remove beginning file separator if requested and NOT a complete file path void ASConsole::standardizePath(string &path, bool removeBeginningSeparator /*false*/) const { #ifdef __VMS struct FAB fab; struct NAML naml; char less[NAML$C_MAXRSS]; char sess[NAM$C_MAXRSS]; int r0_status; // If we are on a VMS system, translate VMS style filenames to unix // style. fab = cc$rms_fab; fab.fab$l_fna = (char*) - 1; fab.fab$b_fns = 0; fab.fab$l_naml = &naml; naml = cc$rms_naml; strcpy(sess, path.c_str()); naml.naml$l_long_filename = (char*)sess; naml.naml$l_long_filename_size = path.length(); naml.naml$l_long_expand = less; naml.naml$l_long_expand_alloc = sizeof(less); naml.naml$l_esa = sess; naml.naml$b_ess = sizeof(sess); naml.naml$v_no_short_upcase = 1; r0_status = sys$parse(&fab); if (r0_status == RMS$_SYN) { error("File syntax error", path.c_str()); } else { if (!$VMS_STATUS_SUCCESS(r0_status)) { (void)lib$signal (r0_status); } } less[naml.naml$l_long_expand_size - naml.naml$b_ver] = '\0'; sess[naml.naml$b_esl - naml.naml$b_ver] = '\0'; if (naml.naml$l_long_expand_size > naml.naml$b_esl) { path = decc$translate_vms (less); } else { path = decc$translate_vms(sess); } #endif /* __VMS */ // make sure separators are correct type (Windows or Linux) for (size_t i = 0; i < path.length(); i++) { i = path.find_first_of("/\\", i); if (i == string::npos) break; path[i] = g_fileSeparator; } // remove beginning separator if requested if (removeBeginningSeparator && (path[0] == g_fileSeparator)) path.erase(0, 1); } void ASConsole::printMsg(const char* msg, const string &data) const { if (isQuiet) return; printf(msg, data.c_str()); } void ASConsole::printSeparatingLine() const { string line; for (size_t i = 0; i < 60; i++) line.append("-"); printMsg("%s\n", line); } void ASConsole::printVerboseHeader() const { assert(isVerbose); if (isQuiet) return; // get the date struct tm* ptr; time_t lt; char str[20]; lt = time(NULL); ptr = localtime(<); strftime(str, 20, "%x", ptr); // print the header printf("Artistic Style %s %s\n", g_version, str); // print options file if (!optionsFileName.empty()) printf(_("Using default options file %s\n"), optionsFileName.c_str()); } void ASConsole::printVerboseStats(clock_t startTime) const { assert(isVerbose); if (isQuiet) return; if (hasWildcard) printSeparatingLine(); string formatted = getNumberFormat(filesFormatted); string unchanged = getNumberFormat(filesUnchanged); printf(_(" %s formatted %s unchanged "), formatted.c_str(), unchanged.c_str()); // show processing time clock_t stopTime = clock(); float secs = (stopTime - startTime) / float (CLOCKS_PER_SEC); if (secs < 60) { if (secs < 2.0) printf("%.2f", secs); else if (secs < 20.0) printf("%.1f", secs); else printf("%.0f", secs); printf("%s", _(" seconds ")); } else { // show minutes and seconds if time is greater than one minute int min = (int) secs / 60; secs -= min * 60; int minsec = int (secs + .5); printf(_("%d min %d sec "), min, minsec); } string lines = getNumberFormat(linesOut); printf(_("%s lines\n"), lines.c_str()); } void ASConsole::sleep(int seconds) const { clock_t endwait; endwait = clock_t (clock () + seconds * CLOCKS_PER_SEC); while (clock() < endwait) {} } bool ASConsole::stringEndsWith(const string &str, const string &suffix) const { int strIndex = (int) str.length() - 1; int suffixIndex = (int) suffix.length() - 1; while (strIndex >= 0 && suffixIndex >= 0) { if (tolower(str[strIndex]) != tolower(suffix[suffixIndex])) return false; --strIndex; --suffixIndex; } // suffix longer than string if (strIndex < 0 && suffixIndex >= 0) return false; return true; } void ASConsole::updateExcludeVector(string suffixParam) { excludeVector.push_back(suffixParam); standardizePath(excludeVector.back(), true); excludeHitsVector.push_back(false); } int ASConsole::waitForRemove(const char* newFileName) const { struct stat stBuf; int seconds; // sleep a max of 20 seconds for the remove for (seconds = 1; seconds <= 20; seconds++) { sleep(1); if (stat(newFileName, &stBuf) != 0) break; } errno = 0; return seconds; } // From The Code Project http://www.codeproject.com/string/wildcmp.asp // Written by Jack Handy - jakkhandy@hotmail.com // Modified to compare case insensitive for Windows int ASConsole::wildcmp(const char* wild, const char* data) const { const char* cp = NULL, *mp = NULL; bool cmpval; while ((*data) && (*wild != '*')) { if (!g_isCaseSensitive) cmpval = (tolower(*wild) != tolower(*data)) && (*wild != '?'); else cmpval = (*wild != *data) && (*wild != '?'); if (cmpval) { return 0; } wild++; data++; } while (*data) { if (*wild == '*') { if (!*++wild) { return 1; } mp = wild; cp = data + 1; } else { if (!g_isCaseSensitive) cmpval = (tolower(*wild) == tolower(*data) || (*wild == '?')); else cmpval = (*wild == *data) || (*wild == '?'); if (cmpval) { wild++; data++; } else { wild = mp; data = cp++; } } } while (*wild == '*') { wild++; } return !*wild; } void ASConsole::writeFile(const string &fileName_, FileEncoding encoding, ostringstream &out) const { // save date accessed and date modified of original file struct stat stBuf; bool statErr = false; if (stat(fileName_.c_str(), &stBuf) == -1) statErr = true; // create a backup if (!noBackup) { string origFileName = fileName_ + origSuffix; removeFile(origFileName.c_str(), "Cannot remove pre-existing backup file"); renameFile(fileName_.c_str(), origFileName.c_str(), "Cannot create backup file"); } // write the output file ofstream fout(fileName_.c_str(), ios::binary | ios::trunc); if (!fout) error("Cannot open output file", fileName_.c_str()); if (encoding == UTF_16LE || encoding == UTF_16BE) { // convert utf-8 to utf-16 bool isBigEndian = (encoding == UTF_16BE); size_t utf16Size = utf8_16.Utf16LengthFromUtf8(out.str().c_str(), out.str().length()); char* utf16Out = new char[utf16Size]; size_t utf16Len = utf8_16.Utf8ToUtf16(const_cast(out.str().c_str()), out.str().length(), isBigEndian, utf16Out); assert(utf16Len == utf16Size); fout << string(utf16Out, utf16Len); delete [] utf16Out; } else fout << out.str(); fout.close(); // change date modified to original file date // Embarcadero must be linked with cw32mt not cw32 if (preserveDate) { if (!statErr) { struct utimbuf outBuf; outBuf.actime = stBuf.st_atime; // add ticks so 'make' will recognize a change // Visual Studio 2008 needs more than 1 outBuf.modtime = stBuf.st_mtime + 10; if (utime(fileName_.c_str(), &outBuf) == -1) statErr = true; } if (statErr) { perror("errno message"); (*_err) << "********* Cannot preserve file date" << endl; } } } //----------------------------------------------------------------------------- // ASLibrary class // used by shared object (DLL) calls //----------------------------------------------------------------------------- #else // ASTYLE_LIB utf16_t* ASLibrary::formatUtf16(const utf16_t* pSourceIn, // the source to be formatted const utf16_t* pOptions, // AStyle options fpError fpErrorHandler, // error handler function fpAlloc fpMemoryAlloc) const // memory allocation function) { const char* utf8In = convertUtf16ToUtf8(pSourceIn); if (utf8In == NULL) { fpErrorHandler(121, "Cannot convert input utf-16 to utf-8."); return NULL; } const char* utf8Options = convertUtf16ToUtf8(pOptions); if (utf8Options == NULL) { delete [] utf8In; fpErrorHandler(122, "Cannot convert options utf-16 to utf-8."); return NULL; } // call the Artistic Style formatting function // cannot use the callers memory allocation here char* utf8Out = AStyleMain(utf8In, utf8Options, fpErrorHandler, ASLibrary::tempMemoryAllocation); // finished with these delete [] utf8In; delete [] utf8Options; utf8In = NULL; utf8Options = NULL; // AStyle error has already been sent if (utf8Out == NULL) return NULL; // convert text to wide char and return it utf16_t* utf16Out = convertUtf8ToUtf16(utf8Out, fpMemoryAlloc); delete [] utf8Out; utf8Out = NULL; if (utf16Out == NULL) { fpErrorHandler(123, "Cannot convert output utf-8 to utf-16."); return NULL; } return utf16Out; } // STATIC method to allocate temporary memory for AStyle formatting. // The data will be converted before being returned to the calling program. char* STDCALL ASLibrary::tempMemoryAllocation(unsigned long memoryNeeded) { char* buffer = new(nothrow) char[memoryNeeded]; return buffer; } /** * Convert utf-8 strings to utf16 strings. * Memory is allocated by the calling program memory allocation function. * The calling function must check for errors. */ utf16_t* ASLibrary::convertUtf8ToUtf16(const char* utf8In, fpAlloc fpMemoryAlloc) const { if (utf8In == NULL) return NULL; char* data = const_cast(utf8In); size_t dataSize = strlen(utf8In); bool isBigEndian = utf8_16.getBigEndian(); // return size is in number of CHARs, not utf16_t size_t utf16Size = (utf8_16.Utf16LengthFromUtf8(data, dataSize) + sizeof(utf16_t)); char* utf16Out = fpMemoryAlloc(utf16Size); if (utf16Out == NULL) return NULL; #ifdef NDEBUG utf8_16.Utf8ToUtf16(data, dataSize + 1, isBigEndian, utf16Out); #else size_t utf16Len = utf8_16.Utf8ToUtf16(data, dataSize + 1, isBigEndian, utf16Out); assert(utf16Len == utf16Size); #endif assert(utf16Size == (utf8_16.utf16len(reinterpret_cast(utf16Out)) + 1) * sizeof(utf16_t)); return reinterpret_cast(utf16Out); } /** * Convert utf16 strings to utf-8. * The calling function must check for errors and delete the * allocated memory. */ char* ASLibrary::convertUtf16ToUtf8(const utf16_t* utf16In) const { if (utf16In == NULL) return NULL; char* data = reinterpret_cast(const_cast(utf16In)); // size must be in chars size_t dataSize = utf8_16.utf16len(utf16In) * sizeof(utf16_t); bool isBigEndian = utf8_16.getBigEndian(); size_t utf8Size = utf8_16.Utf8LengthFromUtf16(data, dataSize, isBigEndian) + 1; char* utf8Out = new(nothrow) char[utf8Size]; if (utf8Out == NULL) return NULL; #ifdef NDEBUG utf8_16.Utf16ToUtf8(data, dataSize + 1, isBigEndian, true, utf8Out); #else size_t utf8Len = utf8_16.Utf16ToUtf8(data, dataSize + 1, isBigEndian, true, utf8Out); assert(utf8Len == utf8Size); #endif assert(utf8Size == strlen(utf8Out) + 1); return utf8Out; } #endif // ASTYLE_LIB //----------------------------------------------------------------------------- // ASOptions class // used by both console and library builds //----------------------------------------------------------------------------- /** * parse the options vector * optionsVector can be either a fileOptionsVector (options file) or an optionsVector (command line) * * @return true if no errors, false if errors */ bool ASOptions::parseOptions(vector &optionsVector, const string &errorInfo) { vector::iterator option; string arg, subArg; optionErrors.clear(); for (option = optionsVector.begin(); option != optionsVector.end(); ++option) { arg = *option; if (arg.compare(0, 2, "--") == 0) parseOption(arg.substr(2), errorInfo); else if (arg[0] == '-') { size_t i; for (i = 1; i < arg.length(); ++i) { if (i > 1 && isalpha((unsigned char)arg[i]) && arg[i - 1] != 'x') { // parse the previous option in subArg parseOption(subArg, errorInfo); subArg = ""; } // append the current option to subArg subArg.append(1, arg[i]); } // parse the last option parseOption(subArg, errorInfo); subArg = ""; } else { parseOption(arg, errorInfo); subArg = ""; } } if (optionErrors.str().length() > 0) return false; return true; } void ASOptions::parseOption(const string &arg, const string &errorInfo) { if ( isOption(arg, "style=allman") || isOption(arg, "style=bsd") || isOption(arg, "style=break") ) { formatter.setFormattingStyle(STYLE_ALLMAN); } else if ( isOption(arg, "style=java") || isOption(arg, "style=attach") ) { formatter.setFormattingStyle(STYLE_JAVA); } else if ( isOption(arg, "style=k&r") || isOption(arg, "style=kr") || isOption(arg, "style=k/r") ) { formatter.setFormattingStyle(STYLE_KR); } else if ( isOption(arg, "style=stroustrup") ) { formatter.setFormattingStyle(STYLE_STROUSTRUP); } else if ( isOption(arg, "style=whitesmith") ) { formatter.setFormattingStyle(STYLE_WHITESMITH); } else if (isOption(arg, "style=vtk")) { formatter.setFormattingStyle(STYLE_VTK); } else if ( isOption(arg, "style=banner") ) { formatter.setFormattingStyle(STYLE_BANNER); } else if ( isOption(arg, "style=gnu") ) { formatter.setFormattingStyle(STYLE_GNU); } else if ( isOption(arg, "style=linux") || isOption(arg, "style=knf") ) { formatter.setFormattingStyle(STYLE_LINUX); } else if ( isOption(arg, "style=horstmann") ) { formatter.setFormattingStyle(STYLE_HORSTMANN); } else if ( isOption(arg, "style=1tbs") || isOption(arg, "style=otbs") ) { formatter.setFormattingStyle(STYLE_1TBS); } else if ( isOption(arg, "style=google") ) { formatter.setFormattingStyle(STYLE_GOOGLE); } else if ( isOption(arg, "style=pico") ) { formatter.setFormattingStyle(STYLE_PICO); } else if ( isOption(arg, "style=lisp") || isOption(arg, "style=python") ) { formatter.setFormattingStyle(STYLE_LISP); } else if ( isParamOption(arg, "A") ) { int style = 0; string styleParam = getParam(arg, "A"); if (styleParam.length() > 0) style = atoi(styleParam.c_str()); if (style == 1) formatter.setFormattingStyle(STYLE_ALLMAN); else if (style == 2) formatter.setFormattingStyle(STYLE_JAVA); else if (style == 3) formatter.setFormattingStyle(STYLE_KR); else if (style == 4) formatter.setFormattingStyle(STYLE_STROUSTRUP); else if (style == 5) formatter.setFormattingStyle(STYLE_WHITESMITH); else if (style == 6) formatter.setFormattingStyle(STYLE_BANNER); else if (style == 7) formatter.setFormattingStyle(STYLE_GNU); else if (style == 8) formatter.setFormattingStyle(STYLE_LINUX); else if (style == 9) formatter.setFormattingStyle(STYLE_HORSTMANN); else if (style == 10) formatter.setFormattingStyle(STYLE_1TBS); else if (style == 11) formatter.setFormattingStyle(STYLE_PICO); else if (style == 12) formatter.setFormattingStyle(STYLE_LISP); else if (style == 14) formatter.setFormattingStyle(STYLE_GOOGLE); else if (style == 15) formatter.setFormattingStyle(STYLE_VTK); else isOptionError(arg, errorInfo); } // must check for mode=cs before mode=c !!! else if ( isOption(arg, "mode=cs") ) { formatter.setSharpStyle(); formatter.setModeManuallySet(true); } else if ( isOption(arg, "mode=c") ) { formatter.setCStyle(); formatter.setModeManuallySet(true); } else if ( isOption(arg, "mode=java") ) { formatter.setJavaStyle(); formatter.setModeManuallySet(true); } else if ( isParamOption(arg, "t", "indent=tab=") ) { int spaceNum = 4; string spaceNumParam = getParam(arg, "t", "indent=tab="); if (spaceNumParam.length() > 0) spaceNum = atoi(spaceNumParam.c_str()); if (spaceNum < 2 || spaceNum > 20) isOptionError(arg, errorInfo); else { formatter.setTabIndentation(spaceNum, false); } } else if ( isOption(arg, "indent=tab") ) { formatter.setTabIndentation(4); } else if ( isParamOption(arg, "T", "indent=force-tab=") ) { int spaceNum = 4; string spaceNumParam = getParam(arg, "T", "indent=force-tab="); if (spaceNumParam.length() > 0) spaceNum = atoi(spaceNumParam.c_str()); if (spaceNum < 2 || spaceNum > 20) isOptionError(arg, errorInfo); else { formatter.setTabIndentation(spaceNum, true); } } else if ( isOption(arg, "indent=force-tab") ) { formatter.setTabIndentation(4, true); } else if ( isParamOption(arg, "xT", "indent=force-tab-x=") ) { int tabNum = 8; string tabNumParam = getParam(arg, "xT", "indent=force-tab-x="); if (tabNumParam.length() > 0) tabNum = atoi(tabNumParam.c_str()); if (tabNum < 2 || tabNum > 20) isOptionError(arg, errorInfo); else { formatter.setForceTabXIndentation(tabNum); } } else if ( isOption(arg, "indent=force-tab-x") ) { formatter.setForceTabXIndentation(8); } else if ( isParamOption(arg, "s", "indent=spaces=") ) { int spaceNum = 4; string spaceNumParam = getParam(arg, "s", "indent=spaces="); if (spaceNumParam.length() > 0) spaceNum = atoi(spaceNumParam.c_str()); if (spaceNum < 2 || spaceNum > 20) isOptionError(arg, errorInfo); else { formatter.setSpaceIndentation(spaceNum); } } else if ( isOption(arg, "indent=spaces") ) { formatter.setSpaceIndentation(4); } else if ( isParamOption(arg, "m", "min-conditional-indent=") ) { int minIndent = MINCOND_TWO; string minIndentParam = getParam(arg, "m", "min-conditional-indent="); if (minIndentParam.length() > 0) minIndent = atoi(minIndentParam.c_str()); if (minIndent >= MINCOND_END) isOptionError(arg, errorInfo); else formatter.setMinConditionalIndentOption(minIndent); } else if ( isParamOption(arg, "M", "max-instatement-indent=") ) { int maxIndent = 40; string maxIndentParam = getParam(arg, "M", "max-instatement-indent="); if (maxIndentParam.length() > 0) maxIndent = atoi(maxIndentParam.c_str()); if (maxIndent < 40) isOptionError(arg, errorInfo); else if (maxIndent > 120) isOptionError(arg, errorInfo); else formatter.setMaxInStatementIndentLength(maxIndent); } else if ( isOption(arg, "N", "indent-namespaces") ) { formatter.setNamespaceIndent(true); } else if ( isOption(arg, "C", "indent-classes") ) { formatter.setClassIndent(true); } else if ( isOption(arg, "xG", "indent-modifiers") ) { formatter.setModifierIndent(true); } else if ( isOption(arg, "S", "indent-switches") ) { formatter.setSwitchIndent(true); } else if ( isOption(arg, "K", "indent-cases") ) { formatter.setCaseIndent(true); } else if ( isOption(arg, "L", "indent-labels") ) { formatter.setLabelIndent(true); } else if (isOption(arg, "xW", "indent-preproc-block")) { formatter.setPreprocBlockIndent(true); } else if ( isOption(arg, "w", "indent-preproc-define") ) { formatter.setPreprocDefineIndent(true); } else if ( isOption(arg, "xw", "indent-preproc-cond") ) { formatter.setPreprocConditionalIndent(true); } else if ( isOption(arg, "y", "break-closing-brackets") ) { formatter.setBreakClosingHeaderBracketsMode(true); } else if ( isOption(arg, "O", "keep-one-line-blocks") ) { formatter.setBreakOneLineBlocksMode(false); } else if ( isOption(arg, "o", "keep-one-line-statements") ) { formatter.setSingleStatementsMode(false); } else if ( isOption(arg, "P", "pad-paren") ) { formatter.setParensOutsidePaddingMode(true); formatter.setParensInsidePaddingMode(true); } else if ( isOption(arg, "d", "pad-paren-out") ) { formatter.setParensOutsidePaddingMode(true); } else if ( isOption(arg, "xd", "pad-first-paren-out") ) { formatter.setParensFirstPaddingMode(true); } else if ( isOption(arg, "D", "pad-paren-in") ) { formatter.setParensInsidePaddingMode(true); } else if ( isOption(arg, "H", "pad-header") ) { formatter.setParensHeaderPaddingMode(true); } else if ( isOption(arg, "U", "unpad-paren") ) { formatter.setParensUnPaddingMode(true); } else if ( isOption(arg, "p", "pad-oper") ) { formatter.setOperatorPaddingMode(true); } else if ( isOption(arg, "xe", "delete-empty-lines") ) { formatter.setDeleteEmptyLinesMode(true); } else if ( isOption(arg, "E", "fill-empty-lines") ) { formatter.setEmptyLineFill(true); } else if ( isOption(arg, "c", "convert-tabs") ) { formatter.setTabSpaceConversionMode(true); } else if ( isOption(arg, "xy", "close-templates") ) { formatter.setCloseTemplatesMode(true); } else if ( isOption(arg, "F", "break-blocks=all") ) { formatter.setBreakBlocksMode(true); formatter.setBreakClosingHeaderBlocksMode(true); } else if ( isOption(arg, "f", "break-blocks") ) { formatter.setBreakBlocksMode(true); } else if ( isOption(arg, "e", "break-elseifs") ) { formatter.setBreakElseIfsMode(true); } else if ( isOption(arg, "j", "add-brackets") ) { formatter.setAddBracketsMode(true); } else if ( isOption(arg, "J", "add-one-line-brackets") ) { formatter.setAddOneLineBracketsMode(true); } else if ( isOption(arg, "xj", "remove-brackets") ) { formatter.setRemoveBracketsMode(true); } else if ( isOption(arg, "Y", "indent-col1-comments") ) { formatter.setIndentCol1CommentsMode(true); } else if ( isOption(arg, "align-pointer=type") ) { formatter.setPointerAlignment(PTR_ALIGN_TYPE); } else if ( isOption(arg, "align-pointer=middle") ) { formatter.setPointerAlignment(PTR_ALIGN_MIDDLE); } else if ( isOption(arg, "align-pointer=name") ) { formatter.setPointerAlignment(PTR_ALIGN_NAME); } else if ( isParamOption(arg, "k") ) { int align = 0; string styleParam = getParam(arg, "k"); if (styleParam.length() > 0) align = atoi(styleParam.c_str()); if (align < 1 || align > 3) isOptionError(arg, errorInfo); else if (align == 1) formatter.setPointerAlignment(PTR_ALIGN_TYPE); else if (align == 2) formatter.setPointerAlignment(PTR_ALIGN_MIDDLE); else if (align == 3) formatter.setPointerAlignment(PTR_ALIGN_NAME); } else if ( isOption(arg, "align-reference=none") ) { formatter.setReferenceAlignment(REF_ALIGN_NONE); } else if ( isOption(arg, "align-reference=type") ) { formatter.setReferenceAlignment(REF_ALIGN_TYPE); } else if ( isOption(arg, "align-reference=middle") ) { formatter.setReferenceAlignment(REF_ALIGN_MIDDLE); } else if ( isOption(arg, "align-reference=name") ) { formatter.setReferenceAlignment(REF_ALIGN_NAME); } else if ( isParamOption(arg, "W") ) { int align = 0; string styleParam = getParam(arg, "W"); if (styleParam.length() > 0) align = atoi(styleParam.c_str()); if (align < 0 || align > 3) isOptionError(arg, errorInfo); else if (align == 0) formatter.setReferenceAlignment(REF_ALIGN_NONE); else if (align == 1) formatter.setReferenceAlignment(REF_ALIGN_TYPE); else if (align == 2) formatter.setReferenceAlignment(REF_ALIGN_MIDDLE); else if (align == 3) formatter.setReferenceAlignment(REF_ALIGN_NAME); } else if ( isParamOption(arg, "max-code-length=") ) { int maxLength = 50; string maxLengthParam = getParam(arg, "max-code-length="); if (maxLengthParam.length() > 0) maxLength = atoi(maxLengthParam.c_str()); if (maxLength < 50) isOptionError(arg, errorInfo); else if (maxLength > 200) isOptionError(arg, errorInfo); else formatter.setMaxCodeLength(maxLength); } else if ( isParamOption(arg, "xC") ) { int maxLength = 50; string maxLengthParam = getParam(arg, "xC"); if (maxLengthParam.length() > 0) maxLength = atoi(maxLengthParam.c_str()); if (maxLength > 200) isOptionError(arg, errorInfo); else formatter.setMaxCodeLength(maxLength); } else if ( isOption(arg, "xL", "break-after-logical") ) { formatter.setBreakAfterMode(true); } else if ( isOption(arg, "xc", "attach-classes") ) { formatter.setAttachClass(true); } else if ( isOption(arg, "xk", "attach-extern-c") ) { formatter.setAttachExternC(true); } else if ( isOption(arg, "xn", "attach-namespaces") ) { formatter.setAttachNamespace(true); } else if ( isOption(arg, "xl", "attach-inlines") ) { formatter.setAttachInline(true); } else if ( isOption(arg, "xp", "remove-comment-prefix") ) { formatter.setStripCommentPrefix(true); } // Objective-C options else if ( isOption(arg, "xM", "align-method-colon") ) { formatter.setAlignMethodColon(true); } else if ( isOption(arg, "xQ", "pad-method-prefix") ) { formatter.setMethodPrefixPaddingMode(true); } else if ( isOption(arg, "xR", "unpad-method-prefix") ) { formatter.setMethodPrefixUnPaddingMode(true); } else if ( isOption(arg, "xP0", "pad-method-colon=none") ) { formatter.setObjCColonPaddingMode(COLON_PAD_NONE); } else if ( isOption(arg, "xP1", "pad-method-colon=all") ) { formatter.setObjCColonPaddingMode(COLON_PAD_ALL); } else if ( isOption(arg, "xP2", "pad-method-colon=after") ) { formatter.setObjCColonPaddingMode(COLON_PAD_AFTER); } else if ( isOption(arg, "xP3", "pad-method-colon=before") ) { formatter.setObjCColonPaddingMode(COLON_PAD_BEFORE); } // depreciated options //////////////////////////////////////////////////////////////////////////////////////////// else if ( isOption(arg, "indent-preprocessor") ) // depreciated release 2.04 { formatter.setPreprocDefineIndent(true); } else if ( isOption(arg, "style=ansi") ) // depreciated release 2.05 { formatter.setFormattingStyle(STYLE_ALLMAN); } // NOTE: Removed in release 2.04. // else if ( isOption(arg, "b", "brackets=break") ) // { // formatter.setBracketFormatMode(BREAK_MODE); // } // else if ( isOption(arg, "a", "brackets=attach") ) // { // formatter.setBracketFormatMode(ATTACH_MODE); // } // else if ( isOption(arg, "l", "brackets=linux") ) // { // formatter.setBracketFormatMode(LINUX_MODE); // } // else if ( isOption(arg, "u", "brackets=stroustrup") ) // { // formatter.setBracketFormatMode(STROUSTRUP_MODE); // } // else if ( isOption(arg, "g", "brackets=run-in") ) // { // formatter.setBracketFormatMode(RUN_IN_MODE); // } // end depreciated options //////////////////////////////////////////////////////////////////////////////////////// #ifdef ASTYLE_LIB // End of options used by GUI ///////////////////////////////////////////////////////////////////////////////////// else isOptionError(arg, errorInfo); #else // Options used by only console /////////////////////////////////////////////////////////////////////////////////// else if ( isOption(arg, "n", "suffix=none") ) { g_console->setNoBackup(true); } else if ( isParamOption(arg, "suffix=") ) { string suffixParam = getParam(arg, "suffix="); if (suffixParam.length() > 0) { g_console->setOrigSuffix(suffixParam); } } else if ( isParamOption(arg, "exclude=") ) { string suffixParam = getParam(arg, "exclude="); if (suffixParam.length() > 0) g_console->updateExcludeVector(suffixParam); } else if ( isOption(arg, "r", "R") || isOption(arg, "recursive") ) { g_console->setIsRecursive(true); } else if (isOption(arg, "dry-run")) { g_console->setIsDryRun(true); } else if ( isOption(arg, "Z", "preserve-date") ) { g_console->setPreserveDate(true); } else if ( isOption(arg, "v", "verbose") ) { g_console->setIsVerbose(true); } else if ( isOption(arg, "Q", "formatted") ) { g_console->setIsFormattedOnly(true); } else if ( isOption(arg, "q", "quiet") ) { g_console->setIsQuiet(true); } else if ( isOption(arg, "i", "ignore-exclude-errors") ) { g_console->setIgnoreExcludeErrors(true); } else if ( isOption(arg, "xi", "ignore-exclude-errors-x") ) { g_console->setIgnoreExcludeErrorsAndDisplay(true); } else if ( isOption(arg, "X", "errors-to-stdout") ) { _err = &cout; } else if ( isOption(arg, "lineend=windows") ) { formatter.setLineEndFormat(LINEEND_WINDOWS); } else if ( isOption(arg, "lineend=linux") ) { formatter.setLineEndFormat(LINEEND_LINUX); } else if ( isOption(arg, "lineend=macold") ) { formatter.setLineEndFormat(LINEEND_MACOLD); } else if ( isParamOption(arg, "z") ) { int lineendType = 0; string lineendParam = getParam(arg, "z"); if (lineendParam.length() > 0) lineendType = atoi(lineendParam.c_str()); if (lineendType < 1 || lineendType > 3) isOptionError(arg, errorInfo); else if (lineendType == 1) formatter.setLineEndFormat(LINEEND_WINDOWS); else if (lineendType == 2) formatter.setLineEndFormat(LINEEND_LINUX); else if (lineendType == 3) formatter.setLineEndFormat(LINEEND_MACOLD); } else isOptionError(arg, errorInfo); #endif } // End of parseOption function // Parse options from the options file. void ASOptions::importOptions(istream &in, vector &optionsVector) { char ch; bool isInQuote = false; char quoteChar = ' '; string currentToken; while (in) { currentToken = ""; do { in.get(ch); if (in.eof()) break; // treat '#' as line comments if (ch == '#') while (in) { in.get(ch); if (ch == '\n' || ch == '\r') break; } // break options on new-lines, tabs, commas, or spaces // remove quotes from output if (in.eof() || ch == '\n' || ch == '\r' || ch == '\t' || ch == ',') break; if (ch == ' ' && !isInQuote) break; if (ch == quoteChar && isInQuote) break; if (ch == '"' || ch == '\'') { isInQuote = true; quoteChar = ch; continue; } currentToken.append(1, ch); } while (in); if (currentToken.length() != 0) optionsVector.push_back(currentToken); isInQuote = false; } } string ASOptions::getOptionErrors() const { return optionErrors.str(); } string ASOptions::getParam(const string &arg, const char* op) { return arg.substr(strlen(op)); } string ASOptions::getParam(const string &arg, const char* op1, const char* op2) { return isParamOption(arg, op1) ? getParam(arg, op1) : getParam(arg, op2); } bool ASOptions::isOption(const string &arg, const char* op) { return arg.compare(op) == 0; } bool ASOptions::isOption(const string &arg, const char* op1, const char* op2) { return (isOption(arg, op1) || isOption(arg, op2)); } void ASOptions::isOptionError(const string &arg, const string &errorInfo) { if (optionErrors.str().length() == 0) optionErrors << errorInfo << endl; // need main error message optionErrors << arg << endl; } bool ASOptions::isParamOption(const string &arg, const char* option) { bool retVal = arg.compare(0, strlen(option), option) == 0; // if comparing for short option, 2nd char of arg must be numeric if (retVal && strlen(option) == 1 && arg.length() > 1) if (!isdigit((unsigned char)arg[1])) retVal = false; return retVal; } bool ASOptions::isParamOption(const string &arg, const char* option1, const char* option2) { return isParamOption(arg, option1) || isParamOption(arg, option2); } //---------------------------------------------------------------------------- // Utf8_16 class //---------------------------------------------------------------------------- // Return true if an int is big endian. bool Utf8_16::getBigEndian() const { short int word = 0x0001; char* byte = (char*) &word; return (byte[0] ? false : true); } // Swap the two low order bytes of a 16 bit integer value. int Utf8_16::swap16bit(int value) const { return ( ((value & 0xff) << 8) | ((value & 0xff00) >> 8) ); } // Return the length of a utf-16 C string. // The length is in number of utf16_t. size_t Utf8_16::utf16len(const utf16* utf16In) const { size_t length = 0; while (*utf16In++ != '\0') length++; return length; } // Adapted from SciTE UniConversion.cxx. // Copyright 1998-2001 by Neil Hodgson // Modified for Artistic Style by Jim Pattee. // Compute the length of an output utf-8 file given a utf-16 file. // Input inLen is the size in BYTES (not wchar_t). size_t Utf8_16::Utf8LengthFromUtf16(const char* utf16In, size_t inLen, bool isBigEndian) const { size_t len = 0; size_t wcharLen = inLen / 2; const short* uptr = reinterpret_cast(utf16In); for (size_t i = 0; i < wcharLen && uptr[i];) { size_t uch = isBigEndian ? swap16bit(uptr[i]) : uptr[i]; if (uch < 0x80) len++; else if (uch < 0x800) len += 2; else if ((uch >= SURROGATE_LEAD_FIRST) && (uch <= SURROGATE_TRAIL_LAST)) { len += 4; i++; } else len += 3; i++; } return len; } // Adapted from SciTE Utf8_16.cxx. // Copyright (C) 2002 Scott Kirkwood. // Modified for Artistic Style by Jim Pattee. // Convert a utf-8 file to utf-16. size_t Utf8_16::Utf8ToUtf16(char* utf8In, size_t inLen, bool isBigEndian, char* utf16Out) const { int nCur = 0; ubyte* pRead = reinterpret_cast(utf8In); utf16* pCur = reinterpret_cast(utf16Out); const ubyte* pEnd = pRead + inLen; const utf16* pCurStart = pCur; eState state = eStart; // the BOM will automatically be converted to utf-16 while (pRead < pEnd) { switch (state) { case eStart: if ((0xF0 & *pRead) == 0xF0) { nCur = (0x7 & *pRead) << 18; state = eSecondOf4Bytes; } else if ((0xE0 & *pRead) == 0xE0) { nCur = (~0xE0 & *pRead) << 12; state = ePenultimate; } else if ((0xC0 & *pRead) == 0xC0) { nCur = (~0xC0 & *pRead) << 6; state = eFinal; } else { nCur = *pRead; state = eStart; } break; case eSecondOf4Bytes: nCur |= (0x3F & *pRead) << 12; state = ePenultimate; break; case ePenultimate: nCur |= (0x3F & *pRead) << 6; state = eFinal; break; case eFinal: nCur |= (0x3F & *pRead); state = eStart; break; } ++pRead; if (state == eStart) { int codePoint = nCur; if (codePoint >= SURROGATE_FIRST_VALUE) { codePoint -= SURROGATE_FIRST_VALUE; int lead = (codePoint >> 10) + SURROGATE_LEAD_FIRST; *pCur++ = static_cast(isBigEndian ? swap16bit(lead) : lead); int trail = (codePoint & 0x3ff) + SURROGATE_TRAIL_FIRST; *pCur++ = static_cast(isBigEndian ? swap16bit(trail) : trail); } else *pCur++ = static_cast(isBigEndian ? swap16bit(codePoint) : codePoint); } } // return value is the output length in BYTES (not wchar_t) return (pCur - pCurStart) * 2; } // Adapted from SciTE UniConversion.cxx. // Copyright 1998-2001 by Neil Hodgson // Modified for Artistic Style by Jim Pattee. // Compute the length of an output utf-16 file given a utf-8 file. // Return value is the size in BYTES (not wchar_t). size_t Utf8_16::Utf16LengthFromUtf8(const char* utf8In, size_t len) const { size_t ulen = 0; size_t charLen; for (size_t i = 0; i < len;) { unsigned char ch = static_cast(utf8In[i]); if (ch < 0x80) charLen = 1; else if (ch < 0x80 + 0x40 + 0x20) charLen = 2; else if (ch < 0x80 + 0x40 + 0x20 + 0x10) charLen = 3; else { charLen = 4; ulen++; } i += charLen; ulen++; } // return value is the length in bytes (not wchar_t) return ulen * 2; } // Adapted from SciTE Utf8_16.cxx. // Copyright (C) 2002 Scott Kirkwood. // Modified for Artistic Style by Jim Pattee. // Convert a utf-16 file to utf-8. size_t Utf8_16::Utf16ToUtf8(char* utf16In, size_t inLen, bool isBigEndian, bool firstBlock, char* utf8Out) const { int nCur16 = 0; int nCur = 0; ubyte* pRead = reinterpret_cast(utf16In); ubyte* pCur = reinterpret_cast(utf8Out); const ubyte* pEnd = pRead + inLen; const ubyte* pCurStart = pCur; static eState state = eStart; // state is retained for subsequent blocks if (firstBlock) state = eStart; // the BOM will automatically be converted to utf-8 while (pRead < pEnd) { switch (state) { case eStart: if (pRead >= pEnd) { ++pRead; break; } if (isBigEndian) { nCur16 = static_cast(*pRead++ << 8); nCur16 |= static_cast(*pRead); } else { nCur16 = *pRead++; nCur16 |= static_cast(*pRead << 8); } if (nCur16 >= SURROGATE_LEAD_FIRST && nCur16 <= SURROGATE_LEAD_LAST) { ++pRead; int trail; if (isBigEndian) { trail = static_cast(*pRead++ << 8); trail |= static_cast(*pRead); } else { trail = *pRead++; trail |= static_cast(*pRead << 8); } nCur16 = (((nCur16 & 0x3ff) << 10) | (trail & 0x3ff)) + SURROGATE_FIRST_VALUE; } ++pRead; if (nCur16 < 0x80) { nCur = static_cast(nCur16 & 0xFF); state = eStart; } else if (nCur16 < 0x800) { nCur = static_cast(0xC0 | (nCur16 >> 6)); state = eFinal; } else if (nCur16 < SURROGATE_FIRST_VALUE) { nCur = static_cast(0xE0 | (nCur16 >> 12)); state = ePenultimate; } else { nCur = static_cast(0xF0 | (nCur16 >> 18)); state = eSecondOf4Bytes; } break; case eSecondOf4Bytes: nCur = static_cast(0x80 | ((nCur16 >> 12) & 0x3F)); state = ePenultimate; break; case ePenultimate: nCur = static_cast(0x80 | ((nCur16 >> 6) & 0x3F)); state = eFinal; break; case eFinal: nCur = static_cast(0x80 | (nCur16 & 0x3F)); state = eStart; break; } *pCur++ = static_cast(nCur); } return pCur - pCurStart; } //---------------------------------------------------------------------------- } // end of astyle namespace //---------------------------------------------------------------------------- using namespace astyle; //---------------------------------------------------------------------------- // ASTYLE_JNI functions for Java library builds //---------------------------------------------------------------------------- #ifdef ASTYLE_JNI // called by a java program to get the version number // the function name is constructed from method names in the calling java program extern "C" EXPORT jstring STDCALL Java_AStyleInterface_AStyleGetVersion(JNIEnv* env, jclass) { return env->NewStringUTF(g_version); } // called by a java program to format the source code // the function name is constructed from method names in the calling java program extern "C" EXPORT jstring STDCALL Java_AStyleInterface_AStyleMain(JNIEnv* env, jobject obj, jstring textInJava, jstring optionsJava) { g_env = env; // make object available globally g_obj = obj; // make object available globally jstring textErr = env->NewStringUTF(""); // zero length text returned if an error occurs // get the method ID jclass cls = env->GetObjectClass(obj); g_mid = env->GetMethodID(cls, "ErrorHandler", "(ILjava/lang/String;)V"); if (g_mid == 0) { cout << "Cannot find java method ErrorHandler" << endl; return textErr; } // convert jstring to char* const char* textIn = env->GetStringUTFChars(textInJava, NULL); const char* options = env->GetStringUTFChars(optionsJava, NULL); // call the C++ formatting function char* textOut = AStyleMain(textIn, options, javaErrorHandler, javaMemoryAlloc); // if an error message occurred it was displayed by errorHandler if (textOut == NULL) return textErr; // release memory jstring textOutJava = env->NewStringUTF(textOut); delete [] textOut; env->ReleaseStringUTFChars(textInJava, textIn); env->ReleaseStringUTFChars(optionsJava, options); return textOutJava; } // Call the Java error handler void STDCALL javaErrorHandler(int errorNumber, const char* errorMessage) { jstring errorMessageJava = g_env->NewStringUTF(errorMessage); g_env->CallVoidMethod(g_obj, g_mid, errorNumber, errorMessageJava); } // Allocate memory for the formatted text char* STDCALL javaMemoryAlloc(unsigned long memoryNeeded) { // error condition is checked after return from AStyleMain char* buffer = new(nothrow) char[memoryNeeded]; return buffer; } #endif // ASTYLE_JNI //---------------------------------------------------------------------------- // Entry point for AStyleMainUtf16 library builds //---------------------------------------------------------------------------- #ifdef ASTYLE_LIB extern "C" EXPORT utf16_t* STDCALL AStyleMainUtf16(const utf16_t* pSourceIn, // the source to be formatted const utf16_t* pOptions, // AStyle options fpError fpErrorHandler, // error handler function fpAlloc fpMemoryAlloc) // memory allocation function { if (fpErrorHandler == NULL) // cannot display a message if no error handler return NULL; if (pSourceIn == NULL) { fpErrorHandler(101, "No pointer to source input."); return NULL; } if (pOptions == NULL) { fpErrorHandler(102, "No pointer to AStyle options."); return NULL; } if (fpMemoryAlloc == NULL) { fpErrorHandler(103, "No pointer to memory allocation function."); return NULL; } #ifndef _WIN32 // check size of utf16_t on Linux int sizeCheck = 2; if (sizeof(utf16_t) != sizeCheck) { fpErrorHandler(104, "Unsigned short is not the correct size."); return NULL; } #endif ASLibrary library; utf16_t* utf16Out = library.formatUtf16(pSourceIn, pOptions, fpErrorHandler, fpMemoryAlloc); return utf16Out; } //---------------------------------------------------------------------------- // ASTYLE_LIB entry point for library builds //---------------------------------------------------------------------------- /* * IMPORTANT VC DLL linker for WIN32 must have the parameter /EXPORT:AStyleMain=_AStyleMain@16 * /EXPORT:AStyleGetVersion=_AStyleGetVersion@0 * No /EXPORT is required for x64 */ extern "C" EXPORT char* STDCALL AStyleMain(const char* pSourceIn, // the source to be formatted const char* pOptions, // AStyle options fpError fpErrorHandler, // error handler function fpAlloc fpMemoryAlloc) // memory allocation function { if (fpErrorHandler == NULL) // cannot display a message if no error handler return NULL; if (pSourceIn == NULL) { fpErrorHandler(101, "No pointer to source input."); return NULL; } if (pOptions == NULL) { fpErrorHandler(102, "No pointer to AStyle options."); return NULL; } if (fpMemoryAlloc == NULL) { fpErrorHandler(103, "No pointer to memory allocation function."); return NULL; } ASFormatter formatter; ASOptions options(formatter); vector optionsVector; istringstream opt(pOptions); options.importOptions(opt, optionsVector); bool ok = options.parseOptions(optionsVector, "Invalid Artistic Style options:"); if (!ok) fpErrorHandler(130, options.getOptionErrors().c_str()); istringstream in(pSourceIn); ASStreamIterator streamIterator(&in); ostringstream out; formatter.init(&streamIterator); while (formatter.hasMoreLines()) { out << formatter.nextLine(); if (formatter.hasMoreLines()) out << streamIterator.getOutputEOL(); else { // this can happen if the file if missing a closing bracket and break-blocks is requested if (formatter.getIsLineReady()) { out << streamIterator.getOutputEOL(); out << formatter.nextLine(); } } } unsigned long textSizeOut = out.str().length(); char* pTextOut = fpMemoryAlloc(textSizeOut + 1); // call memory allocation function if (pTextOut == NULL) { fpErrorHandler(120, "Allocation failure on output."); return NULL; } strcpy(pTextOut, out.str().c_str()); #ifndef NDEBUG // The checksum is an assert in the console build and ASFormatter. // This error returns the incorrectly formatted file to the editor. // This is done to allow the file to be saved for debugging purposes. if (formatter.getChecksumDiff() != 0) fpErrorHandler(220, "Checksum error.\n" "The incorrectly formatted file will be returned for debugging."); #endif return pTextOut; } extern "C" EXPORT const char* STDCALL AStyleGetVersion(void) { return g_version; } // ASTYLECON_LIB is defined to exclude "main" from the test programs #elif !defined(ASTYLECON_LIB) //---------------------------------------------------------------------------- // main function for ASConsole build //---------------------------------------------------------------------------- int main(int argc, char** argv) { // create objects ASFormatter formatter; g_console = new ASConsole(formatter); // process command line and options file // build the vectors fileNameVector, optionsVector, and fileOptionsVector vector argvOptions; argvOptions = g_console->getArgvOptions(argc, argv); g_console->processOptions(argvOptions); // if no files have been given, use cin for input and cout for output if (g_console->fileNameVectorIsEmpty()) { g_console->formatCinToCout(); return EXIT_SUCCESS; } // process entries in the fileNameVector g_console->processFiles(); delete g_console; return EXIT_SUCCESS; } #endif // ASTYLE_LIB ================================================ FILE: 3rdpart/astyle/astyle_main.h ================================================ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * astyle_main.h * * Copyright (C) 2014 by Jim Pattee * * * This file is a part of Artistic Style - an indentation and * reformatting tool for C, C++, C# and Java source files. * * * Artistic Style is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Artistic Style is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Artistic Style. If not, see . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef ASTYLE_MAIN_H #define ASTYLE_MAIN_H //---------------------------------------------------------------------------- // headers //---------------------------------------------------------------------------- #include "astyle.h" #include #include #if defined(__BORLANDC__) && __BORLANDC__ < 0x0650 // Embarcadero needs this for the following utime.h // otherwise "struct utimbuf" gets an error on time_t // 0x0650 for C++Builder XE3 using std::time_t; #endif #if defined(_MSC_VER) || defined(__DMC__) #include #include #else #include #include #endif // end compiler checks #ifdef ASTYLE_JNI #include #ifndef ASTYLE_LIB // ASTYLE_LIB must be defined for ASTYLE_JNI #define ASTYLE_LIB #endif #endif // ASTYLE_JNI #ifndef ASTYLE_LIB // for console build only #include "ASLocalizer.h" #define _(a) localizer.settext(a) #endif // ASTYLE_LIB // for G++ implementation of string.compare: #if defined(__GNUC__) && __GNUC__ < 3 #error - Use GNU C compiler release 3 or higher #endif // for namespace problem in version 5.0 #if defined(_MSC_VER) && _MSC_VER < 1200 // check for V6.0 #error - Use Microsoft compiler version 6 or higher #endif // for mingw BOM, UTF-16, and Unicode functions #if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) #if (__MINGW32_MAJOR_VERSION > 3) || ((__MINGW32_MAJOR_VERSION == 3) && (__MINGW32_MINOR_VERSION < 16)) #error - Use MinGW compiler version 4 or higher #endif #endif //---------------------------------------------------------------------------- // definitions //---------------------------------------------------------------------------- #ifdef ASTYLE_LIB // define STDCALL and EXPORT for Windows // MINGW defines STDCALL in Windows.h (actually windef.h) // EXPORT has no value if ASTYLE_NO_EXPORT is defined #ifdef _WIN32 #ifndef STDCALL #define STDCALL __stdcall #endif // define this to prevent compiler warning and error messages #ifdef ASTYLE_NO_EXPORT #define EXPORT #else #define EXPORT __declspec(dllexport) #endif // define STDCALL and EXPORT for non-Windows // visibility attribute allows "-fvisibility=hidden" compiler option #else #define STDCALL #if __GNUC__ >= 4 #define EXPORT __attribute__ ((visibility ("default"))) #else #define EXPORT #endif #endif // #ifdef _WIN32 // define utf-16 bit text for the platform typedef unsigned short utf16_t; // define pointers to callback error handler and memory allocation typedef void (STDCALL* fpError)(int errorNumber, const char* errorMessage); typedef char* (STDCALL* fpAlloc)(unsigned long memoryNeeded); #endif // #ifdef ASTYLE_LIB //---------------------------------------------------------------------------- // astyle namespace //---------------------------------------------------------------------------- namespace astyle { //---------------------------------------------------------------------------- // ASStreamIterator class // typename will be istringstream for GUI and istream otherwise // ASSourceIterator is an abstract class defined in astyle.h //---------------------------------------------------------------------------- template class ASStreamIterator : public ASSourceIterator { public: bool checkForEmptyLine; // function declarations ASStreamIterator(T* in); virtual ~ASStreamIterator(); bool getLineEndChange(int lineEndFormat) const; int getStreamLength() const; string nextLine(bool emptyLineWasDeleted); string peekNextLine(); void peekReset(); void saveLastInputLine(); streamoff tellg(); private: ASStreamIterator(const ASStreamIterator ©); // copy constructor not to be implemented ASStreamIterator &operator=(ASStreamIterator &); // assignment operator not to be implemented T* inStream; // pointer to the input stream string buffer; // current input line string prevBuffer; // previous input line int eolWindows; // number of Windows line endings, CRLF int eolLinux; // number of Linux line endings, LF int eolMacOld; // number of old Mac line endings. CR char outputEOL[4]; // next output end of line char streamoff streamLength; // length of the input file stream streamoff peekStart; // starting position for peekNextLine bool prevLineDeleted; // the previous input line was deleted public: // inline functions bool compareToInputBuffer(const string &nextLine_) const { return (nextLine_ == prevBuffer); } const char* getOutputEOL() const { return outputEOL; } bool hasMoreLines() const { return !inStream->eof(); } }; //---------------------------------------------------------------------------- // Utf8_16 class for utf8/16 conversions //---------------------------------------------------------------------------- class Utf8_16 { private: typedef unsigned short utf16; // 16 bits typedef unsigned char utf8; // 8 bits typedef unsigned char ubyte; // 8 bits enum { SURROGATE_LEAD_FIRST = 0xD800 }; enum { SURROGATE_LEAD_LAST = 0xDBFF }; enum { SURROGATE_TRAIL_FIRST = 0xDC00 }; enum { SURROGATE_TRAIL_LAST = 0xDFFF }; enum { SURROGATE_FIRST_VALUE = 0x10000 }; enum eState { eStart, eSecondOf4Bytes, ePenultimate, eFinal }; public: bool getBigEndian() const; int swap16bit(int value) const; size_t utf16len(const utf16* utf16In) const; size_t Utf8LengthFromUtf16(const char* utf16In, size_t inLen, bool isBigEndian) const; size_t Utf8ToUtf16(char* utf8In, size_t inLen, bool isBigEndian, char* utf16Out) const; size_t Utf16LengthFromUtf8(const char* utf8In, size_t inLen) const; size_t Utf16ToUtf8(char* utf16In, size_t inLen, bool isBigEndian, bool firstBlock, char* utf8Out) const; }; //---------------------------------------------------------------------------- // ASOptions class for options processing // used by both console and library builds //---------------------------------------------------------------------------- class ASOptions { public: ASOptions(ASFormatter &formatterArg) : formatter(formatterArg) {} string getOptionErrors() const; void importOptions(istream &in, vector &optionsVector); bool parseOptions(vector &optionsVector, const string &errorInfo); private: // variables ASFormatter &formatter; // reference to the ASFormatter object stringstream optionErrors; // option error messages // functions ASOptions &operator=(ASOptions &); // not to be implemented string getParam(const string &arg, const char* op); string getParam(const string &arg, const char* op1, const char* op2); bool isOption(const string &arg, const char* op); bool isOption(const string &arg, const char* op1, const char* op2); void isOptionError(const string &arg, const string &errorInfo); bool isParamOption(const string &arg, const char* option); bool isParamOption(const string &arg, const char* option1, const char* option2); void parseOption(const string &arg, const string &errorInfo); }; #ifndef ASTYLE_LIB //---------------------------------------------------------------------------- // ASConsole class for console build //---------------------------------------------------------------------------- class ASConsole { private: // variables ASFormatter &formatter; // reference to the ASFormatter object ASLocalizer localizer; // ASLocalizer object // command line options bool isRecursive; // recursive option bool isDryRun; // dry-run option bool noBackup; // suffix=none option bool preserveDate; // preserve-date option bool isVerbose; // verbose option bool isQuiet; // quiet option bool isFormattedOnly; // formatted lines only option bool ignoreExcludeErrors; // don't abort on unmatched excludes bool ignoreExcludeErrorsDisplay; // don't display unmatched excludes bool optionsFileRequired; // options= option bool useAscii; // ascii option // other variables bool bypassBrowserOpen; // don't open the browser on html options bool hasWildcard; // file name includes a wildcard size_t mainDirectoryLength; // directory length to be excluded in displays bool filesAreIdentical; // input and output files are identical int filesFormatted; // number of files formatted int filesUnchanged; // number of files unchanged bool lineEndsMixed; // output has mixed line ends int linesOut; // number of output lines char outputEOL[4]; // current line end char prevEOL[4]; // previous line end Utf8_16 utf8_16; // utf8/16 conversion methods string optionsFileName; // file path and name of the options file to use string origSuffix; // suffix= option string targetDirectory; // path to the directory being processed string targetFilename; // file name being processed vector excludeVector; // exclude from wildcard hits vector excludeHitsVector; // exclude flags for error reporting vector fileNameVector; // file paths and names from the command line vector optionsVector; // options from the command line vector fileOptionsVector; // options from the options file vector fileName; // files to be processed including path public: // variables ASConsole(ASFormatter &formatterArg) : formatter(formatterArg) { // command line options isRecursive = false; isDryRun = false; noBackup = false; preserveDate = false; isVerbose = false; isQuiet = false; isFormattedOnly = false; ignoreExcludeErrors = false; ignoreExcludeErrorsDisplay = false; optionsFileRequired = false; useAscii = false; // other variables bypassBrowserOpen = false; hasWildcard = false; filesAreIdentical = true; lineEndsMixed = false; outputEOL[0] = '\0'; prevEOL[0] = '\0'; origSuffix = ".orig"; mainDirectoryLength = 0; filesFormatted = 0; filesUnchanged = 0; linesOut = 0; } public: // functions void convertLineEnds(ostringstream &out, int lineEnd); FileEncoding detectEncoding(const char* data, size_t dataSize) const; void error() const; void error(const char* why, const char* what) const; void formatCinToCout(); vector getArgvOptions(int argc, char** argv) const; bool fileNameVectorIsEmpty() const; bool getFilesAreIdentical() const; int getFilesFormatted() const; bool getIgnoreExcludeErrors() const; bool getIgnoreExcludeErrorsDisplay() const; bool getIsDryRun() const; bool getIsFormattedOnly() const; bool getIsQuiet() const; bool getIsRecursive() const; bool getIsVerbose() const; bool getLineEndsMixed() const; bool getNoBackup() const; bool getPreserveDate() const; string getLanguageID() const; string getNumberFormat(int num, size_t = 0) const; string getNumberFormat(int num, const char* groupingArg, const char* separator) const; string getOptionsFileName() const; string getOrigSuffix() const; void processFiles(); void processOptions(vector &argvOptions); void setBypassBrowserOpen(bool state); void setIgnoreExcludeErrors(bool state); void setIgnoreExcludeErrorsAndDisplay(bool state); void setIsDryRun(bool state); void setIsFormattedOnly(bool state); void setIsQuiet(bool state); void setIsRecursive(bool state); void setIsVerbose(bool state); void setNoBackup(bool state); void setOptionsFileName(string name); void setOrigSuffix(string suffix); void setPreserveDate(bool state); void standardizePath(string &path, bool removeBeginningSeparator = false) const; bool stringEndsWith(const string &str, const string &suffix) const; void updateExcludeVector(string suffixParam); vector getExcludeVector() const; vector getExcludeHitsVector() const; vector getFileNameVector() const; vector getOptionsVector() const; vector getFileOptionsVector() const; vector getFileName() const; private: // functions ASConsole &operator=(ASConsole &); // not to be implemented void correctMixedLineEnds(ostringstream &out); void formatFile(const string &fileName_); string getCurrentDirectory(const string &fileName_) const; void getFileNames(const string &directory, const string &wildcard); void getFilePaths(string &filePath); string getParam(const string &arg, const char* op); void initializeOutputEOL(LineEndFormat lineEndFormat); bool isOption(const string &arg, const char* op); bool isOption(const string &arg, const char* op1, const char* op2); bool isParamOption(const string &arg, const char* option); bool isPathExclued(const string &subPath); void launchDefaultBrowser(const char* filePathIn = NULL) const; void printHelp() const; void printMsg(const char* msg, const string &data) const; void printSeparatingLine() const; void printVerboseHeader() const; void printVerboseStats(clock_t startTime) const; FileEncoding readFile(const string &fileName_, stringstream &in) const; void removeFile(const char* fileName_, const char* errMsg) const; void renameFile(const char* oldFileName, const char* newFileName, const char* errMsg) const; void setOutputEOL(LineEndFormat lineEndFormat, const char* currentEOL); void sleep(int seconds) const; int waitForRemove(const char* oldFileName) const; int wildcmp(const char* wild, const char* data) const; void writeFile(const string &fileName_, FileEncoding encoding, ostringstream &out) const; #ifdef _WIN32 void displayLastError(); #endif }; #else // ASTYLE_LIB //---------------------------------------------------------------------------- // ASLibrary class for library build //---------------------------------------------------------------------------- class ASLibrary { public: ASLibrary() {} virtual ~ASLibrary() {} // virtual functions are mocked in testing utf16_t* formatUtf16(const utf16_t*, const utf16_t*, fpError, fpAlloc) const; virtual utf16_t* convertUtf8ToUtf16(const char* utf8In, fpAlloc fpMemoryAlloc) const; virtual char* convertUtf16ToUtf8(const utf16_t* pSourceIn) const; private: static char* STDCALL tempMemoryAllocation(unsigned long memoryNeeded); private: Utf8_16 utf8_16; // utf8/16 conversion methods }; #endif // ASTYLE_LIB //---------------------------------------------------------------------------- } // end of namespace astyle //---------------------------------------------------------------------------- // declarations for java native interface (JNI) build // they are called externally and are NOT part of the namespace //---------------------------------------------------------------------------- #ifdef ASTYLE_JNI void STDCALL javaErrorHandler(int errorNumber, const char* errorMessage); char* STDCALL javaMemoryAlloc(unsigned long memoryNeeded); // the following function names are constructed from method names in the calling java program extern "C" EXPORT jstring STDCALL Java_AStyleInterface_AStyleGetVersion(JNIEnv* env, jclass); extern "C" EXPORT jstring STDCALL Java_AStyleInterface_AStyleMain(JNIEnv* env, jobject obj, jstring textInJava, jstring optionsJava); #endif // ASTYLE_JNI //---------------------------------------------------------------------------- // declarations for UTF-16 interface // they are called externally and are NOT part of the namespace //---------------------------------------------------------------------------- #ifdef ASTYLE_LIB extern "C" EXPORT utf16_t* STDCALL AStyleMainUtf16(const utf16_t* pSourceIn, const utf16_t* pOptions, fpError fpErrorHandler, fpAlloc fpMemoryAlloc); #endif // ASTYLE_LIB //----------------------------------------------------------------------------- // declarations for standard DLL interface // they are called externally and are NOT part of the namespace //----------------------------------------------------------------------------- #ifdef ASTYLE_LIB extern "C" EXPORT char* STDCALL AStyleMain(const char* sourceIn, const char* optionsIn, fpError errorHandler, fpAlloc memoryAlloc); extern "C" EXPORT const char* STDCALL AStyleGetVersion(void); #endif // ASTYLE_LIB //----------------------------------------------------------------------------- #endif // closes ASTYLE_MAIN_H ================================================ FILE: 3rdpart/backward/backward.cpp ================================================ // Pick your poison. // // On GNU/Linux, you have few choices to get the most out of your stack trace. // // By default you get: // - object filename // - function name // // In order to add: // - source filename // - line and column numbers // - source code snippet (assuming the file is accessible) // Install one of the following library then uncomment one of the macro (or // better, add the detection of the lib and the macro definition in your build // system) // - apt-get install libdw-dev ... // - g++/clang++ -ldw ... // #define BACKWARD_HAS_DW 1 // - apt-get install binutils-dev ... // - g++/clang++ -lbfd ... // #define BACKWARD_HAS_BFD 1 #include "backward.hpp" namespace backward { backward::SignalHandling sh; } // namespace backward ================================================ FILE: 3rdpart/backward/backward.hpp ================================================ /* * backward.hpp * Copyright 2013 Google Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef H_6B9572DA_A64B_49E6_B234_051480991C89 #define H_6B9572DA_A64B_49E6_B234_051480991C89 #ifndef __cplusplus # error "It's not going to compile without a C++ compiler..." #endif #if defined(BACKWARD_CXX11) #elif defined(BACKWARD_CXX98) #else # if __cplusplus >= 201103L # define BACKWARD_CXX11 # define BACKWARD_ATLEAST_CXX11 # define BACKWARD_ATLEAST_CXX98 # else # define BACKWARD_CXX98 # define BACKWARD_ATLEAST_CXX98 # endif #endif // You can define one of the following (or leave it to the auto-detection): // // #define BACKWARD_SYSTEM_LINUX // - specialization for linux // // #define BACKWARD_SYSTEM_DARWIN // - specialization for Mac OS X 10.5 and later. // // #define BACKWARD_SYSTEM_UNKNOWN // - placebo implementation, does nothing. // #if defined(BACKWARD_SYSTEM_LINUX) #elif defined(BACKWARD_SYSTEM_DARWIN) #elif defined(BACKWARD_SYSTEM_UNKNOWN) #else # if defined(__linux) # define BACKWARD_SYSTEM_LINUX # elif defined(__APPLE__) # define BACKWARD_SYSTEM_DARWIN # else # define BACKWARD_SYSTEM_UNKNOWN # endif #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(BACKWARD_SYSTEM_LINUX) // On linux, backtrace can back-trace or "walk" the stack using the following // libraries: // // #define BACKWARD_HAS_UNWIND 1 // - unwind comes from libgcc, but I saw an equivalent inside clang itself. // - with unwind, the stacktrace is as accurate as it can possibly be, since // this is used by the C++ runtine in gcc/clang for stack unwinding on // exception. // - normally libgcc is already linked to your program by default. // // #define BACKWARD_HAS_BACKTRACE == 1 // - backtrace seems to be a little bit more portable than libunwind, but on // linux, it uses unwind anyway, but abstract away a tiny information that is // sadly really important in order to get perfectly accurate stack traces. // - backtrace is part of the (e)glib library. // // The default is: // #define BACKWARD_HAS_UNWIND == 1 // // Note that only one of the define should be set to 1 at a time. // # if BACKWARD_HAS_UNWIND == 1 # elif BACKWARD_HAS_BACKTRACE == 1 # else # undef BACKWARD_HAS_UNWIND # define BACKWARD_HAS_UNWIND 1 # undef BACKWARD_HAS_BACKTRACE # define BACKWARD_HAS_BACKTRACE 0 # endif // On linux, backward can extract detailed information about a stack trace // using one of the following libraries: // // #define BACKWARD_HAS_DW 1 // - libdw gives you the most juicy details out of your stack traces: // - object filename // - function name // - source filename // - line and column numbers // - source code snippet (assuming the file is accessible) // - variables name and values (if not optimized out) // - You need to link with the lib "dw": // - apt-get install libdw-dev // - g++/clang++ -ldw ... // // #define BACKWARD_HAS_BFD 1 // - With libbfd, you get a fair amount of details: // - object filename // - function name // - source filename // - line numbers // - source code snippet (assuming the file is accessible) // - You need to link with the lib "bfd": // - apt-get install binutils-dev // - g++/clang++ -lbfd ... // // #define BACKWARD_HAS_BACKTRACE_SYMBOL 1 // - backtrace provides minimal details for a stack trace: // - object filename // - function name // - backtrace is part of the (e)glib library. // // The default is: // #define BACKWARD_HAS_BACKTRACE_SYMBOL == 1 // // Note that only one of the define should be set to 1 at a time. // # if BACKWARD_HAS_DW == 1 # elif BACKWARD_HAS_BFD == 1 # elif BACKWARD_HAS_BACKTRACE_SYMBOL == 1 # else # undef BACKWARD_HAS_DW # define BACKWARD_HAS_DW 0 # undef BACKWARD_HAS_BFD # define BACKWARD_HAS_BFD 0 # undef BACKWARD_HAS_BACKTRACE_SYMBOL # define BACKWARD_HAS_BACKTRACE_SYMBOL 1 # endif # include # include # include # include # include # include # include # if BACKWARD_HAS_BFD == 1 // NOTE: defining PACKAGE{,_VERSION} is required before including // bfd.h on some platforms, see also: // https://sourceware.org/bugzilla/show_bug.cgi?id=14243 # ifndef PACKAGE # define PACKAGE # endif # ifndef PACKAGE_VERSION # define PACKAGE_VERSION # endif # include # ifndef _GNU_SOURCE # define _GNU_SOURCE # include # undef _GNU_SOURCE # else # include # endif # endif # if BACKWARD_HAS_DW == 1 # include # include # include # endif # if (BACKWARD_HAS_BACKTRACE == 1) || (BACKWARD_HAS_BACKTRACE_SYMBOL == 1) // then we shall rely on backtrace # include # endif #endif // defined(BACKWARD_SYSTEM_LINUX) #if defined(BACKWARD_SYSTEM_DARWIN) // On Darwin, backtrace can back-trace or "walk" the stack using the following // libraries: // // #define BACKWARD_HAS_UNWIND 1 // - unwind comes from libgcc, but I saw an equivalent inside clang itself. // - with unwind, the stacktrace is as accurate as it can possibly be, since // this is used by the C++ runtine in gcc/clang for stack unwinding on // exception. // - normally libgcc is already linked to your program by default. // // #define BACKWARD_HAS_BACKTRACE == 1 // - backtrace is available by default, though it does not produce as much information // as another library might. // // The default is: // #define BACKWARD_HAS_UNWIND == 1 // // Note that only one of the define should be set to 1 at a time. // # if BACKWARD_HAS_UNWIND == 1 # elif BACKWARD_HAS_BACKTRACE == 1 # else # undef BACKWARD_HAS_UNWIND # define BACKWARD_HAS_UNWIND 1 # undef BACKWARD_HAS_BACKTRACE # define BACKWARD_HAS_BACKTRACE 0 # endif // On Darwin, backward can extract detailed information about a stack trace // using one of the following libraries: // // #define BACKWARD_HAS_BACKTRACE_SYMBOL 1 // - backtrace provides minimal details for a stack trace: // - object filename // - function name // // The default is: // #define BACKWARD_HAS_BACKTRACE_SYMBOL == 1 // # if BACKWARD_HAS_BACKTRACE_SYMBOL == 1 # else # undef BACKWARD_HAS_BACKTRACE_SYMBOL # define BACKWARD_HAS_BACKTRACE_SYMBOL 1 # endif # include # include # include # include # include # include # if (BACKWARD_HAS_BACKTRACE == 1) || (BACKWARD_HAS_BACKTRACE_SYMBOL == 1) # include # endif #endif // defined(BACKWARD_SYSTEM_DARWIN) #if BACKWARD_HAS_UNWIND == 1 # include // while gcc's unwind.h defines something like that: // extern _Unwind_Ptr _Unwind_GetIP (struct _Unwind_Context *); // extern _Unwind_Ptr _Unwind_GetIPInfo (struct _Unwind_Context *, int *); // // clang's unwind.h defines something like this: // uintptr_t _Unwind_GetIP(struct _Unwind_Context* __context); // // Even if the _Unwind_GetIPInfo can be linked to, it is not declared, worse we // cannot just redeclare it because clang's unwind.h doesn't define _Unwind_Ptr // anyway. // // Luckily we can play on the fact that the guard macros have a different name: #ifdef __CLANG_UNWIND_H // In fact, this function still comes from libgcc (on my different linux boxes, // clang links against libgcc). # include extern "C" uintptr_t _Unwind_GetIPInfo(_Unwind_Context*, int*); #endif #endif // BACKWARD_HAS_UNWIND == 1 #ifdef BACKWARD_ATLEAST_CXX11 # include # include // for std::swap namespace backward { namespace details { template struct hashtable { typedef std::unordered_map type; }; using std::move; } // namespace details } // namespace backward #else // NOT BACKWARD_ATLEAST_CXX11 # include namespace backward { namespace details { template struct hashtable { typedef std::map type; }; template const T& move(const T& v) { return v; } template T& move(T& v) { return v; } } // namespace details } // namespace backward #endif // BACKWARD_ATLEAST_CXX11 namespace backward { namespace system_tag { struct linux_tag; // seems that I cannot call that "linux" because the name // is already defined... so I am adding _tag everywhere. struct darwin_tag; struct unknown_tag; #if defined(BACKWARD_SYSTEM_LINUX) typedef linux_tag current_tag; #elif defined(BACKWARD_SYSTEM_DARWIN) typedef darwin_tag current_tag; #elif defined(BACKWARD_SYSTEM_UNKNOWN) typedef unknown_tag current_tag; #else # error "May I please get my system defines?" #endif } // namespace system_tag namespace trace_resolver_tag { #if defined(BACKWARD_SYSTEM_LINUX) struct libdw; struct libbfd; struct backtrace_symbol; # if BACKWARD_HAS_DW == 1 typedef libdw current; # elif BACKWARD_HAS_BFD == 1 typedef libbfd current; # elif BACKWARD_HAS_BACKTRACE_SYMBOL == 1 typedef backtrace_symbol current; # else # error "You shall not pass, until you know what you want." # endif #elif defined(BACKWARD_SYSTEM_DARWIN) struct backtrace_symbol; # if BACKWARD_HAS_BACKTRACE_SYMBOL == 1 typedef backtrace_symbol current; # else # error "You shall not pass, until you know what you want." # endif #endif } // namespace trace_resolver_tag namespace details { template struct rm_ptr { typedef T type; }; template struct rm_ptr { typedef T type; }; template struct rm_ptr { typedef const T type; }; template struct deleter { template void operator()(U& ptr) const { (*F)(ptr); } }; template struct default_delete { void operator()(T& ptr) const { delete ptr; } }; template > class handle { struct dummy; T _val; bool _empty; #ifdef BACKWARD_ATLEAST_CXX11 handle(const handle&) = delete; handle& operator=(const handle&) = delete; #endif public: ~handle() { if (!_empty) { Deleter()(_val); } } explicit handle(): _val(), _empty(true) {} explicit handle(T val): _val(val), _empty(false) { if(!_val) _empty = true; } #ifdef BACKWARD_ATLEAST_CXX11 handle(handle&& from): _empty(true) { swap(from); } handle& operator=(handle&& from) { swap(from); return *this; } #else explicit handle(const handle& from): _empty(true) { // some sort of poor man's move semantic. swap(const_cast(from)); } handle& operator=(const handle& from) { // some sort of poor man's move semantic. swap(const_cast(from)); return *this; } #endif void reset(T new_val) { handle tmp(new_val); swap(tmp); } operator const dummy*() const { if (_empty) { return 0; } return reinterpret_cast(_val); } T get() { return _val; } T release() { _empty = true; return _val; } void swap(handle& b) { using std::swap; swap(b._val, _val); // can throw, we are safe here. swap(b._empty, _empty); // should not throw: if you cannot swap two // bools without throwing... It's a lost cause anyway! } T operator->() { return _val; } const T operator->() const { return _val; } typedef typename rm_ptr::type& ref_t; typedef const typename rm_ptr::type& const_ref_t; ref_t operator*() { return *_val; } const_ref_t operator*() const { return *_val; } ref_t operator[](size_t idx) { return _val[idx]; } // Watch out, we've got a badass over here T* operator&() { _empty = false; return &_val; } }; // Default demangler implementation (do nothing). template struct demangler_impl { static std::string demangle(const char* funcname) { return funcname; } }; #if defined(BACKWARD_SYSTEM_LINUX) || defined(BACKWARD_SYSTEM_DARWIN) template <> struct demangler_impl { demangler_impl(): _demangle_buffer_length(0) {} std::string demangle(const char* funcname) { using namespace details; char* result = abi::__cxa_demangle(funcname, _demangle_buffer.release(), &_demangle_buffer_length, 0); if(result) { _demangle_buffer.reset(result); return result; } return funcname; } private: details::handle _demangle_buffer; size_t _demangle_buffer_length; }; #endif // BACKWARD_SYSTEM_LINUX || BACKWARD_SYSTEM_DARWIN struct demangler: public demangler_impl {}; } // namespace details /*************** A TRACE ***************/ struct Trace { void* addr; size_t idx; Trace(): addr(0), idx(0) {} explicit Trace(void* _addr, size_t _idx): addr(_addr), idx(_idx) {} }; struct ResolvedTrace: public Trace { struct SourceLoc { std::string function; std::string filename; unsigned line; unsigned col; SourceLoc(): line(0), col(0) {} bool operator==(const SourceLoc& b) const { return function == b.function && filename == b.filename && line == b.line && col == b.col; } bool operator!=(const SourceLoc& b) const { return !(*this == b); } }; // In which binary object this trace is located. std::string object_filename; // The function in the object that contain the trace. This is not the same // as source.function which can be an function inlined in object_function. std::string object_function; // The source location of this trace. It is possible for filename to be // empty and for line/col to be invalid (value 0) if this information // couldn't be deduced, for example if there is no debug information in the // binary object. SourceLoc source; // An optionals list of "inliners". All the successive sources location // from where the source location of the trace (the attribute right above) // is inlined. It is especially useful when you compiled with optimization. typedef std::vector source_locs_t; source_locs_t inliners; ResolvedTrace(): Trace() {} ResolvedTrace(const Trace& mini_trace): Trace(mini_trace) {} }; /*************** STACK TRACE ***************/ // default implemention. template class StackTraceImpl { public: size_t size() const { return 0; } Trace operator[](size_t) { return Trace(); } size_t load_here(size_t=0) { return 0; } size_t load_from(void*, size_t=0) { return 0; } size_t thread_id() const { return 0; } void skip_n_firsts(size_t) { } }; class StackTraceImplBase { public: StackTraceImplBase(): _thread_id(0), _skip(0) {} size_t thread_id() const { return _thread_id; } void skip_n_firsts(size_t n) { _skip = n; } protected: void load_thread_info() { #ifdef BACKWARD_SYSTEM_LINUX _thread_id = (size_t)syscall(SYS_gettid); if (_thread_id == (size_t) getpid()) { // If the thread is the main one, let's hide that. // I like to keep little secret sometimes. _thread_id = 0; } #elif defined(BACKWARD_SYSTEM_DARWIN) _thread_id = reinterpret_cast(pthread_self()); if (pthread_main_np() == 1) { // If the thread is the main one, let's hide that. _thread_id = 0; } #endif } size_t skip_n_firsts() const { return _skip; } private: size_t _thread_id; size_t _skip; }; class StackTraceImplHolder: public StackTraceImplBase { public: size_t size() const { return _stacktrace.size() ? _stacktrace.size() - skip_n_firsts() : 0; } Trace operator[](size_t idx) const { if (idx >= size()) { return Trace(); } return Trace(_stacktrace[idx + skip_n_firsts()], idx); } void* const* begin() const { if (size()) { return &_stacktrace[skip_n_firsts()]; } return 0; } protected: std::vector _stacktrace; }; #if BACKWARD_HAS_UNWIND == 1 namespace details { template class Unwinder { public: size_t operator()(F& f, size_t depth) { _f = &f; _index = -1; _depth = depth; _Unwind_Backtrace(&this->backtrace_trampoline, this); return _index; } private: F* _f; ssize_t _index; size_t _depth; static _Unwind_Reason_Code backtrace_trampoline( _Unwind_Context* ctx, void *self) { return ((Unwinder*)self)->backtrace(ctx); } _Unwind_Reason_Code backtrace(_Unwind_Context* ctx) { if (_index >= 0 && static_cast(_index) >= _depth) return _URC_END_OF_STACK; int ip_before_instruction = 0; uintptr_t ip = _Unwind_GetIPInfo(ctx, &ip_before_instruction); if (!ip_before_instruction) { // calculating 0-1 for unsigned, looks like a possible bug to sanitiziers, so let's do it explicitly: if (ip==0) { ip = std::numeric_limits::max(); // set it to 0xffff... (as from casting 0-1) } else { ip -= 1; // else just normally decrement it (no overflow/underflow will happen) } } if (_index >= 0) { // ignore first frame. (*_f)(_index, (void*)ip); } _index += 1; return _URC_NO_REASON; } }; template size_t unwind(F f, size_t depth) { Unwinder unwinder; return unwinder(f, depth); } } // namespace details template <> class StackTraceImpl: public StackTraceImplHolder { public: __attribute__ ((noinline)) // TODO use some macro size_t load_here(size_t depth=32) { load_thread_info(); if (depth == 0) { return 0; } _stacktrace.resize(depth); size_t trace_cnt = details::unwind(callback(*this), depth); _stacktrace.resize(trace_cnt); skip_n_firsts(0); return size(); } size_t load_from(void* addr, size_t depth=32) { load_here(depth + 8); for (size_t i = 0; i < _stacktrace.size(); ++i) { if (_stacktrace[i] == addr) { skip_n_firsts(i); break; } } _stacktrace.resize(std::min(_stacktrace.size(), skip_n_firsts() + depth)); return size(); } private: struct callback { StackTraceImpl& self; callback(StackTraceImpl& _self): self(_self) {} void operator()(size_t idx, void* addr) { self._stacktrace[idx] = addr; } }; }; #else // BACKWARD_HAS_UNWIND == 0 template <> class StackTraceImpl: public StackTraceImplHolder { public: __attribute__ ((noinline)) // TODO use some macro size_t load_here(size_t depth=32) { load_thread_info(); if (depth == 0) { return 0; } _stacktrace.resize(depth + 1); size_t trace_cnt = backtrace(&_stacktrace[0], _stacktrace.size()); _stacktrace.resize(trace_cnt); skip_n_firsts(1); return size(); } size_t load_from(void* addr, size_t depth=32) { load_here(depth + 8); for (size_t i = 0; i < _stacktrace.size(); ++i) { if (_stacktrace[i] == addr) { skip_n_firsts(i); _stacktrace[i] = (void*)( (uintptr_t)_stacktrace[i] + 1); break; } } _stacktrace.resize(std::min(_stacktrace.size(), skip_n_firsts() + depth)); return size(); } }; #endif // BACKWARD_HAS_UNWIND class StackTrace: public StackTraceImpl {}; /*************** TRACE RESOLVER ***************/ template class TraceResolverImpl; #ifdef BACKWARD_SYSTEM_UNKNOWN template <> class TraceResolverImpl { public: template void load_stacktrace(ST&) {} ResolvedTrace resolve(ResolvedTrace t) { return t; } }; #endif class TraceResolverImplBase { protected: std::string demangle(const char* funcname) { return _demangler.demangle(funcname); } private: details::demangler _demangler; }; #ifdef BACKWARD_SYSTEM_LINUX template class TraceResolverLinuxImpl; #if BACKWARD_HAS_BACKTRACE_SYMBOL == 1 template <> class TraceResolverLinuxImpl: public TraceResolverImplBase { public: template void load_stacktrace(ST& st) { using namespace details; if (st.size() == 0) { return; } _symbols.reset( backtrace_symbols(st.begin(), (int)st.size()) ); } ResolvedTrace resolve(ResolvedTrace trace) { char* filename = _symbols[trace.idx]; char* funcname = filename; while (*funcname && *funcname != '(') { funcname += 1; } trace.object_filename.assign(filename, funcname); // ok even if funcname is the ending \0 (then we assign entire string) if (*funcname) { // if it's not end of string (e.g. from last frame ip==0) funcname += 1; char* funcname_end = funcname; while (*funcname_end && *funcname_end != ')' && *funcname_end != '+') { funcname_end += 1; } *funcname_end = '\0'; trace.object_function = this->demangle(funcname); trace.source.function = trace.object_function; // we cannot do better. } return trace; } private: details::handle _symbols; }; #endif // BACKWARD_HAS_BACKTRACE_SYMBOL == 1 #if BACKWARD_HAS_BFD == 1 template <> class TraceResolverLinuxImpl: public TraceResolverImplBase { static std::string read_symlink(std::string const & symlink_path) { std::string path; path.resize(100); while(true) { ssize_t len = ::readlink(symlink_path.c_str(), &*path.begin(), path.size()); if(len < 0) { return ""; } if ((size_t)len == path.size()) { path.resize(path.size() * 2); } else { path.resize(len); break; } } return path; } public: TraceResolverLinuxImpl(): _bfd_loaded(false) {} template void load_stacktrace(ST&) {} ResolvedTrace resolve(ResolvedTrace trace) { Dl_info symbol_info; // trace.addr is a virtual address in memory pointing to some code. // Let's try to find from which loaded object it comes from. // The loaded object can be yourself btw. if (!dladdr(trace.addr, &symbol_info)) { return trace; // dat broken trace... } std::string argv0; { std::ifstream ifs("/proc/self/cmdline"); std::getline(ifs, argv0, '\0'); } std::string tmp; if(symbol_info.dli_fname == argv0) { tmp = read_symlink("/proc/self/exe"); symbol_info.dli_fname = tmp.c_str(); } // Now we get in symbol_info: // .dli_fname: // pathname of the shared object that contains the address. // .dli_fbase: // where the object is loaded in memory. // .dli_sname: // the name of the nearest symbol to trace.addr, we expect a // function name. // .dli_saddr: // the exact address corresponding to .dli_sname. if (symbol_info.dli_sname) { trace.object_function = demangle(symbol_info.dli_sname); } if (!symbol_info.dli_fname) { return trace; } trace.object_filename = symbol_info.dli_fname; bfd_fileobject& fobj = load_object_with_bfd(symbol_info.dli_fname); if (!fobj.handle) { return trace; // sad, we couldn't load the object :( } find_sym_result* details_selected; // to be filled. // trace.addr is the next instruction to be executed after returning // from the nested stack frame. In C++ this usually relate to the next // statement right after the function call that leaded to a new stack // frame. This is not usually what you want to see when printing out a // stacktrace... find_sym_result details_call_site = find_symbol_details(fobj, trace.addr, symbol_info.dli_fbase); details_selected = &details_call_site; #if BACKWARD_HAS_UNWIND == 0 // ...this is why we also try to resolve the symbol that is right // before the return address. If we are lucky enough, we will get the // line of the function that was called. But if the code is optimized, // we might get something absolutely not related since the compiler // can reschedule the return address with inline functions and // tail-call optimisation (among other things that I don't even know // or cannot even dream about with my tiny limited brain). find_sym_result details_adjusted_call_site = find_symbol_details(fobj, (void*) (uintptr_t(trace.addr) - 1), symbol_info.dli_fbase); // In debug mode, we should always get the right thing(TM). if (details_call_site.found && details_adjusted_call_site.found) { // Ok, we assume that details_adjusted_call_site is a better estimation. details_selected = &details_adjusted_call_site; trace.addr = (void*) (uintptr_t(trace.addr) - 1); } if (details_selected == &details_call_site && details_call_site.found) { // we have to re-resolve the symbol in order to reset some // internal state in BFD... so we can call backtrace_inliners // thereafter... details_call_site = find_symbol_details(fobj, trace.addr, symbol_info.dli_fbase); } #endif // BACKWARD_HAS_UNWIND if (details_selected->found) { if (details_selected->filename) { trace.source.filename = details_selected->filename; } trace.source.line = details_selected->line; if (details_selected->funcname) { // this time we get the name of the function where the code is // located, instead of the function were the address is // located. In short, if the code was inlined, we get the // function correspoding to the code. Else we already got in // trace.function. trace.source.function = demangle(details_selected->funcname); if (!symbol_info.dli_sname) { // for the case dladdr failed to find the symbol name of // the function, we might as well try to put something // here. trace.object_function = trace.source.function; } } // Maybe the source of the trace got inlined inside the function // (trace.source.function). Let's see if we can get all the inlined // calls along the way up to the initial call site. trace.inliners = backtrace_inliners(fobj, *details_selected); #if 0 if (trace.inliners.size() == 0) { // Maybe the trace was not inlined... or maybe it was and we // are lacking the debug information. Let's try to make the // world better and see if we can get the line number of the // function (trace.source.function) now. // // We will get the location of where the function start (to be // exact: the first instruction that really start the // function), not where the name of the function is defined. // This can be quite far away from the name of the function // btw. // // If the source of the function is the same as the source of // the trace, we cannot say if the trace was really inlined or // not. However, if the filename of the source is different // between the function and the trace... we can declare it as // an inliner. This is not 100% accurate, but better than // nothing. if (symbol_info.dli_saddr) { find_sym_result details = find_symbol_details(fobj, symbol_info.dli_saddr, symbol_info.dli_fbase); if (details.found) { ResolvedTrace::SourceLoc diy_inliner; diy_inliner.line = details.line; if (details.filename) { diy_inliner.filename = details.filename; } if (details.funcname) { diy_inliner.function = demangle(details.funcname); } else { diy_inliner.function = trace.source.function; } if (diy_inliner != trace.source) { trace.inliners.push_back(diy_inliner); } } } } #endif } return trace; } private: bool _bfd_loaded; typedef details::handle > bfd_handle_t; typedef details::handle bfd_symtab_t; struct bfd_fileobject { bfd_handle_t handle; bfd_vma base_addr; bfd_symtab_t symtab; bfd_symtab_t dynamic_symtab; }; typedef details::hashtable::type fobj_bfd_map_t; fobj_bfd_map_t _fobj_bfd_map; bfd_fileobject& load_object_with_bfd(const std::string& filename_object) { using namespace details; if (!_bfd_loaded) { using namespace details; bfd_init(); _bfd_loaded = true; } fobj_bfd_map_t::iterator it = _fobj_bfd_map.find(filename_object); if (it != _fobj_bfd_map.end()) { return it->second; } // this new object is empty for now. bfd_fileobject& r = _fobj_bfd_map[filename_object]; // we do the work temporary in this one; bfd_handle_t bfd_handle; int fd = open(filename_object.c_str(), O_RDONLY); bfd_handle.reset( bfd_fdopenr(filename_object.c_str(), "default", fd) ); if (!bfd_handle) { close(fd); return r; } if (!bfd_check_format(bfd_handle.get(), bfd_object)) { return r; // not an object? You lose. } if ((bfd_get_file_flags(bfd_handle.get()) & HAS_SYMS) == 0) { return r; // that's what happen when you forget to compile in debug. } ssize_t symtab_storage_size = bfd_get_symtab_upper_bound(bfd_handle.get()); ssize_t dyn_symtab_storage_size = bfd_get_dynamic_symtab_upper_bound(bfd_handle.get()); if (symtab_storage_size <= 0 && dyn_symtab_storage_size <= 0) { return r; // weird, is the file is corrupted? } bfd_symtab_t symtab, dynamic_symtab; ssize_t symcount = 0, dyn_symcount = 0; if (symtab_storage_size > 0) { symtab.reset( (bfd_symbol**) malloc(symtab_storage_size) ); symcount = bfd_canonicalize_symtab( bfd_handle.get(), symtab.get() ); } if (dyn_symtab_storage_size > 0) { dynamic_symtab.reset( (bfd_symbol**) malloc(dyn_symtab_storage_size) ); dyn_symcount = bfd_canonicalize_dynamic_symtab( bfd_handle.get(), dynamic_symtab.get() ); } if (symcount <= 0 && dyn_symcount <= 0) { return r; // damned, that's a stripped file that you got there! } r.handle = move(bfd_handle); r.symtab = move(symtab); r.dynamic_symtab = move(dynamic_symtab); return r; } struct find_sym_result { bool found; const char* filename; const char* funcname; unsigned int line; }; struct find_sym_context { TraceResolverLinuxImpl* self; bfd_fileobject* fobj; void* addr; void* base_addr; find_sym_result result; }; find_sym_result find_symbol_details(bfd_fileobject& fobj, void* addr, void* base_addr) { find_sym_context context; context.self = this; context.fobj = &fobj; context.addr = addr; context.base_addr = base_addr; context.result.found = false; bfd_map_over_sections(fobj.handle.get(), &find_in_section_trampoline, (void*)&context); return context.result; } static void find_in_section_trampoline(bfd*, asection* section, void* data) { find_sym_context* context = static_cast(data); context->self->find_in_section( reinterpret_cast(context->addr), reinterpret_cast(context->base_addr), *context->fobj, section, context->result ); } void find_in_section(bfd_vma addr, bfd_vma base_addr, bfd_fileobject& fobj, asection* section, find_sym_result& result) { if (result.found) return; if ((bfd_get_section_flags(fobj.handle.get(), section) & SEC_ALLOC) == 0) return; // a debug section is never loaded automatically. bfd_vma sec_addr = bfd_get_section_vma(fobj.handle.get(), section); bfd_size_type size = bfd_get_section_size(section); // are we in the boundaries of the section? if (addr < sec_addr || addr >= sec_addr + size) { addr -= base_addr; // oups, a relocated object, lets try again... if (addr < sec_addr || addr >= sec_addr + size) { return; } } if (!result.found && fobj.symtab) { result.found = bfd_find_nearest_line(fobj.handle.get(), section, fobj.symtab.get(), addr - sec_addr, &result.filename, &result.funcname, &result.line); } if (!result.found && fobj.dynamic_symtab) { result.found = bfd_find_nearest_line(fobj.handle.get(), section, fobj.dynamic_symtab.get(), addr - sec_addr, &result.filename, &result.funcname, &result.line); } } ResolvedTrace::source_locs_t backtrace_inliners(bfd_fileobject& fobj, find_sym_result previous_result) { // This function can be called ONLY after a SUCCESSFUL call to // find_symbol_details. The state is global to the bfd_handle. ResolvedTrace::source_locs_t results; while (previous_result.found) { find_sym_result result; result.found = bfd_find_inliner_info(fobj.handle.get(), &result.filename, &result.funcname, &result.line); if (result.found) /* and not ( cstrings_eq(previous_result.filename, result.filename) and cstrings_eq(previous_result.funcname, result.funcname) and result.line == previous_result.line )) */ { ResolvedTrace::SourceLoc src_loc; src_loc.line = result.line; if (result.filename) { src_loc.filename = result.filename; } if (result.funcname) { src_loc.function = demangle(result.funcname); } results.push_back(src_loc); } previous_result = result; } return results; } bool cstrings_eq(const char* a, const char* b) { if (!a || !b) { return false; } return strcmp(a, b) == 0; } }; #endif // BACKWARD_HAS_BFD == 1 #if BACKWARD_HAS_DW == 1 template <> class TraceResolverLinuxImpl: public TraceResolverImplBase { public: TraceResolverLinuxImpl(): _dwfl_handle_initialized(false) {} template void load_stacktrace(ST&) {} ResolvedTrace resolve(ResolvedTrace trace) { using namespace details; Dwarf_Addr trace_addr = (Dwarf_Addr) trace.addr; if (!_dwfl_handle_initialized) { // initialize dwfl... _dwfl_cb.reset(new Dwfl_Callbacks); _dwfl_cb->find_elf = &dwfl_linux_proc_find_elf; _dwfl_cb->find_debuginfo = &dwfl_standard_find_debuginfo; _dwfl_cb->debuginfo_path = 0; _dwfl_handle.reset(dwfl_begin(_dwfl_cb.get())); _dwfl_handle_initialized = true; if (!_dwfl_handle) { return trace; } // ...from the current process. dwfl_report_begin(_dwfl_handle.get()); int r = dwfl_linux_proc_report (_dwfl_handle.get(), getpid()); dwfl_report_end(_dwfl_handle.get(), NULL, NULL); if (r < 0) { return trace; } } if (!_dwfl_handle) { return trace; } // find the module (binary object) that contains the trace's address. // This is not using any debug information, but the addresses ranges of // all the currently loaded binary object. Dwfl_Module* mod = dwfl_addrmodule(_dwfl_handle.get(), trace_addr); if (mod) { // now that we found it, lets get the name of it, this will be the // full path to the running binary or one of the loaded library. const char* module_name = dwfl_module_info (mod, 0, 0, 0, 0, 0, 0, 0); if (module_name) { trace.object_filename = module_name; } // We also look after the name of the symbol, equal or before this // address. This is found by walking the symtab. We should get the // symbol corresponding to the function (mangled) containing the // address. If the code corresponding to the address was inlined, // this is the name of the out-most inliner function. const char* sym_name = dwfl_module_addrname(mod, trace_addr); if (sym_name) { trace.object_function = demangle(sym_name); } } // now let's get serious, and find out the source location (file and // line number) of the address. // This function will look in .debug_aranges for the address and map it // to the location of the compilation unit DIE in .debug_info and // return it. Dwarf_Addr mod_bias = 0; Dwarf_Die* cudie = dwfl_module_addrdie(mod, trace_addr, &mod_bias); #if 1 if (!cudie) { // Sadly clang does not generate the section .debug_aranges, thus // dwfl_module_addrdie will fail early. Clang doesn't either set // the lowpc/highpc/range info for every compilation unit. // // So in order to save the world: // for every compilation unit, we will iterate over every single // DIEs. Normally functions should have a lowpc/highpc/range, which // we will use to infer the compilation unit. // note that this is probably badly inefficient. while ((cudie = dwfl_module_nextcu(mod, cudie, &mod_bias))) { Dwarf_Die die_mem; Dwarf_Die* fundie = find_fundie_by_pc(cudie, trace_addr - mod_bias, &die_mem); if (fundie) { break; } } } #endif //#define BACKWARD_I_DO_NOT_RECOMMEND_TO_ENABLE_THIS_HORRIBLE_PIECE_OF_CODE #ifdef BACKWARD_I_DO_NOT_RECOMMEND_TO_ENABLE_THIS_HORRIBLE_PIECE_OF_CODE if (!cudie) { // If it's still not enough, lets dive deeper in the shit, and try // to save the world again: for every compilation unit, we will // load the corresponding .debug_line section, and see if we can // find our address in it. Dwarf_Addr cfi_bias; Dwarf_CFI* cfi_cache = dwfl_module_eh_cfi(mod, &cfi_bias); Dwarf_Addr bias; while ((cudie = dwfl_module_nextcu(mod, cudie, &bias))) { if (dwarf_getsrc_die(cudie, trace_addr - bias)) { // ...but if we get a match, it might be a false positive // because our (address - bias) might as well be valid in a // different compilation unit. So we throw our last card on // the table and lookup for the address into the .eh_frame // section. handle frame; dwarf_cfi_addrframe(cfi_cache, trace_addr - cfi_bias, &frame); if (frame) { break; } } } } #endif if (!cudie) { return trace; // this time we lost the game :/ } // Now that we have a compilation unit DIE, this function will be able // to load the corresponding section in .debug_line (if not already // loaded) and hopefully find the source location mapped to our // address. Dwarf_Line* srcloc = dwarf_getsrc_die(cudie, trace_addr - mod_bias); if (srcloc) { const char* srcfile = dwarf_linesrc(srcloc, 0, 0); if (srcfile) { trace.source.filename = srcfile; } int line = 0, col = 0; dwarf_lineno(srcloc, &line); dwarf_linecol(srcloc, &col); trace.source.line = line; trace.source.col = col; } deep_first_search_by_pc(cudie, trace_addr - mod_bias, inliners_search_cb(trace)); if (trace.source.function.size() == 0) { // fallback. trace.source.function = trace.object_function; } return trace; } private: typedef details::handle > dwfl_handle_t; details::handle > _dwfl_cb; dwfl_handle_t _dwfl_handle; bool _dwfl_handle_initialized; // defined here because in C++98, template function cannot take locally // defined types... grrr. struct inliners_search_cb { void operator()(Dwarf_Die* die) { switch (dwarf_tag(die)) { const char* name; case DW_TAG_subprogram: if ((name = dwarf_diename(die))) { trace.source.function = name; } break; case DW_TAG_inlined_subroutine: ResolvedTrace::SourceLoc sloc; Dwarf_Attribute attr_mem; if ((name = dwarf_diename(die))) { sloc.function = name; } if ((name = die_call_file(die))) { sloc.filename = name; } Dwarf_Word line = 0, col = 0; dwarf_formudata(dwarf_attr(die, DW_AT_call_line, &attr_mem), &line); dwarf_formudata(dwarf_attr(die, DW_AT_call_column, &attr_mem), &col); sloc.line = (unsigned)line; sloc.col = (unsigned)col; trace.inliners.push_back(sloc); break; }; } ResolvedTrace& trace; inliners_search_cb(ResolvedTrace& t): trace(t) {} }; static bool die_has_pc(Dwarf_Die* die, Dwarf_Addr pc) { Dwarf_Addr low, high; // continuous range if (dwarf_hasattr(die, DW_AT_low_pc) && dwarf_hasattr(die, DW_AT_high_pc)) { if (dwarf_lowpc(die, &low) != 0) { return false; } if (dwarf_highpc(die, &high) != 0) { Dwarf_Attribute attr_mem; Dwarf_Attribute* attr = dwarf_attr(die, DW_AT_high_pc, &attr_mem); Dwarf_Word value; if (dwarf_formudata(attr, &value) != 0) { return false; } high = low + value; } return pc >= low && pc < high; } // non-continuous range. Dwarf_Addr base; ptrdiff_t offset = 0; while ((offset = dwarf_ranges(die, offset, &base, &low, &high)) > 0) { if (pc >= low && pc < high) { return true; } } return false; } static Dwarf_Die* find_fundie_by_pc(Dwarf_Die* parent_die, Dwarf_Addr pc, Dwarf_Die* result) { if (dwarf_child(parent_die, result) != 0) { return 0; } Dwarf_Die* die = result; do { switch (dwarf_tag(die)) { case DW_TAG_subprogram: case DW_TAG_inlined_subroutine: if (die_has_pc(die, pc)) { return result; } }; bool declaration = false; Dwarf_Attribute attr_mem; dwarf_formflag(dwarf_attr(die, DW_AT_declaration, &attr_mem), &declaration); if (!declaration) { // let's be curious and look deeper in the tree, // function are not necessarily at the first level, but // might be nested inside a namespace, structure etc. Dwarf_Die die_mem; Dwarf_Die* indie = find_fundie_by_pc(die, pc, &die_mem); if (indie) { *result = die_mem; return result; } } } while (dwarf_siblingof(die, result) == 0); return 0; } template static bool deep_first_search_by_pc(Dwarf_Die* parent_die, Dwarf_Addr pc, CB cb) { Dwarf_Die die_mem; if (dwarf_child(parent_die, &die_mem) != 0) { return false; } bool branch_has_pc = false; Dwarf_Die* die = &die_mem; do { bool declaration = false; Dwarf_Attribute attr_mem; dwarf_formflag(dwarf_attr(die, DW_AT_declaration, &attr_mem), &declaration); if (!declaration) { // let's be curious and look deeper in the tree, function are // not necessarily at the first level, but might be nested // inside a namespace, structure, a function, an inlined // function etc. branch_has_pc = deep_first_search_by_pc(die, pc, cb); } if (!branch_has_pc) { branch_has_pc = die_has_pc(die, pc); } if (branch_has_pc) { cb(die); } } while (dwarf_siblingof(die, &die_mem) == 0); return branch_has_pc; } static const char* die_call_file(Dwarf_Die *die) { Dwarf_Attribute attr_mem; Dwarf_Sword file_idx = 0; dwarf_formsdata(dwarf_attr(die, DW_AT_call_file, &attr_mem), &file_idx); if (file_idx == 0) { return 0; } Dwarf_Die die_mem; Dwarf_Die* cudie = dwarf_diecu(die, &die_mem, 0, 0); if (!cudie) { return 0; } Dwarf_Files* files = 0; size_t nfiles; dwarf_getsrcfiles(cudie, &files, &nfiles); if (!files) { return 0; } return dwarf_filesrc(files, file_idx, 0, 0); } }; #endif // BACKWARD_HAS_DW == 1 template<> class TraceResolverImpl: public TraceResolverLinuxImpl {}; #endif // BACKWARD_SYSTEM_LINUX #ifdef BACKWARD_SYSTEM_DARWIN template class TraceResolverDarwinImpl; template <> class TraceResolverDarwinImpl: public TraceResolverImplBase { public: template void load_stacktrace(ST& st) { using namespace details; if (st.size() == 0) { return; } _symbols.reset( backtrace_symbols(st.begin(), st.size()) ); } ResolvedTrace resolve(ResolvedTrace trace) { // parse: // + char* filename = _symbols[trace.idx]; // skip " " while(*filename && *filename != ' ') filename++; while(*filename == ' ') filename++; // find start of from end ( may contain a space) char* p = filename + strlen(filename) - 1; // skip to start of " + " while(p > filename && *p != ' ') p--; while(p > filename && *p == ' ') p--; while(p > filename && *p != ' ') p--; while(p > filename && *p == ' ') p--; char *funcname_end = p + 1; // skip to start of "" while(p > filename && *p != ' ') p--; char *funcname = p + 1; // skip to start of " " while(p > filename && *p == ' ') p--; while(p > filename && *p != ' ') p--; while(p > filename && *p == ' ') p--; // skip "", handling the case where it contains a char* filename_end = p + 1; if (p == filename) { // something went wrong, give up filename_end = filename + strlen(filename); funcname = filename_end; } trace.object_filename.assign(filename, filename_end); // ok even if filename_end is the ending \0 (then we assign entire string) if (*funcname) { // if it's not end of string *funcname_end = '\0'; trace.object_function = this->demangle(funcname); trace.object_function += " "; trace.object_function += (funcname_end + 1); trace.source.function = trace.object_function; // we cannot do better. } return trace; } private: details::handle _symbols; }; template<> class TraceResolverImpl: public TraceResolverDarwinImpl {}; #endif // BACKWARD_SYSTEM_DARWIN class TraceResolver: public TraceResolverImpl {}; /*************** CODE SNIPPET ***************/ class SourceFile { public: typedef std::vector > lines_t; SourceFile() {} SourceFile(const std::string& path): _file(new std::ifstream(path.c_str())) {} bool is_open() const { return _file->is_open(); } lines_t& get_lines(unsigned line_start, unsigned line_count, lines_t& lines) { using namespace std; // This function make uses of the dumbest algo ever: // 1) seek(0) // 2) read lines one by one and discard until line_start // 3) read line one by one until line_start + line_count // // If you are getting snippets many time from the same file, it is // somewhat a waste of CPU, feel free to benchmark and propose a // better solution ;) _file->clear(); _file->seekg(0); string line; unsigned line_idx; for (line_idx = 1; line_idx < line_start; ++line_idx) { std::getline(*_file, line); if (!*_file) { return lines; } } // think of it like a lambda in C++98 ;) // but look, I will reuse it two times! // What a good boy am I. struct isspace { bool operator()(char c) { return std::isspace(c); } }; bool started = false; for (; line_idx < line_start + line_count; ++line_idx) { getline(*_file, line); if (!*_file) { return lines; } if (!started) { if (std::find_if(line.begin(), line.end(), not_isspace()) == line.end()) continue; started = true; } lines.push_back(make_pair(line_idx, line)); } lines.erase( std::find_if(lines.rbegin(), lines.rend(), not_isempty()).base(), lines.end() ); return lines; } lines_t get_lines(unsigned line_start, unsigned line_count) { lines_t lines; return get_lines(line_start, line_count, lines); } // there is no find_if_not in C++98, lets do something crappy to // workaround. struct not_isspace { bool operator()(char c) { return !std::isspace(c); } }; // and define this one here because C++98 is not happy with local defined // struct passed to template functions, fuuuu. struct not_isempty { bool operator()(const lines_t::value_type& p) { return !(std::find_if(p.second.begin(), p.second.end(), not_isspace()) == p.second.end()); } }; void swap(SourceFile& b) { _file.swap(b._file); } #ifdef BACKWARD_ATLEAST_CXX11 SourceFile(SourceFile&& from): _file(0) { swap(from); } SourceFile& operator=(SourceFile&& from) { swap(from); return *this; } #else explicit SourceFile(const SourceFile& from) { // some sort of poor man's move semantic. swap(const_cast(from)); } SourceFile& operator=(const SourceFile& from) { // some sort of poor man's move semantic. swap(const_cast(from)); return *this; } #endif private: details::handle > _file; #ifdef BACKWARD_ATLEAST_CXX11 SourceFile(const SourceFile&) = delete; SourceFile& operator=(const SourceFile&) = delete; #endif }; class SnippetFactory { public: typedef SourceFile::lines_t lines_t; lines_t get_snippet(const std::string& filename, unsigned line_start, unsigned context_size) { SourceFile& src_file = get_src_file(filename); unsigned start = line_start - context_size / 2; return src_file.get_lines(start, context_size); } lines_t get_combined_snippet( const std::string& filename_a, unsigned line_a, const std::string& filename_b, unsigned line_b, unsigned context_size) { SourceFile& src_file_a = get_src_file(filename_a); SourceFile& src_file_b = get_src_file(filename_b); lines_t lines = src_file_a.get_lines(line_a - context_size / 4, context_size / 2); src_file_b.get_lines(line_b - context_size / 4, context_size / 2, lines); return lines; } lines_t get_coalesced_snippet(const std::string& filename, unsigned line_a, unsigned line_b, unsigned context_size) { SourceFile& src_file = get_src_file(filename); using std::min; using std::max; unsigned a = min(line_a, line_b); unsigned b = max(line_a, line_b); if ((b - a) < (context_size / 3)) { return src_file.get_lines((a + b - context_size + 1) / 2, context_size); } lines_t lines = src_file.get_lines(a - context_size / 4, context_size / 2); src_file.get_lines(b - context_size / 4, context_size / 2, lines); return lines; } private: typedef details::hashtable::type src_files_t; src_files_t _src_files; SourceFile& get_src_file(const std::string& filename) { src_files_t::iterator it = _src_files.find(filename); if (it != _src_files.end()) { return it->second; } SourceFile& new_src_file = _src_files[filename]; new_src_file = SourceFile(filename); return new_src_file; } }; /*************** PRINTER ***************/ namespace ColorMode { enum type { automatic, never, always }; } class cfile_streambuf: public std::streambuf { public: cfile_streambuf(FILE *_sink): sink(_sink) {} int_type underflow() { return traits_type::eof(); } int_type overflow(int_type ch) { if (traits_type::not_eof(ch) && fwrite(&ch, sizeof ch, 1, sink) == 1) { return ch; } return traits_type::eof(); } std::streamsize xsputn(const char_type* s, std::streamsize count) { return fwrite(s, sizeof *s, count, sink); } #ifdef BACKWARD_ATLEAST_CXX11 public: cfile_streambuf(const cfile_streambuf&) = delete; cfile_streambuf& operator=(const cfile_streambuf&) = delete; #else private: cfile_streambuf(const cfile_streambuf &); cfile_streambuf &operator= (const cfile_streambuf &); #endif private: FILE *sink; std::vector buffer; }; #ifdef BACKWARD_SYSTEM_LINUX namespace Color { enum type { yellow = 33, purple = 35, reset = 39 }; } // namespace Color class Colorize { public: Colorize(std::ostream& os): _os(os), _reset(false), _enabled(false) {} void activate(ColorMode::type mode) { _enabled = mode == ColorMode::always; } void activate(ColorMode::type mode, FILE* fp) { activate(mode, fileno(fp)); } void set_color(Color::type ccode) { if (!_enabled) return; // I assume that the terminal can handle basic colors. Seriously I // don't want to deal with all the termcap shit. _os << "\033[" << static_cast(ccode) << "m"; _reset = (ccode != Color::reset); } ~Colorize() { if (_reset) { set_color(Color::reset); } } private: void activate(ColorMode::type mode, int fd) { activate(mode == ColorMode::automatic && isatty(fd) ? ColorMode::always : mode); } std::ostream& _os; bool _reset; bool _enabled; }; #else // ndef BACKWARD_SYSTEM_LINUX namespace Color { enum type { yellow = 0, purple = 0, reset = 0 }; } // namespace Color class Colorize { public: Colorize(std::ostream&) {} void activate(ColorMode::type) {} void activate(ColorMode::type, FILE*) {} void set_color(Color::type) {} }; #endif // BACKWARD_SYSTEM_LINUX class Printer { public: bool snippet; ColorMode::type color_mode; bool address; bool object; int inliner_context_size; int trace_context_size; Printer(): snippet(true), color_mode(ColorMode::automatic), address(false), object(false), inliner_context_size(5), trace_context_size(7) {} template FILE* print(ST& st, FILE* fp = stderr) { cfile_streambuf obuf(fp); std::ostream os(&obuf); Colorize colorize(os); colorize.activate(color_mode, fp); print_stacktrace(st, os, colorize); return fp; } template std::ostream& print(ST& st, std::ostream& os) { Colorize colorize(os); colorize.activate(color_mode); print_stacktrace(st, os, colorize); return os; } template FILE* print(IT begin, IT end, FILE* fp = stderr, size_t thread_id = 0) { cfile_streambuf obuf(fp); std::ostream os(&obuf); Colorize colorize(os); colorize.activate(color_mode, fp); print_stacktrace(begin, end, os, thread_id, colorize); return fp; } template std::ostream& print(IT begin, IT end, std::ostream& os, size_t thread_id = 0) { Colorize colorize(os); colorize.activate(color_mode); print_stacktrace(begin, end, os, thread_id, colorize); return os; } private: TraceResolver _resolver; SnippetFactory _snippets; template void print_stacktrace(ST& st, std::ostream& os, Colorize& colorize) { print_header(os, st.thread_id()); _resolver.load_stacktrace(st); for (size_t trace_idx = st.size(); trace_idx > 0; --trace_idx) { print_trace(os, _resolver.resolve(st[trace_idx-1]), colorize); } } template void print_stacktrace(IT begin, IT end, std::ostream& os, size_t thread_id, Colorize& colorize) { print_header(os, thread_id); for (; begin != end; ++begin) { print_trace(os, *begin, colorize); } } void print_header(std::ostream& os, size_t thread_id) { os << "Stack trace (most recent call last)"; if (thread_id) { os << " in thread " << thread_id; } os << ":\n"; } void print_trace(std::ostream& os, const ResolvedTrace& trace, Colorize& colorize) { os << "#" << std::left << std::setw(2) << trace.idx << std::right; bool already_indented = true; if (!trace.source.filename.size() || object) { os << " Object \"" << trace.object_filename << "\", at " << trace.addr << ", in " << trace.object_function << "\n"; already_indented = false; } for (size_t inliner_idx = trace.inliners.size(); inliner_idx > 0; --inliner_idx) { if (!already_indented) { os << " "; } const ResolvedTrace::SourceLoc& inliner_loc = trace.inliners[inliner_idx-1]; print_source_loc(os, " | ", inliner_loc); if (snippet) { print_snippet(os, " | ", inliner_loc, colorize, Color::purple, inliner_context_size); } already_indented = false; } if (trace.source.filename.size()) { if (!already_indented) { os << " "; } print_source_loc(os, " ", trace.source, trace.addr); if (snippet) { print_snippet(os, " ", trace.source, colorize, Color::yellow, trace_context_size); } } } void print_snippet(std::ostream& os, const char* indent, const ResolvedTrace::SourceLoc& source_loc, Colorize& colorize, Color::type color_code, int context_size) { using namespace std; typedef SnippetFactory::lines_t lines_t; lines_t lines = _snippets.get_snippet(source_loc.filename, source_loc.line, context_size); for (lines_t::const_iterator it = lines.begin(); it != lines.end(); ++it) { if (it-> first == source_loc.line) { colorize.set_color(color_code); os << indent << ">"; } else { os << indent << " "; } os << std::setw(4) << it->first << ": " << it->second << "\n"; if (it-> first == source_loc.line) { colorize.set_color(Color::reset); } } } void print_source_loc(std::ostream& os, const char* indent, const ResolvedTrace::SourceLoc& source_loc, void* addr=0) { os << indent << "Source \"" << source_loc.filename << "\", line " << source_loc.line << ", in " << source_loc.function; if (address && addr != 0) { os << " [" << addr << "]"; } os << "\n"; } }; /*************** SIGNALS HANDLING ***************/ #if defined(BACKWARD_SYSTEM_LINUX) || defined(BACKWARD_SYSTEM_DARWIN) class SignalHandling { public: static std::vector make_default_signals() { const int posix_signals[] = { // Signals for which the default action is "Core". SIGABRT, // Abort signal from abort(3) SIGBUS, // Bus error (bad memory access) SIGFPE, // Floating point exception SIGILL, // Illegal Instruction SIGIOT, // IOT trap. A synonym for SIGABRT SIGQUIT, // Quit from keyboard SIGSEGV, // Invalid memory reference SIGSYS, // Bad argument to routine (SVr4) SIGTRAP, // Trace/breakpoint trap SIGXCPU, // CPU time limit exceeded (4.2BSD) SIGXFSZ, // File size limit exceeded (4.2BSD) #if defined(BACKWARD_SYSTEM_DARWIN) SIGEMT, // emulation instruction executed #endif }; return std::vector(posix_signals, posix_signals + sizeof posix_signals / sizeof posix_signals[0] ); } SignalHandling(const std::vector& posix_signals = make_default_signals()): _loaded(false) { bool success = true; const size_t stack_size = 1024 * 1024 * 8; _stack_content.reset((char*)malloc(stack_size)); if (_stack_content) { stack_t ss; ss.ss_sp = _stack_content.get(); ss.ss_size = stack_size; ss.ss_flags = 0; if (sigaltstack(&ss, 0) < 0) { success = false; } } else { success = false; } for (size_t i = 0; i < posix_signals.size(); ++i) { struct sigaction action; memset(&action, 0, sizeof action); action.sa_flags = (SA_SIGINFO | SA_ONSTACK | SA_NODEFER | SA_RESETHAND); sigfillset(&action.sa_mask); sigdelset(&action.sa_mask, posix_signals[i]); action.sa_sigaction = &sig_handler; int r = sigaction(posix_signals[i], &action, 0); if (r < 0) success = false; } _loaded = success; } bool loaded() const { return _loaded; } static void handleSignal(int, siginfo_t* info, void* _ctx) { ucontext_t *uctx = (ucontext_t*) _ctx; StackTrace st; void* error_addr = 0; #ifdef REG_RIP // x86_64 error_addr = reinterpret_cast(uctx->uc_mcontext.gregs[REG_RIP]); #elif defined(REG_EIP) // x86_32 error_addr = reinterpret_cast(uctx->uc_mcontext.gregs[REG_EIP]); #elif defined(__arm__) error_addr = reinterpret_cast(uctx->uc_mcontext.arm_pc); #elif defined(__aarch64__) error_addr = reinterpret_cast(uctx->uc_mcontext.pc); #elif defined(__ppc__) || defined(__powerpc) || defined(__powerpc__) || defined(__POWERPC__) error_addr = reinterpret_cast(uctx->uc_mcontext.regs->nip); #elif defined(__s390x__) error_addr = reinterpret_cast(uctx->uc_mcontext.psw.addr); #elif defined(__APPLE__) && defined(__x86_64__) error_addr = reinterpret_cast(uctx->uc_mcontext->__ss.__rip); #else # warning ":/ sorry, ain't know no nothing none not of your architecture!" #endif if (error_addr) { st.load_from(error_addr, 32); } else { st.load_here(32); } Printer printer; printer.address = true; printer.print(st, stderr); #if _XOPEN_SOURCE >= 700 || _POSIX_C_SOURCE >= 200809L psiginfo(info, 0); #endif } private: details::handle _stack_content; bool _loaded; #ifdef __GNUC__ __attribute__((noreturn)) #endif static void sig_handler(int signo, siginfo_t* info, void* _ctx) { handleSignal(signo, info, _ctx); // try to forward the signal. raise(info->si_signo); // terminate the process immediately. puts("watf? exit"); _exit(EXIT_FAILURE); } }; #endif // BACKWARD_SYSTEM_LINUX || BACKWARD_SYSTEM_DARWIN #ifdef BACKWARD_SYSTEM_UNKNOWN class SignalHandling { public: SignalHandling(const std::vector& = std::vector()) {} bool init() { return false; } bool loaded() { return false; } }; #endif // BACKWARD_SYSTEM_UNKNOWN } // namespace backward #endif /* H_GUARD */ ================================================ FILE: 3rdpart/backward/backward.pri ================================================ SOURCES += $$PWD/backward.cpp HEADERS += $$PWD/backward.hpp INCLUDEPATH += $$PWD ================================================ FILE: 3rdpart/hoedown/LICENSE ================================================ Copyright (c) 2008, Natacha Porté Copyright (c) 2011, Vicent Martí Copyright (c) 2014, Xavier Mendez, Devin Torres and the Hoedown authors Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ================================================ FILE: 3rdpart/hoedown/README.md ================================================ Hoedown ======= [![Build Status](https://travis-ci.org/hoedown/hoedown.png?branch=master)](https://travis-ci.org/hoedown/hoedown) `Hoedown` is a revived fork of [Sundown](https://github.com/vmg/sundown), the Markdown parser based on the original code of the [Upskirt library](http://fossil.instinctive.eu/libupskirt/index) by Natacha Porté. Features -------- * **Fully standards compliant** `Hoedown` passes out of the box the official Markdown v1.0.0 and v1.0.3 test suites, and has been extensively tested with additional corner cases to make sure its output is as sane as possible at all times. * **Massive extension support** `Hoedown` has optional support for several (unofficial) Markdown extensions, such as non-strict emphasis, fenced code blocks, tables, autolinks, strikethrough and more. * **UTF-8 aware** `Hoedown` is fully UTF-8 aware, both when parsing the source document and when generating the resulting (X)HTML code. * **Tested & Ready to be used on production** `Hoedown` has been extensively security audited, and includes protection against all possible DOS attacks (stack overflows, out of memory situations, malformed Markdown syntax...). We've worked very hard to make `Hoedown` never leak or crash under *any* input. **Warning**: `Hoedown` doesn't validate or post-process the HTML in Markdown documents. Unless you use `HTML_ESCAPE` or `HTML_SKIP`, you should strongly consider using a good post-processor in conjunction with Hoedown to prevent client-side attacks. * **Customizable renderers** `Hoedown` is not stuck with XHTML output: the Markdown parser of the library is decoupled from the renderer, so it's trivial to extend the library with custom renderers. A fully functional (X)HTML renderer is included. * **Optimized for speed** `Hoedown` is written in C, with a special emphasis on performance. When wrapped on a dynamic language such as Python or Ruby, it has shown to be up to 40 times faster than other native alternatives. * **Zero-dependency** `Hoedown` is a zero-dependency library composed of some `.c` files and their headers. No dependencies, no bullshit. Only standard C99 that builds everywhere. * **Additional features** `Hoedown` comes with a fully functional implementation of SmartyPants, a separate autolinker, escaping utilities, buffers and stacks. Bindings -------- You can see a community-maintained list of `Hoedown` bindings at [the wiki](https://github.com/hoedown/hoedown/wiki/Bindings). There is also a [migration guide](https://github.com/hoedown/hoedown/wiki/Migration-Guide) available for authors of Sundown bindings. Help us ------- `Hoedown` is all about security. If you find a (potential) security vulnerability in the library, or a way to make it crash through malicious input, please report it to us by emailing the private [Hoedown Security](mailto:hoedown-security@googlegroups.com) mailing list. The `Hoedown` security team will review the vulnerability and work with you to reproduce and resolve it. Unicode character handling -------------------------- Given that the Markdown spec makes no provision for Unicode character handling, `Hoedown` takes a conservative approach towards deciding which extended characters trigger Markdown features: * Punctuation characters outside of the U+007F codepoint are not handled as punctuation. They are considered as normal, in-word characters for word-boundary checks. * Whitespace characters outside of the U+007F codepoint are not considered as whitespace. They are considered as normal, in-word characters for word-boundary checks. Install ------- Just typing `make` will build `Hoedown` into a dynamic library and create the `hoedown` and `smartypants` executables, which are command-line tools to render Markdown to HTML and perform SmartyPants, respectively. If you are using [CocoaPods](http://cocoapods.org), just add the line `pod 'hoedown'` to your Podfile and call `pod install`. Or, if you prefer, you can just throw the files at `src` into your project. ================================================ FILE: 3rdpart/hoedown/hoedown.def ================================================ LIBRARY HOEDOWN EXPORTS hoedown_autolink_is_safe hoedown_autolink__www hoedown_autolink__email hoedown_autolink__url hoedown_buffer_init hoedown_buffer_new hoedown_buffer_reset hoedown_buffer_grow hoedown_buffer_put hoedown_buffer_puts hoedown_buffer_putc hoedown_buffer_set hoedown_buffer_sets hoedown_buffer_eq hoedown_buffer_eqs hoedown_buffer_prefix hoedown_buffer_slurp hoedown_buffer_cstr hoedown_buffer_printf hoedown_buffer_free hoedown_document_new hoedown_document_render hoedown_document_render_inline hoedown_document_free hoedown_escape_href hoedown_escape_html hoedown_html_smartypants hoedown_html_is_tag hoedown_html_renderer_new hoedown_html_toc_renderer_new hoedown_html_renderer_free hoedown_stack_init hoedown_stack_uninit hoedown_stack_grow hoedown_stack_push hoedown_stack_pop hoedown_stack_top hoedown_version ================================================ FILE: 3rdpart/hoedown/hoedown.pri ================================================ HEADERS += $$PWD/src/escape.h HEADERS += $$PWD/src/html.h HEADERS += $$PWD/src/autolink.h HEADERS += $$PWD/src/document.h HEADERS += $$PWD/src/stack.h HEADERS += $$PWD/src/version.h HEADERS += $$PWD/src/buffer.h SOURCES += $$PWD/src/document.c \ $$PWD/src/hodedown_version.c SOURCES += $$PWD/src/html.c SOURCES += $$PWD/src/stack.c SOURCES += SOURCES += $$PWD/src/autolink.c SOURCES += $$PWD/src/escape.c SOURCES += $$PWD/src/html_smartypants.c SOURCES += $$PWD/src/buffer.c SOURCES += $$PWD/src/html_blocks.c ================================================ FILE: 3rdpart/hoedown/html_block_names.gperf ================================================ p dl h1 h2 h3 h4 h5 h6 ol ul del div ins pre form math style table figure iframe script fieldset noscript blockquote ================================================ FILE: 3rdpart/hoedown/src/autolink.c ================================================ #include "autolink.h" #include #include #include #include #ifndef _MSC_VER #include #else #define strncasecmp _strnicmp #endif int hoedown_autolink_is_safe(const uint8_t *data, size_t size) { static const size_t valid_uris_count = 6; static const char *valid_uris[] = { "http://", "https://", "/", "#", "ftp://", "mailto:" }; static const size_t valid_uris_size[] = { 7, 8, 1, 1, 6, 7 }; size_t i; for (i = 0; i < valid_uris_count; ++i) { size_t len = valid_uris_size[i]; if (size > len && strncasecmp((char *)data, valid_uris[i], len) == 0 && isalnum(data[len])) return 1; } return 0; } static size_t autolink_delim(uint8_t *data, size_t link_end, size_t max_rewind, size_t size) { uint8_t cclose, copen = 0; size_t i; for (i = 0; i < link_end; ++i) if (data[i] == '<') { link_end = i; break; } while (link_end > 0) { if (strchr("?!.,:", data[link_end - 1]) != NULL) link_end--; else if (data[link_end - 1] == ';') { size_t new_end = link_end - 2; while (new_end > 0 && isalpha(data[new_end])) new_end--; if (new_end < link_end - 2 && data[new_end] == '&') link_end = new_end; else link_end--; } else break; } if (link_end == 0) return 0; cclose = data[link_end - 1]; switch (cclose) { case '"': copen = '"'; break; case '\'': copen = '\''; break; case ')': copen = '('; break; case ']': copen = '['; break; case '}': copen = '{'; break; } if (copen != 0) { size_t closing = 0; size_t opening = 0; size_t i = 0; /* Try to close the final punctuation sign in this same line; * if we managed to close it outside of the URL, that means that it's * not part of the URL. If it closes inside the URL, that means it * is part of the URL. * * Examples: * * foo http://www.pokemon.com/Pikachu_(Electric) bar * => http://www.pokemon.com/Pikachu_(Electric) * * foo (http://www.pokemon.com/Pikachu_(Electric)) bar * => http://www.pokemon.com/Pikachu_(Electric) * * foo http://www.pokemon.com/Pikachu_(Electric)) bar * => http://www.pokemon.com/Pikachu_(Electric)) * * (foo http://www.pokemon.com/Pikachu_(Electric)) bar * => foo http://www.pokemon.com/Pikachu_(Electric) */ while (i < link_end) { if (data[i] == copen) opening++; else if (data[i] == cclose) closing++; i++; } if (closing != opening) link_end--; } return link_end; } static size_t check_domain(uint8_t *data, size_t size, int allow_short) { size_t i, np = 0; if (!isalnum(data[0])) return 0; for (i = 1; i < size - 1; ++i) { if (strchr(".:", data[i]) != NULL) np++; else if (!isalnum(data[i]) && data[i] != '-') break; } if (allow_short) { /* We don't need a valid domain in the strict sense (with * least one dot; so just make sure it's composed of valid * domain characters and return the length of the the valid * sequence. */ return i; } else { /* a valid domain needs to have at least a dot. * that's as far as we get */ return np ? i : 0; } } size_t hoedown_autolink__www( size_t *rewind_p, hoedown_buffer *link, uint8_t *data, size_t max_rewind, size_t size, unsigned int flags) { size_t link_end; if (max_rewind > 0 && !ispunct(data[-1]) && !isspace(data[-1])) return 0; if (size < 4 || memcmp(data, "www.", strlen("www.")) != 0) return 0; link_end = check_domain(data, size, 0); if (link_end == 0) return 0; while (link_end < size && !isspace(data[link_end])) link_end++; link_end = autolink_delim(data, link_end, max_rewind, size); if (link_end == 0) return 0; hoedown_buffer_put(link, data, link_end); *rewind_p = 0; return (int)link_end; } size_t hoedown_autolink__email( size_t *rewind_p, hoedown_buffer *link, uint8_t *data, size_t max_rewind, size_t size, unsigned int flags) { size_t link_end, rewind; int nb = 0, np = 0; for (rewind = 0; rewind < max_rewind; ++rewind) { uint8_t c = data[-1 - rewind]; if (isalnum(c)) continue; if (strchr(".+-_", c) != NULL) continue; break; } if (rewind == 0) return 0; for (link_end = 0; link_end < size; ++link_end) { uint8_t c = data[link_end]; if (isalnum(c)) continue; if (c == '@') nb++; else if (c == '.' && link_end < size - 1) np++; else if (c != '-' && c != '_') break; } if (link_end < 2 || nb != 1 || np == 0 || !isalpha(data[link_end - 1])) return 0; link_end = autolink_delim(data, link_end, max_rewind, size); if (link_end == 0) return 0; hoedown_buffer_put(link, data - rewind, link_end + rewind); *rewind_p = rewind; return link_end; } size_t hoedown_autolink__url( size_t *rewind_p, hoedown_buffer *link, uint8_t *data, size_t max_rewind, size_t size, unsigned int flags) { size_t link_end, rewind = 0, domain_len; if (size < 4 || data[1] != '/' || data[2] != '/') return 0; while (rewind < max_rewind && isalpha(data[-1 - rewind])) rewind++; if (!hoedown_autolink_is_safe(data - rewind, size + rewind)) return 0; link_end = strlen("://"); domain_len = check_domain( data + link_end, size - link_end, flags & HOEDOWN_AUTOLINK_SHORT_DOMAINS); if (domain_len == 0) return 0; link_end += domain_len; while (link_end < size && !isspace(data[link_end])) link_end++; link_end = autolink_delim(data, link_end, max_rewind, size); if (link_end == 0) return 0; hoedown_buffer_put(link, data - rewind, link_end + rewind); *rewind_p = rewind; return link_end; } ================================================ FILE: 3rdpart/hoedown/src/autolink.h ================================================ /* autolink.h - versatile autolinker */ #ifndef HOEDOWN_AUTOLINK_H #define HOEDOWN_AUTOLINK_H #include "buffer.h" #ifdef __cplusplus extern "C" { #endif /************* * CONSTANTS * *************/ typedef enum hoedown_autolink_flags { HOEDOWN_AUTOLINK_SHORT_DOMAINS = (1 << 0) } hoedown_autolink_flags; /************* * FUNCTIONS * *************/ /* hoedown_autolink_is_safe: verify that a URL has a safe protocol */ int hoedown_autolink_is_safe(const uint8_t *data, size_t size); /* hoedown_autolink__www: search for the next www link in data */ size_t hoedown_autolink__www(size_t *rewind_p, hoedown_buffer *link, uint8_t *data, size_t offset, size_t size, hoedown_autolink_flags flags); /* hoedown_autolink__email: search for the next email in data */ size_t hoedown_autolink__email(size_t *rewind_p, hoedown_buffer *link, uint8_t *data, size_t offset, size_t size, hoedown_autolink_flags flags); /* hoedown_autolink__url: search for the next URL in data */ size_t hoedown_autolink__url(size_t *rewind_p, hoedown_buffer *link, uint8_t *data, size_t offset, size_t size, hoedown_autolink_flags flags); #ifdef __cplusplus } #endif #endif /** HOEDOWN_AUTOLINK_H **/ ================================================ FILE: 3rdpart/hoedown/src/buffer.c ================================================ #include "buffer.h" #include #include #include #include void * hoedown_malloc(size_t size) { void *ret = malloc(size); if (!ret) { fprintf(stderr, "Allocation failed.\n"); abort(); } return ret; } void * hoedown_calloc(size_t nmemb, size_t size) { void *ret = calloc(nmemb, size); if (!ret) { fprintf(stderr, "Allocation failed.\n"); abort(); } return ret; } void * hoedown_realloc(void *ptr, size_t size) { void *ret = realloc(ptr, size); if (!ret) { fprintf(stderr, "Allocation failed.\n"); abort(); } return ret; } void hoedown_buffer_init( hoedown_buffer *buf, size_t unit, hoedown_realloc_callback data_realloc, hoedown_free_callback data_free, hoedown_free_callback buffer_free) { assert(buf); buf->data = NULL; buf->size = buf->asize = 0; buf->unit = unit; buf->data_realloc = data_realloc; buf->data_free = data_free; buf->buffer_free = buffer_free; } void hoedown_buffer_uninit(hoedown_buffer *buf) { assert(buf && buf->unit); buf->data_free(buf->data); } hoedown_buffer * hoedown_buffer_new(size_t unit) { hoedown_buffer *ret = hoedown_malloc(sizeof (hoedown_buffer)); hoedown_buffer_init(ret, unit, hoedown_realloc, free, free); return ret; } void hoedown_buffer_free(hoedown_buffer *buf) { if (!buf) return; assert(buf && buf->unit); buf->data_free(buf->data); if (buf->buffer_free) buf->buffer_free(buf); } void hoedown_buffer_reset(hoedown_buffer *buf) { assert(buf && buf->unit); buf->data_free(buf->data); buf->data = NULL; buf->size = buf->asize = 0; } void hoedown_buffer_grow(hoedown_buffer *buf, size_t neosz) { size_t neoasz; assert(buf && buf->unit); if (buf->asize >= neosz) return; neoasz = buf->asize + buf->unit; while (neoasz < neosz) neoasz += buf->unit; buf->data = buf->data_realloc(buf->data, neoasz); buf->asize = neoasz; } void hoedown_buffer_put(hoedown_buffer *buf, const uint8_t *data, size_t size) { assert(buf && buf->unit); if (buf->size + size > buf->asize) hoedown_buffer_grow(buf, buf->size + size); memcpy(buf->data + buf->size, data, size); buf->size += size; } void hoedown_buffer_puts(hoedown_buffer *buf, const char *str) { hoedown_buffer_put(buf, (const uint8_t *)str, strlen(str)); } void hoedown_buffer_putc(hoedown_buffer *buf, uint8_t c) { assert(buf && buf->unit); if (buf->size >= buf->asize) hoedown_buffer_grow(buf, buf->size + 1); buf->data[buf->size] = c; buf->size += 1; } int hoedown_buffer_putf(hoedown_buffer *buf, FILE *file) { assert(buf && buf->unit); while (!(feof(file) || ferror(file))) { hoedown_buffer_grow(buf, buf->size + buf->unit); buf->size += fread(buf->data + buf->size, 1, buf->unit, file); } return ferror(file); } void hoedown_buffer_set(hoedown_buffer *buf, const uint8_t *data, size_t size) { assert(buf && buf->unit); if (size > buf->asize) hoedown_buffer_grow(buf, size); memcpy(buf->data, data, size); buf->size = size; } void hoedown_buffer_sets(hoedown_buffer *buf, const char *str) { hoedown_buffer_set(buf, (const uint8_t *)str, strlen(str)); } int hoedown_buffer_eq(const hoedown_buffer *buf, const uint8_t *data, size_t size) { if (buf->size != size) return 0; return memcmp(buf->data, data, size) == 0; } int hoedown_buffer_eqs(const hoedown_buffer *buf, const char *str) { return hoedown_buffer_eq(buf, (const uint8_t *)str, strlen(str)); } int hoedown_buffer_prefix(const hoedown_buffer *buf, const char *prefix) { size_t i; for (i = 0; i < buf->size; ++i) { if (prefix[i] == 0) return 0; if (buf->data[i] != prefix[i]) return buf->data[i] - prefix[i]; } return 0; } void hoedown_buffer_slurp(hoedown_buffer *buf, size_t size) { assert(buf && buf->unit); if (size >= buf->size) { buf->size = 0; return; } buf->size -= size; memmove(buf->data, buf->data + size, buf->size); } const char * hoedown_buffer_cstr(hoedown_buffer *buf) { assert(buf && buf->unit); if (buf->size < buf->asize && buf->data[buf->size] == 0) return (char *)buf->data; hoedown_buffer_grow(buf, buf->size + 1); buf->data[buf->size] = 0; return (char *)buf->data; } void hoedown_buffer_printf(hoedown_buffer *buf, const char *fmt, ...) { va_list ap; int n; assert(buf && buf->unit); if (buf->size >= buf->asize) hoedown_buffer_grow(buf, buf->size + 1); va_start(ap, fmt); n = vsnprintf((char *)buf->data + buf->size, buf->asize - buf->size, fmt, ap); va_end(ap); if (n < 0) { #ifndef _MSC_VER return; #else va_start(ap, fmt); n = _vscprintf(fmt, ap); va_end(ap); #endif } if ((size_t)n >= buf->asize - buf->size) { hoedown_buffer_grow(buf, buf->size + n + 1); va_start(ap, fmt); n = vsnprintf((char *)buf->data + buf->size, buf->asize - buf->size, fmt, ap); va_end(ap); } if (n < 0) return; buf->size += n; } void hoedown_buffer_put_utf8(hoedown_buffer *buf, unsigned int c) { unsigned char unichar[4]; assert(buf && buf->unit); if (c < 0x80) { hoedown_buffer_putc(buf, c); } else if (c < 0x800) { unichar[0] = 192 + (c / 64); unichar[1] = 128 + (c % 64); hoedown_buffer_put(buf, unichar, 2); } else if (c - 0xd800u < 0x800) { HOEDOWN_BUFPUTSL(buf, "\xef\xbf\xbd"); } else if (c < 0x10000) { unichar[0] = 224 + (c / 4096); unichar[1] = 128 + (c / 64) % 64; unichar[2] = 128 + (c % 64); hoedown_buffer_put(buf, unichar, 3); } else if (c < 0x110000) { unichar[0] = 240 + (c / 262144); unichar[1] = 128 + (c / 4096) % 64; unichar[2] = 128 + (c / 64) % 64; unichar[3] = 128 + (c % 64); hoedown_buffer_put(buf, unichar, 4); } else { HOEDOWN_BUFPUTSL(buf, "\xef\xbf\xbd"); } } ================================================ FILE: 3rdpart/hoedown/src/buffer.h ================================================ /* buffer.h - simple, fast buffers */ #ifndef HOEDOWN_BUFFER_H #define HOEDOWN_BUFFER_H #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif #if defined(_MSC_VER) #define __attribute__(x) #define inline __inline #define __builtin_expect(x,n) x #endif /********* * TYPES * *********/ typedef void *(*hoedown_realloc_callback)(void *, size_t); typedef void (*hoedown_free_callback)(void *); struct hoedown_buffer { uint8_t *data; /* actual character data */ size_t size; /* size of the string */ size_t asize; /* allocated size (0 = volatile buffer) */ size_t unit; /* reallocation unit size (0 = read-only buffer) */ hoedown_realloc_callback data_realloc; hoedown_free_callback data_free; hoedown_free_callback buffer_free; }; typedef struct hoedown_buffer hoedown_buffer; /************* * FUNCTIONS * *************/ /* allocation wrappers */ void *hoedown_malloc(size_t size) __attribute__ ((malloc)); void *hoedown_calloc(size_t nmemb, size_t size) __attribute__ ((malloc)); void *hoedown_realloc(void *ptr, size_t size) __attribute__ ((malloc)); /* hoedown_buffer_init: initialize a buffer with custom allocators */ void hoedown_buffer_init( hoedown_buffer *buffer, size_t unit, hoedown_realloc_callback data_realloc, hoedown_free_callback data_free, hoedown_free_callback buffer_free ); /* hoedown_buffer_uninit: uninitialize an existing buffer */ void hoedown_buffer_uninit(hoedown_buffer *buf); /* hoedown_buffer_new: allocate a new buffer */ hoedown_buffer *hoedown_buffer_new(size_t unit) __attribute__ ((malloc)); /* hoedown_buffer_reset: free internal data of the buffer */ void hoedown_buffer_reset(hoedown_buffer *buf); /* hoedown_buffer_grow: increase the allocated size to the given value */ void hoedown_buffer_grow(hoedown_buffer *buf, size_t neosz); /* hoedown_buffer_put: append raw data to a buffer */ void hoedown_buffer_put(hoedown_buffer *buf, const uint8_t *data, size_t size); /* hoedown_buffer_puts: append a NUL-terminated string to a buffer */ void hoedown_buffer_puts(hoedown_buffer *buf, const char *str); /* hoedown_buffer_putc: append a single char to a buffer */ void hoedown_buffer_putc(hoedown_buffer *buf, uint8_t c); /* hoedown_buffer_putf: read from a file and append to a buffer, until EOF or error */ int hoedown_buffer_putf(hoedown_buffer *buf, FILE* file); /* hoedown_buffer_set: replace the buffer's contents with raw data */ void hoedown_buffer_set(hoedown_buffer *buf, const uint8_t *data, size_t size); /* hoedown_buffer_sets: replace the buffer's contents with a NUL-terminated string */ void hoedown_buffer_sets(hoedown_buffer *buf, const char *str); /* hoedown_buffer_eq: compare a buffer's data with other data for equality */ int hoedown_buffer_eq(const hoedown_buffer *buf, const uint8_t *data, size_t size); /* hoedown_buffer_eq: compare a buffer's data with NUL-terminated string for equality */ int hoedown_buffer_eqs(const hoedown_buffer *buf, const char *str); /* hoedown_buffer_prefix: compare the beginning of a buffer with a string */ int hoedown_buffer_prefix(const hoedown_buffer *buf, const char *prefix); /* hoedown_buffer_slurp: remove a given number of bytes from the head of the buffer */ void hoedown_buffer_slurp(hoedown_buffer *buf, size_t size); /* hoedown_buffer_cstr: NUL-termination of the string array (making a C-string) */ const char *hoedown_buffer_cstr(hoedown_buffer *buf); /* hoedown_buffer_printf: formatted printing to a buffer */ void hoedown_buffer_printf(hoedown_buffer *buf, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); /* hoedown_buffer_put_utf8: put a Unicode character encoded as UTF-8 */ void hoedown_buffer_put_utf8(hoedown_buffer *buf, unsigned int codepoint); /* hoedown_buffer_free: free the buffer */ void hoedown_buffer_free(hoedown_buffer *buf); /* HOEDOWN_BUFPUTSL: optimized hoedown_buffer_puts of a string literal */ #define HOEDOWN_BUFPUTSL(output, literal) \ hoedown_buffer_put(output, (const uint8_t *)literal, sizeof(literal) - 1) /* HOEDOWN_BUFSETSL: optimized hoedown_buffer_sets of a string literal */ #define HOEDOWN_BUFSETSL(output, literal) \ hoedown_buffer_set(output, (const uint8_t *)literal, sizeof(literal) - 1) /* HOEDOWN_BUFEQSL: optimized hoedown_buffer_eqs of a string literal */ #define HOEDOWN_BUFEQSL(output, literal) \ hoedown_buffer_eq(output, (const uint8_t *)literal, sizeof(literal) - 1) #ifdef __cplusplus } #endif #endif /** HOEDOWN_BUFFER_H **/ ================================================ FILE: 3rdpart/hoedown/src/document.c ================================================ #include "document.h" #include #include #include #include #include "stack.h" #ifndef _MSC_VER #include #else #define strncasecmp _strnicmp #endif #define REF_TABLE_SIZE 8 #define BUFFER_BLOCK 0 #define BUFFER_SPAN 1 #define HOEDOWN_LI_END 8 /* internal list flag */ const char *hoedown_find_block_tag(const char *str, unsigned int len); /*************** * LOCAL TYPES * ***************/ /* link_ref: reference to a link */ struct link_ref { unsigned int id; hoedown_buffer *link; hoedown_buffer *title; struct link_ref *next; }; /* footnote_ref: reference to a footnote */ struct footnote_ref { unsigned int id; int is_used; unsigned int num; hoedown_buffer *contents; }; /* footnote_item: an item in a footnote_list */ struct footnote_item { struct footnote_ref *ref; struct footnote_item *next; }; /* footnote_list: linked list of footnote_item */ struct footnote_list { unsigned int count; struct footnote_item *head; struct footnote_item *tail; }; /* char_trigger: function pointer to render active chars */ /* returns the number of chars taken care of */ /* data is the pointer of the beginning of the span */ /* offset is the number of valid chars before data */ typedef size_t (*char_trigger)(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); static size_t char_emphasis(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); static size_t char_quote(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); static size_t char_linebreak(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); static size_t char_codespan(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); static size_t char_escape(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); static size_t char_entity(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); static size_t char_langle_tag(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); static size_t char_autolink_url(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); static size_t char_autolink_email(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); static size_t char_autolink_www(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); static size_t char_link(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); static size_t char_image(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); static size_t char_superscript(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); static size_t char_math(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); enum markdown_char_t { MD_CHAR_NONE = 0, MD_CHAR_EMPHASIS, MD_CHAR_CODESPAN, MD_CHAR_LINEBREAK, MD_CHAR_LINK, MD_CHAR_IMAGE, MD_CHAR_LANGLE, MD_CHAR_ESCAPE, MD_CHAR_ENTITY, MD_CHAR_AUTOLINK_URL, MD_CHAR_AUTOLINK_EMAIL, MD_CHAR_AUTOLINK_WWW, MD_CHAR_SUPERSCRIPT, MD_CHAR_QUOTE, MD_CHAR_MATH }; static char_trigger markdown_char_ptrs[] = { NULL, &char_emphasis, &char_codespan, &char_linebreak, &char_link, &char_image, &char_langle_tag, &char_escape, &char_entity, &char_autolink_url, &char_autolink_email, &char_autolink_www, &char_superscript, &char_quote, &char_math }; struct hoedown_document { hoedown_renderer md; hoedown_renderer_data data; struct link_ref *refs[REF_TABLE_SIZE]; struct footnote_list footnotes_found; struct footnote_list footnotes_used; uint8_t active_char[256]; hoedown_stack work_bufs[2]; hoedown_extensions ext_flags; size_t max_nesting; int in_link_body; }; /*************************** * HELPER FUNCTIONS * ***************************/ static hoedown_buffer * newbuf(hoedown_document *doc, int type) { static const size_t buf_size[2] = {256, 64}; hoedown_buffer *work = NULL; hoedown_stack *pool = &doc->work_bufs[type]; if (pool->size < pool->asize && pool->item[pool->size] != NULL) { work = pool->item[pool->size++]; work->size = 0; } else { work = hoedown_buffer_new(buf_size[type]); hoedown_stack_push(pool, work); } return work; } static void popbuf(hoedown_document *doc, int type) { doc->work_bufs[type].size--; } static void unscape_text(hoedown_buffer *ob, hoedown_buffer *src) { size_t i = 0, org; while (i < src->size) { org = i; while (i < src->size && src->data[i] != '\\') i++; if (i > org) hoedown_buffer_put(ob, src->data + org, i - org); if (i + 1 >= src->size) break; hoedown_buffer_putc(ob, src->data[i + 1]); i += 2; } } static unsigned int hash_link_ref(const uint8_t *link_ref, size_t length) { size_t i; unsigned int hash = 0; for (i = 0; i < length; ++i) hash = tolower(link_ref[i]) + (hash << 6) + (hash << 16) - hash; return hash; } static struct link_ref * add_link_ref( struct link_ref **references, const uint8_t *name, size_t name_size) { struct link_ref *ref = hoedown_calloc(1, sizeof(struct link_ref)); ref->id = hash_link_ref(name, name_size); ref->next = references[ref->id % REF_TABLE_SIZE]; references[ref->id % REF_TABLE_SIZE] = ref; return ref; } static struct link_ref * find_link_ref(struct link_ref **references, uint8_t *name, size_t length) { unsigned int hash = hash_link_ref(name, length); struct link_ref *ref = NULL; ref = references[hash % REF_TABLE_SIZE]; while (ref != NULL) { if (ref->id == hash) return ref; ref = ref->next; } return NULL; } static void free_link_refs(struct link_ref **references) { size_t i; for (i = 0; i < REF_TABLE_SIZE; ++i) { struct link_ref *r = references[i]; struct link_ref *next; while (r) { next = r->next; hoedown_buffer_free(r->link); hoedown_buffer_free(r->title); free(r); r = next; } } } static struct footnote_ref * create_footnote_ref(struct footnote_list *list, const uint8_t *name, size_t name_size) { struct footnote_ref *ref = hoedown_calloc(1, sizeof(struct footnote_ref)); ref->id = hash_link_ref(name, name_size); return ref; } static int add_footnote_ref(struct footnote_list *list, struct footnote_ref *ref) { struct footnote_item *item = hoedown_calloc(1, sizeof(struct footnote_item)); if (!item) return 0; item->ref = ref; if (list->head == NULL) { list->head = list->tail = item; } else { list->tail->next = item; list->tail = item; } list->count++; return 1; } static struct footnote_ref * find_footnote_ref(struct footnote_list *list, uint8_t *name, size_t length) { unsigned int hash = hash_link_ref(name, length); struct footnote_item *item = NULL; item = list->head; while (item != NULL) { if (item->ref->id == hash) return item->ref; item = item->next; } return NULL; } static void free_footnote_ref(struct footnote_ref *ref) { hoedown_buffer_free(ref->contents); free(ref); } static void free_footnote_list(struct footnote_list *list, int free_refs) { struct footnote_item *item = list->head; struct footnote_item *next; while (item) { next = item->next; if (free_refs) free_footnote_ref(item->ref); free(item); item = next; } } /* * Check whether a char is a Markdown spacing char. * Right now we only consider spaces the actual * space and a newline: tabs and carriage returns * are filtered out during the preprocessing phase. * * If we wanted to actually be UTF-8 compliant, we * should instead extract an Unicode codepoint from * this character and check for space properties. */ static int _isspace(int c) { return c == ' ' || c == '\n'; } /* is_empty_all: verify that all the data is spacing */ static int is_empty_all(const uint8_t *data, size_t size) { size_t i = 0; while (i < size && _isspace(data[i])) i++; return i == size; } /* * Replace all spacing characters in data with spaces. As a special * case, this collapses a newline with the previous space, if possible. */ static void replace_spacing(hoedown_buffer *ob, const uint8_t *data, size_t size) { size_t i = 0, mark; hoedown_buffer_grow(ob, size); while (1) { mark = i; while (i < size && data[i] != '\n') i++; hoedown_buffer_put(ob, data + mark, i - mark); if (i >= size) break; if (!(i > 0 && data[i-1] == ' ')) hoedown_buffer_putc(ob, ' '); i++; } } /**************************** * INLINE PARSING FUNCTIONS * ****************************/ /* is_mail_autolink • looks for the address part of a mail autolink and '>' */ /* this is less strict than the original markdown e-mail address matching */ static size_t is_mail_autolink(uint8_t *data, size_t size) { size_t i = 0, nb = 0; /* address is assumed to be: [-@._a-zA-Z0-9]+ with exactly one '@' */ for (i = 0; i < size; ++i) { if (isalnum(data[i])) continue; switch (data[i]) { case '@': nb++; case '-': case '.': case '_': break; case '>': return (nb == 1) ? i + 1 : 0; default: return 0; } } return 0; } /* tag_length • returns the length of the given tag, or 0 is it's not valid */ static size_t tag_length(uint8_t *data, size_t size, hoedown_autolink_type *autolink) { size_t i, j; /* a valid tag can't be shorter than 3 chars */ if (size < 3) return 0; if (data[0] != '<') return 0; /* HTML comment, laxist form */ if (size > 5 && data[1] == '!' && data[2] == '-' && data[3] == '-') { i = 5; while (i < size && !(data[i - 2] == '-' && data[i - 1] == '-' && data[i] == '>')) i++; i++; if (i <= size) return i; } /* begins with a '<' optionally followed by '/', followed by letter or number */ i = (data[1] == '/') ? 2 : 1; if (!isalnum(data[i])) return 0; /* scheme test */ *autolink = HOEDOWN_AUTOLINK_NONE; /* try to find the beginning of an URI */ while (i < size && (isalnum(data[i]) || data[i] == '.' || data[i] == '+' || data[i] == '-')) i++; if (i > 1 && data[i] == '@') { if ((j = is_mail_autolink(data + i, size - i)) != 0) { *autolink = HOEDOWN_AUTOLINK_EMAIL; return i + j; } } if (i > 2 && data[i] == ':') { *autolink = HOEDOWN_AUTOLINK_NORMAL; i++; } /* completing autolink test: no spacing or ' or " */ if (i >= size) *autolink = HOEDOWN_AUTOLINK_NONE; else if (*autolink) { j = i; while (i < size) { if (data[i] == '\\') i += 2; else if (data[i] == '>' || data[i] == '\'' || data[i] == '"' || data[i] == ' ' || data[i] == '\n') break; else i++; } if (i >= size) return 0; if (i > j && data[i] == '>') return i + 1; /* one of the forbidden chars has been found */ *autolink = HOEDOWN_AUTOLINK_NONE; } /* looking for something looking like a tag end */ while (i < size && data[i] != '>') i++; if (i >= size) return 0; return i + 1; } /* parse_inline • parses inline markdown elements */ static void parse_inline(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size) { size_t i = 0, end = 0, consumed = 0; hoedown_buffer work = { 0, 0, 0, 0, NULL, NULL, NULL }; uint8_t *active_char = doc->active_char; if (doc->work_bufs[BUFFER_SPAN].size + doc->work_bufs[BUFFER_BLOCK].size > doc->max_nesting) return; while (i < size) { /* copying inactive chars into the output */ while (end < size && active_char[data[end]] == 0) end++; if (doc->md.normal_text) { work.data = data + i; work.size = end - i; doc->md.normal_text(ob, &work, &doc->data); } else hoedown_buffer_put(ob, data + i, end - i); if (end >= size) break; i = end; end = markdown_char_ptrs[ (int)active_char[data[end]] ](ob, doc, data + i, i - consumed, size - i); if (!end) /* no action from the callback */ end = i + 1; else { i += end; end = i; consumed = i; } } } /* is_escaped • returns whether special char at data[loc] is escaped by '\\' */ static int is_escaped(uint8_t *data, size_t loc) { size_t i = loc; while (i >= 1 && data[i - 1] == '\\') i--; /* odd numbers of backslashes escapes data[loc] */ return (loc - i) % 2; } /* find_emph_char • looks for the next emph uint8_t, skipping other constructs */ static size_t find_emph_char(uint8_t *data, size_t size, uint8_t c) { size_t i = 0; while (i < size) { while (i < size && data[i] != c && data[i] != '[' && data[i] != '`') i++; if (i == size) return 0; /* not counting escaped chars */ if (is_escaped(data, i)) { i++; continue; } if (data[i] == c) return i; /* skipping a codespan */ if (data[i] == '`') { size_t span_nb = 0, bt; size_t tmp_i = 0; /* counting the number of opening backticks */ while (i < size && data[i] == '`') { i++; span_nb++; } if (i >= size) return 0; /* finding the matching closing sequence */ bt = 0; while (i < size && bt < span_nb) { if (!tmp_i && data[i] == c) tmp_i = i; if (data[i] == '`') bt++; else bt = 0; i++; } /* not a well-formed codespan; use found matching emph char */ if (bt < span_nb && i >= size) return tmp_i; } /* skipping a link */ else if (data[i] == '[') { size_t tmp_i = 0; uint8_t cc; i++; while (i < size && data[i] != ']') { if (!tmp_i && data[i] == c) tmp_i = i; i++; } i++; while (i < size && _isspace(data[i])) i++; if (i >= size) return tmp_i; switch (data[i]) { case '[': cc = ']'; break; case '(': cc = ')'; break; default: if (tmp_i) return tmp_i; else continue; } i++; while (i < size && data[i] != cc) { if (!tmp_i && data[i] == c) tmp_i = i; i++; } if (i >= size) return tmp_i; i++; } } return 0; } /* parse_emph1 • parsing single emphase */ /* closed by a symbol not preceded by spacing and not followed by symbol */ static size_t parse_emph1(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size, uint8_t c) { size_t i = 0, len; hoedown_buffer *work = 0; int r; /* skipping one symbol if coming from emph3 */ if (size > 1 && data[0] == c && data[1] == c) i = 1; while (i < size) { len = find_emph_char(data + i, size - i, c); if (!len) return 0; i += len; if (i >= size) return 0; if (data[i] == c && !_isspace(data[i - 1])) { if (doc->ext_flags & HOEDOWN_EXT_NO_INTRA_EMPHASIS) { if (i + 1 < size && isalnum(data[i + 1])) continue; } work = newbuf(doc, BUFFER_SPAN); parse_inline(work, doc, data, i); if (doc->ext_flags & HOEDOWN_EXT_UNDERLINE && c == '_') r = doc->md.underline(ob, work, &doc->data); else r = doc->md.emphasis(ob, work, &doc->data); popbuf(doc, BUFFER_SPAN); return r ? i + 1 : 0; } } return 0; } /* parse_emph2 • parsing single emphase */ static size_t parse_emph2(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size, uint8_t c) { size_t i = 0, len; hoedown_buffer *work = 0; int r; while (i < size) { len = find_emph_char(data + i, size - i, c); if (!len) return 0; i += len; if (i + 1 < size && data[i] == c && data[i + 1] == c && i && !_isspace(data[i - 1])) { work = newbuf(doc, BUFFER_SPAN); parse_inline(work, doc, data, i); if (c == '~') r = doc->md.strikethrough(ob, work, &doc->data); else if (c == '=') r = doc->md.highlight(ob, work, &doc->data); else r = doc->md.double_emphasis(ob, work, &doc->data); popbuf(doc, BUFFER_SPAN); return r ? i + 2 : 0; } i++; } return 0; } /* parse_emph3 • parsing single emphase */ /* finds the first closing tag, and delegates to the other emph */ static size_t parse_emph3(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size, uint8_t c) { size_t i = 0, len; int r; while (i < size) { len = find_emph_char(data + i, size - i, c); if (!len) return 0; i += len; /* skip spacing preceded symbols */ if (data[i] != c || _isspace(data[i - 1])) continue; if (i + 2 < size && data[i + 1] == c && data[i + 2] == c && doc->md.triple_emphasis) { /* triple symbol found */ hoedown_buffer *work = newbuf(doc, BUFFER_SPAN); parse_inline(work, doc, data, i); r = doc->md.triple_emphasis(ob, work, &doc->data); popbuf(doc, BUFFER_SPAN); return r ? i + 3 : 0; } else if (i + 1 < size && data[i + 1] == c) { /* double symbol found, handing over to emph1 */ len = parse_emph1(ob, doc, data - 2, size + 2, c); if (!len) return 0; else return len - 2; } else { /* single symbol found, handing over to emph2 */ len = parse_emph2(ob, doc, data - 1, size + 1, c); if (!len) return 0; else return len - 1; } } return 0; } /* parse_math • parses a math span until the given ending delimiter */ static size_t parse_math(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size, const char *end, size_t delimsz, int displaymode) { hoedown_buffer text = { NULL, 0, 0, 0, NULL, NULL, NULL }; size_t i = delimsz; if (!doc->md.math) return 0; /* find ending delimiter */ while (1) { while (i < size && data[i] != (uint8_t)end[0]) i++; if (i >= size) return 0; if (!is_escaped(data, i) && !(i + delimsz > size) && memcmp(data + i, end, delimsz) == 0) break; i++; } /* prepare buffers */ text.data = data + delimsz; text.size = i - delimsz; /* if this is a $$ and MATH_EXPLICIT is not active, * guess whether displaymode should be enabled from the context */ i += delimsz; if (delimsz == 2 && !(doc->ext_flags & HOEDOWN_EXT_MATH_EXPLICIT)) displaymode = is_empty_all(data - offset, offset) && is_empty_all(data + i, size - i); /* call callback */ if (doc->md.math(ob, &text, displaymode, &doc->data)) return i; return 0; } /* char_emphasis • single and double emphasis parsing */ static size_t char_emphasis(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) { uint8_t c = data[0]; size_t ret; if (doc->ext_flags & HOEDOWN_EXT_NO_INTRA_EMPHASIS) { if (offset > 0 && !_isspace(data[-1]) && data[-1] != '>' && data[-1] != '(') return 0; } if (size > 2 && data[1] != c) { /* spacing cannot follow an opening emphasis; * strikethrough and highlight only takes two characters '~~' */ if (c == '~' || c == '=' || _isspace(data[1]) || (ret = parse_emph1(ob, doc, data + 1, size - 1, c)) == 0) return 0; return ret + 1; } if (size > 3 && data[1] == c && data[2] != c) { if (_isspace(data[2]) || (ret = parse_emph2(ob, doc, data + 2, size - 2, c)) == 0) return 0; return ret + 2; } if (size > 4 && data[1] == c && data[2] == c && data[3] != c) { if (c == '~' || c == '=' || _isspace(data[3]) || (ret = parse_emph3(ob, doc, data + 3, size - 3, c)) == 0) return 0; return ret + 3; } return 0; } /* char_linebreak • '\n' preceded by two spaces (assuming linebreak != 0) */ static size_t char_linebreak(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) { if (offset < 2 || data[-1] != ' ' || data[-2] != ' ') return 0; /* removing the last space from ob and rendering */ while (ob->size && ob->data[ob->size - 1] == ' ') ob->size--; return doc->md.linebreak(ob, &doc->data) ? 1 : 0; } /* char_codespan • '`' parsing a code span (assuming codespan != 0) */ static size_t char_codespan(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) { hoedown_buffer work = { NULL, 0, 0, 0, NULL, NULL, NULL }; size_t end, nb = 0, i, f_begin, f_end; /* counting the number of backticks in the delimiter */ while (nb < size && data[nb] == '`') nb++; /* finding the next delimiter */ i = 0; for (end = nb; end < size && i < nb; end++) { if (data[end] == '`') i++; else i = 0; } if (i < nb && end >= size) return 0; /* no matching delimiter */ /* trimming outside spaces */ f_begin = nb; while (f_begin < end && data[f_begin] == ' ') f_begin++; f_end = end - nb; while (f_end > nb && data[f_end-1] == ' ') f_end--; /* real code span */ if (f_begin < f_end) { work.data = data + f_begin; work.size = f_end - f_begin; if (!doc->md.codespan(ob, &work, &doc->data)) end = 0; } else { if (!doc->md.codespan(ob, 0, &doc->data)) end = 0; } return end; } /* char_quote • '"' parsing a quote */ static size_t char_quote(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) { size_t end, nq = 0, i, f_begin, f_end; /* counting the number of quotes in the delimiter */ while (nq < size && data[nq] == '"') nq++; /* finding the next delimiter */ end = nq; while (1) { i = end; end += find_emph_char(data + end, size - end, '"'); if (end == i) return 0; /* no matching delimiter */ i = end; while (end < size && data[end] == '"' && end - i < nq) end++; if (end - i >= nq) break; } /* trimming outside spaces */ f_begin = nq; while (f_begin < end && data[f_begin] == ' ') f_begin++; f_end = end - nq; while (f_end > nq && data[f_end-1] == ' ') f_end--; /* real quote */ if (f_begin < f_end) { hoedown_buffer *work = newbuf(doc, BUFFER_SPAN); parse_inline(work, doc, data + f_begin, f_end - f_begin); if (!doc->md.quote(ob, work, &doc->data)) end = 0; popbuf(doc, BUFFER_SPAN); } else { if (!doc->md.quote(ob, 0, &doc->data)) end = 0; } return end; } /* char_escape • '\\' backslash escape */ static size_t char_escape(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) { static const char *escape_chars = "\\`*_{}[]()#+-.!:|&<>^~=\"$"; hoedown_buffer work = { 0, 0, 0, 0, NULL, NULL, NULL }; size_t w; if (size > 1) { if (data[1] == '\\' && (doc->ext_flags & HOEDOWN_EXT_MATH) && size > 2 && (data[2] == '(' || data[2] == '[')) { const char *end = (data[2] == '[') ? "\\\\]" : "\\\\)"; w = parse_math(ob, doc, data, offset, size, end, 3, data[2] == '['); if (w) return w; } if (strchr(escape_chars, data[1]) == NULL) return 0; if (doc->md.normal_text) { work.data = data + 1; work.size = 1; doc->md.normal_text(ob, &work, &doc->data); } else hoedown_buffer_putc(ob, data[1]); } else if (size == 1) { if (doc->md.normal_text) { work.data = data; work.size = 1; doc->md.normal_text(ob, &work, &doc->data); } else hoedown_buffer_putc(ob, data[0]); } return 2; } /* char_entity • '&' escaped when it doesn't belong to an entity */ /* valid entities are assumed to be anything matching &#?[A-Za-z0-9]+; */ static size_t char_entity(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) { size_t end = 1; hoedown_buffer work = { 0, 0, 0, 0, NULL, NULL, NULL }; if (end < size && data[end] == '#') end++; while (end < size && isalnum(data[end])) end++; if (end < size && data[end] == ';') end++; /* real entity */ else return 0; /* lone '&' */ if (doc->md.entity) { work.data = data; work.size = end; doc->md.entity(ob, &work, &doc->data); } else hoedown_buffer_put(ob, data, end); return end; } /* char_langle_tag • '<' when tags or autolinks are allowed */ static size_t char_langle_tag(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) { hoedown_buffer work = { NULL, 0, 0, 0, NULL, NULL, NULL }; hoedown_autolink_type altype = HOEDOWN_AUTOLINK_NONE; size_t end = tag_length(data, size, &altype); int ret = 0; work.data = data; work.size = end; if (end > 2) { if (doc->md.autolink && altype != HOEDOWN_AUTOLINK_NONE) { hoedown_buffer *u_link = newbuf(doc, BUFFER_SPAN); work.data = data + 1; work.size = end - 2; unscape_text(u_link, &work); ret = doc->md.autolink(ob, u_link, altype, &doc->data); popbuf(doc, BUFFER_SPAN); } else if (doc->md.raw_html) ret = doc->md.raw_html(ob, &work, &doc->data); } if (!ret) return 0; else return end; } static size_t char_autolink_www(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) { hoedown_buffer *link, *link_url, *link_text; size_t link_len, rewind; if (!doc->md.link || doc->in_link_body) return 0; link = newbuf(doc, BUFFER_SPAN); if ((link_len = hoedown_autolink__www(&rewind, link, data, offset, size, HOEDOWN_AUTOLINK_SHORT_DOMAINS)) > 0) { link_url = newbuf(doc, BUFFER_SPAN); HOEDOWN_BUFPUTSL(link_url, "http://"); hoedown_buffer_put(link_url, link->data, link->size); if (ob->size > rewind) ob->size -= rewind; else ob->size = 0; if (doc->md.normal_text) { link_text = newbuf(doc, BUFFER_SPAN); doc->md.normal_text(link_text, link, &doc->data); doc->md.link(ob, link_text, link_url, NULL, &doc->data); popbuf(doc, BUFFER_SPAN); } else { doc->md.link(ob, link, link_url, NULL, &doc->data); } popbuf(doc, BUFFER_SPAN); } popbuf(doc, BUFFER_SPAN); return link_len; } static size_t char_autolink_email(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) { hoedown_buffer *link; size_t link_len, rewind; if (!doc->md.autolink || doc->in_link_body) return 0; link = newbuf(doc, BUFFER_SPAN); if ((link_len = hoedown_autolink__email(&rewind, link, data, offset, size, 0)) > 0) { if (ob->size > rewind) ob->size -= rewind; else ob->size = 0; doc->md.autolink(ob, link, HOEDOWN_AUTOLINK_EMAIL, &doc->data); } popbuf(doc, BUFFER_SPAN); return link_len; } static size_t char_autolink_url(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) { hoedown_buffer *link; size_t link_len, rewind; if (!doc->md.autolink || doc->in_link_body) return 0; link = newbuf(doc, BUFFER_SPAN); if ((link_len = hoedown_autolink__url(&rewind, link, data, offset, size, 0)) > 0) { if (ob->size > rewind) ob->size -= rewind; else ob->size = 0; doc->md.autolink(ob, link, HOEDOWN_AUTOLINK_NORMAL, &doc->data); } popbuf(doc, BUFFER_SPAN); return link_len; } static size_t char_image(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) { size_t ret; if (size < 2 || data[1] != '[') return 0; ret = char_link(ob, doc, data + 1, offset + 1, size - 1); if (!ret) return 0; return ret + 1; } /* char_link • '[': parsing a link, a footnote or an image */ static size_t char_link(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) { int is_img = (offset && data[-1] == '!' && !is_escaped(data - offset, offset - 1)); int is_footnote = (doc->ext_flags & HOEDOWN_EXT_FOOTNOTES && data[1] == '^'); size_t i = 1, txt_e, link_b = 0, link_e = 0, title_b = 0, title_e = 0; hoedown_buffer *content = NULL; hoedown_buffer *link = NULL; hoedown_buffer *title = NULL; hoedown_buffer *u_link = NULL; size_t org_work_size = doc->work_bufs[BUFFER_SPAN].size; int ret = 0, in_title = 0, qtype = 0; /* checking whether the correct renderer exists */ if ((is_footnote && !doc->md.footnote_ref) || (is_img && !doc->md.image) || (!is_img && !is_footnote && !doc->md.link)) goto cleanup; /* looking for the matching closing bracket */ i += find_emph_char(data + i, size - i, ']'); txt_e = i; if (i < size && data[i] == ']') i++; else goto cleanup; /* footnote link */ if (is_footnote) { hoedown_buffer id = { NULL, 0, 0, 0, NULL, NULL, NULL }; struct footnote_ref *fr; if (txt_e < 3) goto cleanup; id.data = data + 2; id.size = txt_e - 2; fr = find_footnote_ref(&doc->footnotes_found, id.data, id.size); /* mark footnote used */ if (fr && !fr->is_used) { if(!add_footnote_ref(&doc->footnotes_used, fr)) goto cleanup; fr->is_used = 1; fr->num = doc->footnotes_used.count; /* render */ if (doc->md.footnote_ref) ret = doc->md.footnote_ref(ob, fr->num, &doc->data); } goto cleanup; } /* skip any amount of spacing */ /* (this is much more laxist than original markdown syntax) */ while (i < size && _isspace(data[i])) i++; /* inline style link */ if (i < size && data[i] == '(') { size_t nb_p; /* skipping initial spacing */ i++; while (i < size && _isspace(data[i])) i++; link_b = i; /* looking for link end: ' " ) */ /* Count the number of open parenthesis */ nb_p = 0; while (i < size) { if (data[i] == '\\') i += 2; else if (data[i] == '(' && i != 0) { nb_p++; i++; } else if (data[i] == ')') { if (nb_p == 0) break; else nb_p--; i++; } else if (i >= 1 && _isspace(data[i-1]) && (data[i] == '\'' || data[i] == '"')) break; else i++; } if (i >= size) goto cleanup; link_e = i; /* looking for title end if present */ if (data[i] == '\'' || data[i] == '"') { qtype = data[i]; in_title = 1; i++; title_b = i; while (i < size) { if (data[i] == '\\') i += 2; else if (data[i] == qtype) {in_title = 0; i++;} else if ((data[i] == ')') && !in_title) break; else i++; } if (i >= size) goto cleanup; /* skipping spacing after title */ title_e = i - 1; while (title_e > title_b && _isspace(data[title_e])) title_e--; /* checking for closing quote presence */ if (data[title_e] != '\'' && data[title_e] != '"') { title_b = title_e = 0; link_e = i; } } /* remove spacing at the end of the link */ while (link_e > link_b && _isspace(data[link_e - 1])) link_e--; /* remove optional angle brackets around the link */ if (data[link_b] == '<' && data[link_e - 1] == '>') { link_b++; link_e--; } /* building escaped link and title */ if (link_e > link_b) { link = newbuf(doc, BUFFER_SPAN); hoedown_buffer_put(link, data + link_b, link_e - link_b); } if (title_e > title_b) { title = newbuf(doc, BUFFER_SPAN); hoedown_buffer_put(title, data + title_b, title_e - title_b); } i++; } /* reference style link */ else if (i < size && data[i] == '[') { hoedown_buffer *id = newbuf(doc, BUFFER_SPAN); struct link_ref *lr; /* looking for the id */ i++; link_b = i; while (i < size && data[i] != ']') i++; if (i >= size) goto cleanup; link_e = i; /* finding the link_ref */ if (link_b == link_e) replace_spacing(id, data + 1, txt_e - 1); else hoedown_buffer_put(id, data + link_b, link_e - link_b); lr = find_link_ref(doc->refs, id->data, id->size); if (!lr) goto cleanup; /* keeping link and title from link_ref */ link = lr->link; title = lr->title; i++; } /* shortcut reference style link */ else { hoedown_buffer *id = newbuf(doc, BUFFER_SPAN); struct link_ref *lr; /* crafting the id */ replace_spacing(id, data + 1, txt_e - 1); /* finding the link_ref */ lr = find_link_ref(doc->refs, id->data, id->size); if (!lr) goto cleanup; /* keeping link and title from link_ref */ link = lr->link; title = lr->title; /* rewinding the spacing */ i = txt_e + 1; } /* building content: img alt is kept, only link content is parsed */ if (txt_e > 1) { content = newbuf(doc, BUFFER_SPAN); if (is_img) { hoedown_buffer_put(content, data + 1, txt_e - 1); } else { /* disable autolinking when parsing inline the * content of a link */ doc->in_link_body = 1; parse_inline(content, doc, data + 1, txt_e - 1); doc->in_link_body = 0; } } if (link) { u_link = newbuf(doc, BUFFER_SPAN); unscape_text(u_link, link); } /* calling the relevant rendering function */ if (is_img) { ret = doc->md.image(ob, u_link, title, content, &doc->data); } else { ret = doc->md.link(ob, content, u_link, title, &doc->data); } /* cleanup */ cleanup: doc->work_bufs[BUFFER_SPAN].size = (int)org_work_size; return ret ? i : 0; } static size_t char_superscript(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) { size_t sup_start, sup_len; hoedown_buffer *sup; if (!doc->md.superscript) return 0; if (size < 2) return 0; if (data[1] == '(') { sup_start = 2; sup_len = find_emph_char(data + 2, size - 2, ')') + 2; if (sup_len == size) return 0; } else { sup_start = sup_len = 1; while (sup_len < size && !_isspace(data[sup_len])) sup_len++; } if (sup_len - sup_start == 0) return (sup_start == 2) ? 3 : 0; sup = newbuf(doc, BUFFER_SPAN); parse_inline(sup, doc, data + sup_start, sup_len - sup_start); doc->md.superscript(ob, sup, &doc->data); popbuf(doc, BUFFER_SPAN); return (sup_start == 2) ? sup_len + 1 : sup_len; } static size_t char_math(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) { /* double dollar */ if (size > 1 && data[1] == '$') return parse_math(ob, doc, data, offset, size, "$$", 2, 1); /* single dollar allowed only with MATH_EXPLICIT flag */ if (doc->ext_flags & HOEDOWN_EXT_MATH_EXPLICIT) return parse_math(ob, doc, data, offset, size, "$", 1, 0); return 0; } /********************************* * BLOCK-LEVEL PARSING FUNCTIONS * *********************************/ /* is_empty • returns the line length when it is empty, 0 otherwise */ static size_t is_empty(const uint8_t *data, size_t size) { size_t i; for (i = 0; i < size && data[i] != '\n'; i++) if (data[i] != ' ') return 0; return i + 1; } /* is_hrule • returns whether a line is a horizontal rule */ static int is_hrule(uint8_t *data, size_t size) { size_t i = 0, n = 0; uint8_t c; /* skipping initial spaces */ if (size < 3) return 0; if (data[0] == ' ') { i++; if (data[1] == ' ') { i++; if (data[2] == ' ') { i++; } } } /* looking at the hrule uint8_t */ if (i + 2 >= size || (data[i] != '*' && data[i] != '-' && data[i] != '_')) return 0; c = data[i]; /* the whole line must be the char or space */ while (i < size && data[i] != '\n') { if (data[i] == c) n++; else if (data[i] != ' ') return 0; i++; } return n >= 3; } /* check if a line is a code fence; return the * end of the code fence. if passed, width of * the fence rule and character will be returned */ static size_t is_codefence(uint8_t *data, size_t size, size_t *width, uint8_t *chr) { size_t i = 0, n = 1; uint8_t c; /* skipping initial spaces */ if (size < 3) return 0; if (data[0] == ' ') { i++; if (data[1] == ' ') { i++; if (data[2] == ' ') { i++; } } } /* looking at the hrule uint8_t */ c = data[i]; if (i + 2 >= size || !(c=='~' || c=='`')) return 0; /* the fence must be that same character */ while (++i < size && data[i] == c) ++n; if (n < 3) return 0; if (width) *width = n; if (chr) *chr = c; return i; } /* expects single line, checks if it's a codefence and extracts language */ static size_t parse_codefence(uint8_t *data, size_t size, hoedown_buffer *lang, size_t *width, uint8_t *chr) { size_t i, w, lang_start; i = w = is_codefence(data, size, width, chr); if (i == 0) return 0; while (i < size && _isspace(data[i])) i++; lang_start = i; while (i < size && !_isspace(data[i])) i++; lang->data = data + lang_start; lang->size = i - lang_start; /* Avoid parsing a codespan as a fence */ i = lang_start + 2; while (i < size && !(data[i] == *chr && data[i-1] == *chr && data[i-2] == *chr)) i++; if (i < size) return 0; return w; } /* is_atxheader • returns whether the line is a hash-prefixed header */ static int is_atxheader(hoedown_document *doc, uint8_t *data, size_t size) { if (data[0] != '#') return 0; if (doc->ext_flags & HOEDOWN_EXT_SPACE_HEADERS) { size_t level = 0; while (level < size && level < 6 && data[level] == '#') level++; if (level < size && data[level] != ' ') return 0; } return 1; } /* is_headerline • returns whether the line is a setext-style hdr underline */ static int is_headerline(uint8_t *data, size_t size) { size_t i = 0; /* test of level 1 header */ if (data[i] == '=') { for (i = 1; i < size && data[i] == '='; i++); while (i < size && data[i] == ' ') i++; return (i >= size || data[i] == '\n') ? 1 : 0; } /* test of level 2 header */ if (data[i] == '-') { for (i = 1; i < size && data[i] == '-'; i++); while (i < size && data[i] == ' ') i++; return (i >= size || data[i] == '\n') ? 2 : 0; } return 0; } static int is_next_headerline(uint8_t *data, size_t size) { size_t i = 0; while (i < size && data[i] != '\n') i++; if (++i >= size) return 0; return is_headerline(data + i, size - i); } /* prefix_quote • returns blockquote prefix length */ static size_t prefix_quote(uint8_t *data, size_t size) { size_t i = 0; if (i < size && data[i] == ' ') i++; if (i < size && data[i] == ' ') i++; if (i < size && data[i] == ' ') i++; if (i < size && data[i] == '>') { if (i + 1 < size && data[i + 1] == ' ') return i + 2; return i + 1; } return 0; } /* prefix_code • returns prefix length for block code*/ static size_t prefix_code(uint8_t *data, size_t size) { if (size > 3 && data[0] == ' ' && data[1] == ' ' && data[2] == ' ' && data[3] == ' ') return 4; return 0; } /* prefix_oli • returns ordered list item prefix */ static size_t prefix_oli(uint8_t *data, size_t size) { size_t i = 0; if (i < size && data[i] == ' ') i++; if (i < size && data[i] == ' ') i++; if (i < size && data[i] == ' ') i++; if (i >= size || data[i] < '0' || data[i] > '9') return 0; while (i < size && data[i] >= '0' && data[i] <= '9') i++; if (i + 1 >= size || data[i] != '.' || data[i + 1] != ' ') return 0; if (is_next_headerline(data + i, size - i)) return 0; return i + 2; } /* prefix_uli • returns ordered list item prefix */ static size_t prefix_uli(uint8_t *data, size_t size) { size_t i = 0; if (i < size && data[i] == ' ') i++; if (i < size && data[i] == ' ') i++; if (i < size && data[i] == ' ') i++; if (i + 1 >= size || (data[i] != '*' && data[i] != '+' && data[i] != '-') || data[i + 1] != ' ') return 0; if (is_next_headerline(data + i, size - i)) return 0; return i + 2; } /* parse_block • parsing of one block, returning next uint8_t to parse */ static void parse_block(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size); /* parse_blockquote • handles parsing of a blockquote fragment */ static size_t parse_blockquote(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size) { size_t beg, end = 0, pre, work_size = 0; uint8_t *work_data = 0; hoedown_buffer *out = 0; out = newbuf(doc, BUFFER_BLOCK); beg = 0; while (beg < size) { for (end = beg + 1; end < size && data[end - 1] != '\n'; end++); pre = prefix_quote(data + beg, end - beg); if (pre) beg += pre; /* skipping prefix */ /* empty line followed by non-quote line */ else if (is_empty(data + beg, end - beg) && (end >= size || (prefix_quote(data + end, size - end) == 0 && !is_empty(data + end, size - end)))) break; if (beg < end) { /* copy into the in-place working buffer */ /* hoedown_buffer_put(work, data + beg, end - beg); */ if (!work_data) work_data = data + beg; else if (data + beg != work_data + work_size) memmove(work_data + work_size, data + beg, end - beg); work_size += end - beg; } beg = end; } parse_block(out, doc, work_data, work_size); if (doc->md.blockquote) doc->md.blockquote(ob, out, &doc->data); popbuf(doc, BUFFER_BLOCK); return end; } static size_t parse_htmlblock(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size, int do_render); /* parse_blockquote • handles parsing of a regular paragraph */ static size_t parse_paragraph(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size) { hoedown_buffer work = { NULL, 0, 0, 0, NULL, NULL, NULL }; size_t i = 0, end = 0; int level = 0; work.data = data; while (i < size) { for (end = i + 1; end < size && data[end - 1] != '\n'; end++) /* empty */; if (is_empty(data + i, size - i)) break; if ((level = is_headerline(data + i, size - i)) != 0) break; if (is_atxheader(doc, data + i, size - i) || is_hrule(data + i, size - i) || prefix_quote(data + i, size - i)) { end = i; break; } i = end; } work.size = i; while (work.size && data[work.size - 1] == '\n') work.size--; if (!level) { hoedown_buffer *tmp = newbuf(doc, BUFFER_BLOCK); parse_inline(tmp, doc, work.data, work.size); if (doc->md.paragraph) doc->md.paragraph(ob, tmp, &doc->data); popbuf(doc, BUFFER_BLOCK); } else { hoedown_buffer *header_work; if (work.size) { size_t beg; i = work.size; work.size -= 1; while (work.size && data[work.size] != '\n') work.size -= 1; beg = work.size + 1; while (work.size && data[work.size - 1] == '\n') work.size -= 1; if (work.size > 0) { hoedown_buffer *tmp = newbuf(doc, BUFFER_BLOCK); parse_inline(tmp, doc, work.data, work.size); if (doc->md.paragraph) doc->md.paragraph(ob, tmp, &doc->data); popbuf(doc, BUFFER_BLOCK); work.data += beg; work.size = i - beg; } else work.size = i; } header_work = newbuf(doc, BUFFER_SPAN); parse_inline(header_work, doc, work.data, work.size); if (doc->md.header) doc->md.header(ob, header_work, (int)level, &doc->data); popbuf(doc, BUFFER_SPAN); } return end; } /* parse_fencedcode • handles parsing of a block-level code fragment */ static size_t parse_fencedcode(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size) { hoedown_buffer text = { 0, 0, 0, 0, NULL, NULL, NULL }; hoedown_buffer lang = { 0, 0, 0, 0, NULL, NULL, NULL }; size_t i = 0, text_start, line_start; size_t w, w2; size_t width, width2; uint8_t chr, chr2; /* parse codefence line */ while (i < size && data[i] != '\n') i++; w = parse_codefence(data, i, &lang, &width, &chr); if (!w) return 0; /* search for end */ i++; text_start = i; while ((line_start = i) < size) { while (i < size && data[i] != '\n') i++; w2 = is_codefence(data + line_start, i - line_start, &width2, &chr2); if (w == w2 && width == width2 && chr == chr2 && is_empty(data + (line_start+w), i - (line_start+w))) break; i++; } text.data = data + text_start; text.size = line_start - text_start; if (doc->md.blockcode) doc->md.blockcode(ob, text.size ? &text : NULL, lang.size ? &lang : NULL, &doc->data); return i; } static size_t parse_blockcode(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size) { size_t beg, end, pre; hoedown_buffer *work = 0; work = newbuf(doc, BUFFER_BLOCK); beg = 0; while (beg < size) { for (end = beg + 1; end < size && data[end - 1] != '\n'; end++) {}; pre = prefix_code(data + beg, end - beg); if (pre) beg += pre; /* skipping prefix */ else if (!is_empty(data + beg, end - beg)) /* non-empty non-prefixed line breaks the pre */ break; if (beg < end) { /* verbatim copy to the working buffer, escaping entities */ if (is_empty(data + beg, end - beg)) hoedown_buffer_putc(work, '\n'); else hoedown_buffer_put(work, data + beg, end - beg); } beg = end; } while (work->size && work->data[work->size - 1] == '\n') work->size -= 1; hoedown_buffer_putc(work, '\n'); if (doc->md.blockcode) doc->md.blockcode(ob, work, NULL, &doc->data); popbuf(doc, BUFFER_BLOCK); return beg; } /* parse_listitem • parsing of a single list item */ /* assuming initial prefix is already removed */ static size_t parse_listitem(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size, hoedown_list_flags *flags) { hoedown_buffer *work = 0, *inter = 0; size_t beg = 0, end, pre, sublist = 0, orgpre = 0, i; int in_empty = 0, has_inside_empty = 0, in_fence = 0; /* keeping track of the first indentation prefix */ while (orgpre < 3 && orgpre < size && data[orgpre] == ' ') orgpre++; beg = prefix_uli(data, size); if (!beg) beg = prefix_oli(data, size); if (!beg) return 0; /* skipping to the beginning of the following line */ end = beg; while (end < size && data[end - 1] != '\n') end++; /* getting working buffers */ work = newbuf(doc, BUFFER_SPAN); inter = newbuf(doc, BUFFER_SPAN); /* putting the first line into the working buffer */ hoedown_buffer_put(work, data + beg, end - beg); beg = end; /* process the following lines */ while (beg < size) { size_t has_next_uli = 0, has_next_oli = 0; end++; while (end < size && data[end - 1] != '\n') end++; /* process an empty line */ if (is_empty(data + beg, end - beg)) { in_empty = 1; beg = end; continue; } /* calculating the indentation */ i = 0; while (i < 4 && beg + i < end && data[beg + i] == ' ') i++; pre = i; if (doc->ext_flags & HOEDOWN_EXT_FENCED_CODE) { if (is_codefence(data + beg + i, end - beg - i, NULL, NULL)) in_fence = !in_fence; } /* Only check for new list items if we are **not** inside * a fenced code block */ if (!in_fence) { has_next_uli = prefix_uli(data + beg + i, end - beg - i); has_next_oli = prefix_oli(data + beg + i, end - beg - i); } /* checking for a new item */ if ((has_next_uli && !is_hrule(data + beg + i, end - beg - i)) || has_next_oli) { if (in_empty) has_inside_empty = 1; /* the following item must have the same (or less) indentation */ if (pre <= orgpre) { /* if the following item has different list type, we end this list */ if (in_empty && ( ((*flags & HOEDOWN_LIST_ORDERED) && has_next_uli) || (!(*flags & HOEDOWN_LIST_ORDERED) && has_next_oli))) *flags |= HOEDOWN_LI_END; break; } if (!sublist) sublist = work->size; } /* joining only indented stuff after empty lines; * note that now we only require 1 space of indentation * to continue a list */ else if (in_empty && pre == 0) { *flags |= HOEDOWN_LI_END; break; } if (in_empty) { hoedown_buffer_putc(work, '\n'); has_inside_empty = 1; in_empty = 0; } /* adding the line without prefix into the working buffer */ hoedown_buffer_put(work, data + beg + i, end - beg - i); beg = end; } /* render of li contents */ if (has_inside_empty) *flags |= HOEDOWN_LI_BLOCK; if (*flags & HOEDOWN_LI_BLOCK) { /* intermediate render of block li */ if (sublist && sublist < work->size) { parse_block(inter, doc, work->data, sublist); parse_block(inter, doc, work->data + sublist, work->size - sublist); } else parse_block(inter, doc, work->data, work->size); } else { /* intermediate render of inline li */ if (sublist && sublist < work->size) { parse_inline(inter, doc, work->data, sublist); parse_block(inter, doc, work->data + sublist, work->size - sublist); } else parse_inline(inter, doc, work->data, work->size); } /* render of li itself */ if (doc->md.listitem) doc->md.listitem(ob, inter, *flags, &doc->data); popbuf(doc, BUFFER_SPAN); popbuf(doc, BUFFER_SPAN); return beg; } /* parse_list • parsing ordered or unordered list block */ static size_t parse_list(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size, hoedown_list_flags flags) { hoedown_buffer *work = 0; size_t i = 0, j; work = newbuf(doc, BUFFER_BLOCK); while (i < size) { j = parse_listitem(work, doc, data + i, size - i, &flags); i += j; if (!j || (flags & HOEDOWN_LI_END)) break; } if (doc->md.list) doc->md.list(ob, work, flags, &doc->data); popbuf(doc, BUFFER_BLOCK); return i; } /* parse_atxheader • parsing of atx-style headers */ static size_t parse_atxheader(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size) { size_t level = 0; size_t i, end, skip; while (level < size && level < 6 && data[level] == '#') level++; for (i = level; i < size && data[i] == ' '; i++); for (end = i; end < size && data[end] != '\n'; end++); skip = end; while (end && data[end - 1] == '#') end--; while (end && data[end - 1] == ' ') end--; if (end > i) { hoedown_buffer *work = newbuf(doc, BUFFER_SPAN); parse_inline(work, doc, data + i, end - i); if (doc->md.header) doc->md.header(ob, work, (int)level, &doc->data); popbuf(doc, BUFFER_SPAN); } return skip; } /* parse_footnote_def • parse a single footnote definition */ static void parse_footnote_def(hoedown_buffer *ob, hoedown_document *doc, unsigned int num, uint8_t *data, size_t size) { hoedown_buffer *work = 0; work = newbuf(doc, BUFFER_SPAN); parse_block(work, doc, data, size); if (doc->md.footnote_def) doc->md.footnote_def(ob, work, num, &doc->data); popbuf(doc, BUFFER_SPAN); } /* parse_footnote_list • render the contents of the footnotes */ static void parse_footnote_list(hoedown_buffer *ob, hoedown_document *doc, struct footnote_list *footnotes) { hoedown_buffer *work = 0; struct footnote_item *item; struct footnote_ref *ref; if (footnotes->count == 0) return; work = newbuf(doc, BUFFER_BLOCK); item = footnotes->head; while (item) { ref = item->ref; parse_footnote_def(work, doc, ref->num, ref->contents->data, ref->contents->size); item = item->next; } if (doc->md.footnotes) doc->md.footnotes(ob, work, &doc->data); popbuf(doc, BUFFER_BLOCK); } /* htmlblock_is_end • check for end of HTML block : ( *)\n */ /* returns tag length on match, 0 otherwise */ /* assumes data starts with "<" */ static size_t htmlblock_is_end( const char *tag, size_t tag_len, hoedown_document *doc, uint8_t *data, size_t size) { size_t i = tag_len + 3, w; /* try to match the end tag */ /* note: we're not considering tags like "" which are still valid */ if (i > size || data[1] != '/' || strncasecmp((char *)data + 2, tag, tag_len) != 0 || data[tag_len + 2] != '>') return 0; /* rest of the line must be empty */ if ((w = is_empty(data + i, size - i)) == 0 && i < size) return 0; return i + w; } /* htmlblock_find_end • try to find HTML block ending tag */ /* returns the length on match, 0 otherwise */ static size_t htmlblock_find_end( const char *tag, size_t tag_len, hoedown_document *doc, uint8_t *data, size_t size) { size_t i = 0, w; while (1) { while (i < size && data[i] != '<') i++; if (i >= size) return 0; w = htmlblock_is_end(tag, tag_len, doc, data + i, size - i); if (w) return i + w; i++; } } /* htmlblock_find_end_strict • try to find end of HTML block in strict mode */ /* (it must be an unindented line, and have a blank line afterwads) */ /* returns the length on match, 0 otherwise */ static size_t htmlblock_find_end_strict( const char *tag, size_t tag_len, hoedown_document *doc, uint8_t *data, size_t size) { size_t i = 0, mark; while (1) { mark = i; while (i < size && data[i] != '\n') i++; if (i < size) i++; if (i == mark) return 0; if (data[mark] == ' ' && mark > 0) continue; mark += htmlblock_find_end(tag, tag_len, doc, data + mark, i - mark); if (mark == i && (is_empty(data + i, size - i) || i >= size)) break; } return i; } /* parse_htmlblock • parsing of inline HTML block */ static size_t parse_htmlblock(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size, int do_render) { hoedown_buffer work = { NULL, 0, 0, 0, NULL, NULL, NULL }; size_t i, j = 0, tag_len, tag_end; const char *curtag = NULL; work.data = data; /* identification of the opening tag */ if (size < 2 || data[0] != '<') return 0; i = 1; while (i < size && data[i] != '>' && data[i] != ' ') i++; if (i < size) curtag = hoedown_find_block_tag((char *)data + 1, (int)i - 1); /* handling of special cases */ if (!curtag) { /* HTML comment, laxist form */ if (size > 5 && data[1] == '!' && data[2] == '-' && data[3] == '-') { i = 5; while (i < size && !(data[i - 2] == '-' && data[i - 1] == '-' && data[i] == '>')) i++; i++; if (i < size) j = is_empty(data + i, size - i); if (j) { work.size = i + j; if (do_render && doc->md.blockhtml) doc->md.blockhtml(ob, &work, &doc->data); return work.size; } } /* HR, which is the only self-closing block tag considered */ if (size > 4 && (data[1] == 'h' || data[1] == 'H') && (data[2] == 'r' || data[2] == 'R')) { i = 3; while (i < size && data[i] != '>') i++; if (i + 1 < size) { i++; j = is_empty(data + i, size - i); if (j) { work.size = i + j; if (do_render && doc->md.blockhtml) doc->md.blockhtml(ob, &work, &doc->data); return work.size; } } } /* no special case recognised */ return 0; } /* looking for a matching closing tag in strict mode */ tag_len = strlen(curtag); tag_end = htmlblock_find_end_strict(curtag, tag_len, doc, data, size); /* if not found, trying a second pass looking for indented match */ /* but not if tag is "ins" or "del" (following original Markdown.pl) */ if (!tag_end && strcmp(curtag, "ins") != 0 && strcmp(curtag, "del") != 0) tag_end = htmlblock_find_end(curtag, tag_len, doc, data, size); if (!tag_end) return 0; /* the end of the block has been found */ work.size = tag_end; if (do_render && doc->md.blockhtml) doc->md.blockhtml(ob, &work, &doc->data); return tag_end; } static void parse_table_row( hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size, size_t columns, hoedown_table_flags *col_data, hoedown_table_flags header_flag) { size_t i = 0, col, len; hoedown_buffer *row_work = 0; if (!doc->md.table_cell || !doc->md.table_row) return; row_work = newbuf(doc, BUFFER_SPAN); if (i < size && data[i] == '|') i++; for (col = 0; col < columns && i < size; ++col) { size_t cell_start, cell_end; hoedown_buffer *cell_work; cell_work = newbuf(doc, BUFFER_SPAN); while (i < size && _isspace(data[i])) i++; cell_start = i; len = find_emph_char(data + i, size - i, '|'); /* Two possibilities for len == 0: 1) No more pipe char found in the current line. 2) The next pipe is right after the current one, i.e. empty cell. For case 1, we skip to the end of line; for case 2 we just continue. */ if (len == 0 && i < size && data[i] != '|') len = size - i; i += len; cell_end = i - 1; while (cell_end > cell_start && _isspace(data[cell_end])) cell_end--; parse_inline(cell_work, doc, data + cell_start, 1 + cell_end - cell_start); doc->md.table_cell(row_work, cell_work, col_data[col] | header_flag, &doc->data); popbuf(doc, BUFFER_SPAN); i++; } for (; col < columns; ++col) { hoedown_buffer empty_cell = { 0, 0, 0, 0, NULL, NULL, NULL }; doc->md.table_cell(row_work, &empty_cell, col_data[col] | header_flag, &doc->data); } doc->md.table_row(ob, row_work, &doc->data); popbuf(doc, BUFFER_SPAN); } static size_t parse_table_header( hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size, size_t *columns, hoedown_table_flags **column_data) { int pipes; size_t i = 0, col, header_end, under_end; pipes = 0; while (i < size && data[i] != '\n') if (data[i++] == '|') pipes++; if (i == size || pipes == 0) return 0; header_end = i; while (header_end > 0 && _isspace(data[header_end - 1])) header_end--; if (data[0] == '|') pipes--; if (header_end && data[header_end - 1] == '|') pipes--; if (pipes < 0) return 0; *columns = pipes + 1; *column_data = hoedown_calloc(*columns, sizeof(hoedown_table_flags)); /* Parse the header underline */ i++; if (i < size && data[i] == '|') i++; under_end = i; while (under_end < size && data[under_end] != '\n') under_end++; for (col = 0; col < *columns && i < under_end; ++col) { size_t dashes = 0; while (i < under_end && data[i] == ' ') i++; if (data[i] == ':') { i++; (*column_data)[col] |= HOEDOWN_TABLE_ALIGN_LEFT; dashes++; } while (i < under_end && data[i] == '-') { i++; dashes++; } if (i < under_end && data[i] == ':') { i++; (*column_data)[col] |= HOEDOWN_TABLE_ALIGN_RIGHT; dashes++; } while (i < under_end && data[i] == ' ') i++; if (i < under_end && data[i] != '|' && data[i] != '+') break; if (dashes < 3) break; i++; } if (col < *columns) return 0; parse_table_row( ob, doc, data, header_end, *columns, *column_data, HOEDOWN_TABLE_HEADER ); return under_end + 1; } static size_t parse_table( hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size) { size_t i; hoedown_buffer *work = 0; hoedown_buffer *header_work = 0; hoedown_buffer *body_work = 0; size_t columns; hoedown_table_flags *col_data = NULL; work = newbuf(doc, BUFFER_BLOCK); header_work = newbuf(doc, BUFFER_SPAN); body_work = newbuf(doc, BUFFER_BLOCK); i = parse_table_header(header_work, doc, data, size, &columns, &col_data); if (i > 0) { while (i < size) { size_t row_start; int pipes = 0; row_start = i; while (i < size && data[i] != '\n') if (data[i++] == '|') pipes++; if (pipes == 0 || i == size) { i = row_start; break; } parse_table_row( body_work, doc, data + row_start, i - row_start, columns, col_data, 0 ); i++; } if (doc->md.table_header) doc->md.table_header(work, header_work, &doc->data); if (doc->md.table_body) doc->md.table_body(work, body_work, &doc->data); if (doc->md.table) doc->md.table(ob, work, &doc->data); } free(col_data); popbuf(doc, BUFFER_SPAN); popbuf(doc, BUFFER_BLOCK); popbuf(doc, BUFFER_BLOCK); return i; } /* parse_block • parsing of one block, returning next uint8_t to parse */ static void parse_block(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size) { size_t beg, end, i; uint8_t *txt_data; beg = 0; if (doc->work_bufs[BUFFER_SPAN].size + doc->work_bufs[BUFFER_BLOCK].size > doc->max_nesting) return; while (beg < size) { txt_data = data + beg; end = size - beg; if (is_atxheader(doc, txt_data, end)) beg += parse_atxheader(ob, doc, txt_data, end); else if (data[beg] == '<' && doc->md.blockhtml && (i = parse_htmlblock(ob, doc, txt_data, end, 1)) != 0) beg += i; else if ((i = is_empty(txt_data, end)) != 0) beg += i; else if (is_hrule(txt_data, end)) { if (doc->md.hrule) doc->md.hrule(ob, &doc->data); while (beg < size && data[beg] != '\n') beg++; beg++; } else if ((doc->ext_flags & HOEDOWN_EXT_FENCED_CODE) != 0 && (i = parse_fencedcode(ob, doc, txt_data, end)) != 0) beg += i; else if ((doc->ext_flags & HOEDOWN_EXT_TABLES) != 0 && (i = parse_table(ob, doc, txt_data, end)) != 0) beg += i; else if (prefix_quote(txt_data, end)) beg += parse_blockquote(ob, doc, txt_data, end); else if (!(doc->ext_flags & HOEDOWN_EXT_DISABLE_INDENTED_CODE) && prefix_code(txt_data, end)) beg += parse_blockcode(ob, doc, txt_data, end); else if (prefix_uli(txt_data, end)) beg += parse_list(ob, doc, txt_data, end, 0); else if (prefix_oli(txt_data, end)) beg += parse_list(ob, doc, txt_data, end, HOEDOWN_LIST_ORDERED); else beg += parse_paragraph(ob, doc, txt_data, end); } } /********************* * REFERENCE PARSING * *********************/ /* is_footnote • returns whether a line is a footnote definition or not */ static int is_footnote(const uint8_t *data, size_t beg, size_t end, size_t *last, struct footnote_list *list) { size_t i = 0; hoedown_buffer *contents = 0; size_t ind = 0; int in_empty = 0; size_t start = 0; size_t id_offset, id_end; /* up to 3 optional leading spaces */ if (beg + 3 >= end) return 0; if (data[beg] == ' ') { i = 1; if (data[beg + 1] == ' ') { i = 2; if (data[beg + 2] == ' ') { i = 3; if (data[beg + 3] == ' ') return 0; } } } i += beg; /* id part: caret followed by anything between brackets */ if (data[i] != '[') return 0; i++; if (i >= end || data[i] != '^') return 0; i++; id_offset = i; while (i < end && data[i] != '\n' && data[i] != '\r' && data[i] != ']') i++; if (i >= end || data[i] != ']') return 0; id_end = i; /* spacer: colon (space | tab)* newline? (space | tab)* */ i++; if (i >= end || data[i] != ':') return 0; i++; /* getting content buffer */ contents = hoedown_buffer_new(64); start = i; /* process lines similar to a list item */ while (i < end) { while (i < end && data[i] != '\n' && data[i] != '\r') i++; /* process an empty line */ if (is_empty(data + start, i - start)) { in_empty = 1; if (i < end && (data[i] == '\n' || data[i] == '\r')) { i++; if (i < end && data[i] == '\n' && data[i - 1] == '\r') i++; } start = i; continue; } /* calculating the indentation */ ind = 0; while (ind < 4 && start + ind < end && data[start + ind] == ' ') ind++; /* joining only indented stuff after empty lines; * note that now we only require 1 space of indentation * to continue, just like lists */ if (ind == 0) { if (start == id_end + 2 && data[start] == '\t') {} else break; } else if (in_empty) { hoedown_buffer_putc(contents, '\n'); } in_empty = 0; /* adding the line into the content buffer */ hoedown_buffer_put(contents, data + start + ind, i - start - ind); /* add carriage return */ if (i < end) { hoedown_buffer_putc(contents, '\n'); if (i < end && (data[i] == '\n' || data[i] == '\r')) { i++; if (i < end && data[i] == '\n' && data[i - 1] == '\r') i++; } } start = i; } if (last) *last = start; if (list) { struct footnote_ref *ref; ref = create_footnote_ref(list, data + id_offset, id_end - id_offset); if (!ref) return 0; if (!add_footnote_ref(list, ref)) { free_footnote_ref(ref); return 0; } ref->contents = contents; } return 1; } /* is_ref • returns whether a line is a reference or not */ static int is_ref(const uint8_t *data, size_t beg, size_t end, size_t *last, struct link_ref **refs) { /* int n; */ size_t i = 0; size_t id_offset, id_end; size_t link_offset, link_end; size_t title_offset, title_end; size_t line_end; /* up to 3 optional leading spaces */ if (beg + 3 >= end) return 0; if (data[beg] == ' ') { i = 1; if (data[beg + 1] == ' ') { i = 2; if (data[beg + 2] == ' ') { i = 3; if (data[beg + 3] == ' ') return 0; } } } i += beg; /* id part: anything but a newline between brackets */ if (data[i] != '[') return 0; i++; id_offset = i; while (i < end && data[i] != '\n' && data[i] != '\r' && data[i] != ']') i++; if (i >= end || data[i] != ']') return 0; id_end = i; /* spacer: colon (space | tab)* newline? (space | tab)* */ i++; if (i >= end || data[i] != ':') return 0; i++; while (i < end && data[i] == ' ') i++; if (i < end && (data[i] == '\n' || data[i] == '\r')) { i++; if (i < end && data[i] == '\r' && data[i - 1] == '\n') i++; } while (i < end && data[i] == ' ') i++; if (i >= end) return 0; /* link: spacing-free sequence, optionally between angle brackets */ if (data[i] == '<') i++; link_offset = i; while (i < end && data[i] != ' ' && data[i] != '\n' && data[i] != '\r') i++; if (data[i - 1] == '>') link_end = i - 1; else link_end = i; /* optional spacer: (space | tab)* (newline | '\'' | '"' | '(' ) */ while (i < end && data[i] == ' ') i++; if (i < end && data[i] != '\n' && data[i] != '\r' && data[i] != '\'' && data[i] != '"' && data[i] != '(') return 0; line_end = 0; /* computing end-of-line */ if (i >= end || data[i] == '\r' || data[i] == '\n') line_end = i; if (i + 1 < end && data[i] == '\n' && data[i + 1] == '\r') line_end = i + 1; /* optional (space|tab)* spacer after a newline */ if (line_end) { i = line_end + 1; while (i < end && data[i] == ' ') i++; } /* optional title: any non-newline sequence enclosed in '"() alone on its line */ title_offset = title_end = 0; if (i + 1 < end && (data[i] == '\'' || data[i] == '"' || data[i] == '(')) { i++; title_offset = i; /* looking for EOL */ while (i < end && data[i] != '\n' && data[i] != '\r') i++; if (i + 1 < end && data[i] == '\n' && data[i + 1] == '\r') title_end = i + 1; else title_end = i; /* stepping back */ i -= 1; while (i > title_offset && data[i] == ' ') i -= 1; if (i > title_offset && (data[i] == '\'' || data[i] == '"' || data[i] == ')')) { line_end = title_end; title_end = i; } } if (!line_end || link_end == link_offset) return 0; /* garbage after the link empty link */ /* a valid ref has been found, filling-in return structures */ if (last) *last = line_end; if (refs) { struct link_ref *ref; ref = add_link_ref(refs, data + id_offset, id_end - id_offset); if (!ref) return 0; ref->link = hoedown_buffer_new(link_end - link_offset); hoedown_buffer_put(ref->link, data + link_offset, link_end - link_offset); if (title_end > title_offset) { ref->title = hoedown_buffer_new(title_end - title_offset); hoedown_buffer_put(ref->title, data + title_offset, title_end - title_offset); } } return 1; } static void expand_tabs(hoedown_buffer *ob, const uint8_t *line, size_t size) { /* This code makes two assumptions: * - Input is valid UTF-8. (Any byte with top two bits 10 is skipped, * whether or not it is a valid UTF-8 continuation byte.) * - Input contains no combining characters. (Combining characters * should be skipped but are not.) */ size_t i = 0, tab = 0; while (i < size) { size_t org = i; while (i < size && line[i] != '\t') { /* ignore UTF-8 continuation bytes */ if ((line[i] & 0xc0) != 0x80) tab++; i++; } if (i > org) hoedown_buffer_put(ob, line + org, i - org); if (i >= size) break; do { hoedown_buffer_putc(ob, ' '); tab++; } while (tab % 4); i++; } } /********************** * EXPORTED FUNCTIONS * **********************/ hoedown_document * hoedown_document_new( const hoedown_renderer *renderer, hoedown_extensions extensions, size_t max_nesting) { hoedown_document *doc = NULL; assert(max_nesting > 0 && renderer); doc = hoedown_malloc(sizeof(hoedown_document)); memcpy(&doc->md, renderer, sizeof(hoedown_renderer)); doc->data.opaque = renderer->opaque; hoedown_stack_init(&doc->work_bufs[BUFFER_BLOCK], 4); hoedown_stack_init(&doc->work_bufs[BUFFER_SPAN], 8); memset(doc->active_char, 0x0, 256); if (extensions & HOEDOWN_EXT_UNDERLINE && doc->md.underline) { doc->active_char['_'] = MD_CHAR_EMPHASIS; } if (doc->md.emphasis || doc->md.double_emphasis || doc->md.triple_emphasis) { doc->active_char['*'] = MD_CHAR_EMPHASIS; doc->active_char['_'] = MD_CHAR_EMPHASIS; if (extensions & HOEDOWN_EXT_STRIKETHROUGH) doc->active_char['~'] = MD_CHAR_EMPHASIS; if (extensions & HOEDOWN_EXT_HIGHLIGHT) doc->active_char['='] = MD_CHAR_EMPHASIS; } if (doc->md.codespan) doc->active_char['`'] = MD_CHAR_CODESPAN; if (doc->md.linebreak) doc->active_char['\n'] = MD_CHAR_LINEBREAK; if (doc->md.image || doc->md.link || doc->md.footnotes || doc->md.footnote_ref) { doc->active_char['['] = MD_CHAR_LINK; doc->active_char['!'] = MD_CHAR_IMAGE; } doc->active_char['<'] = MD_CHAR_LANGLE; doc->active_char['\\'] = MD_CHAR_ESCAPE; doc->active_char['&'] = MD_CHAR_ENTITY; if (extensions & HOEDOWN_EXT_AUTOLINK) { doc->active_char[':'] = MD_CHAR_AUTOLINK_URL; doc->active_char['@'] = MD_CHAR_AUTOLINK_EMAIL; doc->active_char['w'] = MD_CHAR_AUTOLINK_WWW; } if (extensions & HOEDOWN_EXT_SUPERSCRIPT) doc->active_char['^'] = MD_CHAR_SUPERSCRIPT; if (extensions & HOEDOWN_EXT_QUOTE) doc->active_char['"'] = MD_CHAR_QUOTE; if (extensions & HOEDOWN_EXT_MATH) doc->active_char['$'] = MD_CHAR_MATH; /* Extension data */ doc->ext_flags = extensions; doc->max_nesting = max_nesting; doc->in_link_body = 0; return doc; } void hoedown_document_render(hoedown_document *doc, hoedown_buffer *ob, const uint8_t *data, size_t size) { static const uint8_t UTF8_BOM[] = {0xEF, 0xBB, 0xBF}; hoedown_buffer *text; size_t beg, end; int footnotes_enabled; text = hoedown_buffer_new(64); /* Preallocate enough space for our buffer to avoid expanding while copying */ hoedown_buffer_grow(text, size); /* reset the references table */ memset(&doc->refs, 0x0, REF_TABLE_SIZE * sizeof(void *)); footnotes_enabled = doc->ext_flags & HOEDOWN_EXT_FOOTNOTES; /* reset the footnotes lists */ if (footnotes_enabled) { memset(&doc->footnotes_found, 0x0, sizeof(doc->footnotes_found)); memset(&doc->footnotes_used, 0x0, sizeof(doc->footnotes_used)); } /* first pass: looking for references, copying everything else */ beg = 0; /* Skip a possible UTF-8 BOM, even though the Unicode standard * discourages having these in UTF-8 documents */ if (size >= 3 && memcmp(data, UTF8_BOM, 3) == 0) beg += 3; while (beg < size) /* iterating over lines */ if (footnotes_enabled && is_footnote(data, beg, size, &end, &doc->footnotes_found)) beg = end; else if (is_ref(data, beg, size, &end, doc->refs)) beg = end; else { /* skipping to the next line */ end = beg; while (end < size && data[end] != '\n' && data[end] != '\r') end++; /* adding the line body if present */ if (end > beg) expand_tabs(text, data + beg, end - beg); while (end < size && (data[end] == '\n' || data[end] == '\r')) { /* add one \n per newline */ if (data[end] == '\n' || (end + 1 < size && data[end + 1] != '\n')) hoedown_buffer_putc(text, '\n'); end++; } beg = end; } /* pre-grow the output buffer to minimize allocations */ hoedown_buffer_grow(ob, text->size + (text->size >> 1)); /* second pass: actual rendering */ if (doc->md.doc_header) doc->md.doc_header(ob, 0, &doc->data); if (text->size) { /* adding a final newline if not already present */ if (text->data[text->size - 1] != '\n' && text->data[text->size - 1] != '\r') hoedown_buffer_putc(text, '\n'); parse_block(ob, doc, text->data, text->size); } /* footnotes */ if (footnotes_enabled) parse_footnote_list(ob, doc, &doc->footnotes_used); if (doc->md.doc_footer) doc->md.doc_footer(ob, 0, &doc->data); /* clean-up */ hoedown_buffer_free(text); free_link_refs(doc->refs); if (footnotes_enabled) { free_footnote_list(&doc->footnotes_found, 1); free_footnote_list(&doc->footnotes_used, 0); } assert(doc->work_bufs[BUFFER_SPAN].size == 0); assert(doc->work_bufs[BUFFER_BLOCK].size == 0); } void hoedown_document_render_inline(hoedown_document *doc, hoedown_buffer *ob, const uint8_t *data, size_t size) { size_t i = 0, mark; hoedown_buffer *text = hoedown_buffer_new(64); /* reset the references table */ memset(&doc->refs, 0x0, REF_TABLE_SIZE * sizeof(void *)); /* first pass: expand tabs and process newlines */ hoedown_buffer_grow(text, size); while (1) { mark = i; while (i < size && data[i] != '\n' && data[i] != '\r') i++; expand_tabs(text, data + mark, i - mark); if (i >= size) break; while (i < size && (data[i] == '\n' || data[i] == '\r')) { /* add one \n per newline */ if (data[i] == '\n' || (i + 1 < size && data[i + 1] != '\n')) hoedown_buffer_putc(text, '\n'); i++; } } /* second pass: actual rendering */ hoedown_buffer_grow(ob, text->size + (text->size >> 1)); if (doc->md.doc_header) doc->md.doc_header(ob, 1, &doc->data); parse_inline(ob, doc, text->data, text->size); if (doc->md.doc_footer) doc->md.doc_footer(ob, 1, &doc->data); /* clean-up */ hoedown_buffer_free(text); assert(doc->work_bufs[BUFFER_SPAN].size == 0); assert(doc->work_bufs[BUFFER_BLOCK].size == 0); } void hoedown_document_free(hoedown_document *doc) { size_t i; for (i = 0; i < (size_t)doc->work_bufs[BUFFER_SPAN].asize; ++i) hoedown_buffer_free(doc->work_bufs[BUFFER_SPAN].item[i]); for (i = 0; i < (size_t)doc->work_bufs[BUFFER_BLOCK].asize; ++i) hoedown_buffer_free(doc->work_bufs[BUFFER_BLOCK].item[i]); hoedown_stack_uninit(&doc->work_bufs[BUFFER_SPAN]); hoedown_stack_uninit(&doc->work_bufs[BUFFER_BLOCK]); free(doc); } ================================================ FILE: 3rdpart/hoedown/src/document.h ================================================ /* document.h - generic markdown parser */ #ifndef HOEDOWN_DOCUMENT_H #define HOEDOWN_DOCUMENT_H #include "buffer.h" #include "autolink.h" #ifdef __cplusplus extern "C" { #endif /************* * CONSTANTS * *************/ typedef enum hoedown_extensions { /* block-level extensions */ HOEDOWN_EXT_TABLES = (1 << 0), HOEDOWN_EXT_FENCED_CODE = (1 << 1), HOEDOWN_EXT_FOOTNOTES = (1 << 2), /* span-level extensions */ HOEDOWN_EXT_AUTOLINK = (1 << 3), HOEDOWN_EXT_STRIKETHROUGH = (1 << 4), HOEDOWN_EXT_UNDERLINE = (1 << 5), HOEDOWN_EXT_HIGHLIGHT = (1 << 6), HOEDOWN_EXT_QUOTE = (1 << 7), HOEDOWN_EXT_SUPERSCRIPT = (1 << 8), HOEDOWN_EXT_MATH = (1 << 9), /* other flags */ HOEDOWN_EXT_NO_INTRA_EMPHASIS = (1 << 11), HOEDOWN_EXT_SPACE_HEADERS = (1 << 12), HOEDOWN_EXT_MATH_EXPLICIT = (1 << 13), /* negative flags */ HOEDOWN_EXT_DISABLE_INDENTED_CODE = (1 << 14) } hoedown_extensions; #define HOEDOWN_EXT_BLOCK (\ HOEDOWN_EXT_TABLES |\ HOEDOWN_EXT_FENCED_CODE |\ HOEDOWN_EXT_FOOTNOTES ) #define HOEDOWN_EXT_SPAN (\ HOEDOWN_EXT_AUTOLINK |\ HOEDOWN_EXT_STRIKETHROUGH |\ HOEDOWN_EXT_UNDERLINE |\ HOEDOWN_EXT_HIGHLIGHT |\ HOEDOWN_EXT_QUOTE |\ HOEDOWN_EXT_SUPERSCRIPT |\ HOEDOWN_EXT_MATH ) #define HOEDOWN_EXT_FLAGS (\ HOEDOWN_EXT_NO_INTRA_EMPHASIS |\ HOEDOWN_EXT_SPACE_HEADERS |\ HOEDOWN_EXT_MATH_EXPLICIT ) #define HOEDOWN_EXT_NEGATIVE (\ HOEDOWN_EXT_DISABLE_INDENTED_CODE ) typedef enum hoedown_list_flags { HOEDOWN_LIST_ORDERED = (1 << 0), HOEDOWN_LI_BLOCK = (1 << 1) /*
  • containing block data */ } hoedown_list_flags; typedef enum hoedown_table_flags { HOEDOWN_TABLE_ALIGN_LEFT = 1, HOEDOWN_TABLE_ALIGN_RIGHT = 2, HOEDOWN_TABLE_ALIGN_CENTER = 3, HOEDOWN_TABLE_ALIGNMASK = 3, HOEDOWN_TABLE_HEADER = 4 } hoedown_table_flags; typedef enum hoedown_autolink_type { HOEDOWN_AUTOLINK_NONE, /* used internally when it is not an autolink*/ HOEDOWN_AUTOLINK_NORMAL, /* normal http/http/ftp/mailto/etc link */ HOEDOWN_AUTOLINK_EMAIL /* e-mail link without explit mailto: */ } hoedown_autolink_type; /********* * TYPES * *********/ struct hoedown_document; typedef struct hoedown_document hoedown_document; struct hoedown_renderer_data { void *opaque; }; typedef struct hoedown_renderer_data hoedown_renderer_data; /* hoedown_renderer - functions for rendering parsed data */ struct hoedown_renderer { /* state object */ void *opaque; /* block level callbacks - NULL skips the block */ void (*blockcode)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_buffer *lang, const hoedown_renderer_data *data); void (*blockquote)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); void (*header)(hoedown_buffer *ob, const hoedown_buffer *content, int level, const hoedown_renderer_data *data); void (*hrule)(hoedown_buffer *ob, const hoedown_renderer_data *data); void (*list)(hoedown_buffer *ob, const hoedown_buffer *content, hoedown_list_flags flags, const hoedown_renderer_data *data); void (*listitem)(hoedown_buffer *ob, const hoedown_buffer *content, hoedown_list_flags flags, const hoedown_renderer_data *data); void (*paragraph)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); void (*table)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); void (*table_header)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); void (*table_body)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); void (*table_row)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); void (*table_cell)(hoedown_buffer *ob, const hoedown_buffer *content, hoedown_table_flags flags, const hoedown_renderer_data *data); void (*footnotes)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); void (*footnote_def)(hoedown_buffer *ob, const hoedown_buffer *content, unsigned int num, const hoedown_renderer_data *data); void (*blockhtml)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data); /* span level callbacks - NULL or return 0 prints the span verbatim */ int (*autolink)(hoedown_buffer *ob, const hoedown_buffer *link, hoedown_autolink_type type, const hoedown_renderer_data *data); int (*codespan)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data); int (*double_emphasis)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); int (*emphasis)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); int (*underline)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); int (*highlight)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); int (*quote)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); int (*image)(hoedown_buffer *ob, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_buffer *alt, const hoedown_renderer_data *data); int (*linebreak)(hoedown_buffer *ob, const hoedown_renderer_data *data); int (*link)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_renderer_data *data); int (*triple_emphasis)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); int (*strikethrough)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); int (*superscript)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); int (*footnote_ref)(hoedown_buffer *ob, unsigned int num, const hoedown_renderer_data *data); int (*math)(hoedown_buffer *ob, const hoedown_buffer *text, int displaymode, const hoedown_renderer_data *data); int (*raw_html)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data); /* low level callbacks - NULL copies input directly into the output */ void (*entity)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data); void (*normal_text)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data); /* miscellaneous callbacks */ void (*doc_header)(hoedown_buffer *ob, int inline_render, const hoedown_renderer_data *data); void (*doc_footer)(hoedown_buffer *ob, int inline_render, const hoedown_renderer_data *data); }; typedef struct hoedown_renderer hoedown_renderer; /************* * FUNCTIONS * *************/ /* hoedown_document_new: allocate a new document processor instance */ hoedown_document *hoedown_document_new( const hoedown_renderer *renderer, hoedown_extensions extensions, size_t max_nesting ) __attribute__ ((malloc)); /* hoedown_document_render: render regular Markdown using the document processor */ void hoedown_document_render(hoedown_document *doc, hoedown_buffer *ob, const uint8_t *data, size_t size); /* hoedown_document_render_inline: render inline Markdown using the document processor */ void hoedown_document_render_inline(hoedown_document *doc, hoedown_buffer *ob, const uint8_t *data, size_t size); /* hoedown_document_free: deallocate a document processor instance */ void hoedown_document_free(hoedown_document *doc); #ifdef __cplusplus } #endif #endif /** HOEDOWN_DOCUMENT_H **/ ================================================ FILE: 3rdpart/hoedown/src/escape.c ================================================ #include "escape.h" #include #include #include #define likely(x) __builtin_expect((x),1) #define unlikely(x) __builtin_expect((x),0) /* * The following characters will not be escaped: * * -_.+!*'(),%#@?=;:/,+&$ alphanum * * Note that this character set is the addition of: * * - The characters which are safe to be in an URL * - The characters which are *not* safe to be in * an URL because they are RESERVED characters. * * We assume (lazily) that any RESERVED char that * appears inside an URL is actually meant to * have its native function (i.e. as an URL * component/separator) and hence needs no escaping. * * There are two exceptions: the chacters & (amp) * and ' (single quote) do not appear in the table. * They are meant to appear in the URL as components, * yet they require special HTML-entity escaping * to generate valid HTML markup. * * All other characters will be escaped to %XX. * */ static const uint8_t HREF_SAFE[UINT8_MAX+1] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; void hoedown_escape_href(hoedown_buffer *ob, const uint8_t *data, size_t size) { static const char hex_chars[] = "0123456789ABCDEF"; size_t i = 0, mark; char hex_str[3]; hex_str[0] = '%'; while (i < size) { mark = i; while (i < size && HREF_SAFE[data[i]]) i++; /* Optimization for cases where there's nothing to escape */ if (mark == 0 && i >= size) { hoedown_buffer_put(ob, data, size); return; } if (likely(i > mark)) { hoedown_buffer_put(ob, data + mark, i - mark); } /* escaping */ if (i >= size) break; switch (data[i]) { /* amp appears all the time in URLs, but needs * HTML-entity escaping to be inside an href */ case '&': HOEDOWN_BUFPUTSL(ob, "&"); break; /* the single quote is a valid URL character * according to the standard; it needs HTML * entity escaping too */ case '\'': HOEDOWN_BUFPUTSL(ob, "'"); break; /* the space can be escaped to %20 or a plus * sign. we're going with the generic escape * for now. the plus thing is more commonly seen * when building GET strings */ #if 0 case ' ': hoedown_buffer_putc(ob, '+'); break; #endif /* every other character goes with a %XX escaping */ default: hex_str[1] = hex_chars[(data[i] >> 4) & 0xF]; hex_str[2] = hex_chars[data[i] & 0xF]; hoedown_buffer_put(ob, (uint8_t *)hex_str, 3); } i++; } } /** * According to the OWASP rules: * * & --> & * < --> < * > --> > * " --> " * ' --> ' ' is not recommended * / --> / forward slash is included as it helps end an HTML entity * */ static const uint8_t HTML_ESCAPE_TABLE[UINT8_MAX+1] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; static const char *HTML_ESCAPES[] = { "", """, "&", "'", "/", "<", ">" }; void hoedown_escape_html(hoedown_buffer *ob, const uint8_t *data, size_t size, int secure) { size_t i = 0, mark; while (1) { mark = i; while (i < size && HTML_ESCAPE_TABLE[data[i]] == 0) i++; /* Optimization for cases where there's nothing to escape */ if (mark == 0 && i >= size) { hoedown_buffer_put(ob, data, size); return; } if (likely(i > mark)) hoedown_buffer_put(ob, data + mark, i - mark); if (i >= size) break; /* The forward slash is only escaped in secure mode */ if (!secure && data[i] == '/') { hoedown_buffer_putc(ob, '/'); } else { hoedown_buffer_puts(ob, HTML_ESCAPES[HTML_ESCAPE_TABLE[data[i]]]); } i++; } } ================================================ FILE: 3rdpart/hoedown/src/escape.h ================================================ /* escape.h - escape utilities */ #ifndef HOEDOWN_ESCAPE_H #define HOEDOWN_ESCAPE_H #include "buffer.h" #ifdef __cplusplus extern "C" { #endif /************* * FUNCTIONS * *************/ /* hoedown_escape_href: escape (part of) a URL inside HTML */ void hoedown_escape_href(hoedown_buffer *ob, const uint8_t *data, size_t size); /* hoedown_escape_html: escape HTML */ void hoedown_escape_html(hoedown_buffer *ob, const uint8_t *data, size_t size, int secure); #ifdef __cplusplus } #endif #endif /** HOEDOWN_ESCAPE_H **/ ================================================ FILE: 3rdpart/hoedown/src/hodedown_version.c ================================================ #include "version.h" void hoedown_version(int *major, int *minor, int *revision) { *major = HOEDOWN_VERSION_MAJOR; *minor = HOEDOWN_VERSION_MINOR; *revision = HOEDOWN_VERSION_REVISION; } ================================================ FILE: 3rdpart/hoedown/src/html.c ================================================ #include "html.h" #include #include #include #include #include "escape.h" #define UNUSED(p) ((void)p) #define USE_XHTML(opt) (opt->flags & HOEDOWN_HTML_USE_XHTML) hoedown_html_tag hoedown_html_is_tag(const uint8_t *data, size_t size, const char *tagname) { size_t i; int closed = 0; if (size < 3 || data[0] != '<') return HOEDOWN_HTML_TAG_NONE; i = 1; if (data[i] == '/') { closed = 1; i++; } for (; i < size; ++i, ++tagname) { if (*tagname == 0) break; if (data[i] != *tagname) return HOEDOWN_HTML_TAG_NONE; } if (i == size) return HOEDOWN_HTML_TAG_NONE; if (isspace(data[i]) || data[i] == '>') return closed ? HOEDOWN_HTML_TAG_CLOSE : HOEDOWN_HTML_TAG_OPEN; return HOEDOWN_HTML_TAG_NONE; } static void escape_html(hoedown_buffer *ob, const uint8_t *source, size_t length) { hoedown_escape_html(ob, source, length, 0); } static void escape_href(hoedown_buffer *ob, const uint8_t *source, size_t length) { hoedown_escape_href(ob, source, length); } /******************** * GENERIC RENDERER * ********************/ static int rndr_autolink(hoedown_buffer *ob, const hoedown_buffer *link, hoedown_autolink_type type, const hoedown_renderer_data *data) { hoedown_html_renderer_state *state = data->opaque; if (!link || !link->size) return 0; HOEDOWN_BUFPUTSL(ob, "data, link->size); if (state->link_attributes) { hoedown_buffer_putc(ob, '\"'); state->link_attributes(ob, link, data); hoedown_buffer_putc(ob, '>'); } else { HOEDOWN_BUFPUTSL(ob, "\">"); } /* * Pretty printing: if we get an email address as * an actual URI, e.g. `mailto:foo@bar.com`, we don't * want to print the `mailto:` prefix */ if (hoedown_buffer_prefix(link, "mailto:") == 0) { escape_html(ob, link->data + 7, link->size - 7); } else { escape_html(ob, link->data, link->size); } HOEDOWN_BUFPUTSL(ob, ""); return 1; } static void rndr_blockcode(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_buffer *lang, const hoedown_renderer_data *data) { UNUSED(data); if (ob->size) hoedown_buffer_putc(ob, '\n'); if (lang) { HOEDOWN_BUFPUTSL(ob, "
    data, lang->size);
    		HOEDOWN_BUFPUTSL(ob, "\">");
    	} else {
    		HOEDOWN_BUFPUTSL(ob, "
    ");
    	}
    
    	if (text)
    		escape_html(ob, text->data, text->size);
    
    	HOEDOWN_BUFPUTSL(ob, "
    \n"); } static void rndr_blockquote(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) { UNUSED(data); if (ob->size) hoedown_buffer_putc(ob, '\n'); HOEDOWN_BUFPUTSL(ob, "
    \n"); if (content) hoedown_buffer_put(ob, content->data, content->size); HOEDOWN_BUFPUTSL(ob, "
    \n"); } static int rndr_codespan(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data) { UNUSED(data); HOEDOWN_BUFPUTSL(ob, ""); if (text) escape_html(ob, text->data, text->size); HOEDOWN_BUFPUTSL(ob, ""); return 1; } static int rndr_strikethrough(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) { UNUSED(data); if (!content || !content->size) return 0; HOEDOWN_BUFPUTSL(ob, ""); hoedown_buffer_put(ob, content->data, content->size); HOEDOWN_BUFPUTSL(ob, ""); return 1; } static int rndr_double_emphasis(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) { UNUSED(data); if (!content || !content->size) return 0; HOEDOWN_BUFPUTSL(ob, ""); hoedown_buffer_put(ob, content->data, content->size); HOEDOWN_BUFPUTSL(ob, ""); return 1; } static int rndr_emphasis(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) { UNUSED(data); if (!content || !content->size) return 0; HOEDOWN_BUFPUTSL(ob, ""); if (content) hoedown_buffer_put(ob, content->data, content->size); HOEDOWN_BUFPUTSL(ob, ""); return 1; } static int rndr_underline(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) { UNUSED(data); if (!content || !content->size) return 0; HOEDOWN_BUFPUTSL(ob, ""); hoedown_buffer_put(ob, content->data, content->size); HOEDOWN_BUFPUTSL(ob, ""); return 1; } static int rndr_highlight(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) { UNUSED(data); if (!content || !content->size) return 0; HOEDOWN_BUFPUTSL(ob, ""); hoedown_buffer_put(ob, content->data, content->size); HOEDOWN_BUFPUTSL(ob, ""); return 1; } static int rndr_quote(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) { UNUSED(data); if (!content || !content->size) return 0; HOEDOWN_BUFPUTSL(ob, ""); hoedown_buffer_put(ob, content->data, content->size); HOEDOWN_BUFPUTSL(ob, ""); return 1; } static int rndr_linebreak(hoedown_buffer *ob, const hoedown_renderer_data *data) { hoedown_html_renderer_state *state = data->opaque; hoedown_buffer_puts(ob, USE_XHTML(state) ? "
    \n" : "
    \n"); return 1; } static void rndr_header(hoedown_buffer *ob, const hoedown_buffer *content, int level, const hoedown_renderer_data *data) { hoedown_html_renderer_state *state = data->opaque; if (ob->size) hoedown_buffer_putc(ob, '\n'); if (level <= state->toc_data.nesting_level) hoedown_buffer_printf(ob, "", level, state->toc_data.header_count++); else hoedown_buffer_printf(ob, "", level); if (content) hoedown_buffer_put(ob, content->data, content->size); hoedown_buffer_printf(ob, "\n", level); } static int rndr_link(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_renderer_data *data) { hoedown_html_renderer_state *state = data->opaque; HOEDOWN_BUFPUTSL(ob, "size) escape_href(ob, link->data, link->size); if (title && title->size) { HOEDOWN_BUFPUTSL(ob, "\" title=\""); escape_html(ob, title->data, title->size); } if (state->link_attributes) { hoedown_buffer_putc(ob, '\"'); state->link_attributes(ob, link, data); hoedown_buffer_putc(ob, '>'); } else { HOEDOWN_BUFPUTSL(ob, "\">"); } if (content && content->size) hoedown_buffer_put(ob, content->data, content->size); HOEDOWN_BUFPUTSL(ob, ""); return 1; } static void rndr_list(hoedown_buffer *ob, const hoedown_buffer *content, hoedown_list_flags flags, const hoedown_renderer_data *data) { UNUSED(data); if (ob->size) hoedown_buffer_putc(ob, '\n'); hoedown_buffer_put(ob, (const uint8_t *)(flags & HOEDOWN_LIST_ORDERED ? "
      \n" : "
        \n"), 5); if (content) hoedown_buffer_put(ob, content->data, content->size); hoedown_buffer_put(ob, (const uint8_t *)(flags & HOEDOWN_LIST_ORDERED ? "
    \n" : "\n"), 6); } static void rndr_listitem(hoedown_buffer *ob, const hoedown_buffer *content, hoedown_list_flags flags, const hoedown_renderer_data *data) { UNUSED(flags); UNUSED(data); HOEDOWN_BUFPUTSL(ob, "
  • "); if (content) { size_t size = content->size; while (size && content->data[size - 1] == '\n') size--; hoedown_buffer_put(ob, content->data, size); } HOEDOWN_BUFPUTSL(ob, "
  • \n"); } static void rndr_paragraph(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) { hoedown_html_renderer_state *state = data->opaque; size_t i = 0; if (ob->size) hoedown_buffer_putc(ob, '\n'); if (!content || !content->size) return; while (i < content->size && isspace(content->data[i])) i++; if (i == content->size) return; HOEDOWN_BUFPUTSL(ob, "

    "); if (state->flags & HOEDOWN_HTML_HARD_WRAP) { size_t org; while (i < content->size) { org = i; while (i < content->size && content->data[i] != '\n') i++; if (i > org) hoedown_buffer_put(ob, content->data + org, i - org); /* * do not insert a line break if this newline * is the last character on the paragraph */ if (i >= content->size - 1) break; rndr_linebreak(ob, data); i++; } } else { hoedown_buffer_put(ob, content->data + i, content->size - i); } HOEDOWN_BUFPUTSL(ob, "

    \n"); } static void rndr_raw_block(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data) { UNUSED(data); size_t org, sz; if (!text) return; /* FIXME: Do we *really* need to trim the HTML? How does that make a difference? */ sz = text->size; while (sz > 0 && text->data[sz - 1] == '\n') sz--; org = 0; while (org < sz && text->data[org] == '\n') org++; if (org >= sz) return; if (ob->size) hoedown_buffer_putc(ob, '\n'); hoedown_buffer_put(ob, text->data + org, sz - org); hoedown_buffer_putc(ob, '\n'); } static int rndr_triple_emphasis(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) { UNUSED(data); if (!content || !content->size) return 0; HOEDOWN_BUFPUTSL(ob, ""); hoedown_buffer_put(ob, content->data, content->size); HOEDOWN_BUFPUTSL(ob, ""); return 1; } static void rndr_hrule(hoedown_buffer *ob, const hoedown_renderer_data *data) { hoedown_html_renderer_state *state = data->opaque; if (ob->size) hoedown_buffer_putc(ob, '\n'); hoedown_buffer_puts(ob, USE_XHTML(state) ? "
    \n" : "
    \n"); } static int rndr_image(hoedown_buffer *ob, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_buffer *alt, const hoedown_renderer_data *data) { hoedown_html_renderer_state *state = data->opaque; if (!link || !link->size) return 0; HOEDOWN_BUFPUTSL(ob, "data, link->size); HOEDOWN_BUFPUTSL(ob, "\" alt=\""); if (alt && alt->size) escape_html(ob, alt->data, alt->size); if (title && title->size) { HOEDOWN_BUFPUTSL(ob, "\" title=\""); escape_html(ob, title->data, title->size); } hoedown_buffer_puts(ob, USE_XHTML(state) ? "\"/>" : "\">"); return 1; } static int rndr_raw_html(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data) { hoedown_html_renderer_state *state = data->opaque; /* ESCAPE overrides SKIP_HTML. It doesn't look to see if * there are any valid tags, just escapes all of them. */ if((state->flags & HOEDOWN_HTML_ESCAPE) != 0) { escape_html(ob, text->data, text->size); return 1; } if ((state->flags & HOEDOWN_HTML_SKIP_HTML) != 0) return 1; hoedown_buffer_put(ob, text->data, text->size); return 1; } static void rndr_table(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) { UNUSED(data); if (ob->size) hoedown_buffer_putc(ob, '\n'); HOEDOWN_BUFPUTSL(ob, "\n"); hoedown_buffer_put(ob, content->data, content->size); HOEDOWN_BUFPUTSL(ob, "
    \n"); } static void rndr_table_header(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) { UNUSED(data); if (ob->size) hoedown_buffer_putc(ob, '\n'); HOEDOWN_BUFPUTSL(ob, "\n"); hoedown_buffer_put(ob, content->data, content->size); HOEDOWN_BUFPUTSL(ob, "\n"); } static void rndr_table_body(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) { UNUSED(data); if (ob->size) hoedown_buffer_putc(ob, '\n'); HOEDOWN_BUFPUTSL(ob, "\n"); hoedown_buffer_put(ob, content->data, content->size); HOEDOWN_BUFPUTSL(ob, "\n"); } static void rndr_tablerow(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) { UNUSED(data); HOEDOWN_BUFPUTSL(ob, "\n"); if (content) hoedown_buffer_put(ob, content->data, content->size); HOEDOWN_BUFPUTSL(ob, "\n"); } static void rndr_tablecell(hoedown_buffer *ob, const hoedown_buffer *content, hoedown_table_flags flags, const hoedown_renderer_data *data) { UNUSED(data); if (flags & HOEDOWN_TABLE_HEADER) { HOEDOWN_BUFPUTSL(ob, ""); break; case HOEDOWN_TABLE_ALIGN_LEFT: HOEDOWN_BUFPUTSL(ob, " style=\"text-align: left\">"); break; case HOEDOWN_TABLE_ALIGN_RIGHT: HOEDOWN_BUFPUTSL(ob, " style=\"text-align: right\">"); break; default: HOEDOWN_BUFPUTSL(ob, ">"); } if (content) hoedown_buffer_put(ob, content->data, content->size); if (flags & HOEDOWN_TABLE_HEADER) { HOEDOWN_BUFPUTSL(ob, "\n"); } else { HOEDOWN_BUFPUTSL(ob, "\n"); } } static int rndr_superscript(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) { UNUSED(data); if (!content || !content->size) return 0; HOEDOWN_BUFPUTSL(ob, ""); hoedown_buffer_put(ob, content->data, content->size); HOEDOWN_BUFPUTSL(ob, ""); return 1; } static void rndr_normal_text(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) { UNUSED(data); if (content) escape_html(ob, content->data, content->size); } static void rndr_footnotes(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) { hoedown_html_renderer_state *state = data->opaque; if (ob->size) hoedown_buffer_putc(ob, '\n'); HOEDOWN_BUFPUTSL(ob, "
    \n"); hoedown_buffer_puts(ob, USE_XHTML(state) ? "
    \n" : "
    \n"); HOEDOWN_BUFPUTSL(ob, "
      \n"); if (content) hoedown_buffer_put(ob, content->data, content->size); HOEDOWN_BUFPUTSL(ob, "\n
    \n
    \n"); } static void rndr_footnote_def(hoedown_buffer *ob, const hoedown_buffer *content, unsigned int num, const hoedown_renderer_data *data) { UNUSED(data); size_t i = 0; int pfound = 0; /* insert anchor at the end of first paragraph block */ if (content) { while ((i+3) < content->size) { if (content->data[i++] != '<') continue; if (content->data[i++] != '/') continue; if (content->data[i++] != 'p' && content->data[i] != 'P') continue; if (content->data[i] != '>') continue; i -= 3; pfound = 1; break; } } hoedown_buffer_printf(ob, "\n
  • \n", num); if (pfound) { hoedown_buffer_put(ob, content->data, i); hoedown_buffer_printf(ob, " ", num); hoedown_buffer_put(ob, content->data + i, content->size - i); } else if (content) { hoedown_buffer_put(ob, content->data, content->size); } HOEDOWN_BUFPUTSL(ob, "
  • \n"); } static int rndr_footnote_ref(hoedown_buffer *ob, unsigned int num, const hoedown_renderer_data *data) { UNUSED(data); hoedown_buffer_printf(ob, "%d", num, num, num); return 1; } static int rndr_math(hoedown_buffer *ob, const hoedown_buffer *text, int displaymode, const hoedown_renderer_data *data) { UNUSED(data); hoedown_buffer_put(ob, (const uint8_t *)(displaymode ? "\\[" : "\\("), 2); escape_html(ob, text->data, text->size); hoedown_buffer_put(ob, (const uint8_t *)(displaymode ? "\\]" : "\\)"), 2); return 1; } static void toc_header(hoedown_buffer *ob, const hoedown_buffer *content, int level, const hoedown_renderer_data *data) { hoedown_html_renderer_state *state = data->opaque; if (level <= state->toc_data.nesting_level) { /* set the level offset if this is the first header * we're parsing for the document */ if (state->toc_data.current_level == 0) state->toc_data.level_offset = level - 1; level -= state->toc_data.level_offset; if (level > state->toc_data.current_level) { while (level > state->toc_data.current_level) { HOEDOWN_BUFPUTSL(ob, "
      \n
    • \n"); state->toc_data.current_level++; } } else if (level < state->toc_data.current_level) { HOEDOWN_BUFPUTSL(ob, "
    • \n"); while (level < state->toc_data.current_level) { HOEDOWN_BUFPUTSL(ob, "
    \n
  • \n"); state->toc_data.current_level--; } HOEDOWN_BUFPUTSL(ob,"
  • \n"); } else { HOEDOWN_BUFPUTSL(ob,"
  • \n
  • \n"); } hoedown_buffer_printf(ob, "", state->toc_data.header_count++); if (content) hoedown_buffer_put(ob, content->data, content->size); HOEDOWN_BUFPUTSL(ob, "\n"); } } static int toc_link(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_renderer_data *data) { UNUSED(link); UNUSED(title); UNUSED(data); if (content && content->size) hoedown_buffer_put(ob, content->data, content->size); return 1; } static void toc_finalize(hoedown_buffer *ob, int inline_render, const hoedown_renderer_data *data) { hoedown_html_renderer_state *state; if (inline_render) return; state = data->opaque; while (state->toc_data.current_level > 0) { HOEDOWN_BUFPUTSL(ob, "
  • \n\n"); state->toc_data.current_level--; } state->toc_data.header_count = 0; } hoedown_renderer * hoedown_html_toc_renderer_new(int nesting_level) { static const hoedown_renderer cb_default = { NULL, NULL, NULL, toc_header, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, rndr_codespan, rndr_double_emphasis, rndr_emphasis, rndr_underline, rndr_highlight, rndr_quote, NULL, NULL, toc_link, rndr_triple_emphasis, rndr_strikethrough, rndr_superscript, NULL, NULL, NULL, NULL, rndr_normal_text, NULL, toc_finalize }; hoedown_html_renderer_state *state; hoedown_renderer *renderer; /* Prepare the state pointer */ state = hoedown_malloc(sizeof(hoedown_html_renderer_state)); memset(state, 0x0, sizeof(hoedown_html_renderer_state)); state->toc_data.nesting_level = nesting_level; /* Prepare the renderer */ renderer = hoedown_malloc(sizeof(hoedown_renderer)); memcpy(renderer, &cb_default, sizeof(hoedown_renderer)); renderer->opaque = state; return renderer; } hoedown_renderer * hoedown_html_renderer_new(hoedown_html_flags render_flags, int nesting_level) { static const hoedown_renderer cb_default = { NULL, rndr_blockcode, rndr_blockquote, rndr_header, rndr_hrule, rndr_list, rndr_listitem, rndr_paragraph, rndr_table, rndr_table_header, rndr_table_body, rndr_tablerow, rndr_tablecell, rndr_footnotes, rndr_footnote_def, rndr_raw_block, rndr_autolink, rndr_codespan, rndr_double_emphasis, rndr_emphasis, rndr_underline, rndr_highlight, rndr_quote, rndr_image, rndr_linebreak, rndr_link, rndr_triple_emphasis, rndr_strikethrough, rndr_superscript, rndr_footnote_ref, rndr_math, rndr_raw_html, NULL, rndr_normal_text, NULL, NULL }; hoedown_html_renderer_state *state; hoedown_renderer *renderer; /* Prepare the state pointer */ state = hoedown_malloc(sizeof(hoedown_html_renderer_state)); memset(state, 0x0, sizeof(hoedown_html_renderer_state)); state->flags = render_flags; state->toc_data.nesting_level = nesting_level; /* Prepare the renderer */ renderer = hoedown_malloc(sizeof(hoedown_renderer)); memcpy(renderer, &cb_default, sizeof(hoedown_renderer)); if (render_flags & HOEDOWN_HTML_SKIP_HTML || render_flags & HOEDOWN_HTML_ESCAPE) renderer->blockhtml = NULL; renderer->opaque = state; return renderer; } void hoedown_html_renderer_free(hoedown_renderer *renderer) { free(renderer->opaque); free(renderer); } ================================================ FILE: 3rdpart/hoedown/src/html.h ================================================ /* html.h - HTML renderer and utilities */ #ifndef HOEDOWN_HTML_H #define HOEDOWN_HTML_H #include "document.h" #include "buffer.h" #ifdef __cplusplus extern "C" { #endif /************* * CONSTANTS * *************/ typedef enum hoedown_html_flags { HOEDOWN_HTML_SKIP_HTML = (1 << 0), HOEDOWN_HTML_ESCAPE = (1 << 1), HOEDOWN_HTML_HARD_WRAP = (1 << 2), HOEDOWN_HTML_USE_XHTML = (1 << 3) } hoedown_html_flags; typedef enum hoedown_html_tag { HOEDOWN_HTML_TAG_NONE = 0, HOEDOWN_HTML_TAG_OPEN, HOEDOWN_HTML_TAG_CLOSE } hoedown_html_tag; /********* * TYPES * *********/ struct hoedown_html_renderer_state { void *opaque; struct { int header_count; int current_level; int level_offset; int nesting_level; } toc_data; hoedown_html_flags flags; /* extra callbacks */ void (*link_attributes)(hoedown_buffer *ob, const hoedown_buffer *url, const hoedown_renderer_data *data); }; typedef struct hoedown_html_renderer_state hoedown_html_renderer_state; /************* * FUNCTIONS * *************/ /* hoedown_html_smartypants: process an HTML snippet using SmartyPants for smart punctuation */ void hoedown_html_smartypants(hoedown_buffer *ob, const uint8_t *data, size_t size); /* hoedown_html_is_tag: checks if data starts with a specific tag, returns the tag type or NONE */ hoedown_html_tag hoedown_html_is_tag(const uint8_t *data, size_t size, const char *tagname); /* hoedown_html_renderer_new: allocates a regular HTML renderer */ hoedown_renderer *hoedown_html_renderer_new( hoedown_html_flags render_flags, int nesting_level ) __attribute__ ((malloc)); /* hoedown_html_toc_renderer_new: like hoedown_html_renderer_new, but the returned renderer produces the Table of Contents */ hoedown_renderer *hoedown_html_toc_renderer_new( int nesting_level ) __attribute__ ((malloc)); /* hoedown_html_renderer_free: deallocate an HTML renderer */ void hoedown_html_renderer_free(hoedown_renderer *renderer); #ifdef __cplusplus } #endif #endif /** HOEDOWN_HTML_H **/ ================================================ FILE: 3rdpart/hoedown/src/html_blocks.c ================================================ /* ANSI-C code produced by gperf version 3.0.3 */ /* Command-line: gperf -L ANSI-C -N hoedown_find_block_tag -c -C -E -S 1 --ignore-case -m100 html_block_names.gperf */ /* Computed positions: -k'1-2' */ #if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \ && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \ && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \ && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \ && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \ && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \ && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) /* The character set is not based on ISO-646. */ #error "gperf generated tables don't work with this execution character set. Please report a bug to ." #endif /* maximum key range = 24, duplicates = 0 */ #ifndef GPERF_DOWNCASE #define GPERF_DOWNCASE 1 static unsigned char gperf_downcase[256] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 }; #endif #ifndef GPERF_CASE_STRNCMP #define GPERF_CASE_STRNCMP 1 static int gperf_case_strncmp (register const char *s1, register const char *s2, register unsigned int n) { for (; n > 0;) { unsigned char c1 = gperf_downcase[(unsigned char)*s1++]; unsigned char c2 = gperf_downcase[(unsigned char)*s2++]; if (c1 != 0 && c1 == c2) { n--; continue; } return (int)c1 - (int)c2; } return 0; } #endif #ifdef __GNUC__ __inline #else #ifdef __cplusplus inline #endif #endif static unsigned int hash (register const char *str, register unsigned int len) { static const unsigned char asso_values[] = { 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 22, 21, 19, 18, 16, 0, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 1, 25, 0, 25, 1, 0, 0, 13, 0, 25, 25, 11, 2, 1, 0, 25, 25, 5, 0, 2, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 1, 25, 0, 25, 1, 0, 0, 13, 0, 25, 25, 11, 2, 1, 0, 25, 25, 5, 0, 2, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25 }; register int hval = (int)len; switch (hval) { default: hval += asso_values[(unsigned char)str[1]+1]; /*FALLTHROUGH*/ case 1: hval += asso_values[(unsigned char)str[0]]; break; } return hval; } #ifdef __GNUC__ __inline #ifdef __GNUC_STDC_INLINE__ __attribute__ ((__gnu_inline__)) #endif #endif const char * hoedown_find_block_tag (register const char *str, register unsigned int len) { enum { TOTAL_KEYWORDS = 24, MIN_WORD_LENGTH = 1, MAX_WORD_LENGTH = 10, MIN_HASH_VALUE = 1, MAX_HASH_VALUE = 24 }; if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) { register int key = hash (str, len); if (key <= MAX_HASH_VALUE && key >= MIN_HASH_VALUE) { register const char *resword; switch (key - 1) { case 0: resword = "p"; goto compare; case 1: resword = "h6"; goto compare; case 2: resword = "div"; goto compare; case 3: resword = "del"; goto compare; case 4: resword = "form"; goto compare; case 5: resword = "table"; goto compare; case 6: resword = "figure"; goto compare; case 7: resword = "pre"; goto compare; case 8: resword = "fieldset"; goto compare; case 9: resword = "noscript"; goto compare; case 10: resword = "script"; goto compare; case 11: resword = "style"; goto compare; case 12: resword = "dl"; goto compare; case 13: resword = "ol"; goto compare; case 14: resword = "ul"; goto compare; case 15: resword = "math"; goto compare; case 16: resword = "ins"; goto compare; case 17: resword = "h5"; goto compare; case 18: resword = "iframe"; goto compare; case 19: resword = "h4"; goto compare; case 20: resword = "h3"; goto compare; case 21: resword = "blockquote"; goto compare; case 22: resword = "h2"; goto compare; case 23: resword = "h1"; goto compare; } return 0; compare: if ((((unsigned char)*str ^ (unsigned char)*resword) & ~32) == 0 && !gperf_case_strncmp (str, resword, len) && resword[len] == '\0') return resword; } } return 0; } ================================================ FILE: 3rdpart/hoedown/src/html_smartypants.c ================================================ #include "html.h" #include #include #include #include #ifdef _MSC_VER #define snprintf _snprintf #endif struct smartypants_data { int in_squote; int in_dquote; }; static size_t smartypants_cb__ltag(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); static size_t smartypants_cb__dquote(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); static size_t smartypants_cb__amp(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); static size_t smartypants_cb__period(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); static size_t smartypants_cb__number(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); static size_t smartypants_cb__dash(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); static size_t smartypants_cb__parens(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); static size_t smartypants_cb__squote(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); static size_t smartypants_cb__backtick(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); static size_t smartypants_cb__escape(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); static size_t (*smartypants_cb_ptrs[]) (hoedown_buffer *, struct smartypants_data *, uint8_t, const uint8_t *, size_t) = { NULL, /* 0 */ smartypants_cb__dash, /* 1 */ smartypants_cb__parens, /* 2 */ smartypants_cb__squote, /* 3 */ smartypants_cb__dquote, /* 4 */ smartypants_cb__amp, /* 5 */ smartypants_cb__period, /* 6 */ smartypants_cb__number, /* 7 */ smartypants_cb__ltag, /* 8 */ smartypants_cb__backtick, /* 9 */ smartypants_cb__escape, /* 10 */ }; static const uint8_t smartypants_cb_chars[UINT8_MAX+1] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 5, 3, 2, 0, 0, 0, 0, 1, 6, 0, 0, 7, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; static int word_boundary(uint8_t c) { return c == 0 || isspace(c) || ispunct(c); } /* If 'text' begins with any kind of single quote (e.g. "'" or "'" etc.), returns the length of the sequence of characters that makes up the single- quote. Otherwise, returns zero. */ static size_t squote_len(const uint8_t *text, size_t size) { static char* single_quote_list[] = { "'", "'", "'", "'", NULL }; char** p; for (p = single_quote_list; *p; ++p) { size_t len = strlen(*p); if (size >= len && memcmp(text, *p, len) == 0) { return len; } } return 0; } /* Converts " or ' at very beginning or end of a word to left or right quote */ static int smartypants_quotes(hoedown_buffer *ob, uint8_t previous_char, uint8_t next_char, uint8_t quote, int *is_open) { char ent[8]; if (*is_open && !word_boundary(next_char)) return 0; if (!(*is_open) && !word_boundary(previous_char)) return 0; snprintf(ent, sizeof(ent), "&%c%cquo;", (*is_open) ? 'r' : 'l', quote); *is_open = !(*is_open); hoedown_buffer_puts(ob, ent); return 1; } /* Converts ' to left or right single quote; but the initial ' might be in different forms, e.g. ' or ' or '. 'squote_text' points to the original single quote, and 'squote_size' is its length. 'text' points at the last character of the single-quote, e.g. ' or ; */ static size_t smartypants_squote(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size, const uint8_t *squote_text, size_t squote_size) { if (size >= 2) { uint8_t t1 = tolower(text[1]); size_t next_squote_len = squote_len(text+1, size-1); /* convert '' to “ or ” */ if (next_squote_len > 0) { uint8_t next_char = (size > 1+next_squote_len) ? text[1+next_squote_len] : 0; if (smartypants_quotes(ob, previous_char, next_char, 'd', &smrt->in_dquote)) return next_squote_len; } /* Tom's, isn't, I'm, I'd */ if ((t1 == 's' || t1 == 't' || t1 == 'm' || t1 == 'd') && (size == 3 || word_boundary(text[2]))) { HOEDOWN_BUFPUTSL(ob, "’"); return 0; } /* you're, you'll, you've */ if (size >= 3) { uint8_t t2 = tolower(text[2]); if (((t1 == 'r' && t2 == 'e') || (t1 == 'l' && t2 == 'l') || (t1 == 'v' && t2 == 'e')) && (size == 4 || word_boundary(text[3]))) { HOEDOWN_BUFPUTSL(ob, "’"); return 0; } } } if (smartypants_quotes(ob, previous_char, size > 0 ? text[1] : 0, 's', &smrt->in_squote)) return 0; hoedown_buffer_put(ob, squote_text, squote_size); return 0; } /* Converts ' to left or right single quote. */ static size_t smartypants_cb__squote(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) { return smartypants_squote(ob, smrt, previous_char, text, size, text, 1); } /* Converts (c), (r), (tm) */ static size_t smartypants_cb__parens(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) { if (size >= 3) { uint8_t t1 = tolower(text[1]); uint8_t t2 = tolower(text[2]); if (t1 == 'c' && t2 == ')') { HOEDOWN_BUFPUTSL(ob, "©"); return 2; } if (t1 == 'r' && t2 == ')') { HOEDOWN_BUFPUTSL(ob, "®"); return 2; } if (size >= 4 && t1 == 't' && t2 == 'm' && text[3] == ')') { HOEDOWN_BUFPUTSL(ob, "™"); return 3; } } hoedown_buffer_putc(ob, text[0]); return 0; } /* Converts "--" to em-dash, etc. */ static size_t smartypants_cb__dash(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) { if (size >= 3 && text[1] == '-' && text[2] == '-') { HOEDOWN_BUFPUTSL(ob, "—"); return 2; } if (size >= 2 && text[1] == '-') { HOEDOWN_BUFPUTSL(ob, "–"); return 1; } hoedown_buffer_putc(ob, text[0]); return 0; } /* Converts " etc. */ static size_t smartypants_cb__amp(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) { size_t len; if (size >= 6 && memcmp(text, """, 6) == 0) { if (smartypants_quotes(ob, previous_char, size >= 7 ? text[6] : 0, 'd', &smrt->in_dquote)) return 5; } len = squote_len(text, size); if (len > 0) { return (len-1) + smartypants_squote(ob, smrt, previous_char, text+(len-1), size-(len-1), text, len); } if (size >= 4 && memcmp(text, "�", 4) == 0) return 3; hoedown_buffer_putc(ob, '&'); return 0; } /* Converts "..." to ellipsis */ static size_t smartypants_cb__period(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) { if (size >= 3 && text[1] == '.' && text[2] == '.') { HOEDOWN_BUFPUTSL(ob, "…"); return 2; } if (size >= 5 && text[1] == ' ' && text[2] == '.' && text[3] == ' ' && text[4] == '.') { HOEDOWN_BUFPUTSL(ob, "…"); return 4; } hoedown_buffer_putc(ob, text[0]); return 0; } /* Converts `` to opening double quote */ static size_t smartypants_cb__backtick(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) { if (size >= 2 && text[1] == '`') { if (smartypants_quotes(ob, previous_char, size >= 3 ? text[2] : 0, 'd', &smrt->in_dquote)) return 1; } hoedown_buffer_putc(ob, text[0]); return 0; } /* Converts 1/2, 1/4, 3/4 */ static size_t smartypants_cb__number(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) { if (word_boundary(previous_char) && size >= 3) { if (text[0] == '1' && text[1] == '/' && text[2] == '2') { if (size == 3 || word_boundary(text[3])) { HOEDOWN_BUFPUTSL(ob, "½"); return 2; } } if (text[0] == '1' && text[1] == '/' && text[2] == '4') { if (size == 3 || word_boundary(text[3]) || (size >= 5 && tolower(text[3]) == 't' && tolower(text[4]) == 'h')) { HOEDOWN_BUFPUTSL(ob, "¼"); return 2; } } if (text[0] == '3' && text[1] == '/' && text[2] == '4') { if (size == 3 || word_boundary(text[3]) || (size >= 6 && tolower(text[3]) == 't' && tolower(text[4]) == 'h' && tolower(text[5]) == 's')) { HOEDOWN_BUFPUTSL(ob, "¾"); return 2; } } } hoedown_buffer_putc(ob, text[0]); return 0; } /* Converts " to left or right double quote */ static size_t smartypants_cb__dquote(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) { if (!smartypants_quotes(ob, previous_char, size > 0 ? text[1] : 0, 'd', &smrt->in_dquote)) HOEDOWN_BUFPUTSL(ob, """); return 0; } static size_t smartypants_cb__ltag(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) { static const char *skip_tags[] = { "pre", "code", "var", "samp", "kbd", "math", "script", "style" }; static const size_t skip_tags_count = 8; size_t tag, i = 0; /* This is a comment. Copy everything verbatim until --> or EOF is seen. */ if (i + 4 < size && memcmp(text + i, "", 3) != 0) i++; i += 3; hoedown_buffer_put(ob, text, i + 1); return i; } while (i < size && text[i] != '>') i++; for (tag = 0; tag < skip_tags_count; ++tag) { if (hoedown_html_is_tag(text, size, skip_tags[tag]) == HOEDOWN_HTML_TAG_OPEN) break; } if (tag < skip_tags_count) { for (;;) { while (i < size && text[i] != '<') i++; if (i == size) break; if (hoedown_html_is_tag(text + i, size - i, skip_tags[tag]) == HOEDOWN_HTML_TAG_CLOSE) break; i++; } while (i < size && text[i] != '>') i++; } hoedown_buffer_put(ob, text, i + 1); return i; } static size_t smartypants_cb__escape(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) { if (size < 2) return 0; switch (text[1]) { case '\\': case '"': case '\'': case '.': case '-': case '`': hoedown_buffer_putc(ob, text[1]); return 1; default: hoedown_buffer_putc(ob, '\\'); return 0; } } #if 0 static struct { uint8_t c0; const uint8_t *pattern; const uint8_t *entity; int skip; } smartypants_subs[] = { { '\'', "'s>", "’", 0 }, { '\'', "'t>", "’", 0 }, { '\'', "'re>", "’", 0 }, { '\'', "'ll>", "’", 0 }, { '\'', "'ve>", "’", 0 }, { '\'', "'m>", "’", 0 }, { '\'', "'d>", "’", 0 }, { '-', "--", "—", 1 }, { '-', "<->", "–", 0 }, { '.', "...", "…", 2 }, { '.', ". . .", "…", 4 }, { '(', "(c)", "©", 2 }, { '(', "(r)", "®", 2 }, { '(', "(tm)", "™", 3 }, { '3', "<3/4>", "¾", 2 }, { '3', "<3/4ths>", "¾", 2 }, { '1', "<1/2>", "½", 2 }, { '1', "<1/4>", "¼", 2 }, { '1', "<1/4th>", "¼", 2 }, { '&', "�", 0, 3 }, }; #endif void hoedown_html_smartypants(hoedown_buffer *ob, const uint8_t *text, size_t size) { size_t i; struct smartypants_data smrt = {0, 0}; if (!text) return; hoedown_buffer_grow(ob, size); for (i = 0; i < size; ++i) { size_t org; uint8_t action = 0; org = i; while (i < size && (action = smartypants_cb_chars[text[i]]) == 0) i++; if (i > org) hoedown_buffer_put(ob, text + org, i - org); if (i < size) { i += smartypants_cb_ptrs[(int)action] (ob, &smrt, i ? text[i - 1] : 0, text + i, size - i); } } } ================================================ FILE: 3rdpart/hoedown/src/stack.c ================================================ #include "stack.h" #include "buffer.h" #include #include #include void hoedown_stack_init(hoedown_stack *st, size_t initial_size) { assert(st); st->item = NULL; st->size = st->asize = 0; if (!initial_size) initial_size = 8; hoedown_stack_grow(st, initial_size); } void hoedown_stack_uninit(hoedown_stack *st) { assert(st); free(st->item); } void hoedown_stack_grow(hoedown_stack *st, size_t neosz) { assert(st); if (st->asize >= neosz) return; st->item = hoedown_realloc(st->item, neosz * sizeof(void *)); memset(st->item + st->asize, 0x0, (neosz - st->asize) * sizeof(void *)); st->asize = neosz; if (st->size > neosz) st->size = neosz; } void hoedown_stack_push(hoedown_stack *st, void *item) { assert(st); if (st->size >= st->asize) hoedown_stack_grow(st, st->size * 2); st->item[st->size++] = item; } void * hoedown_stack_pop(hoedown_stack *st) { assert(st); if (!st->size) return NULL; return st->item[--st->size]; } void * hoedown_stack_top(const hoedown_stack *st) { assert(st); if (!st->size) return NULL; return st->item[st->size - 1]; } ================================================ FILE: 3rdpart/hoedown/src/stack.h ================================================ /* stack.h - simple stacking */ #ifndef HOEDOWN_STACK_H #define HOEDOWN_STACK_H #include #ifdef __cplusplus extern "C" { #endif /********* * TYPES * *********/ struct hoedown_stack { void **item; size_t size; size_t asize; }; typedef struct hoedown_stack hoedown_stack; /************* * FUNCTIONS * *************/ /* hoedown_stack_init: initialize a stack */ void hoedown_stack_init(hoedown_stack *st, size_t initial_size); /* hoedown_stack_uninit: free internal data of the stack */ void hoedown_stack_uninit(hoedown_stack *st); /* hoedown_stack_grow: increase the allocated size to the given value */ void hoedown_stack_grow(hoedown_stack *st, size_t neosz); /* hoedown_stack_push: push an item to the top of the stack */ void hoedown_stack_push(hoedown_stack *st, void *item); /* hoedown_stack_pop: retrieve and remove the item at the top of the stack */ void *hoedown_stack_pop(hoedown_stack *st); /* hoedown_stack_top: retrieve the item at the top of the stack */ void *hoedown_stack_top(const hoedown_stack *st); #ifdef __cplusplus } #endif #endif /** HOEDOWN_STACK_H **/ ================================================ FILE: 3rdpart/hoedown/src/version.h ================================================ /* version.h - holds Hoedown's version */ #ifndef HOEDOWN_VERSION_H #define HOEDOWN_VERSION_H #ifdef __cplusplus extern "C" { #endif /************* * CONSTANTS * *************/ #define HOEDOWN_VERSION "3.0.7" #define HOEDOWN_VERSION_MAJOR 3 #define HOEDOWN_VERSION_MINOR 0 #define HOEDOWN_VERSION_REVISION 7 /************* * FUNCTIONS * *************/ /* hoedown_version: retrieve Hoedown's version numbers */ void hoedown_version(int *major, int *minor, int *revision); #ifdef __cplusplus } #endif #endif /** HOEDOWN_VERSION_H **/ ================================================ FILE: 3rdpart/qdarkstyle/qdarkstype.pri ================================================ RESOURCES += $$PWD//style.qrc ================================================ FILE: 3rdpart/qdarkstyle/style.qrc ================================================ rc/up_arrow_disabled.png rc/Hmovetoolbar.png rc/stylesheet-branch-end.png rc/branch_closed-on.png rc/stylesheet-vline.png rc/branch_closed.png rc/branch_open-on.png rc/transparent.png rc/right_arrow_disabled.png rc/sizegrip.png rc/close.png rc/close-hover.png rc/close-pressed.png rc/down_arrow.png rc/Vmovetoolbar.png rc/left_arrow.png rc/stylesheet-branch-more.png rc/up_arrow.png rc/right_arrow.png rc/left_arrow_disabled.png rc/Hsepartoolbar.png rc/branch_open.png rc/Vsepartoolbar.png rc/down_arrow_disabled.png rc/undock.png rc/checkbox_checked_disabled.png rc/checkbox_checked_focus.png rc/checkbox_checked.png rc/checkbox_indeterminate.png rc/checkbox_indeterminate_focus.png rc/checkbox_unchecked_disabled.png rc/checkbox_unchecked_focus.png rc/checkbox_unchecked.png rc/radio_checked_disabled.png rc/radio_checked_focus.png rc/radio_checked.png rc/radio_unchecked_disabled.png rc/radio_unchecked_focus.png rc/radio_unchecked.png style.qss ================================================ FILE: 3rdpart/qdarkstyle/style.qss ================================================ /* QDarkStyleSheet -------------------------------------------------------- This is the main style sheet, the palette has nine main colors. It is based on three selecting colors, three greyish (background) colors plus three whitish (foreground) colors. Each set of widgets of the same type have a header like this: ------------------ GroupName -------- ------------------ And each widget is separated with a header like this: QWidgetName ------ This makes more easy to find and change some css field. The basic configuration is described bellow. SELECTION ------------ sel_light #179AE0 #148CD2 (selection/hover/active) sel_normal #3375A3 #1464A0 (selected) sel_dark #18465D #14506E (selected disabled) FOREGROUND ----------- for_light #EFF0F1 #F0F0F0 (texts/labels) for_dark #505F69 #787878 (disabled texts) BACKGROUND ----------- bac_light #4D545B #505F69 (unpressed) bac_normal #31363B #32414B (border, disabled, pressed, checked, toolbars, menus) bac_dark #232629 #19232D (background) If a stranger configuration is required because of a bugfix or anything else, keep the comment on that line to nobodys changed it, including the issue number. --------------------------------------------------------------------------- */ /* QWidget ---------------------------------------------------------------- */ QWidget { background-color: #19232D; border: 0px solid #32414B; padding: 0px; color: #F0F0F0; selection-background-color: #1464A0; selection-color: #F0F0F0; } QWidget:disabled { background-color: #19232D; color: #787878; selection-background-color: #14506E; selection-color: #787878; } QWidget:item:selected { background-color: #1464A0; } QWidget:item:hover { background-color: #148CD2; color: #32414B; } /* QMainWindow ------------------------------------------------------------ */ /* This adjusts the splitter in the dock widget, not qsplitter */ QMainWindow::separator { background-color: #32414B; border: 0 solid #19232D; spacing: 0; padding: 2px; } QMainWindow::separator:hover { background-color: #505F69; border: 0px solid #148CD2; } QMainWindow::separator:horizontal { width: 5px; margin-top: 2px; margin-bottom: 2px; image: url(:/qss_icons/rc/Vsepartoolbar.png); } QMainWindow::separator:vertical { height: 5px; margin-left: 2px; margin-right: 2px; image: url(:/qss_icons/rc/Hsepartoolbar.png); } /* QToolTip --------------------------------------------------------------- */ QToolTip { background-color: #148CD2; border: 1px solid #19232D; color: #19232D; padding: 0; /*remove padding, for fix combo box tooltip*/ opacity: 230; /*reducing transparency to read better*/ } /* QStatusBar ------------------------------------------------------------- */ QStatusBar { border: 1px solid #32414B; } QStatusBar QToolTip { background-color: #148CD2; border: 1px solid #19232D; color: #19232D; padding: 0; /*remove padding, for fix combo box tooltip*/ opacity: 230; /*reducing transparency to read better*/ } /* QCheckBox -------------------------------------------------------------- */ QCheckBox { background-color: #19232D; color: #F0F0F0; spacing: 4px; outline: none; padding-top: 4px; padding-bottom: 4px; } QCheckBox:focus { border: none; } QCheckBox QWidget:disabled { background-color: #19232D; color: #787878; } QCheckBox::indicator { margin-left: 4px; width: 16px; height: 16px; } QCheckBox::indicator:unchecked { image: url(:/qss_icons/rc/checkbox_unchecked.png); } QCheckBox::indicator:unchecked:hover, QCheckBox::indicator:unchecked:focus, QCheckBox::indicator:unchecked:pressed { border: none; image: url(:/qss_icons/rc/checkbox_unchecked_focus.png); } QCheckBox::indicator:unchecked:disabled { image: url(:/qss_icons/rc/checkbox_unchecked_disabled.png); } QCheckBox::indicator:checked { image: url(:/qss_icons/rc/checkbox_checked.png); } QCheckBox::indicator:checked:hover, QCheckBox::indicator:checked:focus, QCheckBox::indicator:checked:pressed { border: none; image: url(:/qss_icons/rc/checkbox_checked_focus.png); } QCheckBox::indicator:checked:disabled{ image: url(:/qss_icons/rc/checkbox_checked_disabled.png); } QCheckBox::indicator:indeterminate { image: url(:/qss_icons/rc/checkbox_indeterminate.png); } QCheckBox::indicator:indeterminate:disabled { image: url(:/qss_icons/rc/checkbox_indeterminate_disabled.png); } QCheckBox::indicator:indeterminate:focus, QCheckBox::indicator:indeterminate:hover, QCheckBox::indicator:indeterminate:pressed { image: url(:/qss_icons/rc/checkbox_indeterminate_focus.png); } /* QGroupBox -------------------------------------------------------------- */ QGroupBox { font-weight: bold; border: 1px solid #32414B; border-radius: 4px; padding: 4px; margin-top: 16px; } QGroupBox::title { subcontrol-origin: margin; subcontrol-position: top left; left: 3px; padding-left: 3px; padding-right: 5px; padding-top: 8px; padding-bottom: 16px; } QGroupBox::indicator { margin-left: 4px; width: 16px; height: 16px; } QGroupBox::indicator { margin-left: 2px; } QGroupBox::indicator:unchecked:hover, QGroupBox::indicator:unchecked:focus, QGroupBox::indicator:unchecked:pressed { border: none; image: url(:/qss_icons/rc/checkbox_unchecked_focus.png); } QGroupBox::indicator:checked:hover, QGroupBox::indicator:checked:focus, QGroupBox::indicator:checked:pressed { border: none; image: url(:/qss_icons/rc/checkbox_checked_focus.png); } QGroupBox::indicator:checked:disabled { image: url(:/qss_icons/rc/checkbox_checked_disabled.png); } QGroupBox::indicator:unchecked:disabled { image: url(:/qss_icons/rc/checkbox_unchecked_disabled.png); } /* QRadioButton ----------------------------------------------------------- */ QRadioButton { background-color: #19232D; color: #F0F0F0; spacing: 0; padding: 0; border: none; outline: none; } QRadioButton:focus { border: none; } QRadioButton:disabled { background-color: #19232D; color: #787878; border: none; outline: none; } QRadioButton QWidget { background-color: #19232D; color: #F0F0F0; spacing: 0px; padding: 0px; outline: none; border: none; } QRadioButton::indicator { border: none; outline: none; margin-bottom: 2px; width: 25px; height: 25px; } QRadioButton::indicator:unchecked { image: url(:/qss_icons/rc/radio_unchecked.png); } QRadioButton::indicator:unchecked:hover, QRadioButton::indicator:unchecked:focus, QRadioButton::indicator:unchecked:pressed { border: none; outline: none; image: url(:/qss_icons/rc/radio_unchecked_focus.png); } QRadioButton::indicator:checked { border: none; outline: none; image: url(:/qss_icons/rc/radio_checked.png); } QRadioButton::indicator:checked:hover, QRadioButton::indicator:checked:focus, QRadioButton::indicator:checked:pressed { border: none; outline: none; image: url(:/qss_icons/rc/radio_checked_focus.png); } QRadioButton::indicator:checked:disabled { outline: none; image: url(:/qss_icons/rc/radio_checked_disabled.png); } QRadioButton::indicator:unchecked:disabled { image: url(:/qss_icons/rc/radio_unchecked_disabled.png); } /* QMenuBar --------------------------------------------------------------- */ QMenuBar { background-color: #32414B; padding: 2px; border: 1px solid #19232D; color: #F0F0F0; } QMenuBar:focus { border: 1px solid #148CD2; } QMenuBar::item { background: transparent; padding: 4px; } QMenuBar::item:selected { padding: 4px; background: transparent; border: 0px solid #32414B; } QMenuBar::item:pressed { padding: 4px; border: 0px solid #32414B; background-color: #148CD2; color: #F0F0F0; margin-bottom: 0px; padding-bottom: 0px; } /* QMenu ------------------------------------------------------------------ */ QMenu { border: 0px solid #32414B; color: #F0F0F0; margin: 0px; } QMenu::separator { height: 2px; background-color: #505F69; color: #F0F0F0; padding-left: 4px; margin-left: 2px; margin-right: 2px; } QMenu::icon { margin: 0px; padding-left:4px; } QMenu::item { padding: 4px 24px 4px 24px; border: 1px transparent #32414B; /* reserve space for selection border */ } QMenu::item:selected { color: #F0F0F0; } QMenu::indicator { width: 12px; height: 12px; padding-left:6px; } /* non-exclusive indicator = check box style indicator (see QActionGroup::setExclusive) */ QMenu::indicator:non-exclusive:unchecked { image: url(:/qss_icons/rc/checkbox_unchecked.png); } QMenu::indicator:non-exclusive:unchecked:selected { image: url(:/qss_icons/rc/checkbox_unchecked_disabled.png); } QMenu::indicator:non-exclusive:checked { image: url(:/qss_icons/rc/checkbox_checked.png); } QMenu::indicator:non-exclusive:checked:selected { image: url(:/qss_icons/rc/checkbox_checked_disabled.png); } /* exclusive indicator = radio button style indicator (see QActionGroup::setExclusive) */ QMenu::indicator:exclusive:unchecked { image: url(:/qss_icons/rc/radio_unchecked.png); } QMenu::indicator:exclusive:unchecked:selected { image: url(:/qss_icons/rc/radio_unchecked_disabled.png); } QMenu::indicator:exclusive:checked { image: url(:/qss_icons/rc/radio_checked.png); } QMenu::indicator:exclusive:checked:selected { image: url(:/qss_icons/rc/radio_checked_disabled.png); } QMenu::right-arrow { margin: 5px; image: url(:/qss_icons/rc/right_arrow.png) } /* QAbstractItemView ------------------------------------------------------ */ QAbstractItemView { alternate-background-color: #19232D; color: #F0F0F0; border: 1px solid #32414B; border-radius: 4px; } QAbstractItemView QLineEdit { padding: 2px; } /* QAbstractScrollArea ---------------------------------------------------- */ QAbstractScrollArea { background-color: #19232D; border: 1px solid #32414B; border-radius: 4px; padding: 4px; color: #F0F0F0; } QAbstractScrollArea:disabled { color: #787878; } /* QScrollArea ------------------------------------------------------------ */ QScrollArea QWidget QWidget:disabled { background-color: #19232D; } /* QScrollBar ------------------------------------------------------------- */ QScrollBar:horizontal { height: 16px; margin: 2px 16px 2px 16px; border: 1px solid #32414B; border-radius: 4px; background-color: #19232D; } QScrollBar::handle:horizontal { background-color: #787878; border: 1px solid #32414B; border-radius: 4px; min-width: 8px; } QScrollBar::handle:horizontal:hover { background-color: #148CD2; border: 1px solid #148CD2; border-radius: 4px; min-width: 8px; } QScrollBar::add-line:horizontal { margin: 0px 0px 0px 0px; border-image: url(:/qss_icons/rc/right_arrow_disabled.png); width: 10px; height: 10px; subcontrol-position: right; subcontrol-origin: margin; } QScrollBar::sub-line:horizontal { margin: 0px 3px 0px 3px; border-image: url(:/qss_icons/rc/left_arrow_disabled.png); height: 10px; width: 10px; subcontrol-position: left; subcontrol-origin: margin; } QScrollBar::add-line:horizontal:hover, QScrollBar::add-line:horizontal:on { border-image: url(:/qss_icons/rc/right_arrow.png); height: 10px; width: 10px; subcontrol-position: right; subcontrol-origin: margin; } QScrollBar::sub-line:horizontal:hover, QScrollBar::sub-line:horizontal:on { border-image: url(:/qss_icons/rc/left_arrow.png); height: 10px; width: 10px; subcontrol-position: left; subcontrol-origin: margin; } QScrollBar::up-arrow:horizontal, QScrollBar::down-arrow:horizontal { background: none; } QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { background: none; } QScrollBar:vertical { background-color: #19232D; width: 16px; margin: 16px 2px 16px 2px; border: 1px solid #32414B; border-radius: 4px; } QScrollBar::handle:vertical { background-color: #787878; border: 1px solid #32414B; min-height: 8px; border-radius: 4px; } QScrollBar::handle:vertical:hover { background-color: #148CD2; border: 1px solid #148CD2; border-radius: 4px; min-height: 8px; } QScrollBar::sub-line:vertical { margin: 3px 0px 3px 0px; border-image: url(:/qss_icons/rc/up_arrow_disabled.png); height: 10px; width: 10px; subcontrol-position: top; subcontrol-origin: margin; } QScrollBar::add-line:vertical { margin: 3px 0px 3px 0px; border-image: url(:/qss_icons/rc/down_arrow_disabled.png); height: 10px; width: 10px; subcontrol-position: bottom; subcontrol-origin: margin; } QScrollBar::sub-line:vertical:hover, QScrollBar::sub-line:vertical:on { border-image: url(:/qss_icons/rc/up_arrow.png); height: 10px; width: 10px; subcontrol-position: top; subcontrol-origin: margin; } QScrollBar::add-line:vertical:hover, QScrollBar::add-line:vertical:on { border-image: url(:/qss_icons/rc/down_arrow.png); height: 10px; width: 10px; subcontrol-position: bottom; subcontrol-origin: margin; } QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical { background: none; } QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { background: none; } /* QTextEdit--------------------------------------------------------------- */ QTextEdit { background-color: #19232D; color: #F0F0F0; border: 1px solid #32414B; } QTextEdit:hover { border: 1px solid #148CD2; color: #F0F0F0; } QTextEdit:selected { background: #1464A0; color: #32414B; } /* QPlainTextEdit --------------------------------------------------------- */ QPlainTextEdit { background-color: #19232D; color: #F0F0F0; border-radius: 4px; border: 1px solid #32414B; } QPlainTextEdit:hover { border: 1px solid #148CD2; color: #F0F0F0; } QPlainTextEdit:selected { background: #1464A0; color: #32414B; } /* QSizeGrip --------------------------------------------------------------- */ QSizeGrip { image: url(:/qss_icons/rc/sizegrip.png); width: 12px; height: 12px; } /* QStackedWidget --------------------------------------------------------- */ QStackedWidget { padding: 4px; border: 1px solid #32414B; border: 1px solid #19232D; } /* QToolBar --------------------------------------------------------------- */ QToolBar { background-color: #32414B; border-bottom: 1px solid #19232D; padding: 2px; font-weight: bold; } QToolBar QToolButton{ background-color: #32414B; } QToolBar::handle:horizontal { width: 6px; image: url(:/qss_icons/rc/Hmovetoolbar.png); } QToolBar::handle:vertical { height: 6px; image: url(:/qss_icons/rc/Vmovetoolbar.png); } QToolBar::separator:horizontal { width: 3px; image: url(:/qss_icons/rc/Hsepartoolbar.png); } QToolBar::separator:vertical { height: 3px; image: url(:/qss_icons/rc/Vsepartoolbar.png); } QToolButton#qt_toolbar_ext_button { background: #32414B; border: 0px; color: #F0F0F0; image: url(:/qss_icons/rc/right_arrow.png); } /* QAbstractSpinBox ------------------------------------------------------- */ QAbstractSpinBox { background-color: #19232D; border: 1px solid #32414B; color: #F0F0F0; padding-top: 2px; /* This fix 103, 111*/ padding-bottom: 2px; /* This fix 103, 111*/ padding-left: 4px; padding-right: 4px; border-radius: 4px; /* min-width: 5px; removed to fix 109 */ } QAbstractSpinBox:up-button { background-color: transparent #19232D; subcontrol-origin: border; subcontrol-position: top right; border-left: 1px solid #32414B; margin: 1px; } QAbstractSpinBox::up-arrow, QAbstractSpinBox::up-arrow:disabled, QAbstractSpinBox::up-arrow:off { image: url(:/qss_icons/rc/up_arrow_disabled.png); width: 9px; height: 9px; } QAbstractSpinBox::up-arrow:hover { image: url(:/qss_icons/rc/up_arrow.png); } QAbstractSpinBox:down-button { background-color: transparent #19232D; subcontrol-origin: border; subcontrol-position: bottom right; border-left: 1px solid #32414B; margin: 1px; } QAbstractSpinBox::down-arrow, QAbstractSpinBox::down-arrow:disabled, QAbstractSpinBox::down-arrow:off { image: url(:/qss_icons/rc/down_arrow_disabled.png); width: 9px; height: 9px; } QAbstractSpinBox::down-arrow:hover { image: url(:/qss_icons/rc/down_arrow.png); } QAbstractSpinBox:hover{ border: 1px solid #148CD2; color: #F0F0F0; } QAbstractSpinBox:selected { background: #1464A0; color: #32414B; } /* ------------------------------------------------------------------------ */ /* DISPLAYS --------------------------------------------------------------- */ /* ------------------------------------------------------------------------ */ /* QLabel ----------------------------------------------------------------- */ QLabel { background-color: #19232D; border: 0px solid #32414B; padding: 2px; margin: 0px; color: #F0F0F0 } QLabel::disabled { background-color: #19232D; border: 0px solid #32414B; color: #787878; } /* QTextBrowser ----------------------------------------------------------- */ QTextBrowser { background-color: #19232D; border: 1px solid #32414B; color: #F0F0F0; border-radius: 4px; } QTextBrowser:disabled { background-color: #19232D; border: 1px solid #32414B; color: #787878; border-radius: 4px; } QTextBrowser:hover, QTextBrowser:!hover, QTextBrowser::selected, QTextBrowser::pressed { border: 1px solid #32414B; } /* QGraphicsView --------------------------------------------------------- */ QGraphicsView { background-color: #19232D; border: 1px solid #32414B; color: #F0F0F0; border-radius: 4px; } QGraphicsView:disabled { background-color: #19232D; border: 1px solid #32414B; color: #787878; border-radius: 4px; } QGraphicsView:hover, QGraphicsView:!hover, QGraphicsView::selected, QGraphicsView::pressed { border: 1px solid #32414B; } /* QCalendarWidget -------------------------------------------------------- */ QCalendarWidget { border: 1px solid #32414B; border-radius: 4px; } QCalendarWidget:disabled { background-color: #19232D; color: #787878; } /* QLCDNumber ------------------------------------------------------------- */ QLCDNumber { background-color: #19232D; color: #F0F0F0; } QLCDNumber:disabled { background-color: #19232D; color: #787878; } /* QProgressBar ----------------------------------------------------------- */ QProgressBar { background-color: #19232D; border: 1px solid #32414B; color: #F0F0F0; border-radius: 4px; text-align: center; } QProgressBar:disabled { background-color: #19232D; border: 1px solid #32414B; color: #787878; border-radius: 4px; text-align: center; } QProgressBar::chunk { background-color: #1464A0; color: #19232D; border-radius: 4px; } QProgressBar::chunk:disabled { background-color: #14506E; color: #787878; border-radius: 4px; } /* ------------------------------------------------------------------------ */ /* BUTTONS ---------------------------------------------------------------- */ /* ------------------------------------------------------------------------ */ /* QPushButton ------------------------------------------------------------ */ QPushButton { background-color: #505F69 ; border: 1px solid #32414B; color: #F0F0F0; border-radius: 4px; padding: 3px; outline: none; } QPushButton:disabled { background-color: #32414B; border: 1px solid #32414B; color: #787878; border-radius: 4px; padding: 3px; } QPushButton:checked { background-color: #32414B; border: 1px solid #32414B; border-radius: 4px; padding: 3px; outline: none; } QPushButton:checked:disabled { background-color: #19232D; border: 1px solid #32414B; color: #787878; border-radius: 4px; padding: 3px; outline: none; } QPushButton::menu-indicator { subcontrol-origin: padding; subcontrol-position: bottom right; bottom: 4px; } QPushButton:pressed { background-color: #19232D; border: 1px solid #19232D; } QPushButton:hover, QPushButton:checked:hover{ border: 1px solid #148CD2; color: #F0F0F0; } QPushButton:selected, QPushButton:checked:selected{ background: #1464A0; color: #32414B; } /* QToolButton ------------------------------------------------------------ */ QToolButton { background-color: transparent; border: 1px solid #32414B; border-radius: 4px; margin: 0px; padding: 2px; } QToolButton:checked { background-color: #19232D; border: 1px solid #19232D; } QToolButton:disabled { border: 1px solid #32414B; } QToolButton:hover, QToolButton:checked:hover{ border: 1px solid #148CD2; } /* the subcontrols below are used only in the MenuButtonPopup mode */ QToolButton[popupMode="1"] { padding: 2px; padding-right: 12px; /* only for MenuButtonPopup */ border: 1px solid #32414B; /* make way for the popup button */ border-radius: 4px; } /* The subcontrol below is used only in the InstantPopup or DelayedPopup mode */ QToolButton[popupMode="2"] { padding: 2px; padding-right: 12px; /* only for InstantPopup */ border: 1px solid #32414B; /* make way for the popup button */ } QToolButton::menu-button { padding: 2px; border-radius: 4px; border: 1px solid #32414B; border-top-right-radius: 4px; border-bottom-right-radius: 4px; /* 16px width + 4px for border = 20px allocated above */ width: 16px; outline: none; } QToolButton::menu-button:hover, QToolButton::menu-button:checked:hover { border: 1px solid #148CD2; } QToolButton::menu-indicator { image: url(:/qss_icons/rc/down_arrow.png); top: -8px; /* shift it a bit */ left: -4px; /* shift it a bit */ } QToolButton::menu-arrow { image: url(:/qss_icons/rc/down_arrow.png); } QToolButton::menu-arrow:open { border: 1px solid #32414B; } /* QCommandLinkButton ----------------------------------------------------- */ QCommandLinkButton { background-color: transparent; border: 1px solid #32414B; color: #F0F0F0; border-radius: 4px; padding: 0px; margin: 0px; } QCommandLinkButton:disabled { background-color: transparent; color: #787878; } /* ------------------------------------------------------------------------ */ /* INPUTS - NO FIELDS ----------------------------------------------------- */ /* ------------------------------------------------------------------------ */ /* QCombobox -------------------------------------------------------------- */ QComboBox { border: 1px solid #32414B; border-radius: 4px; selection-background-color: #1464A0; padding-top: 2px; /* This fix #103, #111*/ padding-bottom: 2px; /* This fix #103, #111*/ padding-left: 4px; padding-right: 4px; /* min-width: 75px; removed to fix 109 */ } QComboBox:disabled { background-color: #19232D; color: #787878; } QComboBox:hover{ border: 1px solid #148CD2; } QComboBox:on { selection-background-color: #19232D; } QComboBox QAbstractItemView { background-color: #19232D; border-radius: 4px; border: 1px solid #32414B; selection-color: #148CD2; selection-background-color: #32414B; } QComboBox::drop-down { subcontrol-origin: padding; subcontrol-position: top right; width: 20px; border-left-width: 0px; border-left-color: #32414B; border-left-style: solid; border-top-right-radius: 3px; border-bottom-right-radius: 3px; } QComboBox::down-arrow { image: url(:/qss_icons/rc/down_arrow_disabled.png); } QComboBox::down-arrow:on, QComboBox::down-arrow:hover, QComboBox::down-arrow:focus { image: url(:/qss_icons/rc/down_arrow.png); } /* QSlider ---------------------------------------------------------------- */ QSlider:disabled { background: #19232D; } QSlider:focus { border: none; } QSlider::groove:horizontal { background: #32414B; border: 1px solid #32414B; height: 4px; margin: 0px; border-radius: 4px; } QSlider::sub-page:horizontal { background: #1464A0; border: 1px solid #32414B; height: 4px; margin: 0px; border-radius: 4px; } QSlider::sub-page:horizontal:disabled { background: #14506E; } QSlider::handle:horizontal { background: #787878; border: 1px solid #32414B; width: 8px; height: 8px; margin: -8px 0; border-radius: 4px; } QSlider::handle:horizontal:hover { background: #148CD2; border: 1px solid #148CD2; } QSlider::groove:vertical { background: #32414B; border: 1px solid #32414B; width: 4px; margin: 0px; border-radius: 4px; } QSlider::sub-page:vertical { background: #1464A0; border: 1px solid #32414B; width: 4px; margin: 0px; border-radius: 4px; } QSlider::sub-page:vertical:disabled { background: #14506E; } QSlider::handle:vertical { background: #787878; border: 1px solid #32414B; width: 8px; height: 8px; margin: 0 -8px; border-radius: 4px; } QSlider::handle:vertical:hover { background: #148CD2; border: 1px solid #148CD2; } /* QLine ------------------------------------------------------------------ */ QLineEdit { background-color: #19232D; padding-top: 2px; /* This QLineEdit fix 103, 111 */ padding-bottom: 2px; /* This QLineEdit fix 103, 111 */ padding-left: 4px; padding-right: 4px; border-style: solid; border: 1px solid #32414B; border-radius: 4px; color: #F0F0F0; } QLineEdit:disabled { background-color: #19232D; color: #787878; } QLineEdit:hover{ border: 1px solid #148CD2; color: #F0F0F0; } QLineEdit:selected{ background: #1464A0; color: #32414B; } /* QTabWiget -------------------------------------------------------------- */ QTabWidget { padding: 2px; selection-background-color: #32414B; } QTabWidget QFrame{ border: 0; } QTabWidget::pane { border: 1px solid #32414B; border-radius: 4px; padding: 2px; margin: 0px; } QTabWidget::pane:selected { background-color: #32414B; border: 1px solid #1464A0; } /* QTabBar ---------------------------------------------------------------- */ QTabBar { qproperty-drawBase: 0; border-radius: 4px; margin: 0px; padding: 2px; border: 0; /* left: 5px; move to the right by 5px - removed for fix */ } QTabBar::close-button { border: 0; margin: 2px; padding: 0; image: url(:/qss_icons/rc/close.png); } QTabBar::close-button:hover { image: url(:/qss_icons/rc/close-hover.png); } QTabBar::close-button:pressed { image: url(:/qss_icons/rc/close-pressed.png); } /* QTabBar::tab - selected ----------------------------------------------- */ QTabBar::tab:top:selected:disabled { border-bottom: 3px solid #14506E; color: #787878; background-color: #32414B; } QTabBar::tab:bottom:selected:disabled { border-top: 3px solid #14506E; color: #787878; background-color: #32414B; } QTabBar::tab:left:selected:disabled { border-left: 3px solid #14506E; color: #787878; background-color: #32414B; } QTabBar::tab:right:selected:disabled { border-right: 3px solid #14506E; color: #787878; background-color: #32414B; } /* QTabBar::tab - !selected and disabled ---------------------------------- */ QTabBar::tab:top:!selected:disabled { border-bottom: 3px solid #19232D; color: #787878; background-color: #19232D; } QTabBar::tab:bottom:!selected:disabled { border-top: 3px solid #19232D; color: #787878; background-color: #19232D; } QTabBar::tab:left:!selected:disabled { border-right: 3px solid #19232D; color: #787878; background-color: #19232D; } QTabBar::tab:right:!selected:disabled { border-left: 3px solid #19232D; color: #787878; background-color: #19232D; } /* QTabBar::tab - selected ----------------------------------------------- */ QTabBar::tab:top:!selected { border-bottom: 2px solid #19232D; margin-top: 2px; } QTabBar::tab:bottom:!selected { border-top: 2px solid #19232D; margin-bottom: 3px; } QTabBar::tab:left:!selected { border-left: 2px solid #19232D; margin-right: 2px; } QTabBar::tab:right:!selected { border-right: 2px solid #19232D; margin-left: 2px; } QTabBar::tab:top { background-color: #32414B; color: #F0F0F0; margin-left: 2px; padding-left: 4px; padding-right: 4px; padding-top: 2px; padding-bottom: 2px; min-width: 5px; border-bottom: 3px solid #32414B; border-top-left-radius: 3px; border-top-right-radius: 3px; } QTabBar::tab:top:selected { background-color: #505F69; color: #F0F0F0; border-bottom: 3px solid #1464A0; border-top-left-radius: 3px; border-top-right-radius: 3px; } QTabBar::tab:top:!selected:hover { border: 1px solid #148CD2; border-bottom: 3px solid #148CD2; padding: 0px; } QTabBar::tab:bottom { color: #F0F0F0; border-top: 3px solid #32414B; background-color: #32414B; margin-left: 2px; padding-left: 4px; padding-right: 4px; padding-top: 2px; padding-bottom: 2px; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; min-width: 5px; } QTabBar::tab:bottom:selected { color: #F0F0F0; background-color: #505F69; border-top: 3px solid #1464A0; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; } QTabBar::tab:bottom:!selected:hover { border: 1px solid #148CD2; border-top: 3px solid #148CD2; padding: 0px; } QTabBar::tab:left { color: #F0F0F0; background-color: #32414B; margin-top: 2px; padding-left: 2px; padding-right: 2px; padding-top: 4px; padding-bottom: 4px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; min-height: 5px; } QTabBar::tab:left:selected { color: #F0F0F0; background-color: #505F69; border-left: 3px solid #1464A0; border-top-right-radius: 3px; border-bottom-right-radius: 3px; } QTabBar::tab:left:!selected:hover { border: 1px solid #148CD2; border-left: 3px solid #148CD2; padding: 0px; } QTabBar::tab:right { color: #F0F0F0; background-color: #32414B; margin-top: 2px; padding-left: 2px; padding-right: 2px; padding-top: 4px; padding-bottom: 4px; border-top-left-radius: 3px; border-bottom-left-radius: 3px; min-height: 5px; } QTabBar::tab:right:selected { color: #F0F0F0; background-color: #505F69; border-right: 3px solid #1464A0; border-top-left-radius: 3px; border-bottom-left-radius: 3px; } QTabBar::tab:right:!selected:hover { border: 1px solid #148CD2; border-right: 3px solid #148CD2; padding: 0px; } QTabBar QToolButton::right-arrow:enabled { image: url(:/qss_icons/rc/right_arrow.png); } QTabBar QToolButton::left-arrow:enabled { image: url(:/qss_icons/rc/left_arrow.png); } QTabBar QToolButton::right-arrow:disabled { image: url(:/qss_icons/rc/right_arrow_disabled.png); } QTabBar QToolButton::left-arrow:disabled { image: url(:/qss_icons/rc/left_arrow_disabled.png); } /* Some examples from internet to check QTabBar::tabButton() and QTabBar::tabIcon() QTabBar::tear {width: 0px; border: none;} QTabBar::tear {image: url(tear_indicator.png);} QTabBar::scroller{width:85pix;} QTabBar QToolbutton{background-color:"light blue";} But that left the buttons transparant. Looked confusing as the tab buttons migrated behind the scroller buttons. So we had to color the back ground of the scroller buttons */ /* QDockWiget ------------------------------------------------------------- */ QDockWidget { outline: 1px solid #32414B; background-color: #19232D; border: 1px solid #32414B; border-radius: 4px; titlebar-close-icon: url(:/qss_icons/rc/close.png); titlebar-normal-icon: url(:/qss_icons/rc/undock.png); } QDockWidget::title { padding: 6px; /* better size for title bar */ border: none; background-color: #32414B; } QDockWidget::close-button { background-color: #32414B; border-radius: 4px; border: none; } QDockWidget::close-button:hover { border: 1px solid #32414B; } QDockWidget::close-button:pressed { border: 1px solid #32414B; } QDockWidget::float-button { background-color: #32414B; border-radius: 4px; border: none; } QDockWidget::float-button:hover { border: 1px solid #32414B; } QDockWidget::float-button:pressed { border: 1px solid #32414B; } /* QTreeView QTableView QListView ----------------------------------------- */ QTreeView:branch:selected, QTreeView:branch:hover { background: url(:/qss_icons/rc/transparent.png); } QTreeView::branch:has-siblings:!adjoins-item { border-image: url(:/qss_icons/rc/transparent.png); } QTreeView::branch:has-siblings:adjoins-item { border-image: url(:/qss_icons/rc/transparent.png); } QTreeView::branch:!has-children:!has-siblings:adjoins-item { border-image: url(:/qss_icons/rc/transparent.png); } QTreeView::branch:has-children:!has-siblings:closed, QTreeView::branch:closed:has-children:has-siblings { image: url(:/qss_icons/rc/branch_closed.png); } QTreeView::branch:open:has-children:!has-siblings, QTreeView::branch:open:has-children:has-siblings { image: url(:/qss_icons/rc/branch_open.png); } QTreeView::branch:has-children:!has-siblings:closed:hover, QTreeView::branch:closed:has-children:has-siblings:hover { image: url(:/qss_icons/rc/branch_closed-on.png); } QTreeView::branch:open:has-children:!has-siblings:hover, QTreeView::branch:open:has-children:has-siblings:hover { image: url(:/qss_icons/rc/branch_open-on.png); } QListView::item:!selected:hover, QTreeView::item:!selected:hover, QTableView::item:!selected:hover, QColumnView::item:!selected:hover { outline: 0; color: #148CD2; background-color: #32414B; } QListView::item:selected:hover, QTreeView::item:selected:hover, QTableView::item:selected:hover, QColumnView::item:selected:hover { background: #1464A0; color: #19232D; } QTreeView::indicator:checked, QListView::indicator:checked { image: url(:/qss_icons/rc/checkbox_checked.png); } QTreeView::indicator:unchecked, QListView::indicator:unchecked { image: url(:/qss_icons/rc/checkbox_unchecked.png); } QTreeView::indicator:checked:hover, QTreeView::indicator:checked:focus, QTreeView::indicator:checked:pressed, QListView::indicator:checked:hover, QListView::indicator:checked:focus, QListView::indicator:checked:pressed { image: url(:/qss_icons/rc/checkbox_checked_focus.png); } QTreeView::indicator:unchecked:hover, QTreeView::indicator:unchecked:focus, QTreeView::indicator:unchecked:pressed, QListView::indicator:unchecked:hover, QListView::indicator:unchecked:focus, QListView::indicator:unchecked:pressed { image: url(:/qss_icons/rc/checkbox_unchecked_focus.png); } QTreeView::indicator:indeterminate:hover, QTreeView::indicator:indeterminate:focus, QTreeView::indicator:indeterminate:pressed, QListView::indicator:indeterminate:hover, QListView::indicator:indeterminate:focus, QListView::indicator:indeterminate:pressed { image: url(:/qss_icons/rc/checkbox_indeterminate_focus.png); } QTreeView::indicator:indeterminate, QListView::indicator:indeterminate { image: url(:/qss_icons/rc/checkbox_indeterminate.png); } QListView, QTreeView, QTableView, QColumnView { background-color: #19232D; border: 1px solid #32414B; color: #F0F0F0; gridline-color: #32414B; border-radius: 4px; } QListView:disabled, QTreeView:disabled, QTableView:disabled, QColumnView:disabled { background-color: #19232D; color: #787878; } QListView:selected, QTreeView:selected, QTableView:selected, QColumnView:selected { background: #1464A0; color: #32414B; } QListView:hover, QTreeView::hover, QTableView::hover, QColumnView::hover { background-color: #19232D; border: 1px solid #148CD2; } QListView::item:pressed, QTreeView::item:pressed, QTableView::item:pressed, QColumnView::item:pressed { background-color: #1464A0; } QListView::item:selected:active, QTreeView::item:selected:active, QTableView::item:selected:active, QColumnView::item:selected:active { background-color: #1464A0; } QTableCornerButton::section { background-color: #19232D; border: 1px transparent #32414B; border-radius: 0px; } /* QHeaderView ------------------------------------------------------------ */ QHeaderView { background-color: #32414B; border: 0px transparent #32414B; padding: 0px; margin: 0px; border-radius: 0px; } QHeaderView:disabled { background-color: #32414B; border: 1px transparent #32414B; padding: 2px; } QHeaderView::section { background-color: #32414B; color: #F0F0F0; padding: 2px; border-radius: 0px; text-align: left; } QHeaderView::section:checked { color: #F0F0F0; background-color: #1464A0; } QHeaderView::section:checked:disabled { color: #787878; background-color: #14506E; } QHeaderView::section::horizontal:disabled, QHeaderView::section::vertical:disabled { color: #787878; } QHeaderView::section::vertical::first, QHeaderView::section::vertical::only-one { border-top: 1px solid #32414B; } QHeaderView::section::vertical { border-top: 1px solid #19232D; } QHeaderView::section::horizontal::first, QHeaderView::section::horizontal::only-one { border-left: 1px solid #32414B; } QHeaderView::section::horizontal { border-left: 1px solid #19232D; } /* Those settings (border/width/height/background-color) solve bug */ /* transparent arrow background and size */ QHeaderView::down-arrow { background-color: #32414B; width: 16px; height: 16px; border-right: 1px solid #19232D; image: url(:/qss_icons/rc/down_arrow.png); } QHeaderView::up-arrow { background-color: #32414B; width: 16px; height: 16px; border-right: 1px solid #19232D; image: url(:/qss_icons/rc/up_arrow.png); } /* QToolBox -------------------------------------------------------------- */ QToolBox { padding: 0px; border: 1px solid #32414B; } QToolBox::selected { padding: 0px; border: 2px solid #1464A0; } QToolBox::tab { background-color: #19232D; border: 1px solid #32414B; color: #F0F0F0; border-top-left-radius: 4px; border-top-right-radius: 4px; } QToolBox::tab:disabled { color: #787878; } QToolBox::tab:selected { background-color: #505F69; border-bottom: 2px solid #1464A0; } QToolBox::tab:!selected { background-color: #32414B; border-bottom: 2px solid #32414B; } QToolBox::tab:selected:disabled { background-color: #32414B; border-bottom: 2px solid #14506E; } QToolBox::tab:!selected:disabled { background-color: #19232D; } QToolBox::tab:hover { border-color: #148CD2; border-bottom: 2px solid #148CD2; } QToolBox QScrollArea QWidget QWidget { padding: 0px; background-color: #19232D; } /* QFrame ----------------------------------------------------------------- */ QFrame { border-radius: 4px; border: 1px solid #32414B; } QFrame[frameShape="0"] { border-radius: 4px; border: 1px transparent #32414B; } QFrame[height="3"], QFrame[width="3"] { background-color: #19232D; } /* QSplitter -------------------------------------------------------------- */ QSplitter { background-color: #32414B; spacing: 0; padding: 0; margin: 0; } QSplitter::separator { background-color: #32414B; border: 0 solid #19232D; spacing: 0; padding: 1px; margin: 0; } QSplitter::separator:hover { background-color: #787878; } QSplitter::separator:horizontal { width: 5px; image: url(:/qss_icons/rc/Vsepartoolbar.png); } QSplitter::separator:vertical { height: 5px; image: url(:/qss_icons/rc/Hsepartoolbar.png); } /* QDateEdit-------------------------------------------------------------- */ QDateEdit { selection-background-color: #1464A0; border-style: solid; border: 1px solid #32414B; border-radius: 4px; padding-top: 2px; /* This fix #103, #111*/ padding-bottom: 2px; /* This fix #103, #111*/ padding-left: 4px; padding-right: 4px; min-width: 10px; } QDateEdit:on { selection-background-color: #1464A0; } QDateEdit::drop-down { subcontrol-origin: padding; subcontrol-position: top right; width: 20px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; } QDateEdit::down-arrow { image: url(:/qss_icons/rc/down_arrow_disabled.png); } QDateEdit::down-arrow:on, QDateEdit::down-arrow:hover, QDateEdit::down-arrow:focus { image: url(:/qss_icons/rc/down_arrow.png); } QDateEdit QAbstractItemView { background-color: #19232D; border-radius: 4px; border: 1px solid #32414B; selection-background-color: #1464A0; } QAbstractView:hover{ border: 1px solid #148CD2; color: #F0F0F0; } QAbstractView:selected { background: #1464A0; color: #32414B; } ================================================ FILE: 3rdpart/qhexview/.gitignore ================================================ build moc_* *.o example/qhexview Makefile ================================================ FILE: 3rdpart/qhexview/LICENSE ================================================ The MIT License (MIT) Copyright (c) 2015 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: 3rdpart/qhexview/README.md ================================================ QHexView ========== This is Qt widget for display binary data in traditional hex-editor style. This widget doesn`t have any editing capabilities. Only viewing and copying. GUI ----- ![2015-03-29 13 49 54](https://cloud.githubusercontent.com/assets/10683398/6884566/d438a350-d61a-11e4-9c4b-16f67f5fdf07.png) Building the example ----- * cd QHexView * mkdir build * cd build * qmake ../example/qhexview.pro * make Usage ----- ... QByteArray data; ... QHexView *phexView = new QHexView; ... phexView -> setData(data); ... ================================================ FILE: 3rdpart/qhexview/include/QHexView.h ================================================ #ifndef Q_HEX_VIEWER_H_ #define Q_HEX_VIEWER_H_ #include #include #include class QHexView: public QAbstractScrollArea { public: class DataStorage { public: virtual ~DataStorage() {} virtual QByteArray getData(std::size_t position, std::size_t length) = 0; virtual std::size_t size() = 0; }; class DataStorageArray: public DataStorage { public: DataStorageArray(const QByteArray &arr); virtual QByteArray getData(std::size_t position, std::size_t length); virtual std::size_t size(); private: QByteArray m_data; }; class DataStorageFile: public DataStorage { public: DataStorageFile(const QString &fileName); virtual QByteArray getData(std::size_t position, std::size_t length); virtual std::size_t size(); private: QFile m_file; }; QHexView(QWidget *parent = nullptr); ~QHexView(); public slots: void setData(QHexView::DataStorage *pData); void clear(); void showFromOffset(std::size_t offset); protected: void paintEvent(QPaintEvent *event); void keyPressEvent(QKeyEvent *event); void mouseMoveEvent(QMouseEvent *event); void mousePressEvent(QMouseEvent *event); private: DataStorage *m_pdata; std::size_t m_posAddr; std::size_t m_posHex; std::size_t m_posAscii; std::size_t m_charWidth; std::size_t m_charHeight; std::size_t m_selectBegin; std::size_t m_selectEnd; std::size_t m_selectInit; std::size_t m_cursorPos; QSize fullSize() const; void resetSelection(); void resetSelection(int pos); void setSelection(int pos); void ensureVisible(); protected: void setCursorPos(int pos); std::size_t cursorPos(const QPoint &position); int cursorPos() const { return m_cursorPos; } }; #endif ================================================ FILE: 3rdpart/qhexview/qhexview.pri ================================================ SOURCES += $$PWD/src/QHexView.cpp HEADERS += $$PWD/include/QHexView.h INCLUDEPATH += $$PWD/include ================================================ FILE: 3rdpart/qhexview/src/QHexView.cpp ================================================ #include "../include/QHexView.h" #include #include #include #include #include #include #include #include #include const int HEXCHARS_IN_LINE = 47; const int GAP_ADR_HEX = 10; const int GAP_HEX_ASCII = 16; const int BYTES_PER_LINE = 16; QHexView::QHexView(QWidget *parent): QAbstractScrollArea(parent), m_pdata(NULL) { setFont(QFont("Courier", 10)); m_charWidth = fontMetrics().width(QLatin1Char('9')); m_charHeight = fontMetrics().height(); m_posAddr = 0; m_posHex = 10 * m_charWidth + GAP_ADR_HEX; m_posAscii = m_posHex + HEXCHARS_IN_LINE * m_charWidth + GAP_HEX_ASCII; setMinimumWidth(m_posAscii + (BYTES_PER_LINE * m_charWidth)); setFocusPolicy(Qt::StrongFocus); } QHexView::~QHexView() { if(m_pdata) delete m_pdata; } void QHexView::setData(QHexView::DataStorage *pData) { verticalScrollBar()->setValue(0); if(m_pdata) delete m_pdata; m_pdata = pData; m_cursorPos = 0; resetSelection(0); } void QHexView::showFromOffset(std::size_t offset) { if(m_pdata && offset < m_pdata->size()) { setCursorPos(offset * 2); int cursorY = m_cursorPos / (2 * BYTES_PER_LINE); verticalScrollBar() -> setValue(cursorY); } } void QHexView::clear() { verticalScrollBar()->setValue(0); } QSize QHexView::fullSize() const { if(!m_pdata) return QSize(0, 0); std::size_t width = m_posAscii + (BYTES_PER_LINE * m_charWidth); std::size_t height = m_pdata->size() / BYTES_PER_LINE; if(m_pdata->size() % BYTES_PER_LINE) height++; height *= m_charHeight; return QSize(width, height); } void QHexView::paintEvent(QPaintEvent *event) { if(!m_pdata) return; QPainter painter(viewport()); QSize areaSize = viewport()->size(); QSize widgetSize = fullSize(); verticalScrollBar()->setPageStep(areaSize.height() / m_charHeight); verticalScrollBar()->setRange(0, (widgetSize.height() - areaSize.height()) / m_charHeight + 1); int firstLineIdx = verticalScrollBar() -> value(); std::size_t lastLineIdx = firstLineIdx + areaSize.height() / m_charHeight; if(lastLineIdx > m_pdata->size() / BYTES_PER_LINE) { lastLineIdx = m_pdata->size() / BYTES_PER_LINE; if(m_pdata->size() % BYTES_PER_LINE) lastLineIdx++; } painter.fillRect(event->rect(), this->palette().color(QPalette::Base)); QColor addressAreaColor = QColor(0xd4, 0xd4, 0xd4, 0xff); painter.fillRect(QRect(m_posAddr, event->rect().top(), m_posHex - GAP_ADR_HEX + 2 , height()), addressAreaColor); int linePos = m_posAscii - (GAP_HEX_ASCII / 2); painter.setPen(Qt::gray); painter.drawLine(linePos, event->rect().top(), linePos, height()); painter.setPen(Qt::black); int yPosStart = m_charHeight; QBrush def = painter.brush(); QBrush selected = QBrush(QColor(0x6d, 0x9e, 0xff, 0xff)); QByteArray data = m_pdata->getData(firstLineIdx * BYTES_PER_LINE, (lastLineIdx - firstLineIdx) * BYTES_PER_LINE); for (int lineIdx = firstLineIdx, yPos = yPosStart; lineIdx < int(lastLineIdx); lineIdx += 1, yPos += m_charHeight) { QString address = QString("%1").arg(lineIdx * 16, 10, 16, QChar('0')); painter.drawText(m_posAddr, yPos, address); for(int xPos = m_posHex, i=0; i= m_selectBegin && pos < m_selectEnd) { painter.setBackground(selected); painter.setBackgroundMode(Qt::OpaqueMode); } QString val = QString::number((data.at((lineIdx - firstLineIdx) * BYTES_PER_LINE + i) & 0xF0) >> 4, 16); painter.drawText(xPos, yPos, val); if((pos+1) >= m_selectBegin && (pos+1) < m_selectEnd) { painter.setBackground(selected); painter.setBackgroundMode(Qt::OpaqueMode); } else { painter.setBackground(def); painter.setBackgroundMode(Qt::OpaqueMode); } val = QString::number((data.at((lineIdx - firstLineIdx) * BYTES_PER_LINE + i) & 0xF), 16); painter.drawText(xPos+m_charWidth, yPos, val); painter.setBackground(def); painter.setBackgroundMode(Qt::OpaqueMode); } for (int xPosAscii = m_posAscii, i=0; ((lineIdx - firstLineIdx) * BYTES_PER_LINE + i) < data.size() && (i < BYTES_PER_LINE); i++, xPosAscii += m_charWidth) { char ch = data[(lineIdx - firstLineIdx) * BYTES_PER_LINE + i]; if ((ch < 0x20) or (ch > 0x7e)) ch = '.'; painter.drawText(xPosAscii, yPos, QString(ch)); } } if (hasFocus()) { int x = (m_cursorPos % (2 * BYTES_PER_LINE)); int y = m_cursorPos / (2 * BYTES_PER_LINE); y -= firstLineIdx; int cursorX = (((x / 2) * 3) + (x % 2)) * m_charWidth + m_posHex; int cursorY = y * m_charHeight + 4; painter.fillRect(cursorX, cursorY, 2, m_charHeight, this->palette().color(QPalette::WindowText)); } } void QHexView::keyPressEvent(QKeyEvent *event) { bool setVisible = false; /*****************************************************************************/ /* Cursor movements */ /*****************************************************************************/ if(event->matches(QKeySequence::MoveToNextChar)) { setCursorPos(m_cursorPos + 1); resetSelection(m_cursorPos); setVisible = true; } if(event->matches(QKeySequence::MoveToPreviousChar)) { setCursorPos(m_cursorPos - 1); resetSelection(m_cursorPos); setVisible = true; } if(event->matches(QKeySequence::MoveToEndOfLine)) { setCursorPos(m_cursorPos | ((BYTES_PER_LINE * 2) - 1)); resetSelection(m_cursorPos); setVisible = true; } if(event->matches(QKeySequence::MoveToStartOfLine)) { setCursorPos(m_cursorPos | (m_cursorPos % (BYTES_PER_LINE * 2))); resetSelection(m_cursorPos); setVisible = true; } if(event->matches(QKeySequence::MoveToPreviousLine)) { setCursorPos(m_cursorPos - BYTES_PER_LINE * 2); resetSelection(m_cursorPos); setVisible = true; } if(event->matches(QKeySequence::MoveToNextLine)) { setCursorPos(m_cursorPos + BYTES_PER_LINE * 2); resetSelection(m_cursorPos); setVisible = true; } if(event->matches(QKeySequence::MoveToNextPage)) { setCursorPos(m_cursorPos + (viewport()->height() / m_charHeight - 1) * 2 * BYTES_PER_LINE); resetSelection(m_cursorPos); setVisible = true; } if(event->matches(QKeySequence::MoveToPreviousPage)) { setCursorPos(m_cursorPos - (viewport()->height() / m_charHeight - 1) * 2 * BYTES_PER_LINE); resetSelection(m_cursorPos); setVisible = true; } if(event->matches(QKeySequence::MoveToEndOfDocument)) { if(m_pdata) setCursorPos(m_pdata->size() * 2); resetSelection(m_cursorPos); setVisible = true; } if(event->matches(QKeySequence::MoveToStartOfDocument)) { setCursorPos(0); resetSelection(m_cursorPos); setVisible = true; } /*****************************************************************************/ /* Select commands */ /*****************************************************************************/ if (event->matches(QKeySequence::SelectAll)) { resetSelection(0); if(m_pdata) setSelection(2 * m_pdata->size() + 1); setVisible = true; } if (event->matches(QKeySequence::SelectNextChar)) { int pos = m_cursorPos + 1; setCursorPos(pos); setSelection(pos); setVisible = true; } if (event->matches(QKeySequence::SelectPreviousChar)) { int pos = m_cursorPos - 1; setSelection(pos); setCursorPos(pos); setVisible = true; } if (event->matches(QKeySequence::SelectEndOfLine)) { int pos = m_cursorPos - (m_cursorPos % (2 * BYTES_PER_LINE)) + (2 * BYTES_PER_LINE); setCursorPos(pos); setSelection(pos); setVisible = true; } if (event->matches(QKeySequence::SelectStartOfLine)) { int pos = m_cursorPos - (m_cursorPos % (2 * BYTES_PER_LINE)); setCursorPos(pos); setSelection(pos); setVisible = true; } if (event->matches(QKeySequence::SelectPreviousLine)) { int pos = m_cursorPos - (2 * BYTES_PER_LINE); setCursorPos(pos); setSelection(pos); setVisible = true; } if (event->matches(QKeySequence::SelectNextLine)) { int pos = m_cursorPos + (2 * BYTES_PER_LINE); setCursorPos(pos); setSelection(pos); setVisible = true; } if (event->matches(QKeySequence::SelectNextPage)) { int pos = m_cursorPos + (((viewport()->height() / m_charHeight) - 1) * 2 * BYTES_PER_LINE); setCursorPos(pos); setSelection(pos); setVisible = true; } if (event->matches(QKeySequence::SelectPreviousPage)) { int pos = m_cursorPos - (((viewport()->height() / m_charHeight) - 1) * 2 * BYTES_PER_LINE); setCursorPos(pos); setSelection(pos); setVisible = true; } if (event->matches(QKeySequence::SelectEndOfDocument)) { int pos = 0; if(m_pdata) pos = m_pdata->size() * 2; setCursorPos(pos); setSelection(pos); setVisible = true; } if (event->matches(QKeySequence::SelectStartOfDocument)) { int pos = 0; setCursorPos(pos); setSelection(pos); setVisible = true; } if (event->matches(QKeySequence::Copy)) { if(m_pdata) { QString res; int idx = 0; int copyOffset = 0; QByteArray data = m_pdata->getData(m_selectBegin / 2, (m_selectEnd - m_selectBegin) / 2 + 1); if(m_selectBegin % 2) { res += QString::number((data.at((idx+1) / 2) & 0xF), 16); res += " "; idx++; copyOffset = 1; } int selectedSize = m_selectEnd - m_selectBegin; for (;idx < selectedSize; idx+= 2) { QString val = QString::number((data.at((copyOffset + idx) / 2) & 0xF0) >> 4, 16); if(idx + 1 < selectedSize) { val += QString::number((data.at((copyOffset + idx) / 2) & 0xF), 16); val += " "; } res += val; if((idx/2) % BYTES_PER_LINE == (BYTES_PER_LINE - 1)) res += "\n"; } QClipboard *clipboard = QApplication::clipboard(); clipboard -> setText(res); } } if(setVisible) ensureVisible(); viewport() -> update(); } void QHexView::mouseMoveEvent(QMouseEvent * event) { int actPos = cursorPos(event->pos()); setCursorPos(actPos); setSelection(actPos); viewport() -> update(); } void QHexView::mousePressEvent(QMouseEvent * event) { int cPos = cursorPos(event->pos()); if((QApplication::keyboardModifiers() & Qt::ShiftModifier) && event -> button() == Qt::LeftButton) setSelection(cPos); else resetSelection(cPos); setCursorPos(cPos); viewport() -> update(); } std::size_t QHexView::cursorPos(const QPoint &position) { int pos = -1; if ((std::size_t(position.x()) >= m_posHex) && (std::size_t(position.x()) < (m_posHex + HEXCHARS_IN_LINE * m_charWidth))) { int x = (position.x() - m_posHex) / m_charWidth; if ((x % 3) == 0) x = (x / 3) * 2; else x = ((x / 3) * 2) + 1; int firstLineIdx = verticalScrollBar() -> value(); int y = (position.y() / m_charHeight) * 2 * BYTES_PER_LINE; pos = x + y + firstLineIdx * BYTES_PER_LINE * 2; } return pos; } void QHexView::resetSelection() { m_selectBegin = m_selectInit; m_selectEnd = m_selectInit; } void QHexView::resetSelection(int pos) { if (pos < 0) pos = 0; m_selectInit = pos; m_selectBegin = pos; m_selectEnd = pos; } void QHexView::setSelection(int pos) { if (pos < 0) pos = 0; if (std::size_t(pos) >= m_selectInit) { m_selectEnd = pos; m_selectBegin = m_selectInit; } else { m_selectBegin = pos; m_selectEnd = m_selectInit; } } void QHexView::setCursorPos(int position) { if(position < 0) position = 0; int maxPos = 0; if(m_pdata) { maxPos = m_pdata->size() * 2; if(m_pdata->size() % BYTES_PER_LINE) maxPos++; } if(position > maxPos) position = maxPos; m_cursorPos = position; } void QHexView::ensureVisible() { QSize areaSize = viewport()->size(); int firstLineIdx = verticalScrollBar() -> value(); int lastLineIdx = firstLineIdx + areaSize.height() / m_charHeight; int cursorY = m_cursorPos / (2 * BYTES_PER_LINE); if(cursorY < firstLineIdx) verticalScrollBar() -> setValue(cursorY); else if(cursorY >= lastLineIdx) verticalScrollBar() -> setValue(cursorY - areaSize.height() / m_charHeight + 1); } QHexView::DataStorageArray::DataStorageArray(const QByteArray &arr) { m_data = arr; } QByteArray QHexView::DataStorageArray::getData(std::size_t position, std::size_t length) { return m_data.mid(position, length); } std::size_t QHexView::DataStorageArray::size() { return m_data.count(); } QHexView::DataStorageFile::DataStorageFile(const QString &fileName): m_file(fileName) { m_file.open(QIODevice::ReadOnly); if(!m_file.isOpen()) throw std::runtime_error(std::string("Failed to open file `") + fileName.toStdString() + "`"); } QByteArray QHexView::DataStorageFile::getData(std::size_t position, std::size_t length) { m_file.seek(position); return m_file.read(length); } std::size_t QHexView::DataStorageFile::size() { return m_file.size(); } ================================================ FILE: 3rdpart/qt-mustache-master/.gitignore ================================================ build-debug ================================================ FILE: 3rdpart/qt-mustache-master/.travis.yml ================================================ language: cpp env: - QT_SELECT=qt4 - QT_SELECT=qt5 before_install: - sudo add-apt-repository --yes ppa:ubuntu-sdk-team/ppa - sudo apt-get update -qq - sudo apt-get install -qq libqtcore4 qt4-qmake libqt5core5a qt5-qmake qt5-default qtchooser script: - qmake && make && ./qt-mustache ================================================ FILE: 3rdpart/qt-mustache-master/README.md ================================================ [![Build Status](https://travis-ci.org/robertknight/qt-mustache.svg?branch=master)](https://travis-ci.org/robertknight/qt-mustache) # Qt Mustache qt-mustache is a simple library for rendering [Mustache templates](http://mustache.github.com/). ### Example Usage ```cpp #include "mustache.h" QVariantHash contact; contact["name"] = "John Smith"; contact["email"] = "john.smith@gmail.com"; QString contactTemplate = "{{name}} {{email}}"; Mustache::Renderer renderer; Mustache::QtVariantContext context(contact); QTextStream output(stdout); output << renderer.render(contactTemplate, &context); ``` Outputs: `John Smith john.smith@gmail.com` For further examples, see the tests in `test_mustache.cpp` ### Building * To build the tests, run `qmake` followed by `make` * To use qt-mustache in your project, just add the `mustache.h` and `mustache.cpp` files to your project. ### License qt-mustache is licensed under the BSD license. ### Dependencies qt-mustache depends on the QtCore library. It is compatible with Qt 4 and Qt 5. ## Usage ### Syntax qt-mustache uses the standard Mustache syntax. See the [Mustache manual](http://mustache.github.com/mustache.5.html) for details. ### Data Sources qt-mustache expands Mustache tags using values from a `Mustache::Context`. `Mustache::QtVariantContext` is a simple context implementation which wraps a `QVariantHash` or `QVariantMap`. If you want to render a template using a custom data source, you can either create a `QVariantHash` which mirrors the data source or you can re-implement `Mustache::Context`. ### Partials When a `{{>partial}}` Mustache tag is encountered, qt-mustache will attempt to load the partial using a `Mustache::PartialResolver` provided by the context. `Mustache::PartialMap` is a simple resolver which takes a `QHash` map of partial names to values and looks up partials in that map. `Mustache::PartialFileLoader` is another simple resolver which fetches partials from `.mustache` files in a specified directory. You can re-implement the `Mustache::PartialResolver` interface if you want to load partials from a custom source (eg. a database). ### Error Handling If an error occurs when rendering a template, `Mustache::Renderer::errorPosition()` is set to non-negative value and template rendering stops. If the error occurs whilst rendering a partial template, `errorPartial()` contains the name of the partial. ### Lambdas The [Mustache manual](http://mustache.github.com/mustache.5.html) provides a mechanism to customize rendering of template sections by setting the value for a tag to a callable object (eg. a lambda in Ruby or Javascript), which takes the unrendered block of text for a template section and renders it itself. qt-mustache supports this via the `Context::canEval()` and `Context::eval()` methods. ================================================ FILE: 3rdpart/qt-mustache-master/qt-mustache.pri ================================================ INCLUDEPATH += $$PWD/src HEADERS += $$PWD/src/mustache.h SOURCES += $$PWD/src/mustache.cpp ================================================ FILE: 3rdpart/qt-mustache-master/qt-mustache.pro ================================================ ###################################################################### # Automatically generated by qmake (2.01a) Mon Aug 27 11:20:05 2012 ###################################################################### TEMPLATE = app DEPENDPATH += . src tests INCLUDEPATH += . src tests QT += testlib QT -= gui CONFIG -= app_bundle !win32 { QMAKE_CXXFLAGS += -Werror -Wall -Wextra -Wnon-virtual-dtor } # Input HEADERS += src/mustache.h tests/test_mustache.h SOURCES += src/mustache.cpp tests/test_mustache.cpp # Copies the given files to the destination directory defineTest(copyToDestdir) { files = $$1 for(FILE, files) { DDIR = $$OUT_PWD # Replace slashes in paths with backslashes for Windows win32:FILE ~= s,/,\\,g win32:DDIR ~= s,/,\\,g QMAKE_POST_LINK += $$QMAKE_COPY $$quote($$FILE) $$quote($$DDIR) $$escape_expand(\\n\\t) } export(QMAKE_POST_LINK) } copyToDestdir($$PWD/tests/*.mustache) copyToDestdir($$PWD/tests/specs/*.json) ================================================ FILE: 3rdpart/qt-mustache-master/src/mustache.cpp ================================================ /* Copyright 2012, Robert Knight Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. */ #include "mustache.h" #include #include #include #include using namespace Mustache; QString Mustache::renderTemplate(const QString& templateString, const QVariantHash& args) { Mustache::QtVariantContext context(args); Mustache::Renderer renderer; return renderer.render(templateString, &context); } QString escapeHtml(const QString& input) { QString escaped(input); for (int i=0; i < escaped.count();) { const char* replacement = 0; ushort ch = escaped.at(i).unicode(); if (ch == '&') { replacement = "&"; } else if (ch == '<') { replacement = "<"; } else if (ch == '>') { replacement = ">"; } else if (ch == '"') { replacement = """; } if (replacement) { escaped.replace(i, 1, QLatin1String(replacement)); i += (int)strlen(replacement); } else { ++i; } } return escaped; } QString unescapeHtml(const QString& escaped) { QString unescaped(escaped); unescaped.replace(QLatin1String("<"), QLatin1String("<")); unescaped.replace(QLatin1String(">"), QLatin1String(">")); unescaped.replace(QLatin1String("""), QLatin1String("\"")); unescaped.replace(QLatin1String("&"), QLatin1String("&")); return unescaped; } Context::Context(PartialResolver* resolver) : m_partialResolver(resolver) {} PartialResolver* Context::partialResolver() const { return m_partialResolver; } QString Context::partialValue(const QString& key) const { if (!m_partialResolver) { return QString(); } return m_partialResolver->getPartial(key); } bool Context::canEval(const QString&) const { return false; } QString Context::eval(const QString& key, const QString& _template, Renderer* renderer) { Q_UNUSED(key); Q_UNUSED(_template); Q_UNUSED(renderer); return QString(); } QtVariantContext::QtVariantContext(const QVariant& root, PartialResolver* resolver) : Context(resolver) { m_contextStack << root; } QVariant variantMapValue(const QVariant& value, const QString& key) { if (value.userType() == QVariant::Map) { return value.toMap().value(key); } else { return value.toHash().value(key); } } QVariant variantMapValueForKeyPath(const QVariant& value, const QStringList keyPath) { if (keyPath.count() > 1) { QVariant firstValue = variantMapValue(value, keyPath.first()); return firstValue.isNull() ? QVariant() : variantMapValueForKeyPath(firstValue, keyPath.mid(1)); } else if (!keyPath.isEmpty()) { return variantMapValue(value, keyPath.first()); } return QVariant(); } QVariant QtVariantContext::value(const QString& key) const { if (key == "." && !m_contextStack.isEmpty()) { return m_contextStack.last(); } QStringList keyPath = key.split("."); for (int i = m_contextStack.count()-1; i >= 0; i--) { QVariant value = variantMapValueForKeyPath(m_contextStack.at(i), keyPath); if (!value.isNull()) { return value; } } return QVariant(); } bool QtVariantContext::isFalse(const QString& key) const { QVariant value = this->value(key); switch (value.userType()) { case QMetaType::QChar: case QMetaType::Double: case QMetaType::Float: case QMetaType::Int: case QMetaType::UInt: case QMetaType::LongLong: case QMetaType::ULongLong: case QVariant::Bool: return !value.toBool(); case QVariant::List: case QVariant::StringList: return value.toList().isEmpty(); case QVariant::Hash: return value.toHash().isEmpty(); case QVariant::Map: return value.toMap().isEmpty(); default: return value.toString().isEmpty(); } } QString QtVariantContext::stringValue(const QString& key) const { if (isFalse(key) && value(key).userType() != QVariant::Bool) { return QString(); } return value(key).toString(); } void QtVariantContext::push(const QString& key, int index) { QVariant mapItem = value(key); if (index == -1) { m_contextStack << mapItem; } else { QVariantList list = mapItem.toList(); m_contextStack << list.value(index, QVariant()); } } void QtVariantContext::pop() { m_contextStack.pop(); } int QtVariantContext::listCount(const QString& key) const { const QVariant& item = value(key); if (item.canConvert()) { return item.toList().count(); } return 0; } bool QtVariantContext::canEval(const QString& key) const { return value(key).canConvert(); } QString QtVariantContext::eval(const QString& key, const QString& _template, Renderer* renderer) { QVariant fn = value(key); if (fn.isNull()) { return QString(); } return fn.value()(_template, renderer, this); } PartialMap::PartialMap(const QHash& partials) : m_partials(partials) {} QString PartialMap::getPartial(const QString& name) { return m_partials.value(name); } PartialFileLoader::PartialFileLoader(const QString& basePath) : m_basePath(basePath) {} QString PartialFileLoader::getPartial(const QString& name) { if (!m_cache.contains(name)) { QString path = m_basePath + '/' + name + ".mustache"; QFile file(path); if (file.open(QIODevice::ReadOnly)) { QTextStream stream(&file); m_cache.insert(name, stream.readAll()); } } return m_cache.value(name); } Renderer::Renderer() : m_errorPos(-1) , m_defaultTagStartMarker("{{") , m_defaultTagEndMarker("}}") { } QString Renderer::error() const { return m_error; } int Renderer::errorPos() const { return m_errorPos; } QString Renderer::errorPartial() const { return m_errorPartial; } QString Renderer::render(const QString& _template, Context* context) { m_error.clear(); m_errorPos = -1; m_errorPartial.clear(); m_tagStartMarker = m_defaultTagStartMarker; m_tagEndMarker = m_defaultTagEndMarker; return render(_template, 0, _template.length(), context); } QString Renderer::render(const QString& _template, int startPos, int endPos, Context* context) { QString output; int lastTagEnd = startPos; while (m_errorPos == -1) { Tag tag = findTag(_template, lastTagEnd, endPos); if (tag.type == Tag::Null) { output += _template.midRef(lastTagEnd, endPos - lastTagEnd); break; } output += _template.midRef(lastTagEnd, tag.start - lastTagEnd); switch (tag.type) { case Tag::Value: { QString value = context->stringValue(tag.key); if (tag.escapeMode == Tag::Escape) { value = escapeHtml(value); } else if (tag.escapeMode == Tag::Unescape) { value = unescapeHtml(value); } output += value; lastTagEnd = tag.end; } break; case Tag::SectionStart: { Tag endTag = findEndTag(_template, tag, endPos); if (endTag.type == Tag::Null) { if (m_errorPos == -1) { setError("No matching end tag found for section", tag.start); } } else { int listCount = context->listCount(tag.key); if (listCount > 0) { for (int i=0; i < listCount; i++) { context->push(tag.key, i); output += render(_template, tag.end, endTag.start, context); context->pop(); } } else if (context->canEval(tag.key)) { output += context->eval(tag.key, _template.mid(tag.end, endTag.start - tag.end), this); } else if (!context->isFalse(tag.key)) { context->push(tag.key); output += render(_template, tag.end, endTag.start, context); context->pop(); } lastTagEnd = endTag.end; } } break; case Tag::InvertedSectionStart: { Tag endTag = findEndTag(_template, tag, endPos); if (endTag.type == Tag::Null) { if (m_errorPos == -1) { setError("No matching end tag found for inverted section", tag.start); } } else { if (context->isFalse(tag.key)) { output += render(_template, tag.end, endTag.start, context); } lastTagEnd = endTag.end; } } break; case Tag::SectionEnd: setError("Unexpected end tag", tag.start); lastTagEnd = tag.end; break; case Tag::Partial: { QString tagStartMarker = m_tagStartMarker; QString tagEndMarker = m_tagEndMarker; m_tagStartMarker = m_defaultTagStartMarker; m_tagEndMarker = m_defaultTagEndMarker; m_partialStack.push(tag.key); QString partialContent = context->partialValue(tag.key); // If there is a need to add a special indentation to the partial if (tag.indentation > 0) { output += QString(" ").repeated(tag.indentation); // Indenting the output to keep the parent indentation. int posOfLF = partialContent.indexOf("\n", 0); while (posOfLF > 0 && posOfLF < (partialContent.length() - 1)) { // .length() - 1 because we dont want indentation AFTER the last character if it's a LF partialContent = partialContent.insert(posOfLF + 1, QString(" ").repeated(tag.indentation)); posOfLF = partialContent.indexOf("\n", posOfLF + 1); } } QString partialRendered = render(partialContent, 0, partialContent.length(), context); output += partialRendered; lastTagEnd = tag.end; m_partialStack.pop(); m_tagStartMarker = tagStartMarker; m_tagEndMarker = tagEndMarker; } break; case Tag::SetDelimiter: lastTagEnd = tag.end; break; case Tag::Comment: lastTagEnd = tag.end; break; case Tag::Null: break; } } return output; } void Renderer::setError(const QString& error, int pos) { Q_ASSERT(!error.isEmpty()); Q_ASSERT(pos >= 0); m_error = error; m_errorPos = pos; if (!m_partialStack.isEmpty()) { m_errorPartial = m_partialStack.top(); } } Tag Renderer::findTag(const QString& content, int pos, int endPos) { int tagStartPos = content.indexOf(m_tagStartMarker, pos); if (tagStartPos == -1 || tagStartPos >= endPos) { return Tag(); } int tagEndPos = content.indexOf(m_tagEndMarker, tagStartPos + m_tagStartMarker.length()); if (tagEndPos == -1) { return Tag(); } tagEndPos += m_tagEndMarker.length(); Tag tag; tag.type = Tag::Value; tag.start = tagStartPos; tag.end = tagEndPos; pos = tagStartPos + m_tagStartMarker.length(); endPos = tagEndPos - m_tagEndMarker.length(); QChar typeChar = content.at(pos); if (typeChar == '#') { tag.type = Tag::SectionStart; tag.key = readTagName(content, pos+1, endPos); } else if (typeChar == '^') { tag.type = Tag::InvertedSectionStart; tag.key = readTagName(content, pos+1, endPos); } else if (typeChar == '/') { tag.type = Tag::SectionEnd; tag.key = readTagName(content, pos+1, endPos); } else if (typeChar == '!') { tag.type = Tag::Comment; } else if (typeChar == '>') { tag.type = Tag::Partial; tag.key = readTagName(content, pos+1, endPos); } else if (typeChar == '=') { tag.type = Tag::SetDelimiter; readSetDelimiter(content, pos+1, tagEndPos - m_tagEndMarker.length()); } else { if (typeChar == '&') { tag.escapeMode = Tag::Unescape; ++pos; } else if (typeChar == '{') { tag.escapeMode = Tag::Raw; ++pos; int endTache = content.indexOf('}', pos); if (endTache == tag.end - m_tagEndMarker.length()) { ++tag.end; } else { endPos = endTache; } } tag.type = Tag::Value; tag.key = readTagName(content, pos, endPos); } if (tag.type != Tag::Value) { expandTag(tag, content); } return tag; } QString Renderer::readTagName(const QString& content, int pos, int endPos) { QString name; name.reserve(endPos - pos); while (content.at(pos).isSpace()) { ++pos; } while (!content.at(pos).isSpace() && pos < endPos) { name += content.at(pos); ++pos; } return name; } void Renderer::readSetDelimiter(const QString& content, int pos, int endPos) { QString startMarker; QString endMarker; while (content.at(pos).isSpace() && pos < endPos) { ++pos; } while (!content.at(pos).isSpace() && pos < endPos) { if (content.at(pos) == '=') { setError("Custom delimiters may not contain '='.", pos); return; } startMarker += content.at(pos); ++pos; } while (content.at(pos).isSpace() && pos < endPos) { ++pos; } while (!content.at(pos).isSpace() && pos < endPos - 1) { if (content.at(pos) == '=') { setError("Custom delimiters may not contain '='.", pos); return; } endMarker += content.at(pos); ++pos; } m_tagStartMarker = startMarker; m_tagEndMarker = endMarker; } Tag Renderer::findEndTag(const QString& content, const Tag& startTag, int endPos) { int tagDepth = 1; int pos = startTag.end; while (true) { Tag nextTag = findTag(content, pos, endPos); if (nextTag.type == Tag::Null) { return nextTag; } else if (nextTag.type == Tag::SectionStart || nextTag.type == Tag::InvertedSectionStart) { ++tagDepth; } else if (nextTag.type == Tag::SectionEnd) { --tagDepth; if (tagDepth == 0) { if (nextTag.key != startTag.key) { setError("Tag start/end key mismatch", nextTag.start); return Tag(); } return nextTag; } } pos = nextTag.end; } return Tag(); } void Renderer::setTagMarkers(const QString& startMarker, const QString& endMarker) { m_defaultTagStartMarker = startMarker; m_defaultTagEndMarker = endMarker; } void Renderer::expandTag(Tag& tag, const QString& content) { int start = tag.start; int end = tag.end; int indentation = 0; // Move start to beginning of line. while (start > 0 && content.at(start - 1) != QLatin1Char('\n')) { --start; if (!content.at(start).isSpace()) { return; // Not standalone. } else if (content.at(start).category() == QChar::Separator_Space) { // If its an actual "white space" and not a new line, it counts toward indentation. ++indentation; } } // Move end to one past end of line. while (end <= content.size() && content.at(end - 1) != QLatin1Char('\n')) { if (end < content.size() && !content.at(end).isSpace()) { return; // Not standalone. } ++end; } tag.start = start; tag.end = end; tag.indentation = indentation; } ================================================ FILE: 3rdpart/qt-mustache-master/src/mustache.h ================================================ /* Copyright 2012, Robert Knight Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. */ #pragma once #include #include #include #if __cplusplus >= 201103L #include /* for std::function */ #endif namespace Mustache { class PartialResolver; class Renderer; /** Context is an interface that Mustache::Renderer::render() uses to * fetch substitutions for template tags. */ class Context { public: /** Create a context. @p resolver is used to fetch the expansions for any {{>partial}} tags * which appear in a template. */ explicit Context(PartialResolver* resolver = 0); virtual ~Context() {} /** Returns a string representation of the value for @p key in the current context. * This is used to replace a Mustache value tag. */ virtual QString stringValue(const QString& key) const = 0; /** Returns true if the value for @p key is 'false' or an empty list. * 'False' values typically include empty strings, the boolean value false etc. * * When processing a section Mustache tag, the section is not rendered if the key * is false, or for an inverted section tag, the section is only rendered if the key * is false. */ virtual bool isFalse(const QString& key) const = 0; /** Returns the number of items in the list value for @p key or 0 if * the value for @p key is not a list. */ virtual int listCount(const QString& key) const = 0; /** Set the current context to the value for @p key. * If index is >= 0, set the current context to the @p index'th value * in the list value for @p key. */ virtual void push(const QString& key, int index = -1) = 0; /** Exit the current context. */ virtual void pop() = 0; /** Returns the partial template for a given @p key. */ QString partialValue(const QString& key) const; /** Returns the partial resolver passed to the constructor. */ PartialResolver* partialResolver() const; /** Returns true if eval() should be used to render section tags using @p key. * If canEval() returns true for a key, the renderer will pass the literal, unrendered * block of text for the section to eval() and replace the section with the result. * * canEval() and eval() are equivalents for callable objects (eg. lambdas) in other * Mustache implementations. * * The default implementation always returns false. */ virtual bool canEval(const QString& key) const; /** Callback used to render a template section with the given @p key. * @p renderer will substitute the original section tag with the result of eval(). * * The default implementation returns an empty string. */ virtual QString eval(const QString& key, const QString& _template, Renderer* renderer); private: PartialResolver* m_partialResolver; }; /** A context implementation which wraps a QVariantHash or QVariantMap. */ class QtVariantContext : public Context { public: /** Construct a QtVariantContext which wraps a dictionary in a QVariantHash * or a QVariantMap. */ #if __cplusplus >= 201103L typedef std::function fn_t; #else typedef QString (*fn_t)(const QString&, Mustache::Renderer*, Mustache::Context*); #endif explicit QtVariantContext(const QVariant& root, PartialResolver* resolver = 0); virtual QString stringValue(const QString& key) const; virtual bool isFalse(const QString& key) const; virtual int listCount(const QString& key) const; virtual void push(const QString& key, int index = -1); virtual void pop(); virtual bool canEval(const QString& key) const; virtual QString eval(const QString& key, const QString& _template, Mustache::Renderer* renderer); private: QVariant value(const QString& key) const; QStack m_contextStack; }; /** Interface for fetching template partials. */ class PartialResolver { public: virtual ~PartialResolver() {} /** Returns the partial template with a given @p name. */ virtual QString getPartial(const QString& name) = 0; }; /** A simple partial fetcher which returns templates from a map of (partial name -> template) */ class PartialMap : public PartialResolver { public: explicit PartialMap(const QHash& partials); virtual QString getPartial(const QString& name); private: QHash m_partials; }; /** A partial fetcher when loads templates from '.mustache' files * in a given directory. * * Once a partial has been loaded, it is cached for future use. */ class PartialFileLoader : public PartialResolver { public: explicit PartialFileLoader(const QString& basePath); virtual QString getPartial(const QString& name); private: QString m_basePath; QHash m_cache; }; /** Holds properties of a tag in a mustache template. */ struct Tag { enum Type { Null, Value, /// A {{key}} or {{{key}}} tag SectionStart, /// A {{#section}} tag InvertedSectionStart, /// An {{^inverted-section}} tag SectionEnd, /// A {{/section}} tag Partial, /// A {{^partial}} tag Comment, /// A {{! comment }} tag SetDelimiter /// A {{=<% %>=}} tag }; enum EscapeMode { Escape, Unescape, Raw }; Tag() : type(Null) , start(0) , end(0) , escapeMode(Escape) , indentation(0) {} Type type; QString key; int start; int end; EscapeMode escapeMode; int indentation; }; /** Renders Mustache templates, replacing mustache tags with * values from a provided context. */ class Renderer { public: Renderer(); /** Render a Mustache template, using @p context to fetch * the values used to replace Mustache tags. */ QString render(const QString& _template, Context* context); /** Returns a message describing the last error encountered by the previous * render() call. */ QString error() const; /** Returns the position in the template where the last error occurred * when rendering the template or -1 if no error occurred. * * If the error occurred in a partial template, the returned position is the offset * in the partial template. */ int errorPos() const; /** Returns the name of the partial where the error occurred, or an empty string * if the error occurred in the main template. */ QString errorPartial() const; /** Sets the default tag start and end markers. * This can be overridden within a template. */ void setTagMarkers(const QString& startMarker, const QString& endMarker); private: QString render(const QString& _template, int startPos, int endPos, Context* context); Tag findTag(const QString& content, int pos, int endPos); Tag findEndTag(const QString& content, const Tag& startTag, int endPos); void setError(const QString& error, int pos); void readSetDelimiter(const QString& content, int pos, int endPos); static QString readTagName(const QString& content, int pos, int endPos); /** Expands @p tag to fill the line, but only if it is standalone. * * The start position is moved to the beginning of the line. The end position is * moved to one past the end of the line. If @p tag is not standalone, it is * left unmodified. * * A tag is standalone if it is the only non-whitespace token on the the line. */ static void expandTag(Tag& tag, const QString& content); QStack m_partialStack; QString m_error; int m_errorPos; QString m_errorPartial; QString m_tagStartMarker; QString m_tagEndMarker; QString m_defaultTagStartMarker; QString m_defaultTagEndMarker; }; /** A convenience function which renders a template using the given data. */ QString renderTemplate(const QString& templateString, const QVariantHash& args); }; Q_DECLARE_METATYPE(Mustache::QtVariantContext::fn_t) ================================================ FILE: 3rdpart/qt-mustache-master/tests/partial.mustache ================================================ {{name}} -- {{email}} ================================================ FILE: 3rdpart/qt-mustache-master/tests/specs/comments.json ================================================ {"__ATTN__":"Do not edit this file; changes belong in the appropriate YAML file.","overview":"Comment tags represent content that should never appear in the resulting\noutput.\n\nThe tag's content may contain any substring (including newlines) EXCEPT the\nclosing delimiter.\n\nComment tags SHOULD be treated as standalone when appropriate.\n","tests":[{"name":"Inline","data":{},"expected":"1234567890","template":"12345{{! Comment Block! }}67890","desc":"Comment blocks should be removed from the template."},{"name":"Multiline","data":{},"expected":"1234567890\n","template":"12345{{!\n This is a\n multi-line comment...\n}}67890\n","desc":"Multiline comments should be permitted."},{"name":"Standalone","data":{},"expected":"Begin.\nEnd.\n","template":"Begin.\n{{! Comment Block! }}\nEnd.\n","desc":"All standalone comment lines should be removed."},{"name":"Indented Standalone","data":{},"expected":"Begin.\nEnd.\n","template":"Begin.\n {{! Indented Comment Block! }}\nEnd.\n","desc":"All standalone comment lines should be removed."},{"name":"Standalone Line Endings","data":{},"expected":"|\r\n|","template":"|\r\n{{! Standalone Comment }}\r\n|","desc":"\"\\r\\n\" should be considered a newline for standalone tags."},{"name":"Standalone Without Previous Line","data":{},"expected":"!","template":" {{! I'm Still Standalone }}\n!","desc":"Standalone tags should not require a newline to precede them."},{"name":"Standalone Without Newline","data":{},"expected":"!\n","template":"!\n {{! I'm Still Standalone }}","desc":"Standalone tags should not require a newline to follow them."},{"name":"Multiline Standalone","data":{},"expected":"Begin.\nEnd.\n","template":"Begin.\n{{!\nSomething's going on here...\n}}\nEnd.\n","desc":"All standalone comment lines should be removed."},{"name":"Indented Multiline Standalone","data":{},"expected":"Begin.\nEnd.\n","template":"Begin.\n {{!\n Something's going on here...\n }}\nEnd.\n","desc":"All standalone comment lines should be removed."},{"name":"Indented Inline","data":{},"expected":" 12 \n","template":" 12 {{! 34 }}\n","desc":"Inline comments should not strip whitespace"},{"name":"Surrounding Whitespace","data":{},"expected":"12345 67890","template":"12345 {{! Comment Block! }} 67890","desc":"Comment removal should preserve surrounding whitespace."}]} ================================================ FILE: 3rdpart/qt-mustache-master/tests/specs/comments.yml ================================================ overview: | Comment tags represent content that should never appear in the resulting output. The tag's content may contain any substring (including newlines) EXCEPT the closing delimiter. Comment tags SHOULD be treated as standalone when appropriate. tests: - name: Inline desc: Comment blocks should be removed from the template. data: { } template: '12345{{! Comment Block! }}67890' expected: '1234567890' - name: Multiline desc: Multiline comments should be permitted. data: { } template: | 12345{{! This is a multi-line comment... }}67890 expected: | 1234567890 - name: Standalone desc: All standalone comment lines should be removed. data: { } template: | Begin. {{! Comment Block! }} End. expected: | Begin. End. - name: Indented Standalone desc: All standalone comment lines should be removed. data: { } template: | Begin. {{! Indented Comment Block! }} End. expected: | Begin. End. - name: Standalone Line Endings desc: '"\r\n" should be considered a newline for standalone tags.' data: { } template: "|\r\n{{! Standalone Comment }}\r\n|" expected: "|\r\n|" - name: Standalone Without Previous Line desc: Standalone tags should not require a newline to precede them. data: { } template: " {{! I'm Still Standalone }}\n!" expected: "!" - name: Standalone Without Newline desc: Standalone tags should not require a newline to follow them. data: { } template: "!\n {{! I'm Still Standalone }}" expected: "!\n" - name: Multiline Standalone desc: All standalone comment lines should be removed. data: { } template: | Begin. {{! Something's going on here... }} End. expected: | Begin. End. - name: Indented Multiline Standalone desc: All standalone comment lines should be removed. data: { } template: | Begin. {{! Something's going on here... }} End. expected: | Begin. End. - name: Indented Inline desc: Inline comments should not strip whitespace data: { } template: " 12 {{! 34 }}\n" expected: " 12 \n" - name: Surrounding Whitespace desc: Comment removal should preserve surrounding whitespace. data: { } template: '12345 {{! Comment Block! }} 67890' expected: '12345 67890' ================================================ FILE: 3rdpart/qt-mustache-master/tests/specs/delimiters.json ================================================ {"__ATTN__":"Do not edit this file; changes belong in the appropriate YAML file.","overview":"Set Delimiter tags are used to change the tag delimiters for all content\nfollowing the tag in the current compilation unit.\n\nThe tag's content MUST be any two non-whitespace sequences (separated by\nwhitespace) EXCEPT an equals sign ('=') followed by the current closing\ndelimiter.\n\nSet Delimiter tags SHOULD be treated as standalone when appropriate.\n","tests":[{"name":"Pair Behavior","data":{"text":"Hey!"},"expected":"(Hey!)","template":"{{=<% %>=}}(<%text%>)","desc":"The equals sign (used on both sides) should permit delimiter changes."},{"name":"Special Characters","data":{"text":"It worked!"},"expected":"(It worked!)","template":"({{=[ ]=}}[text])","desc":"Characters with special meaning regexen should be valid delimiters."},{"name":"Sections","data":{"section":true,"data":"I got interpolated."},"expected":"[\n I got interpolated.\n |data|\n\n {{data}}\n I got interpolated.\n]\n","template":"[\n{{#section}}\n {{data}}\n |data|\n{{/section}}\n\n{{= | | =}}\n|#section|\n {{data}}\n |data|\n|/section|\n]\n","desc":"Delimiters set outside sections should persist."},{"name":"Inverted Sections","data":{"section":false,"data":"I got interpolated."},"expected":"[\n I got interpolated.\n |data|\n\n {{data}}\n I got interpolated.\n]\n","template":"[\n{{^section}}\n {{data}}\n |data|\n{{/section}}\n\n{{= | | =}}\n|^section|\n {{data}}\n |data|\n|/section|\n]\n","desc":"Delimiters set outside inverted sections should persist."},{"name":"Partial Inheritence","data":{"value":"yes"},"expected":"[ .yes. ]\n[ .yes. ]\n","template":"[ {{>include}} ]\n{{= | | =}}\n[ |>include| ]\n","desc":"Delimiters set in a parent template should not affect a partial.","partials":{"include":".{{value}}."}},{"name":"Post-Partial Behavior","data":{"value":"yes"},"expected":"[ .yes. .yes. ]\n[ .yes. .|value|. ]\n","template":"[ {{>include}} ]\n[ .{{value}}. .|value|. ]\n","desc":"Delimiters set in a partial should not affect the parent template.","partials":{"include":".{{value}}. {{= | | =}} .|value|."}},{"name":"Surrounding Whitespace","data":{},"expected":"| |","template":"| {{=@ @=}} |","desc":"Surrounding whitespace should be left untouched."},{"name":"Outlying Whitespace (Inline)","data":{},"expected":" | \n","template":" | {{=@ @=}}\n","desc":"Whitespace should be left untouched."},{"name":"Standalone Tag","data":{},"expected":"Begin.\nEnd.\n","template":"Begin.\n{{=@ @=}}\nEnd.\n","desc":"Standalone lines should be removed from the template."},{"name":"Indented Standalone Tag","data":{},"expected":"Begin.\nEnd.\n","template":"Begin.\n {{=@ @=}}\nEnd.\n","desc":"Indented standalone lines should be removed from the template."},{"name":"Standalone Line Endings","data":{},"expected":"|\r\n|","template":"|\r\n{{= @ @ =}}\r\n|","desc":"\"\\r\\n\" should be considered a newline for standalone tags."},{"name":"Standalone Without Previous Line","data":{},"expected":"=","template":" {{=@ @=}}\n=","desc":"Standalone tags should not require a newline to precede them."},{"name":"Standalone Without Newline","data":{},"expected":"=\n","template":"=\n {{=@ @=}}","desc":"Standalone tags should not require a newline to follow them."},{"name":"Pair with Padding","data":{},"expected":"||","template":"|{{= @ @ =}}|","desc":"Superfluous in-tag whitespace should be ignored."}]} ================================================ FILE: 3rdpart/qt-mustache-master/tests/specs/delimiters.yml ================================================ overview: | Set Delimiter tags are used to change the tag delimiters for all content following the tag in the current compilation unit. The tag's content MUST be any two non-whitespace sequences (separated by whitespace) EXCEPT an equals sign ('=') followed by the current closing delimiter. Set Delimiter tags SHOULD be treated as standalone when appropriate. tests: - name: Pair Behavior desc: The equals sign (used on both sides) should permit delimiter changes. data: { text: 'Hey!' } template: '{{=<% %>=}}(<%text%>)' expected: '(Hey!)' - name: Special Characters desc: Characters with special meaning regexen should be valid delimiters. data: { text: 'It worked!' } template: '({{=[ ]=}}[text])' expected: '(It worked!)' - name: Sections desc: Delimiters set outside sections should persist. data: { section: true, data: 'I got interpolated.' } template: | [ {{#section}} {{data}} |data| {{/section}} {{= | | =}} |#section| {{data}} |data| |/section| ] expected: | [ I got interpolated. |data| {{data}} I got interpolated. ] - name: Inverted Sections desc: Delimiters set outside inverted sections should persist. data: { section: false, data: 'I got interpolated.' } template: | [ {{^section}} {{data}} |data| {{/section}} {{= | | =}} |^section| {{data}} |data| |/section| ] expected: | [ I got interpolated. |data| {{data}} I got interpolated. ] - name: Partial Inheritence desc: Delimiters set in a parent template should not affect a partial. data: { value: 'yes' } partials: include: '.{{value}}.' template: | [ {{>include}} ] {{= | | =}} [ |>include| ] expected: | [ .yes. ] [ .yes. ] - name: Post-Partial Behavior desc: Delimiters set in a partial should not affect the parent template. data: { value: 'yes' } partials: include: '.{{value}}. {{= | | =}} .|value|.' template: | [ {{>include}} ] [ .{{value}}. .|value|. ] expected: | [ .yes. .yes. ] [ .yes. .|value|. ] # Whitespace Sensitivity - name: Surrounding Whitespace desc: Surrounding whitespace should be left untouched. data: { } template: '| {{=@ @=}} |' expected: '| |' - name: Outlying Whitespace (Inline) desc: Whitespace should be left untouched. data: { } template: " | {{=@ @=}}\n" expected: " | \n" - name: Standalone Tag desc: Standalone lines should be removed from the template. data: { } template: | Begin. {{=@ @=}} End. expected: | Begin. End. - name: Indented Standalone Tag desc: Indented standalone lines should be removed from the template. data: { } template: | Begin. {{=@ @=}} End. expected: | Begin. End. - name: Standalone Line Endings desc: '"\r\n" should be considered a newline for standalone tags.' data: { } template: "|\r\n{{= @ @ =}}\r\n|" expected: "|\r\n|" - name: Standalone Without Previous Line desc: Standalone tags should not require a newline to precede them. data: { } template: " {{=@ @=}}\n=" expected: "=" - name: Standalone Without Newline desc: Standalone tags should not require a newline to follow them. data: { } template: "=\n {{=@ @=}}" expected: "=\n" # Whitespace Insensitivity - name: Pair with Padding desc: Superfluous in-tag whitespace should be ignored. data: { } template: '|{{= @ @ =}}|' expected: '||' ================================================ FILE: 3rdpart/qt-mustache-master/tests/specs/interpolation.json ================================================ {"__ATTN__":"Do not edit this file; changes belong in the appropriate YAML file.","overview":"Interpolation tags are used to integrate dynamic content into the template.\n\nThe tag's content MUST be a non-whitespace character sequence NOT containing\nthe current closing delimiter.\n\nThis tag's content names the data to replace the tag. A single period (`.`)\nindicates that the item currently sitting atop the context stack should be\nused; otherwise, name resolution is as follows:\n 1) Split the name on periods; the first part is the name to resolve, any\n remaining parts should be retained.\n 2) Walk the context stack from top to bottom, finding the first context\n that is a) a hash containing the name as a key OR b) an object responding\n to a method with the given name.\n 3) If the context is a hash, the data is the value associated with the\n name.\n 4) If the context is an object, the data is the value returned by the\n method with the given name.\n 5) If any name parts were retained in step 1, each should be resolved\n against a context stack containing only the result from the former\n resolution. If any part fails resolution, the result should be considered\n falsey, and should interpolate as the empty string.\nData should be coerced into a string (and escaped, if appropriate) before\ninterpolation.\n\nThe Interpolation tags MUST NOT be treated as standalone.\n","tests":[{"name":"No Interpolation","data":{},"expected":"Hello from {Mustache}!\n","template":"Hello from {Mustache}!\n","desc":"Mustache-free templates should render as-is."},{"name":"Basic Interpolation","data":{"subject":"world"},"expected":"Hello, world!\n","template":"Hello, {{subject}}!\n","desc":"Unadorned tags should interpolate content into the template."},{"name":"HTML Escaping","data":{"forbidden":"& \" < >"},"expected":"These characters should be HTML escaped: & " < >\n","template":"These characters should be HTML escaped: {{forbidden}}\n","desc":"Basic interpolation should be HTML escaped."},{"name":"Triple Mustache","data":{"forbidden":"& \" < >"},"expected":"These characters should not be HTML escaped: & \" < >\n","template":"These characters should not be HTML escaped: {{{forbidden}}}\n","desc":"Triple mustaches should interpolate without HTML escaping."},{"name":"Ampersand","data":{"forbidden":"& \" < >"},"expected":"These characters should not be HTML escaped: & \" < >\n","template":"These characters should not be HTML escaped: {{&forbidden}}\n","desc":"Ampersand should interpolate without HTML escaping."},{"name":"Basic Integer Interpolation","data":{"mph":85},"expected":"\"85 miles an hour!\"","template":"\"{{mph}} miles an hour!\"","desc":"Integers should interpolate seamlessly."},{"name":"Triple Mustache Integer Interpolation","data":{"mph":85},"expected":"\"85 miles an hour!\"","template":"\"{{{mph}}} miles an hour!\"","desc":"Integers should interpolate seamlessly."},{"name":"Ampersand Integer Interpolation","data":{"mph":85},"expected":"\"85 miles an hour!\"","template":"\"{{&mph}} miles an hour!\"","desc":"Integers should interpolate seamlessly."},{"name":"Basic Decimal Interpolation","data":{"power":1.21},"expected":"\"1.21 jiggawatts!\"","template":"\"{{power}} jiggawatts!\"","desc":"Decimals should interpolate seamlessly with proper significance."},{"name":"Triple Mustache Decimal Interpolation","data":{"power":1.21},"expected":"\"1.21 jiggawatts!\"","template":"\"{{{power}}} jiggawatts!\"","desc":"Decimals should interpolate seamlessly with proper significance."},{"name":"Ampersand Decimal Interpolation","data":{"power":1.21},"expected":"\"1.21 jiggawatts!\"","template":"\"{{&power}} jiggawatts!\"","desc":"Decimals should interpolate seamlessly with proper significance."},{"name":"Basic Context Miss Interpolation","data":{},"expected":"I () be seen!","template":"I ({{cannot}}) be seen!","desc":"Failed context lookups should default to empty strings."},{"name":"Triple Mustache Context Miss Interpolation","data":{},"expected":"I () be seen!","template":"I ({{{cannot}}}) be seen!","desc":"Failed context lookups should default to empty strings."},{"name":"Ampersand Context Miss Interpolation","data":{},"expected":"I () be seen!","template":"I ({{&cannot}}) be seen!","desc":"Failed context lookups should default to empty strings."},{"name":"Dotted Names - Basic Interpolation","data":{"person":{"name":"Joe"}},"expected":"\"Joe\" == \"Joe\"","template":"\"{{person.name}}\" == \"{{#person}}{{name}}{{/person}}\"","desc":"Dotted names should be considered a form of shorthand for sections."},{"name":"Dotted Names - Triple Mustache Interpolation","data":{"person":{"name":"Joe"}},"expected":"\"Joe\" == \"Joe\"","template":"\"{{{person.name}}}\" == \"{{#person}}{{{name}}}{{/person}}\"","desc":"Dotted names should be considered a form of shorthand for sections."},{"name":"Dotted Names - Ampersand Interpolation","data":{"person":{"name":"Joe"}},"expected":"\"Joe\" == \"Joe\"","template":"\"{{&person.name}}\" == \"{{#person}}{{&name}}{{/person}}\"","desc":"Dotted names should be considered a form of shorthand for sections."},{"name":"Dotted Names - Arbitrary Depth","data":{"a":{"b":{"c":{"d":{"e":{"name":"Phil"}}}}}},"expected":"\"Phil\" == \"Phil\"","template":"\"{{a.b.c.d.e.name}}\" == \"Phil\"","desc":"Dotted names should be functional to any level of nesting."},{"name":"Dotted Names - Broken Chains","data":{"a":{}},"expected":"\"\" == \"\"","template":"\"{{a.b.c}}\" == \"\"","desc":"Any falsey value prior to the last part of the name should yield ''."},{"name":"Dotted Names - Broken Chain Resolution","data":{"a":{"b":{}},"c":{"name":"Jim"}},"expected":"\"\" == \"\"","template":"\"{{a.b.c.name}}\" == \"\"","desc":"Each part of a dotted name should resolve only against its parent."},{"name":"Dotted Names - Initial Resolution","data":{"a":{"b":{"c":{"d":{"e":{"name":"Phil"}}}}},"b":{"c":{"d":{"e":{"name":"Wrong"}}}}},"expected":"\"Phil\" == \"Phil\"","template":"\"{{#a}}{{b.c.d.e.name}}{{/a}}\" == \"Phil\"","desc":"The first part of a dotted name should resolve as any other name."},{"name":"Interpolation - Surrounding Whitespace","data":{"string":"---"},"expected":"| --- |","template":"| {{string}} |","desc":"Interpolation should not alter surrounding whitespace."},{"name":"Triple Mustache - Surrounding Whitespace","data":{"string":"---"},"expected":"| --- |","template":"| {{{string}}} |","desc":"Interpolation should not alter surrounding whitespace."},{"name":"Ampersand - Surrounding Whitespace","data":{"string":"---"},"expected":"| --- |","template":"| {{&string}} |","desc":"Interpolation should not alter surrounding whitespace."},{"name":"Interpolation - Standalone","data":{"string":"---"},"expected":" ---\n","template":" {{string}}\n","desc":"Standalone interpolation should not alter surrounding whitespace."},{"name":"Triple Mustache - Standalone","data":{"string":"---"},"expected":" ---\n","template":" {{{string}}}\n","desc":"Standalone interpolation should not alter surrounding whitespace."},{"name":"Ampersand - Standalone","data":{"string":"---"},"expected":" ---\n","template":" {{&string}}\n","desc":"Standalone interpolation should not alter surrounding whitespace."},{"name":"Interpolation With Padding","data":{"string":"---"},"expected":"|---|","template":"|{{ string }}|","desc":"Superfluous in-tag whitespace should be ignored."},{"name":"Triple Mustache With Padding","data":{"string":"---"},"expected":"|---|","template":"|{{{ string }}}|","desc":"Superfluous in-tag whitespace should be ignored."},{"name":"Ampersand With Padding","data":{"string":"---"},"expected":"|---|","template":"|{{& string }}|","desc":"Superfluous in-tag whitespace should be ignored."}]} ================================================ FILE: 3rdpart/qt-mustache-master/tests/specs/interpolation.yml ================================================ overview: | Interpolation tags are used to integrate dynamic content into the template. The tag's content MUST be a non-whitespace character sequence NOT containing the current closing delimiter. This tag's content names the data to replace the tag. A single period (`.`) indicates that the item currently sitting atop the context stack should be used; otherwise, name resolution is as follows: 1) Split the name on periods; the first part is the name to resolve, any remaining parts should be retained. 2) Walk the context stack from top to bottom, finding the first context that is a) a hash containing the name as a key OR b) an object responding to a method with the given name. 3) If the context is a hash, the data is the value associated with the name. 4) If the context is an object, the data is the value returned by the method with the given name. 5) If any name parts were retained in step 1, each should be resolved against a context stack containing only the result from the former resolution. If any part fails resolution, the result should be considered falsey, and should interpolate as the empty string. Data should be coerced into a string (and escaped, if appropriate) before interpolation. The Interpolation tags MUST NOT be treated as standalone. tests: - name: No Interpolation desc: Mustache-free templates should render as-is. data: { } template: | Hello from {Mustache}! expected: | Hello from {Mustache}! - name: Basic Interpolation desc: Unadorned tags should interpolate content into the template. data: { subject: "world" } template: | Hello, {{subject}}! expected: | Hello, world! - name: HTML Escaping desc: Basic interpolation should be HTML escaped. data: { forbidden: '& " < >' } template: | These characters should be HTML escaped: {{forbidden}} expected: | These characters should be HTML escaped: & " < > - name: Triple Mustache desc: Triple mustaches should interpolate without HTML escaping. data: { forbidden: '& " < >' } template: | These characters should not be HTML escaped: {{{forbidden}}} expected: | These characters should not be HTML escaped: & " < > - name: Ampersand desc: Ampersand should interpolate without HTML escaping. data: { forbidden: '& " < >' } template: | These characters should not be HTML escaped: {{&forbidden}} expected: | These characters should not be HTML escaped: & " < > - name: Basic Integer Interpolation desc: Integers should interpolate seamlessly. data: { mph: 85 } template: '"{{mph}} miles an hour!"' expected: '"85 miles an hour!"' - name: Triple Mustache Integer Interpolation desc: Integers should interpolate seamlessly. data: { mph: 85 } template: '"{{{mph}}} miles an hour!"' expected: '"85 miles an hour!"' - name: Ampersand Integer Interpolation desc: Integers should interpolate seamlessly. data: { mph: 85 } template: '"{{&mph}} miles an hour!"' expected: '"85 miles an hour!"' - name: Basic Decimal Interpolation desc: Decimals should interpolate seamlessly with proper significance. data: { power: 1.210 } template: '"{{power}} jiggawatts!"' expected: '"1.21 jiggawatts!"' - name: Triple Mustache Decimal Interpolation desc: Decimals should interpolate seamlessly with proper significance. data: { power: 1.210 } template: '"{{{power}}} jiggawatts!"' expected: '"1.21 jiggawatts!"' - name: Ampersand Decimal Interpolation desc: Decimals should interpolate seamlessly with proper significance. data: { power: 1.210 } template: '"{{&power}} jiggawatts!"' expected: '"1.21 jiggawatts!"' # Context Misses - name: Basic Context Miss Interpolation desc: Failed context lookups should default to empty strings. data: { } template: "I ({{cannot}}) be seen!" expected: "I () be seen!" - name: Triple Mustache Context Miss Interpolation desc: Failed context lookups should default to empty strings. data: { } template: "I ({{{cannot}}}) be seen!" expected: "I () be seen!" - name: Ampersand Context Miss Interpolation desc: Failed context lookups should default to empty strings. data: { } template: "I ({{&cannot}}) be seen!" expected: "I () be seen!" # Dotted Names - name: Dotted Names - Basic Interpolation desc: Dotted names should be considered a form of shorthand for sections. data: { person: { name: 'Joe' } } template: '"{{person.name}}" == "{{#person}}{{name}}{{/person}}"' expected: '"Joe" == "Joe"' - name: Dotted Names - Triple Mustache Interpolation desc: Dotted names should be considered a form of shorthand for sections. data: { person: { name: 'Joe' } } template: '"{{{person.name}}}" == "{{#person}}{{{name}}}{{/person}}"' expected: '"Joe" == "Joe"' - name: Dotted Names - Ampersand Interpolation desc: Dotted names should be considered a form of shorthand for sections. data: { person: { name: 'Joe' } } template: '"{{&person.name}}" == "{{#person}}{{&name}}{{/person}}"' expected: '"Joe" == "Joe"' - name: Dotted Names - Arbitrary Depth desc: Dotted names should be functional to any level of nesting. data: a: { b: { c: { d: { e: { name: 'Phil' } } } } } template: '"{{a.b.c.d.e.name}}" == "Phil"' expected: '"Phil" == "Phil"' - name: Dotted Names - Broken Chains desc: Any falsey value prior to the last part of the name should yield ''. data: a: { } template: '"{{a.b.c}}" == ""' expected: '"" == ""' - name: Dotted Names - Broken Chain Resolution desc: Each part of a dotted name should resolve only against its parent. data: a: { b: { } } c: { name: 'Jim' } template: '"{{a.b.c.name}}" == ""' expected: '"" == ""' - name: Dotted Names - Initial Resolution desc: The first part of a dotted name should resolve as any other name. data: a: { b: { c: { d: { e: { name: 'Phil' } } } } } b: { c: { d: { e: { name: 'Wrong' } } } } template: '"{{#a}}{{b.c.d.e.name}}{{/a}}" == "Phil"' expected: '"Phil" == "Phil"' - name: Dotted Names - Context Precedence desc: Dotted names should be resolved against former resolutions. data: a: { b: { } } b: { c: 'ERROR' } template: '{{#a}}{{b.c}}{{/a}}' expected: '' # Whitespace Sensitivity - name: Interpolation - Surrounding Whitespace desc: Interpolation should not alter surrounding whitespace. data: { string: '---' } template: '| {{string}} |' expected: '| --- |' - name: Triple Mustache - Surrounding Whitespace desc: Interpolation should not alter surrounding whitespace. data: { string: '---' } template: '| {{{string}}} |' expected: '| --- |' - name: Ampersand - Surrounding Whitespace desc: Interpolation should not alter surrounding whitespace. data: { string: '---' } template: '| {{&string}} |' expected: '| --- |' - name: Interpolation - Standalone desc: Standalone interpolation should not alter surrounding whitespace. data: { string: '---' } template: " {{string}}\n" expected: " ---\n" - name: Triple Mustache - Standalone desc: Standalone interpolation should not alter surrounding whitespace. data: { string: '---' } template: " {{{string}}}\n" expected: " ---\n" - name: Ampersand - Standalone desc: Standalone interpolation should not alter surrounding whitespace. data: { string: '---' } template: " {{&string}}\n" expected: " ---\n" # Whitespace Insensitivity - name: Interpolation With Padding desc: Superfluous in-tag whitespace should be ignored. data: { string: "---" } template: '|{{ string }}|' expected: '|---|' - name: Triple Mustache With Padding desc: Superfluous in-tag whitespace should be ignored. data: { string: "---" } template: '|{{{ string }}}|' expected: '|---|' - name: Ampersand With Padding desc: Superfluous in-tag whitespace should be ignored. data: { string: "---" } template: '|{{& string }}|' expected: '|---|' ================================================ FILE: 3rdpart/qt-mustache-master/tests/specs/inverted.json ================================================ {"__ATTN__":"Do not edit this file; changes belong in the appropriate YAML file.","overview":"Inverted Section tags and End Section tags are used in combination to wrap a\nsection of the template.\n\nThese tags' content MUST be a non-whitespace character sequence NOT\ncontaining the current closing delimiter; each Inverted Section tag MUST be\nfollowed by an End Section tag with the same content within the same\nsection.\n\nThis tag's content names the data to replace the tag. Name resolution is as\nfollows:\n 1) Split the name on periods; the first part is the name to resolve, any\n remaining parts should be retained.\n 2) Walk the context stack from top to bottom, finding the first context\n that is a) a hash containing the name as a key OR b) an object responding\n to a method with the given name.\n 3) If the context is a hash, the data is the value associated with the\n name.\n 4) If the context is an object and the method with the given name has an\n arity of 1, the method SHOULD be called with a String containing the\n unprocessed contents of the sections; the data is the value returned.\n 5) Otherwise, the data is the value returned by calling the method with\n the given name.\n 6) If any name parts were retained in step 1, each should be resolved\n against a context stack containing only the result from the former\n resolution. If any part fails resolution, the result should be considered\n falsey, and should interpolate as the empty string.\nIf the data is not of a list type, it is coerced into a list as follows: if\nthe data is truthy (e.g. `!!data == true`), use a single-element list\ncontaining the data, otherwise use an empty list.\n\nThis section MUST NOT be rendered unless the data list is empty.\n\nInverted Section and End Section tags SHOULD be treated as standalone when\nappropriate.\n","tests":[{"name":"Falsey","data":{"boolean":false},"expected":"\"This should be rendered.\"","template":"\"{{^boolean}}This should be rendered.{{/boolean}}\"","desc":"Falsey sections should have their contents rendered."},{"name":"Truthy","data":{"boolean":true},"expected":"\"\"","template":"\"{{^boolean}}This should not be rendered.{{/boolean}}\"","desc":"Truthy sections should have their contents omitted."},{"name":"Context","data":{"context":{"name":"Joe"}},"expected":"\"\"","template":"\"{{^context}}Hi {{name}}.{{/context}}\"","desc":"Objects and hashes should behave like truthy values."},{"name":"List","data":{"list":[{"n":1},{"n":2},{"n":3}]},"expected":"\"\"","template":"\"{{^list}}{{n}}{{/list}}\"","desc":"Lists should behave like truthy values."},{"name":"Empty List","data":{"list":[]},"expected":"\"Yay lists!\"","template":"\"{{^list}}Yay lists!{{/list}}\"","desc":"Empty lists should behave like falsey values."},{"name":"Doubled","data":{"two":"second","bool":false},"expected":"* first\n* second\n* third\n","template":"{{^bool}}\n* first\n{{/bool}}\n* {{two}}\n{{^bool}}\n* third\n{{/bool}}\n","desc":"Multiple inverted sections per template should be permitted."},{"name":"Nested (Falsey)","data":{"bool":false},"expected":"| A B C D E |","template":"| A {{^bool}}B {{^bool}}C{{/bool}} D{{/bool}} E |","desc":"Nested falsey sections should have their contents rendered."},{"name":"Nested (Truthy)","data":{"bool":true},"expected":"| A E |","template":"| A {{^bool}}B {{^bool}}C{{/bool}} D{{/bool}} E |","desc":"Nested truthy sections should be omitted."},{"name":"Context Misses","data":{},"expected":"[Cannot find key 'missing'!]","template":"[{{^missing}}Cannot find key 'missing'!{{/missing}}]","desc":"Failed context lookups should be considered falsey."},{"name":"Dotted Names - Truthy","data":{"a":{"b":{"c":true}}},"expected":"\"\" == \"\"","template":"\"{{^a.b.c}}Not Here{{/a.b.c}}\" == \"\"","desc":"Dotted names should be valid for Inverted Section tags."},{"name":"Dotted Names - Falsey","data":{"a":{"b":{"c":false}}},"expected":"\"Not Here\" == \"Not Here\"","template":"\"{{^a.b.c}}Not Here{{/a.b.c}}\" == \"Not Here\"","desc":"Dotted names should be valid for Inverted Section tags."},{"name":"Dotted Names - Broken Chains","data":{"a":{}},"expected":"\"Not Here\" == \"Not Here\"","template":"\"{{^a.b.c}}Not Here{{/a.b.c}}\" == \"Not Here\"","desc":"Dotted names that cannot be resolved should be considered falsey."},{"name":"Surrounding Whitespace","data":{"boolean":false},"expected":" | \t|\t | \n","template":" | {{^boolean}}\t|\t{{/boolean}} | \n","desc":"Inverted sections should not alter surrounding whitespace."},{"name":"Internal Whitespace","data":{"boolean":false},"expected":" | \n | \n","template":" | {{^boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n","desc":"Inverted should not alter internal whitespace."},{"name":"Indented Inline Sections","data":{"boolean":false},"expected":" NO\n WAY\n","template":" {{^boolean}}NO{{/boolean}}\n {{^boolean}}WAY{{/boolean}}\n","desc":"Single-line sections should not alter surrounding whitespace."},{"name":"Standalone Lines","data":{"boolean":false},"expected":"| This Is\n|\n| A Line\n","template":"| This Is\n{{^boolean}}\n|\n{{/boolean}}\n| A Line\n","desc":"Standalone lines should be removed from the template."},{"name":"Standalone Indented Lines","data":{"boolean":false},"expected":"| This Is\n|\n| A Line\n","template":"| This Is\n {{^boolean}}\n|\n {{/boolean}}\n| A Line\n","desc":"Standalone indented lines should be removed from the template."},{"name":"Standalone Line Endings","data":{"boolean":false},"expected":"|\r\n|","template":"|\r\n{{^boolean}}\r\n{{/boolean}}\r\n|","desc":"\"\\r\\n\" should be considered a newline for standalone tags."},{"name":"Standalone Without Previous Line","data":{"boolean":false},"expected":"^\n/","template":" {{^boolean}}\n^{{/boolean}}\n/","desc":"Standalone tags should not require a newline to precede them."},{"name":"Standalone Without Newline","data":{"boolean":false},"expected":"^\n/\n","template":"^{{^boolean}}\n/\n {{/boolean}}","desc":"Standalone tags should not require a newline to follow them."},{"name":"Padding","data":{"boolean":false},"expected":"|=|","template":"|{{^ boolean }}={{/ boolean }}|","desc":"Superfluous in-tag whitespace should be ignored."}]} ================================================ FILE: 3rdpart/qt-mustache-master/tests/specs/inverted.yml ================================================ overview: | Inverted Section tags and End Section tags are used in combination to wrap a section of the template. These tags' content MUST be a non-whitespace character sequence NOT containing the current closing delimiter; each Inverted Section tag MUST be followed by an End Section tag with the same content within the same section. This tag's content names the data to replace the tag. Name resolution is as follows: 1) Split the name on periods; the first part is the name to resolve, any remaining parts should be retained. 2) Walk the context stack from top to bottom, finding the first context that is a) a hash containing the name as a key OR b) an object responding to a method with the given name. 3) If the context is a hash, the data is the value associated with the name. 4) If the context is an object and the method with the given name has an arity of 1, the method SHOULD be called with a String containing the unprocessed contents of the sections; the data is the value returned. 5) Otherwise, the data is the value returned by calling the method with the given name. 6) If any name parts were retained in step 1, each should be resolved against a context stack containing only the result from the former resolution. If any part fails resolution, the result should be considered falsey, and should interpolate as the empty string. If the data is not of a list type, it is coerced into a list as follows: if the data is truthy (e.g. `!!data == true`), use a single-element list containing the data, otherwise use an empty list. This section MUST NOT be rendered unless the data list is empty. Inverted Section and End Section tags SHOULD be treated as standalone when appropriate. tests: - name: Falsey desc: Falsey sections should have their contents rendered. data: { boolean: false } template: '"{{^boolean}}This should be rendered.{{/boolean}}"' expected: '"This should be rendered."' - name: Truthy desc: Truthy sections should have their contents omitted. data: { boolean: true } template: '"{{^boolean}}This should not be rendered.{{/boolean}}"' expected: '""' - name: Context desc: Objects and hashes should behave like truthy values. data: { context: { name: 'Joe' } } template: '"{{^context}}Hi {{name}}.{{/context}}"' expected: '""' - name: List desc: Lists should behave like truthy values. data: { list: [ { n: 1 }, { n: 2 }, { n: 3 } ] } template: '"{{^list}}{{n}}{{/list}}"' expected: '""' - name: Empty List desc: Empty lists should behave like falsey values. data: { list: [ ] } template: '"{{^list}}Yay lists!{{/list}}"' expected: '"Yay lists!"' - name: Doubled desc: Multiple inverted sections per template should be permitted. data: { bool: false, two: 'second' } template: | {{^bool}} * first {{/bool}} * {{two}} {{^bool}} * third {{/bool}} expected: | * first * second * third - name: Nested (Falsey) desc: Nested falsey sections should have their contents rendered. data: { bool: false } template: "| A {{^bool}}B {{^bool}}C{{/bool}} D{{/bool}} E |" expected: "| A B C D E |" - name: Nested (Truthy) desc: Nested truthy sections should be omitted. data: { bool: true } template: "| A {{^bool}}B {{^bool}}C{{/bool}} D{{/bool}} E |" expected: "| A E |" - name: Context Misses desc: Failed context lookups should be considered falsey. data: { } template: "[{{^missing}}Cannot find key 'missing'!{{/missing}}]" expected: "[Cannot find key 'missing'!]" # Dotted Names - name: Dotted Names - Truthy desc: Dotted names should be valid for Inverted Section tags. data: { a: { b: { c: true } } } template: '"{{^a.b.c}}Not Here{{/a.b.c}}" == ""' expected: '"" == ""' - name: Dotted Names - Falsey desc: Dotted names should be valid for Inverted Section tags. data: { a: { b: { c: false } } } template: '"{{^a.b.c}}Not Here{{/a.b.c}}" == "Not Here"' expected: '"Not Here" == "Not Here"' - name: Dotted Names - Broken Chains desc: Dotted names that cannot be resolved should be considered falsey. data: { a: { } } template: '"{{^a.b.c}}Not Here{{/a.b.c}}" == "Not Here"' expected: '"Not Here" == "Not Here"' # Whitespace Sensitivity - name: Surrounding Whitespace desc: Inverted sections should not alter surrounding whitespace. data: { boolean: false } template: " | {{^boolean}}\t|\t{{/boolean}} | \n" expected: " | \t|\t | \n" - name: Internal Whitespace desc: Inverted should not alter internal whitespace. data: { boolean: false } template: " | {{^boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n" expected: " | \n | \n" - name: Indented Inline Sections desc: Single-line sections should not alter surrounding whitespace. data: { boolean: false } template: " {{^boolean}}NO{{/boolean}}\n {{^boolean}}WAY{{/boolean}}\n" expected: " NO\n WAY\n" - name: Standalone Lines desc: Standalone lines should be removed from the template. data: { boolean: false } template: | | This Is {{^boolean}} | {{/boolean}} | A Line expected: | | This Is | | A Line - name: Standalone Indented Lines desc: Standalone indented lines should be removed from the template. data: { boolean: false } template: | | This Is {{^boolean}} | {{/boolean}} | A Line expected: | | This Is | | A Line - name: Standalone Line Endings desc: '"\r\n" should be considered a newline for standalone tags.' data: { boolean: false } template: "|\r\n{{^boolean}}\r\n{{/boolean}}\r\n|" expected: "|\r\n|" - name: Standalone Without Previous Line desc: Standalone tags should not require a newline to precede them. data: { boolean: false } template: " {{^boolean}}\n^{{/boolean}}\n/" expected: "^\n/" - name: Standalone Without Newline desc: Standalone tags should not require a newline to follow them. data: { boolean: false } template: "^{{^boolean}}\n/\n {{/boolean}}" expected: "^\n/\n" # Whitespace Insensitivity - name: Padding desc: Superfluous in-tag whitespace should be ignored. data: { boolean: false } template: '|{{^ boolean }}={{/ boolean }}|' expected: '|=|' ================================================ FILE: 3rdpart/qt-mustache-master/tests/specs/partials.json ================================================ {"__ATTN__":"Do not edit this file; changes belong in the appropriate YAML file.","overview":"Partial tags are used to expand an external template into the current\ntemplate.\n\nThe tag's content MUST be a non-whitespace character sequence NOT containing\nthe current closing delimiter.\n\nThis tag's content names the partial to inject. Set Delimiter tags MUST NOT\naffect the parsing of a partial. The partial MUST be rendered against the\ncontext stack local to the tag. If the named partial cannot be found, the\nempty string SHOULD be used instead, as in interpolations.\n\nPartial tags SHOULD be treated as standalone when appropriate. If this tag\nis used standalone, any whitespace preceding the tag should treated as\nindentation, and prepended to each line of the partial before rendering.\n","tests":[{"name":"Basic Behavior","data":{},"expected":"\"from partial\"","template":"\"{{>text}}\"","desc":"The greater-than operator should expand to the named partial.","partials":{"text":"from partial"}},{"name":"Failed Lookup","data":{},"expected":"\"\"","template":"\"{{>text}}\"","desc":"The empty string should be used when the named partial is not found.","partials":{}},{"name":"Context","data":{"text":"content"},"expected":"\"*content*\"","template":"\"{{>partial}}\"","desc":"The greater-than operator should operate within the current context.","partials":{"partial":"*{{text}}*"}},{"name":"Recursion","data":{"content":"X","nodes":[{"content":"Y","nodes":[]}]},"expected":"X>","template":"{{>node}}","desc":"The greater-than operator should properly recurse.","partials":{"node":"{{content}}<{{#nodes}}{{>node}}{{/nodes}}>"}},{"name":"Surrounding Whitespace","data":{},"expected":"| \t|\t |","template":"| {{>partial}} |","desc":"The greater-than operator should not alter surrounding whitespace.","partials":{"partial":"\t|\t"}},{"name":"Inline Indentation","data":{"data":"|"},"expected":" | >\n>\n","template":" {{data}} {{> partial}}\n","desc":"Whitespace should be left untouched.","partials":{"partial":">\n>"}},{"name":"Standalone Line Endings","data":{},"expected":"|\r\n>|","template":"|\r\n{{>partial}}\r\n|","desc":"\"\\r\\n\" should be considered a newline for standalone tags.","partials":{"partial":">"}},{"name":"Standalone Without Previous Line","data":{},"expected":" >\n >>","template":" {{>partial}}\n>","desc":"Standalone tags should not require a newline to precede them.","partials":{"partial":">\n>"}},{"name":"Standalone Without Newline","data":{},"expected":">\n >\n >","template":">\n {{>partial}}","desc":"Standalone tags should not require a newline to follow them.","partials":{"partial":">\n>"}},{"name":"Standalone Indentation","data":{"content":"<\n->"},"expected":"\\\n |\n <\n->\n |\n/\n","template":"\\\n {{>partial}}\n/\n","desc":"Each line of the partial should be indented before rendering.","partials":{"partial":"|\n{{{content}}}\n|\n"}},{"name":"Padding Whitespace","data":{"boolean":true},"expected":"|[]|","template":"|{{> partial }}|","desc":"Superfluous in-tag whitespace should be ignored.","partials":{"partial":"[]"}}]} ================================================ FILE: 3rdpart/qt-mustache-master/tests/specs/partials.yml ================================================ overview: | Partial tags are used to expand an external template into the current template. The tag's content MUST be a non-whitespace character sequence NOT containing the current closing delimiter. This tag's content names the partial to inject. Set Delimiter tags MUST NOT affect the parsing of a partial. The partial MUST be rendered against the context stack local to the tag. If the named partial cannot be found, the empty string SHOULD be used instead, as in interpolations. Partial tags SHOULD be treated as standalone when appropriate. If this tag is used standalone, any whitespace preceding the tag should treated as indentation, and prepended to each line of the partial before rendering. tests: - name: Basic Behavior desc: The greater-than operator should expand to the named partial. data: { } template: '"{{>text}}"' partials: { text: 'from partial' } expected: '"from partial"' - name: Failed Lookup desc: The empty string should be used when the named partial is not found. data: { } template: '"{{>text}}"' partials: { } expected: '""' - name: Context desc: The greater-than operator should operate within the current context. data: { text: 'content' } template: '"{{>partial}}"' partials: { partial: '*{{text}}*' } expected: '"*content*"' - name: Recursion desc: The greater-than operator should properly recurse. data: { content: "X", nodes: [ { content: "Y", nodes: [] } ] } template: '{{>node}}' partials: { node: '{{content}}<{{#nodes}}{{>node}}{{/nodes}}>' } expected: 'X>' # Whitespace Sensitivity - name: Surrounding Whitespace desc: The greater-than operator should not alter surrounding whitespace. data: { } template: '| {{>partial}} |' partials: { partial: "\t|\t" } expected: "| \t|\t |" - name: Inline Indentation desc: Whitespace should be left untouched. data: { data: '|' } template: " {{data}} {{> partial}}\n" partials: { partial: ">\n>" } expected: " | >\n>\n" - name: Standalone Line Endings desc: '"\r\n" should be considered a newline for standalone tags.' data: { } template: "|\r\n{{>partial}}\r\n|" partials: { partial: ">" } expected: "|\r\n>|" - name: Standalone Without Previous Line desc: Standalone tags should not require a newline to precede them. data: { } template: " {{>partial}}\n>" partials: { partial: ">\n>"} expected: " >\n >>" - name: Standalone Without Newline desc: Standalone tags should not require a newline to follow them. data: { } template: ">\n {{>partial}}" partials: { partial: ">\n>" } expected: ">\n >\n >" - name: Standalone Indentation desc: Each line of the partial should be indented before rendering. data: { content: "<\n->" } template: | \ {{>partial}} / partials: partial: | | {{{content}}} | expected: | \ | < -> | / # Whitespace Insensitivity - name: Padding Whitespace desc: Superfluous in-tag whitespace should be ignored. data: { boolean: true } template: "|{{> partial }}|" partials: { partial: "[]" } expected: '|[]|' ================================================ FILE: 3rdpart/qt-mustache-master/tests/specs/sections.json ================================================ {"__ATTN__":"Do not edit this file; changes belong in the appropriate YAML file.","overview":"Section tags and End Section tags are used in combination to wrap a section\nof the template for iteration\n\nThese tags' content MUST be a non-whitespace character sequence NOT\ncontaining the current closing delimiter; each Section tag MUST be followed\nby an End Section tag with the same content within the same section.\n\nThis tag's content names the data to replace the tag. Name resolution is as\nfollows:\n 1) Split the name on periods; the first part is the name to resolve, any\n remaining parts should be retained.\n 2) Walk the context stack from top to bottom, finding the first context\n that is a) a hash containing the name as a key OR b) an object responding\n to a method with the given name.\n 3) If the context is a hash, the data is the value associated with the\n name.\n 4) If the context is an object and the method with the given name has an\n arity of 1, the method SHOULD be called with a String containing the\n unprocessed contents of the sections; the data is the value returned.\n 5) Otherwise, the data is the value returned by calling the method with\n the given name.\n 6) If any name parts were retained in step 1, each should be resolved\n against a context stack containing only the result from the former\n resolution. If any part fails resolution, the result should be considered\n falsey, and should interpolate as the empty string.\nIf the data is not of a list type, it is coerced into a list as follows: if\nthe data is truthy (e.g. `!!data == true`), use a single-element list\ncontaining the data, otherwise use an empty list.\n\nFor each element in the data list, the element MUST be pushed onto the\ncontext stack, the section MUST be rendered, and the element MUST be popped\noff the context stack.\n\nSection and End Section tags SHOULD be treated as standalone when\nappropriate.\n","tests":[{"name":"Truthy","data":{"boolean":true},"expected":"\"This should be rendered.\"","template":"\"{{#boolean}}This should be rendered.{{/boolean}}\"","desc":"Truthy sections should have their contents rendered."},{"name":"Falsey","data":{"boolean":false},"expected":"\"\"","template":"\"{{#boolean}}This should not be rendered.{{/boolean}}\"","desc":"Falsey sections should have their contents omitted."},{"name":"Context","data":{"context":{"name":"Joe"}},"expected":"\"Hi Joe.\"","template":"\"{{#context}}Hi {{name}}.{{/context}}\"","desc":"Objects and hashes should be pushed onto the context stack."},{"name":"Deeply Nested Contexts","data":{"a":{"one":1},"b":{"two":2},"c":{"three":3},"d":{"four":4},"e":{"five":5}},"expected":"1\n121\n12321\n1234321\n123454321\n1234321\n12321\n121\n1\n","template":"{{#a}}\n{{one}}\n{{#b}}\n{{one}}{{two}}{{one}}\n{{#c}}\n{{one}}{{two}}{{three}}{{two}}{{one}}\n{{#d}}\n{{one}}{{two}}{{three}}{{four}}{{three}}{{two}}{{one}}\n{{#e}}\n{{one}}{{two}}{{three}}{{four}}{{five}}{{four}}{{three}}{{two}}{{one}}\n{{/e}}\n{{one}}{{two}}{{three}}{{four}}{{three}}{{two}}{{one}}\n{{/d}}\n{{one}}{{two}}{{three}}{{two}}{{one}}\n{{/c}}\n{{one}}{{two}}{{one}}\n{{/b}}\n{{one}}\n{{/a}}\n","desc":"All elements on the context stack should be accessible."},{"name":"List","data":{"list":[{"item":1},{"item":2},{"item":3}]},"expected":"\"123\"","template":"\"{{#list}}{{item}}{{/list}}\"","desc":"Lists should be iterated; list items should visit the context stack."},{"name":"Empty List","data":{"list":[]},"expected":"\"\"","template":"\"{{#list}}Yay lists!{{/list}}\"","desc":"Empty lists should behave like falsey values."},{"name":"Doubled","data":{"two":"second","bool":true},"expected":"* first\n* second\n* third\n","template":"{{#bool}}\n* first\n{{/bool}}\n* {{two}}\n{{#bool}}\n* third\n{{/bool}}\n","desc":"Multiple sections per template should be permitted."},{"name":"Nested (Truthy)","data":{"bool":true},"expected":"| A B C D E |","template":"| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E |","desc":"Nested truthy sections should have their contents rendered."},{"name":"Nested (Falsey)","data":{"bool":false},"expected":"| A E |","template":"| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E |","desc":"Nested falsey sections should be omitted."},{"name":"Context Misses","data":{},"expected":"[]","template":"[{{#missing}}Found key 'missing'!{{/missing}}]","desc":"Failed context lookups should be considered falsey."},{"name":"Implicit Iterator - String","data":{"list":["a","b","c","d","e"]},"expected":"\"(a)(b)(c)(d)(e)\"","template":"\"{{#list}}({{.}}){{/list}}\"","desc":"Implicit iterators should directly interpolate strings."},{"name":"Implicit Iterator - Integer","data":{"list":[1,2,3,4,5]},"expected":"\"(1)(2)(3)(4)(5)\"","template":"\"{{#list}}({{.}}){{/list}}\"","desc":"Implicit iterators should cast integers to strings and interpolate."},{"name":"Implicit Iterator - Decimal","data":{"list":[1.1,2.2,3.3,4.4,5.5]},"expected":"\"(1.1)(2.2)(3.3)(4.4)(5.5)\"","template":"\"{{#list}}({{.}}){{/list}}\"","desc":"Implicit iterators should cast decimals to strings and interpolate."},{"name":"Dotted Names - Truthy","data":{"a":{"b":{"c":true}}},"expected":"\"Here\" == \"Here\"","template":"\"{{#a.b.c}}Here{{/a.b.c}}\" == \"Here\"","desc":"Dotted names should be valid for Section tags."},{"name":"Dotted Names - Falsey","data":{"a":{"b":{"c":false}}},"expected":"\"\" == \"\"","template":"\"{{#a.b.c}}Here{{/a.b.c}}\" == \"\"","desc":"Dotted names should be valid for Section tags."},{"name":"Dotted Names - Broken Chains","data":{"a":{}},"expected":"\"\" == \"\"","template":"\"{{#a.b.c}}Here{{/a.b.c}}\" == \"\"","desc":"Dotted names that cannot be resolved should be considered falsey."},{"name":"Surrounding Whitespace","data":{"boolean":true},"expected":" | \t|\t | \n","template":" | {{#boolean}}\t|\t{{/boolean}} | \n","desc":"Sections should not alter surrounding whitespace."},{"name":"Internal Whitespace","data":{"boolean":true},"expected":" | \n | \n","template":" | {{#boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n","desc":"Sections should not alter internal whitespace."},{"name":"Indented Inline Sections","data":{"boolean":true},"expected":" YES\n GOOD\n","template":" {{#boolean}}YES{{/boolean}}\n {{#boolean}}GOOD{{/boolean}}\n","desc":"Single-line sections should not alter surrounding whitespace."},{"name":"Standalone Lines","data":{"boolean":true},"expected":"| This Is\n|\n| A Line\n","template":"| This Is\n{{#boolean}}\n|\n{{/boolean}}\n| A Line\n","desc":"Standalone lines should be removed from the template."},{"name":"Indented Standalone Lines","data":{"boolean":true},"expected":"| This Is\n|\n| A Line\n","template":"| This Is\n {{#boolean}}\n|\n {{/boolean}}\n| A Line\n","desc":"Indented standalone lines should be removed from the template."},{"name":"Standalone Line Endings","data":{"boolean":true},"expected":"|\r\n|","template":"|\r\n{{#boolean}}\r\n{{/boolean}}\r\n|","desc":"\"\\r\\n\" should be considered a newline for standalone tags."},{"name":"Standalone Without Previous Line","data":{"boolean":true},"expected":"#\n/","template":" {{#boolean}}\n#{{/boolean}}\n/","desc":"Standalone tags should not require a newline to precede them."},{"name":"Standalone Without Newline","data":{"boolean":true},"expected":"#\n/\n","template":"#{{#boolean}}\n/\n {{/boolean}}","desc":"Standalone tags should not require a newline to follow them."},{"name":"Padding","data":{"boolean":true},"expected":"|=|","template":"|{{# boolean }}={{/ boolean }}|","desc":"Superfluous in-tag whitespace should be ignored."}]} ================================================ FILE: 3rdpart/qt-mustache-master/tests/specs/sections.yml ================================================ overview: | Section tags and End Section tags are used in combination to wrap a section of the template for iteration These tags' content MUST be a non-whitespace character sequence NOT containing the current closing delimiter; each Section tag MUST be followed by an End Section tag with the same content within the same section. This tag's content names the data to replace the tag. Name resolution is as follows: 1) Split the name on periods; the first part is the name to resolve, any remaining parts should be retained. 2) Walk the context stack from top to bottom, finding the first context that is a) a hash containing the name as a key OR b) an object responding to a method with the given name. 3) If the context is a hash, the data is the value associated with the name. 4) If the context is an object and the method with the given name has an arity of 1, the method SHOULD be called with a String containing the unprocessed contents of the sections; the data is the value returned. 5) Otherwise, the data is the value returned by calling the method with the given name. 6) If any name parts were retained in step 1, each should be resolved against a context stack containing only the result from the former resolution. If any part fails resolution, the result should be considered falsey, and should interpolate as the empty string. If the data is not of a list type, it is coerced into a list as follows: if the data is truthy (e.g. `!!data == true`), use a single-element list containing the data, otherwise use an empty list. For each element in the data list, the element MUST be pushed onto the context stack, the section MUST be rendered, and the element MUST be popped off the context stack. Section and End Section tags SHOULD be treated as standalone when appropriate. tests: - name: Truthy desc: Truthy sections should have their contents rendered. data: { boolean: true } template: '"{{#boolean}}This should be rendered.{{/boolean}}"' expected: '"This should be rendered."' - name: Falsey desc: Falsey sections should have their contents omitted. data: { boolean: false } template: '"{{#boolean}}This should not be rendered.{{/boolean}}"' expected: '""' - name: Context desc: Objects and hashes should be pushed onto the context stack. data: { context: { name: 'Joe' } } template: '"{{#context}}Hi {{name}}.{{/context}}"' expected: '"Hi Joe."' - name: Deeply Nested Contexts desc: All elements on the context stack should be accessible. data: a: { one: 1 } b: { two: 2 } c: { three: 3 } d: { four: 4 } e: { five: 5 } template: | {{#a}} {{one}} {{#b}} {{one}}{{two}}{{one}} {{#c}} {{one}}{{two}}{{three}}{{two}}{{one}} {{#d}} {{one}}{{two}}{{three}}{{four}}{{three}}{{two}}{{one}} {{#e}} {{one}}{{two}}{{three}}{{four}}{{five}}{{four}}{{three}}{{two}}{{one}} {{/e}} {{one}}{{two}}{{three}}{{four}}{{three}}{{two}}{{one}} {{/d}} {{one}}{{two}}{{three}}{{two}}{{one}} {{/c}} {{one}}{{two}}{{one}} {{/b}} {{one}} {{/a}} expected: | 1 121 12321 1234321 123454321 1234321 12321 121 1 - name: List desc: Lists should be iterated; list items should visit the context stack. data: { list: [ { item: 1 }, { item: 2 }, { item: 3 } ] } template: '"{{#list}}{{item}}{{/list}}"' expected: '"123"' - name: Empty List desc: Empty lists should behave like falsey values. data: { list: [ ] } template: '"{{#list}}Yay lists!{{/list}}"' expected: '""' - name: Doubled desc: Multiple sections per template should be permitted. data: { bool: true, two: 'second' } template: | {{#bool}} * first {{/bool}} * {{two}} {{#bool}} * third {{/bool}} expected: | * first * second * third - name: Nested (Truthy) desc: Nested truthy sections should have their contents rendered. data: { bool: true } template: "| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E |" expected: "| A B C D E |" - name: Nested (Falsey) desc: Nested falsey sections should be omitted. data: { bool: false } template: "| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E |" expected: "| A E |" - name: Context Misses desc: Failed context lookups should be considered falsey. data: { } template: "[{{#missing}}Found key 'missing'!{{/missing}}]" expected: "[]" # Implicit Iterators - name: Implicit Iterator - String desc: Implicit iterators should directly interpolate strings. data: list: [ 'a', 'b', 'c', 'd', 'e' ] template: '"{{#list}}({{.}}){{/list}}"' expected: '"(a)(b)(c)(d)(e)"' - name: Implicit Iterator - Integer desc: Implicit iterators should cast integers to strings and interpolate. data: list: [ 1, 2, 3, 4, 5 ] template: '"{{#list}}({{.}}){{/list}}"' expected: '"(1)(2)(3)(4)(5)"' - name: Implicit Iterator - Decimal desc: Implicit iterators should cast decimals to strings and interpolate. data: list: [ 1.10, 2.20, 3.30, 4.40, 5.50 ] template: '"{{#list}}({{.}}){{/list}}"' expected: '"(1.1)(2.2)(3.3)(4.4)(5.5)"' # Dotted Names - name: Dotted Names - Truthy desc: Dotted names should be valid for Section tags. data: { a: { b: { c: true } } } template: '"{{#a.b.c}}Here{{/a.b.c}}" == "Here"' expected: '"Here" == "Here"' - name: Dotted Names - Falsey desc: Dotted names should be valid for Section tags. data: { a: { b: { c: false } } } template: '"{{#a.b.c}}Here{{/a.b.c}}" == ""' expected: '"" == ""' - name: Dotted Names - Broken Chains desc: Dotted names that cannot be resolved should be considered falsey. data: { a: { } } template: '"{{#a.b.c}}Here{{/a.b.c}}" == ""' expected: '"" == ""' # Whitespace Sensitivity - name: Surrounding Whitespace desc: Sections should not alter surrounding whitespace. data: { boolean: true } template: " | {{#boolean}}\t|\t{{/boolean}} | \n" expected: " | \t|\t | \n" - name: Internal Whitespace desc: Sections should not alter internal whitespace. data: { boolean: true } template: " | {{#boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n" expected: " | \n | \n" - name: Indented Inline Sections desc: Single-line sections should not alter surrounding whitespace. data: { boolean: true } template: " {{#boolean}}YES{{/boolean}}\n {{#boolean}}GOOD{{/boolean}}\n" expected: " YES\n GOOD\n" - name: Standalone Lines desc: Standalone lines should be removed from the template. data: { boolean: true } template: | | This Is {{#boolean}} | {{/boolean}} | A Line expected: | | This Is | | A Line - name: Indented Standalone Lines desc: Indented standalone lines should be removed from the template. data: { boolean: true } template: | | This Is {{#boolean}} | {{/boolean}} | A Line expected: | | This Is | | A Line - name: Standalone Line Endings desc: '"\r\n" should be considered a newline for standalone tags.' data: { boolean: true } template: "|\r\n{{#boolean}}\r\n{{/boolean}}\r\n|" expected: "|\r\n|" - name: Standalone Without Previous Line desc: Standalone tags should not require a newline to precede them. data: { boolean: true } template: " {{#boolean}}\n#{{/boolean}}\n/" expected: "#\n/" - name: Standalone Without Newline desc: Standalone tags should not require a newline to follow them. data: { boolean: true } template: "#{{#boolean}}\n/\n {{/boolean}}" expected: "#\n/\n" # Whitespace Insensitivity - name: Padding desc: Superfluous in-tag whitespace should be ignored. data: { boolean: true } template: '|{{# boolean }}={{/ boolean }}|' expected: '|=|' ================================================ FILE: 3rdpart/qt-mustache-master/tests/test_mustache.cpp ================================================ /* Copyright 2012, Robert Knight Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. */ #include "test_mustache.h" #include #include #include #include #if QT_VERSION >= 0x050000 #include #include #include #endif // QT_VERSION >= 0x050000 // To be able to use QHash in QFETCH(..). typedef QHash PartialsHash; Q_DECLARE_METATYPE(PartialsHash) void TestMustache::testValues() { QVariantHash map; map["name"] = "John Smith"; map["age"] = 42; map["sex"] = "Male"; map["company"] = "Smith & Co"; map["signature"] = "John Smith of Smith & Co"; map["alive"] = false; QString _template = "Name: {{name}}, Age: {{age}}, Sex: {{sex}}, Alive: {{alive}}\n" "Company: {{company}}\n" " {{{signature}}}" "{{missing-key}}"; QString expectedOutput = "Name: John Smith, Age: 42, Sex: Male, Alive: false\n" "Company: Smith & Co\n" " John Smith of Smith & Co"; Mustache::Renderer renderer; Mustache::QtVariantContext context(map); QString output = renderer.render(_template, &context); QCOMPARE(output, expectedOutput); } QVariantHash contactInfo(const QString& name, const QString& email) { QVariantHash map; map["name"] = name; map["email"] = email; return map; } void TestMustache::testSections() { QVariantHash map = contactInfo("John Smith", "john.smith@gmail.com"); QVariantList contacts; contacts << contactInfo("James Dee", "james@dee.org"); contacts << contactInfo("Jim Jones", "jim-jones@yahoo.com"); map["contacts"] = contacts; QString _template = "Name: {{name}}, Email: {{email}}\n" "{{#contacts}} {{name}} - {{email}}\n{{/contacts}}" "{{^contacts}} No contacts{{/contacts}}"; QString expectedOutput = "Name: John Smith, Email: john.smith@gmail.com\n" " James Dee - james@dee.org\n" " Jim Jones - jim-jones@yahoo.com\n"; Mustache::Renderer renderer; Mustache::QtVariantContext context(map); QString output = renderer.render(_template, &context); QCOMPARE(output, expectedOutput); // test inverted sections map.remove("contacts"); context = Mustache::QtVariantContext(map); output = renderer.render(_template, &context); expectedOutput = "Name: John Smith, Email: john.smith@gmail.com\n" " No contacts"; QCOMPARE(output, expectedOutput); // test with an empty list instead of an empty key map["contacts"] = QVariantHash(); context = Mustache::QtVariantContext(map); output = renderer.render(_template, &context); QCOMPARE(output, expectedOutput); } void TestMustache::testFalsiness() { Mustache::Renderer renderer; QVariantHash data; QString _template = "{{#bool}}This should not be shown{{/bool}}"; // test falsiness of 0 data["bool"] = 0; Mustache::QtVariantContext context = Mustache::QtVariantContext(data); QString output = renderer.render(_template, &context); QVERIFY2(output.isEmpty(), "0 evaluated as truthy"); // test falsiness of 0u data["bool"] = 0u; context = Mustache::QtVariantContext(data); output = renderer.render(_template, &context); QVERIFY2(output.isEmpty(), "0u evaluated as truthy"); // test falsiness of 0ll data["bool"] = 0ll; context = Mustache::QtVariantContext(data); output = renderer.render(_template, &context); QVERIFY2(output.isEmpty(), "0ll evaluated as truthy"); // test falsiness of 0ull data["bool"] = 0ull; context = Mustache::QtVariantContext(data); output = renderer.render(_template, &context); QVERIFY2(output.isEmpty(), "0ull evaluated as truthy"); // test falsiness of 0.0 data["bool"] = 0.0; context = Mustache::QtVariantContext(data); output = renderer.render(_template, &context); QVERIFY2(output.isEmpty(), "0.0 evaluated as truthy"); // test falsiness of 0.0f data["bool"] = 0.0f; context = Mustache::QtVariantContext(data); output = renderer.render(_template, &context); QVERIFY2(output.isEmpty(), "0.0f evaluated as truthy"); // test falsiness of '\0' data["bool"] = '\0'; context = Mustache::QtVariantContext(data); output = renderer.render(_template, &context); QVERIFY2(output.isEmpty(), "'\0' evaluated as truthy"); // test falsiness of 'false' data["bool"] = false; context = Mustache::QtVariantContext(data); output = renderer.render(_template, &context); QVERIFY2(output.isEmpty(), "'\0' evaluated as truthy"); } void TestMustache::testContextLookup() { QVariantHash fileMap; fileMap["dir"] = "/home/robert"; fileMap["name"] = "robert"; QVariantList files; QVariantHash file; file["name"] = "test.pdf"; files << file; fileMap["files"] = files; QString _template = "{{#files}}{{dir}}/{{name}}{{/files}}"; Mustache::Renderer renderer; Mustache::QtVariantContext context(fileMap); QString output = renderer.render(_template, &context); QCOMPARE(output, QString("/home/robert/test.pdf")); } void TestMustache::testPartials() { QHash partials; partials["file-info"] = "{{name}} {{size}} {{type}}\n"; QString _template = "{{#files}}{{>file-info}}{{/files}}"; QVariantHash map; QVariantList fileList; QVariantHash file1; file1["name"] = "mustache.pdf"; file1["size"] = "200KB"; file1["type"] = "PDF Document"; QVariantHash file2; file2["name"] = "cv.doc"; file2["size"] = "300KB"; file2["type"] = "Microsoft Word Document"; fileList << file1 << file2; map["files"] = fileList; Mustache::Renderer renderer; Mustache::PartialMap partialMap(partials); Mustache::QtVariantContext context(map, &partialMap); QString output = renderer.render(_template, &context); QCOMPARE(output, QString("mustache.pdf 200KB PDF Document\n" "cv.doc 300KB Microsoft Word Document\n")); } void TestMustache::testSetDelimiters() { // test changing the markers within a template QVariantHash map; map["name"] = "John Smith"; map["phone"] = "01234 567890"; QString _template = "{{=<% %>=}}" "<%name%>{{ }}<%phone%>" "<%={{ }}=%>" " {{name}}<% %>{{phone}}"; QString expectedOutput = "John Smith{{ }}01234 567890 John Smith<% %>01234 567890"; Mustache::Renderer renderer; Mustache::QtVariantContext context(map); QString output = renderer.render(_template, &context); QCOMPARE(output, expectedOutput); // test changing the default markers renderer.setTagMarkers("%", "%"); output = renderer.render("%name%'s phone number is %phone%", &context); QCOMPARE(output, QString("John Smith's phone number is 01234 567890")); renderer.setTagMarkers("{{", "}}"); output = renderer.render("{{== ==}}", &context); QCOMPARE(renderer.error(), QString("Custom delimiters may not contain '='.")); } void TestMustache::testErrors() { QVariantHash map; map["name"] = "Jim Jones"; QHash partials; partials["buggy-partial"] = "--{{/one}}--"; QString _template = "{{name}}"; Mustache::Renderer renderer; Mustache::PartialMap partialMap(partials); Mustache::QtVariantContext context(map, &partialMap); QString output = renderer.render(_template, &context); QCOMPARE(output, QString("Jim Jones")); QCOMPARE(renderer.error(), QString()); QCOMPARE(renderer.errorPos(), -1); _template = "{{#one}} {{/two}}"; output = renderer.render(_template, &context); QCOMPARE(renderer.error(), QString("Tag start/end key mismatch")); QCOMPARE(renderer.errorPos(), 9); QCOMPARE(renderer.errorPartial(), QString()); _template = "Hello {{>buggy-partial}}"; output = renderer.render(_template, &context); QCOMPARE(renderer.error(), QString("Unexpected end tag")); QCOMPARE(renderer.errorPos(), 2); QCOMPARE(renderer.errorPartial(), QString("buggy-partial")); } void TestMustache::testPartialFile() { QString path = QCoreApplication::applicationDirPath(); QVariantHash map = contactInfo("Jim Smith", "jim.smith@gmail.com"); QString _template = "{{>partial}}"; Mustache::Renderer renderer; Mustache::PartialFileLoader partialLoader(path); Mustache::QtVariantContext context(map, &partialLoader); QString output = renderer.render(_template, &context); QCOMPARE(output, QString("Jim Smith -- jim.smith@gmail.com\n")); } void TestMustache::testEscaping() { QVariantHash map; map["escape"] = "foo"; map["unescape"] = "One & Two "quoted""; map["raw"] = "foo"; QString _template = "{{escape}} {{&unescape}} {{{raw}}}"; Mustache::Renderer renderer; Mustache::QtVariantContext context(map); QString output = renderer.render(_template, &context); QCOMPARE(output, QString("<b>foo</b> One & Two \"quoted\" foo")); } class CounterContext : public Mustache::QtVariantContext { public: int counter; CounterContext(const QVariantHash& map) : Mustache::QtVariantContext(map) , counter(0) {} virtual bool canEval(const QString& key) const { return key == "counter"; } virtual QString eval(const QString& key, const QString& _template, Mustache::Renderer* renderer) { if (key == "counter") { ++counter; } return renderer->render(_template, this); } virtual QString stringValue(const QString& key) const { if (key == "count") { return QString::number(counter); } else { return Mustache::QtVariantContext::stringValue(key); } } }; void TestMustache::testEval() { QVariantHash map; QVariantList list; list << contactInfo("Rob Knight", "robertknight@gmail.com"); list << contactInfo("Jim Smith", "jim.smith@smith.org"); map["list"] = list; QString _template = "{{#list}}{{#counter}}#{{count}} {{name}} {{email}}{{/counter}}\n{{/list}}"; Mustache::Renderer renderer; CounterContext context(map); QString output = renderer.render(_template, &context); QCOMPARE(output, QString("#1 Rob Knight robertknight@gmail.com\n" "#2 Jim Smith jim.smith@smith.org\n")); } void TestMustache::testHelpers() { QVariantHash args; args.insert("name", "Jim Smith"); args.insert("age", 42); QString output = Mustache::renderTemplate("Hello {{name}}, you are {{age}}", args); QCOMPARE(output, QString("Hello Jim Smith, you are 42")); } void TestMustache::testIncompleteTag() { QVariantHash args; args.insert("name", "Jim Smith"); QString output = Mustache::renderTemplate("Hello {{name}}, you are {", args); QCOMPARE(output, QString("Hello Jim Smith, you are {")); output = Mustache::renderTemplate("Hello {{name}}, you are {{", args); QCOMPARE(output, QString("Hello Jim Smith, you are {{")); output = Mustache::renderTemplate("Hello {{name}}, you are {{}", args); QCOMPARE(output, QString("Hello Jim Smith, you are {{}")); } void TestMustache::testIncompleteSection() { QVariantHash args; args.insert("list", QVariantList() << QVariantHash()); Mustache::Renderer renderer; Mustache::QtVariantContext context(args); QString output = renderer.render("{{#list}}", &context); QCOMPARE(output, QString()); QCOMPARE(renderer.error(), QString("No matching end tag found for section")); output = renderer.render("{{^list}}", &context); QCOMPARE(output, QString()); QCOMPARE(renderer.error(), QString("No matching end tag found for inverted section")); output = renderer.render("{{/list}}", &context); QCOMPARE(output, QString()); QCOMPARE(renderer.error(), QString("Unexpected end tag")); output = renderer.render("{{#list}}{{/foo}}", &context); QCOMPARE(output, QString()); QCOMPARE(renderer.error(), QString("Tag start/end key mismatch")); } static QString decorate(const QString& text, Mustache::Renderer* r, Mustache::Context* ctx) { return "~" + r->render(text, ctx) + "~"; } void TestMustache::testLambda() { QVariantHash args; args["text"] = "test"; args["fn"] = QVariant::fromValue(Mustache::QtVariantContext::fn_t(decorate)); QString output = Mustache::renderTemplate("{{#fn}}{{text}}{{/fn}}", args); QCOMPARE(output, QString("~test~")); } void TestMustache::testQStringListIteration() { QStringList list; list << "str1" << "str2" << "str3"; QVariantHash args; args["list"] = list; QString output = Mustache::renderTemplate("{{#list}}{{.}}{{/list}}", args); QCOMPARE(output, QString("str1str2str3")); } void TestMustache::testUnescapeHtml() { QVariantHash args; args["s"] = "<>&"&quot;"; QString output = Mustache::renderTemplate("{{&s}}", args); QCOMPARE(output, QString("<>&\""")); } #if QT_VERSION >= 0x050000 // JSON classes only in Qt 5+. void TestMustache::testConformance_data() { QTest::addColumn("data"); QTest::addColumn("template_"); QTest::addColumn >("partials"); QTest::addColumn("expected"); QDir specsDir = QDir("."); foreach (const QString &fileName, specsDir.entryList(QStringList() << "*.json")) { QFile file(specsDir.filePath(fileName)); QVERIFY2(file.open(QIODevice::ReadOnly), qPrintable(fileName + ": " + file.errorString())); QJsonDocument document = QJsonDocument::fromJson(file.readAll()); QJsonArray testCaseValues = document.object()["tests"].toArray(); foreach (const QJsonValue &testCaseValue, testCaseValues) { QJsonObject testCaseObject = testCaseValue.toObject(); QString name = fileName + " - " + testCaseObject["name"].toString(); QVariantMap data = testCaseObject["data"].toObject().toVariantMap(); QString template_ = testCaseObject["template"].toString(); QJsonObject partialsObject = testCaseObject["partials"].toObject(); PartialsHash partials; foreach (const QString &partialName, partialsObject.keys()) { partials.insert(partialName, partialsObject[partialName].toString()); } QString expected = testCaseObject["expected"].toString(); QTest::newRow(qPrintable(name)) << data << template_ << partials << expected; } } } /* * This test will run once for each test case defined in version 1.1.2 of the * Mustache specification [1]. * * [1] https://github.com/mustache/spec/tree/v1.1.2/specs */ void TestMustache::testConformance() { QFETCH(QVariantMap, data); QFETCH(QString, template_); QFETCH(PartialsHash, partials); QFETCH(QString, expected); Mustache::Renderer renderer; Mustache::PartialMap partialsMap(partials); Mustache::QtVariantContext context(data, &partialsMap); QString output = renderer.render(template_, &context); QCOMPARE(output, expected); } #endif // QT_VERSION >= 0x050000 // Create a QCoreApplication for the test. In Qt 5 this can be // done with QTEST_GUILESS_MAIN(). int main(int argc, char** argv) { QCoreApplication app(argc, argv); TestMustache testObject; return QTest::qExec(&testObject, argc, argv); } ================================================ FILE: 3rdpart/qt-mustache-master/tests/test_mustache.h ================================================ /* Copyright 2012, Robert Knight Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. */ #pragma once #include "mustache.h" #include class TestMustache : public QObject { Q_OBJECT private Q_SLOTS: void testContextLookup(); void testErrors(); void testPartialFile(); void testPartials(); void testSections(); void testFalsiness(); void testSetDelimiters(); void testValues(); void testEscaping(); void testEval(); void testHelpers(); void testIncompleteTag(); void testIncompleteSection(); void testLambda(); void testQStringListIteration(); void testUnescapeHtml(); #if QT_VERSION >= 0x050000 void testConformance(); void testConformance_data(); #endif // QT_VERSION >= 0x050000 }; ================================================ FILE: LICENSE ================================================ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. {one line to give the program's name and a brief idea of what it does.} Copyright (C) {year} {name of author} This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: {project} Copyright (C) {year} {fullname} This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . ================================================ FILE: README.md ================================================ # Embedded IDE Makefile based, C/C++ IDE ![Main Screen](docs/screen_0.png) ## Features - Syntax highlighter (C/C++/Makefile) - Autocomplete (requires clang installed on path) - Target autodiscover - Source filter - Project import/export - Console log ## Requirements - GNU Make (required) - Qt5 - QScintilla2 - clang (optional for autocompletion) - diff and patch (optional for import/export project) - universal-ctags (optional for code indexing) ## Installation To compile and install IDE you need Qt5 (5.2 or later) and make/gcc (build-essential en Ubuntu and derived) In base directory run: ```bash qmake && make ``` When the process is finished, the executable is in `build` directory with the name `embedded-ide` (with EXE extention on windows) To install it into the system copy `build/embedded-ide` to directory into the PATH ### Install dependencies The full toolset installation (for ubuntu and derivatives) is: ```bash sudo apt-get install clang diffutils patch ctags make ``` Additionally you need a compiler to work correctly. All gcc based compilers are supported, such as: - System gcc/g++ with `sudo apt-get install build-essential` - [ARM Embedded](https://launchpad.net/gcc-arm-embedded) - [RISC-V GNU toolchan](https://riscv.org/software-tools/) - [MIPS32/PIC32 gcc](https://github.com/chipKIT32/chipKIT-compiler-builds/releases) - All gcc based toolchain [from CodeSourcery](https://www.mentor.com/embedded-software/sourcery-tools/sourcery-codebench/editions/lite-edition/) - All [linaro toolchains](http://www.linaro.org/downloads/) - And much others... - [Cygwin toolchains](https://www.cygwin.com/) - [MinGW/MSYS enviroment](http://www.mingw.org/) ### Adding tools to the PATH In order to find utilities, you need to add them to the PATH, but doing it globally is dangerous in certain cases (Example, windows with multiple toolchains with similar names) Alternatively, the IDE provides an **Additional PATHs** feature to configure the PATH only for IDE and not for the entire system. Go to **Configure** icon and next go to **Tools** tab. ![](docs/config-tools.png) Into **Additional PATHs** section you can add multiple directories. The list is append to system PATH at runtime in top-to-bottom order. ## Screenshots ![](docs/screen_1.png) ![](docs/screen_2.png) ![](docs/screen_3.png) ![](docs/screen_4.png) ================================================ FILE: ci/BuildQSCI.mk ================================================ BASE=/tmp/qsci QSCI_VER=2.11.1 SOURCE_URL=https://www.riverbankcomputing.com/static/Downloads/QScintilla/${QSCI_VER}/QScintilla_gpl-$(QSCI_VER).tar.gz SOURCE_TARGZ=$(notdir $(SOURCE_URL)) SOURCE_DIR=$(BASE)/QScintilla_gpl-$(QSCI_VER) BUILD_DIR=$(SOURCE_DIR)/Qt4Qt5 BINARY_BUILD=$(BUILD_DIR)/libqscintilla2_qt5.so all: $(BINARY_BUILD) $(SOURCE_TARGZ): cd $(BASE) wget $(SOURCE_URL) -O $@ $(SOURCE_DIR): $(SOURCE_TARGZ) cd $(BASE) tar xf $< $(BINARY_BUILD): $(SOURCE_DIR) mkdir -p $(BUILD_DIR) && \ cd $(BUILD_DIR) && \ qmake && \ make -j4 && \ sudo make install ================================================ FILE: ci/extract-qt-installer ================================================ #!/bin/sh B=$(dirname $(realpath $0)) set -x set -e MAJOR=5 MINOR=12 REV=9 #VER=5124 VER=${MAJOR}${MINOR}${REV} VERSION=${MAJOR}.${MINOR}.${REV} PKG=$(cat ${B}/qt${VER}-linux-packages) mkdir -p /opt/qt/ ( cd /opt/qt && wget ${PKG} && for f in *; do 7z x $f > /dev/null; done && cat < ${VERSION}/gcc_64/bin/qt.conf [Paths] Prefix=.. EOF sed -i -r 's/QT_EDITION = Enterprise/QT_EDITION = OpenSource/' ${VERSION}/gcc_64/mkspecs/qconfig.pri sed -i -r 's/QT_LICHECK = lichec.*/QT_LICHECK = /' ${VERSION}/gcc_64/mkspecs/qconfig.pri ) ================================================ FILE: ci/qt-installer-silent.js ================================================ /* * Qt Installer script for a non-interactive installation of Qt5 on Windows. * Installs the 64-bit package if environment variable PLATFORM="x64". */ // jshint strict:false /* globals QInstaller, QMessageBox, buttons, gui, installer, console */ // Run with: // .\qt-unified-windows-x86-3.0.4-online.exe --verbose --script tools\qt-installer-windows.qs // Look for Name elements in // https://download.qt.io/online/qtsdkrepository/windows_x86/desktop/qt5_5120/Updates.xml // Unfortunately it is not possible to disable deps like qt.tools.qtcreator var INSTALL_COMPONENTS = [ "qt.qt5.5124.gcc_64" ]; function Controller() { // Continue on installing to an existing (possibly empty) directory. installer.setMessageBoxAutomaticAnswer("OverwriteTargetDirectory", QMessageBox.Yes); // Continue at "SHOW FINISHED PAGE" installer.installationFinished.connect(function() { console.log("installationFinished"); gui.clickButton(buttons.NextButton); }); } Controller.prototype.WelcomePageCallback = function() { console.log("Step: " + gui.currentPageWidget()); // At least for 3.0.4 immediately clicking Next fails, so wait a bit. // https://github.com/benlau/qtci/commit/85cb986b66af4807a928c70e13d82d00dc26ebf0 gui.clickButton(buttons.NextButton, 1000); }; Controller.prototype.CredentialsPageCallback = function() { console.log("Step: " + gui.currentPageWidget()); gui.clickButton(buttons.NextButton); }; Controller.prototype.IntroductionPageCallback = function() { console.log("Step: " + gui.currentPageWidget()); gui.clickButton(buttons.NextButton); }; Controller.prototype.TargetDirectoryPageCallback = function() { console.log("Step: " + gui.currentPageWidget()); // Keep default at "C:\Qt". //gui.currentPageWidget().TargetDirectoryLineEdit.setText("E:\\Qt"); gui.clickButton(buttons.NextButton); }; Controller.prototype.ComponentSelectionPageCallback = function() { console.log("Step: " + gui.currentPageWidget()); var page = gui.currentPageWidget(); page.deselectAll(); for (var i = 0; i < INSTALL_COMPONENTS.length; i++) { page.selectComponent(INSTALL_COMPONENTS[i]); } gui.clickButton(buttons.NextButton); }; Controller.prototype.LicenseAgreementPageCallback = function() { console.log("Step: " + gui.currentPageWidget()); gui.currentPageWidget().AcceptLicenseRadioButton.setChecked(true); gui.clickButton(buttons.NextButton); }; Controller.prototype.StartMenuDirectoryPageCallback = function() { console.log("Step: " + gui.currentPageWidget()); gui.clickButton(buttons.NextButton); }; Controller.prototype.ReadyForInstallationPageCallback = function() { console.log("Step: " + gui.currentPageWidget()); gui.clickButton(buttons.NextButton); }; Controller.prototype.FinishedPageCallback = function() { console.log("Step: " + gui.currentPageWidget()); // TODO somehow the installer crashes after this step. // https://stackoverflow.com/questions/25105269/silent-install-qt-run-installer-on-ubuntu-server var checkBoxForm = gui.currentPageWidget().LaunchQtCreatorCheckBoxForm; if (checkBoxForm && checkBoxForm.launchQtCreatorCheckBox) { checkBoxForm.launchQtCreatorCheckBox.checked = false; } gui.clickButton(buttons.FinishButton); }; // vim: set ft=javascript: ================================================ FILE: ci/qt5124-linux-packages ================================================ http://download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt5_5124/qt.qt5.5124.gcc_64/5.12.4-0-201906140147qtbase-Linux-RHEL_7_4-GCC-Linux-RHEL_7_4-X86_64.7z http://download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt5_5124/qt.qt5.5124.gcc_64/5.12.4-0-201906140147qtx11extras-Linux-RHEL_7_4-GCC-Linux-RHEL_7_4-X86_64.7z http://download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt5_5124/qt.qt5.5124.gcc_64/5.12.4-0-201906140147qtwebchannel-Linux-RHEL_7_4-GCC-Linux-RHEL_7_4-X86_64.7z http://download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt5_5124/qt.qt5.5124.gcc_64/5.12.4-0-201906140147qtmultimedia-Linux-RHEL_7_4-GCC-Linux-RHEL_7_4-X86_64.7z http://download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt5_5124/qt.qt5.5124.gcc_64/5.12.4-0-201906140147qttranslations-Linux-RHEL_7_4-GCC-Linux-RHEL_7_4-X86_64.7z http://download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt5_5124/qt.qt5.5124.gcc_64/5.12.4-0-201906140147qtgraphicaleffects-Linux-RHEL_7_4-GCC-Linux-RHEL_7_4-X86_64.7z http://download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt5_5124/qt.qt5.5124.gcc_64/5.12.4-0-201906140147qtsvg-Linux-RHEL_7_4-GCC-Linux-RHEL_7_4-X86_64.7z http://download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt5_5124/qt.qt5.5124.gcc_64/5.12.4-0-201906140147qtdeclarative-Linux-RHEL_7_4-GCC-Linux-RHEL_7_4-X86_64.7z http://download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt5_5124/qt.qt5.5124.gcc_64/5.12.4-0-201906140147qtimageformats-Linux-RHEL_7_4-GCC-Linux-RHEL_7_4-X86_64.7z http://download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt5_5124/qt.qt5.5124.gcc_64/5.12.4-0-201906140147qttools-Linux-RHEL_7_4-GCC-Linux-RHEL_7_4-X86_64.7z http://download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt5_5124/qt.qt5.5124.gcc_64/5.12.4-0-201906140147qtxmlpatterns-Linux-RHEL_7_4-GCC-Linux-RHEL_7_4-X86_64.7z http://download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt5_5124/qt.qt5.5124.gcc_64/5.12.4-0-201906140147qtserialport-Linux-RHEL_7_4-GCC-Linux-RHEL_7_4-X86_64.7z http://download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt5_5124/qt.qt5.5124.gcc_64/5.12.4-0-201906140147qtquickcontrols-Linux-RHEL_7_4-GCC-Linux-RHEL_7_4-X86_64.7z http://download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt5_5124/qt.qt5.5124.gcc_64/5.12.4-0-201906140147qtquickcontrols2-Linux-RHEL_7_4-GCC-Linux-RHEL_7_4-X86_64.7z http://download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt5_5124/qt.qt5.5124.gcc_64/5.12.4-0-201906140147qtserialbus-Linux-RHEL_7_4-GCC-Linux-RHEL_7_4-X86_64.7z http://download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt5_5124/qt.qt5.5124.gcc_64/5.12.4-0-201906140147qtscxml-Linux-RHEL_7_4-GCC-Linux-RHEL_7_4-X86_64.7z http://download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt5_5124/qt.qt5.5124.gcc_64/5.12.4-0-201906140147icu-linux-Rhel7.2-x64.7z ================================================ FILE: ci/qt5129-linux-packages ================================================ http://download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt5_5129/qt.qt5.5129.gcc_64/5.12.9-0-202006121744qtbase-Linux-RHEL_7_4-GCC-Linux-RHEL_7_4-X86_64.7z http://download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt5_5129/qt.qt5.5129.gcc_64/5.12.9-0-202006121744qtx11extras-Linux-RHEL_7_4-GCC-Linux-RHEL_7_4-X86_64.7z http://download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt5_5129/qt.qt5.5129.gcc_64/5.12.9-0-202006121744qtwebchannel-Linux-RHEL_7_4-GCC-Linux-RHEL_7_4-X86_64.7z http://download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt5_5129/qt.qt5.5129.gcc_64/5.12.9-0-202006121744qtmultimedia-Linux-RHEL_7_4-GCC-Linux-RHEL_7_4-X86_64.7z http://download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt5_5129/qt.qt5.5129.gcc_64/5.12.9-0-202006121744qttranslations-Linux-RHEL_7_4-GCC-Linux-RHEL_7_4-X86_64.7z http://download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt5_5129/qt.qt5.5129.gcc_64/5.12.9-0-202006121744qtgraphicaleffects-Linux-RHEL_7_4-GCC-Linux-RHEL_7_4-X86_64.7z http://download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt5_5129/qt.qt5.5129.gcc_64/5.12.9-0-202006121744qtsvg-Linux-RHEL_7_4-GCC-Linux-RHEL_7_4-X86_64.7z http://download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt5_5129/qt.qt5.5129.gcc_64/5.12.9-0-202006121744qtdeclarative-Linux-RHEL_7_4-GCC-Linux-RHEL_7_4-X86_64.7z http://download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt5_5129/qt.qt5.5129.gcc_64/5.12.9-0-202006121744qtimageformats-Linux-RHEL_7_4-GCC-Linux-RHEL_7_4-X86_64.7z http://download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt5_5129/qt.qt5.5129.gcc_64/5.12.9-0-202006121744qttools-Linux-RHEL_7_4-GCC-Linux-RHEL_7_4-X86_64.7z http://download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt5_5129/qt.qt5.5129.gcc_64/5.12.9-0-202006121744qtxmlpatterns-Linux-RHEL_7_4-GCC-Linux-RHEL_7_4-X86_64.7z http://download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt5_5129/qt.qt5.5129.gcc_64/5.12.9-0-202006121744qtserialport-Linux-RHEL_7_4-GCC-Linux-RHEL_7_4-X86_64.7z http://download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt5_5129/qt.qt5.5129.gcc_64/5.12.9-0-202006121744qtquickcontrols-Linux-RHEL_7_4-GCC-Linux-RHEL_7_4-X86_64.7z http://download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt5_5129/qt.qt5.5129.gcc_64/5.12.9-0-202006121744qtquickcontrols2-Linux-RHEL_7_4-GCC-Linux-RHEL_7_4-X86_64.7z http://download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt5_5129/qt.qt5.5129.gcc_64/5.12.9-0-202006121744qtserialbus-Linux-RHEL_7_4-GCC-Linux-RHEL_7_4-X86_64.7z http://download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt5_5129/qt.qt5.5129.gcc_64/5.12.9-0-202006121744qtscxml-Linux-RHEL_7_4-GCC-Linux-RHEL_7_4-X86_64.7z http://download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt5_5129/qt.qt5.5129.gcc_64/5.12.9-0-202006121744icu-linux-Rhel7.2-x64.7z ================================================ FILE: ci/tests-ci.sh ================================================ #!/bin/bash set -x #export QTDIR=$(readlink -f /opt/qt*/) export QTDIR=$(readlink -f /opt/qt*/5.12.*/gcc_64) PATH=${QTDIR}/bin:${PATH} export QMAKE=${QTDIR}/bin/qmake set -e qmake --version VERSION=$(git rev-parse --short HEAD) INSTALL_DIR=/tmp/embedded-ide APP_IMAGE_NAME=Embedded_IDE-x86_64.AppImage DEPLOY_OPT="-no-translations -verbose=2 -executable=$INSTALL_DIR/usr/bin/embedded-ide" DESKTOP_FILE=$INSTALL_DIR/usr/share/applications/embedded-ide.desktop wget https://raw.githubusercontent.com/martinribelotta/embedded-ide-builder/master/linux-x86_64/universal-ctags -O /tmp/universal-ctags echo ************** LINUX BUILD *********************** qmake CONFIG+=release CONFIG+=force_debug_info embedded-ide.pro make -j4 make install INSTALL_ROOT=${INSTALL_DIR} cat > ${INSTALL_DIR}/usr/share/embedded-ide/embedded_ide-config.json <<"EOF" { "workspacePath": "${APPLICATION_DIR_PATH}/../../../embedded-ide-workspace" } EOF cat > ${INSTALL_DIR}/usr/share/embedded-ide/embedded-ide.hardconf <<"EOF" { "additionalPaths": [ "${APPLICATION_DIR_PATH}", "${APPLICATION_DIR_PATH}/../../../tools/gcc-arm-embedded/bin", "${APPLICATION_DIR_PATH}/../../../tools/openocd/bin", "${APPLICATION_DIR_PATH}/../../../tools/system/bin" ], "editor": { "font": { "name": "Ubuntu Mono", "size": 12 }, "formatterStyle": "linux", "saveOnAction": false, "style": "Default", "tabWidth": 3, "tabsOnSpaces": true }, "externalTools": [ { "Install FTDI drivers": "bash ${APPLICATION_DIR_PATH}/ftdi-tools.sh --install" }, { "Uninstall FTDI drivers": "bash ${APPLICATION_DIR_PATH}/ftdi-tools.sh --uninstall" }, { "Add desktop integration": "bash ${APPLICATION_DIR_PATH}/desktop-integration.sh --install" }, { "Remove desktop integration": "bash ${APPLICATION_DIR_PATH}/desktop-integration.sh --uninstall" } ], "history": [ ], "logger": { "font": { "name": "Ubuntu Mono", "size": 10 } }, "network": { "proxy": { "host": "", "pass": "", "port": "", "type": "None", "useCredentials": false, "user": "" } }, "templates": { "autoUpdate": true, "url": "https://api.github.com/repos/ciaa/EmbeddedIDE-templates/contents" }, "useDevelopMode": false } EOF install -m 0755 /tmp/universal-ctags $INSTALL_DIR/usr/bin linuxdeploy-x86_64.AppImage --plugin qt --output appimage --appdir=$INSTALL_DIR ( APPIMAGE_DIR=${PWD} APPIMAGE=${PWD}/Embedded_IDE-${VERSION}-x86_64.AppImage cd /tmp chmod a+x ${APPIMAGE} ${APPIMAGE} --appimage-extract mv squashfs-root Embedded_IDE-${VERSION}-x86_64 tar -jcvf ${APPIMAGE_DIR}/Embedded_IDE-${VERSION}-x86_64.tar.bz2 Embedded_IDE-${VERSION}-x86_64 ) echo ************** WINDOWS BUILD *********************** make distclean MXE=/usr/lib/mxe/usr MXEQT=${MXE}/${MXE_TRIPLE}/qt5 PATH=${MXE}/bin:${PATH} MXE_PKG=Embedded_IDE-${VERSION}-win32 ${MXEQT}/bin/qmake CONFIG+=release CONFIG+=force_debug_info embedded-ide.pro make -j4 pydeployqt --objdump ${MXE_TRIPLE}-objdump ${PWD}/build/embedded-ide.exe \ --libs ${MXE}/${MXE_TRIPLE}/bin/:${MXEQT}/bin/:${MXEQT}/lib/ \ --extradll Qt5Svg.dll:Qt5Qml.dll:libjpeg-9.dll \ --qmake ${MXEQT}/bin/qmake mv build ${MXE_PKG} cat > ${MXE_PKG}/embedded_ide-config.json <<"EOF" { "workspacePath": "${APPLICATION_DIR_PATH}/../embedded-ide-workspace" } EOF cat > ${MXE_PKG}/embedded-ide.hardconf <<"EOF" { "additionalPaths": [ "${APPLICATION_DIR_PATH}", "${APPLICATION_DIR_PATH}/../tools/arm-none-eabi-gcc/bin", "${APPLICATION_DIR_PATH}/../tools/openocd/bin", "${APPLICATION_DIR_PATH}/../tools/system", "${APPLICATION_DIR_PATH}/../tools/zenity", "${APPLICATION_DIR_PATH}/../tools/drivers", "${APPLICATION_DIR_PATH}/../tools/serial-terminal" ], "editor": { "font": { "name": "Ubuntu Mono", "size": 12 }, "formatterStyle": "linux", "saveOnAction": false, "style": "Default", "tabWidth": 3, "tabsOnSpaces": true }, "externalTools": [ { "Launch Zadig": "cmd /C start zadigv2.0.1.154.exe" }, { "Serial Terminal": "cmd /C start Terminal.exe" } ], "history": [ ], "logger": { "font": { "name": "Ubuntu Mono", "size": 10 } }, "network": { "proxy": { "host": "", "pass": "", "port": "", "type": "None", "useCredentials": false, "user": "" } }, "templates": { "autoUpdate": true, "url": "https://api.github.com/repos/ciaa/EmbeddedIDE-templates/contents" }, "useDevelopMode": false } EOF zip -9 -r ${MXE_PKG}.zip ${MXE_PKG} ================================================ FILE: ci/tests-environment.sh ================================================ #!/bin/bash set -e set -x # sudo add-apt-repository --yes ppa:beineri/opt-qt-5.12.8-xenial sudo add-apt-repository --yes ppa:ubuntu-toolchain-r/test echo "deb http://pkg.mxe.cc/repos/apt trusty main" | sudo tee /etc/apt/sources.list.d/mxeapt.list sudo apt-get update -qq --allow-unauthenticated sudo fallocate -l 1G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile sudo wget https://raw.githubusercontent.com/martinribelotta/pydeployqt/master/deploy.py -O /usr/bin/pydeployqt sudo chmod a+x /usr/bin/pydeployqt MXE=mxe-${MXE_TRIPLE} #sudo apt-get install -y --allow-unauthenticated -o Dpkg::Options::="--force-overwrite" \ #wget fuse gcc-8 g++-8 build-essential p7zip-full \ #qt512base qt512tools qt512svg qt512imageformats qt512x11extras libglu1-mesa-dev \ #${MXE}-gcc ${MXE}-g++ \ #${MXE}-qtbase ${MXE}-qtsvg ${MXE}-qscintilla2 ${MXE}-qttools #export QTDIR=$(readlink -f /opt/qt*/) sudo apt-get install -y --allow-unauthenticated -o Dpkg::Options::="--force-overwrite" \ wget fuse gcc-8 g++-8 build-essential p7zip-full \ libglu1-mesa-dev libxkbcommon-dev libxkbcommon-x11-0 \ ${MXE}-gcc ${MXE}-g++ \ ${MXE}-qtbase ${MXE}-qtsvg ${MXE}-qscintilla2 ${MXE}-qttools sudo bash ci/extract-qt-installer export QTDIR=$(readlink -f /opt/qt*/5.12.*/gcc_64) gcc --version # sudo update-alternatives --remove-all gcc sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 90 --slave /usr/bin/g++ g++ /usr/bin/g++-8 gcc --version mkdir -p /tmp/qsci cp ./ci/BuildQSCI.mk /tmp/qsci cd /tmp/qsci export PATH=${QTDIR}/bin:${PATH} make -f BuildQSCI.mk URLS="https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage \ https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage \ https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-x86_64.AppImage" sudo wget -P /usr/bin $URLS for url in $URLS do F=$(basename $url) P=/usr/bin/$F sudo chmod a+x $P done ================================================ FILE: docs/DIFF_TEMPLATES.md ================================================ # Diff project documentation Embedded-IDE base export/import project on diff/patch utils. ## Export For project export, the IDE call `diff` utility with two directories, the project directory and emptry temporal directory without any contents. The process is equivalent to: ```bash diff -Naur $(BASE) $(TEMP_DIR) > my_project.template ``` When `$(BASE)` is directory contains Makefile file and $(TEMP_DIR) is temporal directory created by IDE without contents. The `-Naur` extention build recursive difference with unified format to produce a patch format output. Reciselly, the command options to diff are: ```bash diff -aur --unidirectional-new-file ``` By convention, the Embedded-IDE template extention is `*.template` but anny extention can be used. ## Import To import project, the inverse process to diff, `patch` is invoked. The patch utility is invoked with selected *.template contents and -p0 parameter to create entire directory structure. ### Template support The import process, support a templated-based replace mechanism. #### Format Every text with `${{...}}` format is replaced by data edited in "New Project" dialog or this default value before to send to `patch` utility. Into `${{...}}` brackets, the importer expect this string format: > ${{field_name type:values}} - **field_name** is the name of entry with underscores are replaced by spaces in visualization. - **type** is the type of options. The supported options are: - `string`: Text entry. The `values` is any character except the final `}}`. This field is shown as input-box on "New Project dialog - `items`: Multiple items separated with `|` character. The items can contains any characters except `|` and `}}` but is recomended the use of `[a-zA-Z_][a-zA-Z0-9_]*` convention (common languages indentifier rules) This field is shown as combo-box on "New Project" dialog. ================================================ FILE: docs/TOOL_CMDLINE.md ================================================ # External tool command line syntax - `${{text: label}}` Replaced by text entered in textbox with ==label== text label - `${{item: label#item1|item2|...|itemN}}` Replace by selected item in the combo dialog with label ==label== - `${{projectpath}}` Replaced by project directory - `${{projectname}}` Replaced by project name (last part of `${{projectpath}}`) ================================================ FILE: embedded-ide.pro ================================================ TEMPLATE = subdirs SUBDIRS = ide socketwaiter qtshdialog ================================================ FILE: ide/appconfig.cpp ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "appconfig.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class AppConfig::Priv_t { public: QJsonObject global; QJsonObject local; QProcessEnvironment sysenv; }; static const QString BUNDLE_GLOBAL_PATH = ":/default-global.json"; static const QString BUNDLE_LOCAL_PATH = ":/default-local.json"; static const QJsonValue& valueOrDefault(const QJsonValue& v, const QJsonValue& d) { return v.isUndefined()? d : v; } static QByteArray readEntireFile(const QString& path, const QByteArray& ifFail = QByteArray()) { QFile f(path); if (f.open(QFile::ReadOnly)) return QTextStream(&f).readAll().toUtf8(); return ifFail; } static bool writeEntireFile(const QString& path, const QByteArray& data) { QSaveFile f(path); if (!f.open(QFile::WriteOnly)) return false; f.write(data); return f.commit(); } static QJsonObject loadJson(const QString& path) { QJsonParseError err{}; if (!QFileInfo{path}.exists()) return {}; auto doc = QJsonDocument::fromJson(readEntireFile(path), &err); if (err.error != QJsonParseError::NoError) { qDebug() << "error reading" << path << err.errorString(); } return doc.object(); } static bool isAppImage() { return !qgetenv("APPIMAGE").isEmpty(); } static bool isWindows() { return QOperatingSystemVersion::current().type() == QOperatingSystemVersion::Windows; } static QString appFilePath() { return isAppImage()? qgetenv("APPIMAGE") : QApplication::applicationFilePath(); } static QString appDirPath() { return isAppImage()? QFileInfo(qgetenv("APPIMAGE")).absolutePath() : QApplication::applicationDirPath(); } static QString globalConfigFilePath() { auto name = "." + appDirPath().replace("/", "-") .replace("\\", "-") .replace(":", "") + ".json"; return QDir::home().absoluteFilePath(name); } static QDir sharedDir() { auto sharedDirPath = (isAppImage() || isWindows())? "./" : "../share/embedded-ide"; return QDir(appDirPath()).absoluteFilePath(sharedDirPath); } static QString systemGlobalConfigPath() { return sharedDir().absoluteFilePath("embedded_ide-config.json"); } static QString systemLocalConfigPath() { return sharedDir().absoluteFilePath("embedded-ide.hardconf"); } static QString systemTranslationPath() { return sharedDir().absoluteFilePath("translations/"); } static void addResourcesFont() { for(const auto& fontPath: QDir(":/fonts/").entryInfoList({ "*.ttf" })) QFontDatabase::addApplicationFont(fontPath.absoluteFilePath()); } AppConfig::AppConfig() : QObject(QApplication::instance()), priv(std::make_unique()) { priv->sysenv = QProcessEnvironment::systemEnvironment(); addResourcesFont(); adjustEnv(); load(); adjustEnv(); } AppConfig::~AppConfig() { } AppConfig &AppConfig::instance() { static AppConfig *singleton = nullptr; if (!singleton) singleton = new AppConfig; return *singleton; } void AppConfig::adjustEnv() { if (!priv->sysenv.contains("HOME")) { auto homePath = QDir::home().absolutePath(); auto homePaths = QStandardPaths::standardLocations(QStandardPaths::HomeLocation); if (!homePaths.isEmpty()) homePath = homePaths.first(); qputenv("HOME", homePath.toLocal8Bit()); } qputenv("APPLICATION_DIR_PATH", appDirPath().toLocal8Bit()); qputenv("APPLICATION_FILE_PATH", appFilePath().toLocal8Bit()); if (!priv->local.isEmpty()) { qputenv("WORKSPACE_PATH", workspacePath().toLocal8Bit()); qputenv("WORKSPACE_PROJECT_PATH", projectsPath().toLocal8Bit()); qputenv("WORKSPACE_TEMPLATE_PATH", templatesPath().toLocal8Bit()); qputenv("WORKSPACE_CONFIG_FILE", localConfigFilePath().toLocal8Bit()); } else { qputenv("WORKSPACE_PATH", ""); qputenv("WORKSPACE_PROJECT_PATH", ""); qputenv("WORKSPACE_TEMPLATE_PATH", ""); qputenv("WORKSPACE_CONFIG_FILE", ""); } auto old = priv->sysenv.value("PATH"); auto separator = isWindows()? ";" : ":"; auto extras = replaceWithEnv(additionalPaths().join(separator)); auto path = QString("%1%2%3").arg(extras).arg(separator).arg(old); qputenv("PATH", path.toLocal8Bit()); auto extraEnv = additionalEnv(); for (auto it = extraEnv.constBegin(); it != extraEnv.constEnd(); ++it) { auto key = it.key().toLocal8Bit(); auto val = replaceWithEnv(it.value()).toLocal8Bit(); qputenv(key.constData(), val); } } QString AppConfig::replaceWithEnv(const QString &str) { QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); QString copy(str); for(const auto& k: env.keys()) copy.replace(QString("${%1}").arg(k), env.value(k)); return copy; } QByteArray AppConfig::readEntireTextFile(const QString &path) { return readEntireFile(path); } QString AppConfig::workspacePath() const { QJsonValue defaultPath = QDir::home().absoluteFilePath(".embedded_ide-workspace"); return replaceWithEnv(valueOrDefault((priv->global).value("workspacePath"), defaultPath).toString()); } const QString& AppConfig::ensureExist(const QString& d) { if (!QDir(d).exists()) QDir::root().mkpath(d); return d; } QStringList AppConfig::langList() { QStringList langs; for(const auto& p: langPaths()) for(const auto& l: QDir(p).entryInfoList({ "*.qm" })) langs += l.baseName(); return langs; } QStringList AppConfig::langPaths() { return { systemTranslationPath(), ":/i18n/" }; } QString AppConfig::resourceImage(const QString &path, const QString &ext) { auto style = instance().useDarkStyle()? "dark" : "light"; auto s = QString(":/images/%1/%2.%3").arg(style, path, ext); return s; } QString AppConfig::resourceImage(const QStringList &pathPart, const QString &ext) { return AppConfig::resourceImage(pathPart.join(QDir::separator()), ext); } void AppConfig::fixIconTheme(QWidget *w) { Q_UNUSED(w) // for (auto *b: w->findChildren()) { // auto iconName = b->icon().name(); // if (!iconName.isEmpty()) { // auto resPath = resourceImage({ "actions", iconName }); // b->setIcon(QIcon(resPath)); // qDebug() << b->objectName() << "change" << iconName << "for" << resPath; // } else { // qDebug() << "button" << b->objectName() << "no icon"; // } // } } QString AppConfig::projectsPath() const { return ensureExist(QDir(workspacePath()).absoluteFilePath("projects")); } QString AppConfig::templatesPath() const { return ensureExist(QDir(workspacePath()).absoluteFilePath("templates")); } QString AppConfig::localConfigFilePath() const { return QDir(ensureExist(workspacePath())).absoluteFilePath("config.json"); } QList > AppConfig::externalTools() const { QList > map; auto vtools = priv->local["externalTools"]; if (vtools.isObject()) { auto tools = vtools.toObject(); for (const auto& k: tools.keys()) map.append({ k, tools.value(k).toString() }); } else if (vtools.isArray()) { for (const auto v: vtools.toArray()) { auto o = v.toObject(); auto ks = o.keys(); if (!ks.first().isEmpty()) { auto k = ks.first(); map.append({ k, o.value(k).toString() }); } } } return map; } QFileInfoList AppConfig::recentProjects() const { QFileInfoList list; for(const auto& e: QDir(projectsPath()).entryInfoList(QDir::Dirs)) { QFileInfo info(QDir(e.absoluteFilePath()).absoluteFilePath("Makefile")); if (info.isFile()) list.append(info); } auto history = priv->local["history"].toArray(); for(const auto e: history) { QFileInfo info(e.toString()); if (info.exists() && !list.contains(info)) list.append(info); } return list; } QStringList AppConfig::additionalPaths() const { QStringList paths; for(const auto e: priv->local.value("additionalPaths").toArray()) paths.append(e.toString()); return paths; } static void objectToMap(QMap& map, const QJsonObject& obj) { for (auto it = obj.constBegin(); it != obj.constEnd(); ++it) map.insert(it.key(), it.value().toString()); } QMap AppConfig::additionalEnv() const { QMap map; auto env = priv->local.value("additionalEnv"); qDebug() << env.toString(); if (env.isArray()) { for (const auto& e: env.toArray()) objectToMap(map, e.toObject()); } else { objectToMap(map, env.toObject()); } return map; } QString AppConfig::templatesUrl() const { return priv->local.value("templates").toObject().value("url").toString(); } QString AppConfig::editorStyle() const { return valueOrDefault(priv->local.value("editor").toObject().value("style"), "Default").toString(); } QFont AppConfig::editorFont() const { auto ed = priv->local.value("editor").toObject(); auto f = ed.value("font").toObject(); auto name = f.value("name").toString(); auto size = f.value("size").toInt(-1); return QFont(name, size); } bool AppConfig::editorSaveOnAction() const { return priv->local.value("editor").toObject().value("saveOnAction").toBool(); } bool AppConfig::editorTabsToSpaces() const { return priv->local.value("editor").toObject().value("tabsOnSpaces").toBool(); } int AppConfig::editorTabWidth() const { return priv->local.value("editor").toObject().value("tabWidth").toInt(); } bool AppConfig::editorShowSpaces() const { return priv->local.value("editor").toObject().value("showSpaces").toBool(); } QString AppConfig::editorFormatterStyle() const { return priv->local.value("editor").toObject().value("formatterStyle").toString(); } QString AppConfig::editorFormatterExtra() const { return priv->local.value("editor").toObject().value("formatterExtra").toString(); } bool AppConfig::editorDetectIdent() const { return priv->local.value("editor").toObject().value("detectIdent").toBool(); } QFont AppConfig::loggerFont() const { auto ed = priv->local.value("logger").toObject(); auto f = ed.value("font").toObject(); auto name = f.value("name").toString(); auto size = f.value("size").toInt(-1); return QFont(name, size); } QString AppConfig::networkProxyHost() const { return priv->local.value("network").toObject().value("proxy").toObject().value("host").toString(); } QString AppConfig::networkProxyPort() const { return priv->local.value("network").toObject().value("proxy").toObject().value("port").toString(); } bool AppConfig::networkProxyUseCredentials() const { return priv->local.value("network").toObject().value("proxy").toObject().value("useCredentials").toBool(); } AppConfig::NetworkProxyType AppConfig::networkProxyType() const { auto type = priv->local.value("network").toObject().value("proxy").toObject().value("type").toString(); bool ok = false; auto t = NetworkProxyType(QMetaEnum::fromType().keyToValue(type.toLatin1().data(), &ok)); return ok? t : NetworkProxyType::None; } QString AppConfig::networkProxyUsername() const { return priv->local.value("network").toObject().value("proxy").toObject().value("user").toString(); } QString AppConfig::networkProxyPassword() const { return priv->local.value("network").toObject().value("proxy").toObject().value("pass").toString(); } bool AppConfig::projectTemplatesAutoUpdate() const { return priv->local.value("templates").toObject().value("autoUpdate").toBool(); } bool AppConfig::useDevelopMode() const { return priv->local.value("useDevelopMode").toBool(); } bool AppConfig::useDarkStyle() const { return priv->local.value("useDarkStyle").toBool(); } QString AppConfig::language() const { return priv->local.value("lang").toString(); } int AppConfig::numberOfJobs() const { return priv->local.value("numberOfJobs").toInt(1); } bool AppConfig::numberOfJobsOptimal() const { return priv->local.value("numberOfJobsOptimal").toBool(false); } QByteArray AppConfig::fileHash(const QString &filename) { auto path = QDir(workspacePath()).filePath("hashes.json"); auto o = QJsonDocument::fromJson(readEntireTextFile(path)).object(); auto v = o.value(QFileInfo(filename).fileName()); if (v.isUndefined()) return QByteArray(); return QByteArray::fromHex(v.toString().toLatin1()); } static QString templateGlobalConfigPath() { QFileInfo sysGlobalPath{systemGlobalConfigPath()}; return sysGlobalPath.exists()? sysGlobalPath.absoluteFilePath() : BUNDLE_GLOBAL_PATH; } static QString templateLocalConfigPath() { QFileInfo sysLocalPath{systemLocalConfigPath()}; return sysLocalPath.exists()? sysLocalPath.absoluteFilePath() : BUNDLE_LOCAL_PATH; } void AppConfig::load() { QFileInfo globalCfgInfo{globalConfigFilePath()}; if (!globalCfgInfo.exists()) QFile::copy(templateGlobalConfigPath(), globalCfgInfo.absoluteFilePath()); priv->global = loadJson(globalCfgInfo.absoluteFilePath()); QFileInfo localCfgInfo{localConfigFilePath()}; if (!localCfgInfo.exists()) QFile::copy(templateLocalConfigPath(), localCfgInfo.absoluteFilePath()); priv->local = loadJson(localCfgInfo.absoluteFilePath()); projectsPath(); templatesPath(); emit configChanged(this); } void AppConfig::save() { writeEntireFile(globalConfigFilePath(), QJsonDocument((priv->global)).toJson()); writeEntireFile(localConfigFilePath(), QJsonDocument(priv->local).toJson()); adjustEnv(); emit configChanged(this); } void AppConfig::setWorkspacePath(const QString &path) { (priv->global).insert("workspacePath", path); } void AppConfig::setExternalTools(const QList > &tools) { QJsonArray a; for (const auto& it: tools) a.append(QJsonObject{ { it.first, it.second } }); priv->local.insert("externalTools", a); } void AppConfig::appendToRecentProjects(const QString &path) { if (!path.startsWith(projectsPath())) { QJsonArray history = priv->local["history"].toArray(); if (!history.contains(path)) history.append(path); priv->local["history"] = history; } } void AppConfig::setAdditionalPaths(const QStringList &paths) { QJsonArray array; for(const auto& p: paths) array.append(p); priv->local.insert("additionalPaths", array); } void AppConfig::setAdditionalEnv(const QMap &env) { QJsonArray array; for (auto it= env.constBegin(); it!=env.constEnd(); ++it) array.append(QJsonObject{{ it.key(), it.value() }}); priv->local.insert("additionalEnv", array); } void AppConfig::setTemplatesUrl(const QString &url) { auto t = priv->local["templates"].toObject(); t.insert("url", url); priv->local["templates"] = t; } void AppConfig::setEditorStyle(const QString &name) { auto ed = priv->local["editor"].toObject(); ed.insert("style", name); priv->local["editor"] = ed; } void AppConfig::setEditorFont(const QFont &f) { auto ed = priv->local["editor"].toObject(); ed["font"] = QJsonObject{ { "name", f.family() }, { "size", f.pointSize() } }; priv->local["editor"] = ed; } void AppConfig::setEditorSaveOnAction(bool enable) { auto ed = priv->local["editor"].toObject(); ed.insert("saveOnAction", enable); priv->local["editor"] = ed; } void AppConfig::setEditorTabsToSpaces(bool enable) { auto ed = priv->local["editor"].toObject(); ed.insert("tabsOnSpaces", enable); priv->local["editor"] = ed; } void AppConfig::setEditorTabWidth(int n) { auto ed = priv->local["editor"].toObject(); ed.insert("tabWidth", n); priv->local["editor"] = ed; } void AppConfig::setEditorShowSpaces(bool show) { auto ed = priv->local["editor"].toObject(); ed.insert("showSpaces", show); priv->local["editor"] = ed; } void AppConfig::setEditorFormatterStyle(const QString &name) { auto ed = priv->local["editor"].toObject(); ed.insert("formatterStyle", name); priv->local["editor"] = ed; } void AppConfig::setEditorFormatterExtra(const QString &text) { auto ed = priv->local["editor"].toObject(); ed.insert("formatterExtra", text); priv->local["editor"] = ed; } void AppConfig::setEditorDetectIdent(bool enable) { auto ed = priv->local["editor"].toObject(); ed.insert("detectIdent", enable); priv->local["editor"] = ed; } void AppConfig::setLoggerFont(const QFont &f) { auto log = priv->local["logger"].toObject(); log.insert("font", QJsonObject{ { "name", f.family() }, { "size", f.pointSize() } }); priv->local["logger"] = log; } void AppConfig::setNetworkProxyHost(const QString &name) { auto net = priv->local["network"].toObject(); auto proxy = net.value("proxy").toObject(); proxy.insert("host", name); net["proxy"] = proxy; priv->local["network"] = net; } void AppConfig::setNetworkProxyPort(const QString &port) { auto net = priv->local["network"].toObject(); auto proxy = net.value("proxy").toObject(); proxy.insert("port", port); net["proxy"] = proxy; priv->local["network"] = net; } void AppConfig::setNetworkProxyUseCredentials(bool use) { auto net = priv->local["network"].toObject(); auto proxy = net.value("proxy").toObject(); proxy.insert("useCredentials", use); net["proxy"] = proxy; priv->local["network"] = net; } void AppConfig::setNetworkProxyType(AppConfig::NetworkProxyType type) { auto typeName = QString(QMetaEnum::fromType().valueToKey(int(type))); auto net = priv->local["network"].toObject(); auto proxy = net.value("proxy").toObject(); proxy.insert("type", typeName); net["proxy"] = proxy; priv->local["network"] = net; } void AppConfig::setNetworkProxyUsername(const QString &user) { auto net = priv->local["network"].toObject(); auto proxy = net.value("proxy").toObject(); proxy.insert("user", user); net["proxy"] = proxy; priv->local["network"] = net; } void AppConfig::setNetworkProxyPassword(const QString &pass) { auto net = priv->local["network"].toObject(); auto proxy = net.value("proxy").toObject(); proxy.insert("pass", pass); net["proxy"] = proxy; priv->local["network"] = net; } void AppConfig::setProjectTemplatesAutoUpdate(bool en) { auto t = priv->local["templates"].toObject(); t.insert("autoUpdate", en); priv->local["templates"] = t; } void AppConfig::setUseDevelopMode(bool use) { priv->local.insert("useDevelopMode", use); } void AppConfig::setUseDarkStyle(bool use) { priv->local.insert("useDarkStyle", use); } void AppConfig::setLanguage(const QString &lang) { priv->local.insert("lang", lang); } void AppConfig::setNumberOfJobs(int n) { priv->local.insert("numberOfJobs", n); } void AppConfig::setNumberOfJobsOptimal(bool en) { priv->local.insert("numberOfJobsOptimal", en); } void AppConfig::addHash(const QString &filename, const QByteArray &hash) { auto path = QDir(workspacePath()).filePath("hashes.json"); auto o = QJsonDocument::fromJson(readEntireTextFile(path)).object(); o.insert(QFileInfo(filename).fileName(), QString(hash.toHex())); writeEntireFile(path, QJsonDocument(o).toJson()); } void AppConfig::purgeHash() { auto path = QDir(workspacePath()).filePath("hashes.json"); auto o = QJsonDocument::fromJson(readEntireTextFile(path)).object(); for(auto& k: o.keys()) if (!QFileInfo::exists(QDir(templatesPath()).filePath(k))) o.remove(k); writeEntireFile(path, QJsonDocument(o).toJson()); } ================================================ FILE: ide/appconfig.h ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef APPCONFIG_H #define APPCONFIG_H #include #include #include class AppConfig : public QObject { Q_OBJECT Q_DISABLE_COPY(AppConfig) private: explicit AppConfig(); virtual ~AppConfig() override; void adjustEnv(); class Priv_t; std::unique_ptr priv; public: static AppConfig &instance(); static QString replaceWithEnv(const QString& str); static QByteArray readEntireTextFile(const QString& path); static const QString& ensureExist(const QString& d); static QStringList langList(); static QStringList langPaths(); static QString resourceImage(const QString& path, const QString& ext="svg"); static QString resourceImage(const QStringList& pathPart, const QString& ext="svg"); static void fixIconTheme(QWidget *w); enum class NetworkProxyType { None, System, Custom }; Q_ENUM(NetworkProxyType) QString workspacePath() const; QString projectsPath() const; QString templatesPath() const; QString localConfigFilePath() const; QList > externalTools() const; QFileInfoList recentProjects() const; QStringList additionalPaths() const; QMap additionalEnv() const; QString templatesUrl() const; QString editorStyle() const; QFont editorFont() const; bool editorSaveOnAction() const; bool editorTabsToSpaces() const; int editorTabWidth() const; bool editorShowSpaces() const; QString editorFormatterStyle() const; QString editorFormatterExtra() const; bool editorDetectIdent() const; QFont loggerFont() const; QString networkProxyHost() const; QString networkProxyPort() const; bool networkProxyUseCredentials() const; NetworkProxyType networkProxyType() const; QString networkProxyUsername() const; QString networkProxyPassword() const; bool projectTemplatesAutoUpdate() const; bool useDevelopMode() const; bool useDarkStyle() const; QString language() const; int numberOfJobs() const; bool numberOfJobsOptimal() const; QByteArray fileHash(const QString& filename); signals: void configChanged(AppConfig*); public slots: void load(); void save(); void setWorkspacePath(const QString& path); void setExternalTools(const QList > &tools); void appendToRecentProjects(const QString& path); void setAdditionalPaths(const QStringList& paths); void setAdditionalEnv(const QMap &env); void setTemplatesUrl(const QString& url); void setEditorStyle(const QString& name); void setEditorFont(const QFont& f); void setEditorSaveOnAction(bool enable); void setEditorTabsToSpaces(bool enable); void setEditorTabWidth(int n); void setEditorShowSpaces(bool show); void setEditorFormatterStyle(const QString& name); void setEditorFormatterExtra(const QString& text); void setEditorDetectIdent(bool enable); void setLoggerFont(const QFont& f); void setNetworkProxyHost(const QString& name); void setNetworkProxyPort(const QString& port); void setNetworkProxyUseCredentials(bool use); void setNetworkProxyType(AppConfig::NetworkProxyType type); void setNetworkProxyUsername(const QString& user); void setNetworkProxyPassword(const QString& pass); void setProjectTemplatesAutoUpdate(bool en); void setUseDevelopMode(bool use); void setUseDarkStyle(bool use); void setLanguage(const QString& lang); void setNumberOfJobs(int n); void setNumberOfJobsOptimal(bool en); void addHash(const QString& filename, const QByteArray& hash); void purgeHash(); }; #endif // APPCONFIG_H ================================================ FILE: ide/binaryviewer.cpp ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "binaryviewer.h" BinaryViewer::BinaryViewer(QWidget *parent) : QHexView(parent) { } bool BinaryViewer::load(const QString &path) { try { setData(new DataStorageFile(path)); setPath(path); return true; } catch(const std::runtime_error&) { return false; } } QPoint BinaryViewer::cursor() const { return { cursorPos(), 0 }; } void BinaryViewer::setCursor(const QPoint &pos) { setCursorPos(pos.x()); } class BinaryViewerCreator: public IDocumentEditorCreator { public: ~BinaryViewerCreator() override = default; bool canHandleMime(const QMimeType &mime) const override { return mime.inherits("application/octet-stream"); } IDocumentEditor *create(QWidget *parent = nullptr) const override { return new BinaryViewer(parent); } }; IDocumentEditorCreator *BinaryViewer::creator() { return IDocumentEditorCreator::staticCreator(); } ================================================ FILE: ide/binaryviewer.h ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef BINARYVIEWER_H #define BINARYVIEWER_H #include #include class BinaryViewer : public IDocumentEditor, public QHexView { public: explicit BinaryViewer(QWidget *parent = nullptr); virtual ~BinaryViewer() override {} virtual const QWidget *widget() const override { return this; } virtual QWidget *widget() override { return this; } virtual bool load(const QString& path) override; virtual bool save(const QString& path) override { Q_UNUSED(path); return false; } virtual void reload() override { load(path()); } virtual QString path() const override { return widget()->windowFilePath(); } virtual void setPath(const QString& path) override { widget()->setWindowFilePath(path); } virtual bool isReadonly() const override { return true; } virtual void setReadonly(bool rdOnly) override { Q_UNUSED(rdOnly); } virtual bool isModified() const override { return false; } virtual void setModified(bool m) override { Q_UNUSED(m); } virtual QPoint cursor() const override; virtual void setCursor(const QPoint& pos) override; static IDocumentEditorCreator *creator(); }; #endif // BINARYVIEWER_H ================================================ FILE: ide/buildmanager.cpp ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "appconfig.h" #include "buildmanager.h" #include "processmanager.h" #include "projectmanager.h" #include #include #include const QString BuildManager::PROCESS_NAME = "makeBuild"; static int getOptimalNumberOfJobs() { return QThread::idealThreadCount(); } BuildManager::BuildManager(ProjectManager *_proj, ProcessManager *_pman, QObject *parent) : QObject(parent), proj(_proj), pman(_pman) { pman->setErrorHandler(PROCESS_NAME, [](QProcess *proc, QProcess::ProcessError err) { // TODO Implement this (maybe unnecesary?) Q_UNUSED(proc) Q_UNUSED(err) }); pman->setTerminationHandler(PROCESS_NAME, [this](QProcess *proc, int code, QProcess::ExitStatus status) { emit buildTerminated(code, status == QProcess::NormalExit? tr("Exit normal") : proc->errorString()); }); } void BuildManager::startBuild(const QString &target) { auto &c = AppConfig::instance(); auto nJobs = c.numberOfJobsOptimal()? getOptimalNumberOfJobs() : c.numberOfJobs(); auto params = QStringList{ "-j", QString("%1").arg(nJobs), "-f", proj->projectFile(), target }; pman->start(PROCESS_NAME, "make", params, { { "LC_ALL", "C" }, { "LANG", "C" } }, proj->projectPath()); emit buildStarted(target); } void BuildManager::cancelBuild() { pman->terminate(BuildManager::PROCESS_NAME, true); } ================================================ FILE: ide/buildmanager.h ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef BUILDMANAGER_H #define BUILDMANAGER_H #include class ProcessManager; class ProjectManager; class BuildManager : public QObject { Q_OBJECT Q_DISABLE_COPY(BuildManager) public: static const QString PROCESS_NAME; explicit BuildManager(ProjectManager *_proj, ProcessManager *_pman, QObject *parent = nullptr); signals: void buildStarted(const QString& target); void buildTerminated(int code, const QString& error); public slots: void startBuild(const QString& target); void cancelBuild(); private: ProjectManager *proj; ProcessManager *pman; }; #endif // BUILDMANAGER_H ================================================ FILE: ide/buttoneditoritemdelegate.h ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef BUTTONEDITORITEMDELEGATE_H #define BUTTONEDITORITEMDELEGATE_H #include "appconfig.h" #include #include #include #include template class ButtonEditorItemDelegate: public QItemDelegate { public: ButtonEditorItemDelegate(const QString& ict, const Functor& f) : iconToolTip(ict), func(f) {} virtual QWidget * createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override { auto w = QItemDelegate::createEditor(parent, option, index); QLineEdit* e = qobject_cast(w); if (e) { auto a = e->addAction(QIcon(AppConfig::resourceImage({ "actions", "document-open" })), QLineEdit::TrailingPosition); a->setToolTip(iconToolTip); connect(a, &QAction::triggered, [index, this]() { func(index); }); } return w; } private: QString iconToolTip; Functor func; }; #endif // BUTTONEDITORITEMDELEGATE_H ================================================ FILE: ide/childprocess.cpp ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "childprocess.h" QProcess *ChildProcess::safeStop(QProcess *p, int timeoutMilis) { p->blockSignals(true); if (p->state() == QProcess::Running) { p->terminate(); p->waitForFinished(timeoutMilis); if (p->state() == QProcess::Running) { p->kill(); p->waitForFinished(timeoutMilis); } } p->blockSignals(false); return p; } ChildProcess::~ChildProcess() { safeStop(this); } ================================================ FILE: ide/childprocess.h ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef CHILDPROCESS_H #define CHILDPROCESS_H #include class ChildProcess : public QProcess { Q_OBJECT public: static ChildProcess& create(QObject *parent = nullptr) { return *new ChildProcess(parent); } static QProcess *safeStop(QProcess *p, int timeoutMilis = 100); explicit ChildProcess(QObject *parent = nullptr): QProcess(parent) {} virtual ~ChildProcess(); ChildProcess& changeCWD(const QString& path) { setWorkingDirectory(path); return *this; } ChildProcess& makeDeleteLater() { connect(this, QOverload::of(&ChildProcess::finished), this, &ChildProcess::deleteLater); connect(this, &ChildProcess::errorOccurred, this, &ChildProcess::deleteLater); return *this; } ChildProcess& mergeStdOutAndErr() { setProcessChannelMode(MergedChannels); return *this; } ChildProcess& setenv(const QHash &extraEnv) { auto env = QProcessEnvironment::systemEnvironment(); for(auto it = extraEnv.begin(); it != extraEnv.end(); ++it) env.insert(it.key(), it.value()); setProcessEnvironment(env); return *this; } template ChildProcess& onFinished(T f) { connect(this, QOverload::of(&QProcess::finished), [this, f](int e) { f(this, e); }); return *this; } template ChildProcess& onStarted(T f) { connect(this, &QProcess::started, [this, f](){ f(this); }); return *this; } template ChildProcess& onError(T f) { connect(this, &QProcess::errorOccurred, [this, f](ProcessError e) { f(this, e); }); return *this; } template ChildProcess& onReadyReadStdout(T f) { connect(this, &QProcess::readyReadStandardOutput, [this, f]() { f(this); }); return *this; } template ChildProcess& onReadyReadStderr(T f) { connect(this, &QProcess::readyReadStandardError, [this, f]() { f(this); }); return *this; } template ChildProcess& onStateChange(T f) { connect(this, &QProcess::stateChanged, [this, f](ProcessState st) { f(this, st); }); return *this; } signals: public slots: void stopSafety() { safeStop(this); emit finished(exitCode()); } }; #endif // CHILDPROCESS_H ================================================ FILE: ide/clangautocompletionprovider.cpp ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "appconfig.h" #include "childprocess.h" #include "clangautocompletionprovider.h" #include "projectmanager.h" #include "textmessagebrocker.h" #include #include #include #include #include #include #include #include static QString getToken(QTextStream *s) { bool escaped = false; QChar cuoting = QChar(); QString token; while(!s->atEnd()) { QChar c; *s >> c; if (QString(" \t").contains(c)) { if (!cuoting.isNull()) { token += c; continue; } if (token.isEmpty()) { if (cuoting == QChar()) continue; } else break; } if (QString("\"'").contains(c)) { if (!escaped) { if (cuoting == c) cuoting = QChar(); else if (cuoting.isNull()) cuoting = c; } else escaped = false; token += c; continue; } escaped = !cuoting.isNull() && (c == '\\'); token += c; } return token; } static QStringList cmdLineTokenizer(const QString& line) { QStringList tokens; QString token; QTextStream stream(line.toLocal8Bit(), QIODevice::ReadOnly); while(!(token = getToken(&stream)).isNull()) tokens.append(token); return tokens; } static const QRegularExpression EOL(R"([\r\n])"); static void parseCompilerInfo(const QString& text, QStringList *incs, QStringList *defs) { #if 0 QRegularExpression definesRe(R"(^\#define (\S+) (.*?)$)", QRegularExpression::MultilineOption); auto it = definesRe.globalMatch(text); while (it.hasNext()) { QRegularExpressionMatch m = it.next(); #if 1 QString define = m.captured(0); defs->append(define); #else QString symbol = m.captured(1).remove(EOL); QString value = m.captured(2).remove(EOL); if (value.contains(QRegularExpression("[ \\t]"))) value = value.prepend('"').append('"'); defs->append(QString("%1=%2").arg(symbol).arg(value)); #endif } #else Q_UNUSED(defs) #endif bool onIncludes = false; for(const QString& line: text.split('\n')) { if (!onIncludes) { if (line.startsWith("#include <")) { onIncludes = true; } } else { if (line.startsWith("End of search list")) onIncludes = false; else { QString ipath = line.trimmed(); if (!incs->contains(ipath)) incs->append("-I" + ipath); } } } } class ClangAutocompletionProvider::Priv_t { public: ProjectManager *project{ nullptr }; QHash nameMap; QHash symbolsForFiles; QStringList includes; QStringList defines; QByteArray buffer; }; ClangAutocompletionProvider::ClangAutocompletionProvider(ProjectManager *proj, QObject *parent): QObject(parent), priv(std::make_unique()) { priv->project = proj; } ClangAutocompletionProvider::~ClangAutocompletionProvider() {} void ClangAutocompletionProvider::startIndexingProject(const QString &path, FinishIndexProjectCallback_t cb) { priv->nameMap.clear(); auto& p = ChildProcess::create(this) .changeCWD(path) .onError([this](QProcess *ctags, QProcess::ProcessError) { constexpr auto TIMEOUT = 5000; priv->project->showMessageTimed(tr("ctags error: %1").arg(ctags->errorString()), TIMEOUT); }) .onFinished([this, cb](QProcess *ctags, int exitStatus) { qDebug() << "ctags end with" << exitStatus; QtConcurrent::run([ctags, this, cb]() { ctags->setReadChannel(QProcess::StandardOutput); QDir cwd{ctags->workingDirectory()}; while(ctags->bytesAvailable() > 0) { auto line = ctags->readLine(); auto entry = QJsonDocument::fromJson(line).object(); if (!entry.isEmpty()) { auto name = entry.value("name").toString(); auto text = entry.value("text").toString(); auto type = entry.value("type").toString(); auto lang = entry.value("lang").toString(); auto path = entry.value("path").toString(); auto line = entry.value("line").toInt(); static const QStringList TYPES{ "array", "boolean", "chapter", "enum", "enumerator", "externvar", "function", "macro", "object", "prototype", "section", "struct", "symbol", "typedef", "union", "variable", }; if (TYPES.contains(type)) { ICodeModelProvider::FileReference ref{ path, line, 0, text }; ICodeModelProvider::Symbol sym{ name, text, lang, type, ref }; priv->nameMap[name].append(ref); priv->symbolsForFiles[cwd.absoluteFilePath(path)][sym.type].insert(sym); } } if (!priv->project->isProjectOpen()) break; } priv->project->showMessageTimed(tr("Index finished")); ctags->deleteLater(); cb(); }); priv->project->showMessage(tr("ctags end, processing...")); }); p.setTextModeEnabled(true); p.start("universal-ctags", { "--map-R=-.s", "-n", "-R", "-e", "--all-kinds=*", "--extras=*", "--fields=*", "-x", R"(--_xformat={ "name": "%N", "lang": "%l", "type": "%K", "path": "%F", "line": %n, "text": "%C" })" }); priv->project->showMessage(tr("Indexing by ctags...")); priv->project->deleteOnCloseProject(&p); } void ClangAutocompletionProvider::startIndexingFile(const QString &path, FinishIndexFileCallback_t cb) { Q_UNUSED(path) auto targets = priv->project->targetsOfDependency(path); auto& p = ChildProcess::create(this) .makeDeleteLater() .changeCWD(priv->project->projectPath()) .setenv({ { "LC_ALL", "C" }, { "LANG", "C" } }) .onFinished([this, cb](QProcess *make, int exitCode) { qDebug() << "make discover exit with" << exitCode; QString out = make->readAllStandardOutput(); QRegularExpression re(R"((\S+[g]*(cc|\+\+))\S*\s+(.*?$))", QRegularExpression::MultilineOption); QRegularExpressionMatch m = re.match(out); if (m.hasMatch()) { QString compiler = m.captured(1); QString compiler_type = m.captured(2); QString parameters = m.captured(3); qDebug() << "CC:" << compiler << ", type " << compiler_type; QStringList parameterList = cmdLineTokenizer(parameters); QList toRemove; int idx = 0; for(const QString& arg: parameterList) { if (arg.startsWith("-I")) priv->includes.append(arg); else if (arg.startsWith("-D")) priv->defines.append(arg); else if (QRegularExpression(R"(^-(?:MMD|MM|MG|MP|MD|M)$)").match(arg).hasMatch()) toRemove << idx; else if (QRegularExpression(R"(^-(?:MQ|MT|MF)$)").match(arg).hasMatch()) toRemove << idx << (idx + 1); else if (arg == "-c") toRemove.append(idx); else if (arg == "-o") toRemove << idx << (idx + 1); idx++; } std::sort(toRemove.begin(), toRemove.end(), [](int a, int b) -> bool { return a > b; }); for(const auto& i: toRemove) parameterList.removeAt(i); parameterList.append("-dM"); parameterList.append("-E"); parameterList.append("-v"); qDebug() << parameterList; auto& p = ChildProcess::create(this) .changeCWD(make->workingDirectory()) .mergeStdOutAndErr() .makeDeleteLater() .onFinished([this](QProcess *cc, int) { QString out = cc->readAll(); parseCompilerInfo(out, &priv->includes, &priv->defines); qDebug() << "Includes:" << priv->includes; qDebug() << "Defines:" << priv->defines; }).onError([](QProcess *cc, QProcess::ProcessError err) { Q_UNUSED(err) qDebug() << "CC ERROR: " << cc->program() << cc->arguments() << "\n" << "\t" << cc->errorString(); }); p.start(compiler, parameterList); priv->project->deleteOnCloseProject(&p); cb(); } }); p.start("make", QStringList{ "-B", "-n" } + targets); priv->project->deleteOnCloseProject(&p); } void ClangAutocompletionProvider::referenceOf(const QString &entity, ICodeModelProvider::FindReferenceCallback_t cb) { cb(priv->nameMap.value(entity)); } static QString parseCompletion(const QString& text) { return text.startsWith("Pattern : ")? QString(text).remove("Pattern : ").remove(QRegularExpression(R"([\[|\\<]\#[^\#]*\#[\]|\>])")) : text.contains(':')? QString(text.split(':').at(0)).trimmed() : text; } void ClangAutocompletionProvider::completionAt(const ICodeModelProvider::FileReference &ref, const QString &unsaved, ICodeModelProvider::CompletionCallback_t cb) { auto& p = ChildProcess::create(this) .makeDeleteLater() .changeCWD(priv->project->projectPath()) .onStarted([unsaved](QProcess *clang) { clang->write(unsaved.toLocal8Bit()); clang->waitForBytesWritten(); clang->closeWriteChannel(); }).onError([](QProcess *clang, QProcess::ProcessError err) { qDebug() << "clang error:" << clang->errorString() << err; }).onFinished([cb](QProcess *clang, int exitStatus) { Q_UNUSED(exitStatus) clang->deleteLater(); // qDebug() << "clang finish:" << exitStatus; QStringList list; QString out = clang->readAllStandardOutput(); // qDebug() << "clang out:\n" << out; QRegularExpression re(R"(^COMPLETION: (.*?)$)", QRegularExpression::MultilineOption); auto it = re.globalMatch(out); while(it.hasNext()) { auto m = it.next(); list.append(parseCompletion(m.captured(1))); } cb(list); }); p.start("clang", QStringList{ "-x", "c", "-fcolor-diagnostics", "-fsyntax-only", "-Xclang", "-code-completion-macros", "-Xclang", "-code-completion-patterns", "-Xclang", "-code-completion-brief-comments", "-Xclang", QString("-code-completion-at=-:%1:%2").arg(ref.line + 1).arg(ref.column + 1), "-" } + priv->defines + priv->includes); priv->project->deleteOnCloseProject(&p); } void ClangAutocompletionProvider::requestSymbolForFile(const QString &path, ICodeModelProvider::SymbolRequestCallback_t cb) { cb(priv->symbolsForFiles.value(path)); } ================================================ FILE: ide/clangautocompletionprovider.h ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef CLANGAUTOCOMPLETIONPROVIDER_H #define CLANGAUTOCOMPLETIONPROVIDER_H #include #include #include class ProjectManager; class ClangAutocompletionProvider: public QObject, public ICodeModelProvider { Q_OBJECT public: explicit ClangAutocompletionProvider(ProjectManager *proj, QObject *parent); virtual ~ClangAutocompletionProvider() override; void startIndexingProject(const QString& path, FinishIndexProjectCallback_t cb) override; void startIndexingFile(const QString& path, FinishIndexFileCallback_t cb) override; void referenceOf(const QString& entity, FindReferenceCallback_t cb) override; void completionAt(const FileReference& ref, const QString& unsaved, CompletionCallback_t cb) override; void requestSymbolForFile(const QString& path, SymbolRequestCallback_t cb) override; private: class Priv_t; std::unique_ptr priv; }; #endif // CLANGAUTOCOMPLETIONPROVIDER_H ================================================ FILE: ide/codetexteditor.cpp ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "appconfig.h" #include "codetexteditor.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if QSCINTILLA_VERSION > 0x020900 #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const QStringList MAKEFILES_NAME = { "makefile", "Makefile", "GNUMakefile", }; CodeTextEditor::CodeTextEditor(QWidget *parent) : PlainTextEditor(parent) { } CodeTextEditor::~CodeTextEditor() {} bool CodeTextEditor::load(const QString &path) { setLexer(lexerFromFile(path)); auto r = PlainTextEditor::load(path); QFileInfo info(path); auto name = info.fileName(); auto suffix = info.suffix(); if (suffix == "mk" || MAKEFILES_NAME.contains(name)) { setTabIndents(false); setIndentationsUseTabs(true); } return r; } template QsciLexer *helperCreator() { return new T(); } using creator_t = QsciLexer *(*)(); static const QHash EXTENTION_MAP = { #if QSCINTILLA_VERSION > 0x020900 { "json", &helperCreator }, { "md", &helperCreator }, #endif { "sh", &helperCreator }, { "diff", &helperCreator }, { "patch", &helperCreator }, { "bat", &helperCreator }, { "coffee", &helperCreator }, { "litcoffee", &helperCreator }, { "cs", &helperCreator }, { "css", &helperCreator }, { "html", &helperCreator }, { "htm", &helperCreator }, { "java", &helperCreator }, { "js", &helperCreator }, { "lua", &helperCreator }, { "mk", &helperCreator }, { "pas", &helperCreator }, { "ps", &helperCreator }, { "py", &helperCreator }, { "rb", &helperCreator }, { "tcl", &helperCreator }, { "tex", &helperCreator }, { "v", &helperCreator }, { "vhdl", &helperCreator }, { "vhd", &helperCreator }, { "xml", &helperCreator }, { "yaml", &helperCreator }, { "yml", &helperCreator }, }; static const QHash MIMETYPE_MAP = { #if QSCINTILLA_VERSION > 0x020900 { "application/json", &helperCreator }, { "text/markdown", &helperCreator }, { "text/x-markdown", &helperCreator }, #endif { "application/x-shellscript", &helperCreator }, { "text/x-avs", &helperCreator }, { "text/x-bat", &helperCreator }, { "text/x-cmake", &helperCreator }, { "text/x-coffe", &helperCreator }, { "text/x-csharp", &helperCreator }, { "text/x-csrc", &helperCreator }, { "text/x-css", &helperCreator }, { "text/x-diff", &helperCreator }, { "text/x-dlang", &helperCreator }, { "text/x-fortran", &helperCreator }, { "text/x-html", &helperCreator }, { "text/x-idl", &helperCreator }, { "text/x-java", &helperCreator }, { "text/x-javascript", &helperCreator }, { "text/x-lua", &helperCreator }, { "text/x-makefile", &helperCreator }, { "text/x-matlab", &helperCreator }, { "text/x-octave", &helperCreator }, { "text/x-pascal", &helperCreator }, { "text/x-perl", &helperCreator }, { "text/x-po", &helperCreator }, { "text/x-pov", &helperCreator }, { "text/x-properties", &helperCreator }, { "text/x-ps", &helperCreator }, { "text/x-python", &helperCreator }, { "text/x-ruby", &helperCreator }, { "text/x-spice", &helperCreator }, { "text/x-sql", &helperCreator }, { "text/x-tcl", &helperCreator }, { "text/x-tex", &helperCreator }, { "text/x-verilog", &helperCreator }, { "text/x-vhdl", &helperCreator }, { "text/x-xml", &helperCreator }, { "text/x-yaml", &helperCreator }, }; #undef _ class CodeEditorCreator: public IDocumentEditorCreator { public: ~CodeEditorCreator() override = default; bool canHandleExtentions(const QStringList &suffixes) const override { for (const auto& suffix: suffixes) if (EXTENTION_MAP.contains(suffix)) return true; return false; } bool canHandleMime(const QMimeType &mime) const override { return MIMETYPE_MAP.contains(mime.name()); } IDocumentEditor *create(QWidget *parent = nullptr) const override { return new CodeTextEditor(parent); } }; IDocumentEditorCreator *CodeTextEditor::creator() { return IDocumentEditorCreator::staticCreator(); } QMenu *CodeTextEditor::createContextualMenu() { QMenu *menu = PlainTextEditor::createContextualMenu(); return menu; } QsciLexer *CodeTextEditor::lexerFromFile(const QString& name) { auto suffix = QFileInfo(name).suffix(); if (EXTENTION_MAP.contains(suffix)) { qDebug() << "for" << name << "suffix found as" << suffix; return EXTENTION_MAP.value(suffix)(); } auto type = QMimeDatabase().mimeTypeForFile(name); auto mimename = type.name(); if (MIMETYPE_MAP.contains(mimename)) { qDebug() << "for" << name << "mime found as" << mimename; return MIMETYPE_MAP.value(mimename)(); } for(const auto& mname: type.parentMimeTypes()) { if (MIMETYPE_MAP.contains(mname)) { qDebug() << "for" << name << "parent mime found as" << mname; return MIMETYPE_MAP.value(mname)(); } } qDebug() << "No lexer found"; return nullptr; } ================================================ FILE: ide/codetexteditor.h ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef CODETEXTEDITOR_H #define CODETEXTEDITOR_H #include "plaintexteditor.h" class CodeTextEditor : public PlainTextEditor { Q_OBJECT public: explicit CodeTextEditor(QWidget *parent = nullptr); ~CodeTextEditor() override; bool load(const QString &path) override; static IDocumentEditorCreator *creator(); protected: QMenu *createContextualMenu() override; virtual QsciLexer *lexerFromFile(const QString& name); }; #endif // CODETEXTEDITOR_H ================================================ FILE: ide/configwidget.cpp ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "configwidget.h" #include "ui_configwidget.h" #include "appconfig.h" #include "buttoneditoritemdelegate.h" #include "envinputdialog.h" #include #include #include #include #include #include #include static const QStringList ASTYLE_STYLES = { "1tbs", "allman", "attach", "banner", "break", "bsd", "gnu", "google", "horstmann", "java", "k", "knf", "kr", "linux", "lisp", "otbs", "pico", "stroustrup", "vtk", "whitesmith" }; class EnvironmentModel: public QStandardItemModel { public: EnvironmentModel(const QMap& m, QTableView *parent = nullptr): QStandardItemModel(parent) { setHorizontalHeaderLabels({ tr("Name"), tr("Value") }); for (auto it = m.constBegin(); it != m.constEnd(); ++it) appendEntry(it.key(), it.value()); connect(this, &QStandardItemModel::itemChanged, [parent]() { parent->resizeColumnToContents(0); }); } void appendEntry(const QString& key, const QString& val) { appendRow({new QStandardItem(key), new QStandardItem(val)}); } QMap toMap() const { QMap m; for (int r = 0; r < rowCount(); r++) { auto k = item(r, 0)->text(); auto v = item(r, 1)->text(); m.insert(k, v); } return m; } }; ConfigWidget::ConfigWidget(QWidget *parent) : QDialog(parent), ui(std::make_unique()) { ui->setupUi(this); const struct { QToolButton *b; const char *icon; } buttonmap[] = { { ui->projectPathSetButton, "document-open" }, { ui->tbPathAdd, "list-add" }, { ui->tbPathRm, "list-remove" }, { ui->tbEnvAdd, "list-add" }, { ui->tbEnvRm, "list-remove" }, }; for (const auto& e: buttonmap) e.b->setIcon(QIcon{AppConfig::resourceImage({ "actions", e.icon })}); ui->languageList->addItems(QStringList{ "" } + AppConfig::langList()); ui->tabWidget->setCurrentIndex(0); for(const auto& fi: QDir(":/styles").entryInfoList({ "*.xml" })) ui->editorStyle->addItem(fi.baseName()); ui->codeEditor->load(":/reference-code.c"); ui->codeEditor->setReadonly(true); ui->formatterStyle->addItems(ASTYLE_STYLES); auto updateEditor = [this]() { // Defer execution to next event loop due sender actualization QTimer::singleShot(0, [this]() { auto font = ui->editorFontName->currentFont(); font.setPointSize(ui->editorFontSize->value()); ui->codeEditor->loadConfigWithStyle( ui->editorStyle->currentText(), font, ui->editorTabWidth->value(), ui->editorReplaceTabs->isChecked()); ui->codeEditor->reload(); }); }; auto delegateFunc = [this](const QModelIndex& m) { auto model = ui->additionalPathList->model(); auto currPath = AppConfig::replaceWithEnv(model->data(m).toString()); auto newPath = QFileDialog::getExistingDirectory(window(), tr("Select directory"), currPath); if (!newPath.isEmpty()) model->setData(m, newPath); }; auto delegate = new ButtonEditorItemDelegate(tr("Select File"), delegateFunc); ui->additionalPathList->setItemDelegateForColumn(0, delegate); connect(ui->editorStyle, &QComboBox::currentTextChanged, updateEditor); connect(ui->editorFontName, &QComboBox::currentTextChanged, updateEditor); connect(ui->editorFontSize, QOverload::of(&QSpinBox::valueChanged), updateEditor); connect(ui->editorReplaceTabs, &QCheckBox::toggled, updateEditor); connect(ui->editorTabWidth, QOverload::of(&QSpinBox::valueChanged), updateEditor); connect(ui->projectPathSetButton, &QToolButton::clicked, [this]() { auto dir = QFileInfo(AppConfig::instance().workspacePath()).absolutePath(); auto path = QFileDialog::getExistingDirectory(this, tr("Select workspace directory"), dir); if (!path.isEmpty()) ui->workspacePath->setText(path); }); connect(ui->tbPathRm, &QToolButton::clicked, [this]() { auto idx = ui->additionalPathList->currentIndex().row(); if (idx != -1) { auto m = qobject_cast(ui->additionalPathList->model()); m->removeRows(idx, 1); } }); connect(ui->tbPathAdd, &QToolButton::clicked, [this]() { auto path = QDir::homePath(); auto idx = ui->additionalPathList->currentIndex(); if (idx.isValid()) { path = ui->additionalPathList->model()->data(idx, Qt::DisplayRole).toString(); path = AppConfig::replaceWithEnv(path); } path = QFileDialog::getExistingDirectory(window(), tr("Select directory"), path, nullptr); if (!path.isEmpty()) { auto m = qobject_cast(ui->additionalPathList->model()); auto list = m->stringList(); list.append(path); m->setStringList(list); } }); connect(ui->tbEnvRm, &QToolButton::clicked, [this]() { auto idx = ui->additionalEnvTable->currentIndex().row(); if (idx != -1) { auto m = qobject_cast(ui->additionalEnvTable->model()); if (m) { m->removeRows(idx, 1); ui->additionalEnvTable->selectionModel()->clear(); } } }); connect(ui->tbEnvAdd, &QToolButton::clicked, [this]() { EnvInputDialog d(window()); if (d.exec() == QDialog::Accepted) { auto m = static_cast(ui->additionalEnvTable->model()); if (m) { m->appendEntry(d.envName(), d.envValue()); } } }); } ConfigWidget::~ConfigWidget() { } void ConfigWidget::save() { auto &conf = AppConfig::instance(); conf.setWorkspacePath(ui->workspacePath->text()); conf.setAdditionalPaths(qobject_cast(ui->additionalPathList->model())->stringList()); auto envModel = static_cast(ui->additionalEnvTable->model()); if (envModel) conf.setAdditionalEnv(envModel->toMap()); conf.setEditorStyle(ui->editorStyle->currentText()); auto editorFont = ui->editorFontName->currentFont(); editorFont.setPointSize(ui->editorFontSize->value()); conf.setEditorFont(editorFont); conf.setEditorSaveOnAction(ui->saveOnActionTarget->isChecked()); conf.setEditorTabsToSpaces(ui->editorReplaceTabs->isChecked()); conf.setEditorTabWidth(ui->editorTabWidth->value()); conf.setEditorShowSpaces(ui->editorShowSpaces->isChecked()); conf.setEditorFormatterStyle(ui->formatterStyle->currentText()); conf.setEditorDetectIdent(ui->editorDetectIdent->isChecked()); conf.setTemplatesUrl(ui->templateSettings->repositoryUrl().toString()); auto loggerFont = ui->loggerFontName->currentFont(); loggerFont.setPointSize(ui->loggerFontSize->value()); conf.setLoggerFont(loggerFont); conf.setNetworkProxyHost(ui->proxyHost->text()); conf.setNetworkProxyPort(ui->proxyPort->text()); conf.setNetworkProxyUseCredentials(ui->useAutentication->isChecked()); conf.setNetworkProxyType([this]() { if(ui->systemProxy->isChecked()) { return AppConfig::NetworkProxyType::System; } if(ui->userProxy->isChecked()) { return AppConfig::NetworkProxyType::Custom; } { return AppConfig::NetworkProxyType::None;} }()); conf.setNetworkProxyUsername(ui->username->text()); conf.setNetworkProxyPassword(ui->password->text()); conf.setProjectTemplatesAutoUpdate(ui->autoUpdateProjectTmplates->isChecked()); conf.setUseDevelopMode(ui->useDevelopment->isChecked()); conf.setUseDarkStyle(ui->useDarkStyle->isChecked()); conf.setLanguage(ui->languageList->currentText()); conf.setNumberOfJobs(ui->numberOfJobs->value()); conf.setNumberOfJobsOptimal(ui->numberOfJobsOptimal->isChecked()); conf.save(); } void ConfigWidget::load() { auto &conf = AppConfig::instance(); ui->workspacePath->setText(conf.workspacePath()); ui->additionalPathList->setModel(new QStringListModel(conf.additionalPaths(), this)); ui->additionalEnvTable->setModel(new EnvironmentModel(conf.additionalEnv(), ui->additionalEnvTable)); ui->additionalEnvTable->resizeColumnToContents(0); ui->editorStyle->setCurrentText(conf.editorStyle()); auto editorFont = conf.editorFont(); ui->editorFontName->setCurrentFont(editorFont); ui->editorFontSize->setValue(editorFont.pointSize()); ui->saveOnActionTarget->setChecked(conf.editorSaveOnAction()); ui->editorReplaceTabs->setChecked(conf.editorTabsToSpaces()); ui->editorTabWidth->setValue(conf.editorTabWidth()); ui->editorDetectIdent->setChecked(conf.editorDetectIdent()); ui->editorShowSpaces->setChecked(conf.editorShowSpaces()); ui->formatterStyle->setCurrentText(conf.editorFormatterStyle()); ui->formatterExtra->setText(conf.editorFormatterExtra()); ui->templateSettings->setRepositoryUrl(conf.templatesUrl()); auto loggerFont = conf.loggerFont(); ui->loggerFontName->setCurrentFont(loggerFont); ui->loggerFontSize->setValue(loggerFont.pointSize()); ui->proxyHost->setText(conf.networkProxyHost()); ui->proxyPort->setText(conf.networkProxyPort()); ui->useAutentication->setChecked(conf.networkProxyUseCredentials()); switch (static_cast(conf.networkProxyType())) { case AppConfig::NetworkProxyType::None: ui->noProxy->setChecked(true); break; case AppConfig::NetworkProxyType::System: ui->systemProxy->setChecked(true); break; case AppConfig::NetworkProxyType::Custom: ui->userProxy->setChecked(true); break; } ui->username->setText(conf.networkProxyUsername()); ui->password->setText(conf.networkProxyPassword()); ui->autoUpdateProjectTmplates->setChecked(conf.projectTemplatesAutoUpdate()); ui->useDevelopment->setChecked(conf.useDevelopMode()); ui->useDarkStyle->setChecked(conf.useDarkStyle()); ui->languageList->setCurrentText(conf.language()); ui->numberOfJobs->setValue(conf.numberOfJobs()); ui->numberOfJobsOptimal->setChecked(conf.numberOfJobsOptimal()); } ================================================ FILE: ide/configwidget.h ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef CONFIGWIDGET_H #define CONFIGWIDGET_H #include #include namespace Ui { class ConfigWidget; } class ConfigWidget : public QDialog { Q_OBJECT public: explicit ConfigWidget(QWidget *parent = nullptr); virtual ~ConfigWidget(); public slots: void save(); void load(); protected: void showEvent(QShowEvent *) { load(); } private: std::unique_ptr ui; }; #endif // CONFIGWIDGET_H ================================================ FILE: ide/configwidget.ui ================================================ ConfigWidget 0 0 662 609 Configuration 1 Editor Save editor contents on target action 0 0 Editor Font spaces Tab width is 1 80 4 Detect file identation 0 0 6 72 0 0 Color style 0 0 Show spaces in editor Replace tabs with spaces Environment Additional PATHs ... .. 32 32 ... .. 32 32 .. Workspace PATH Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Additional Environment Variables true QAbstractItemView::SingleSelection QAbstractItemView::SelectRows true false ... .. 32 32 ... .. 32 32 Qt::Vertical 0 0 Templates Proxy HTTP proxy for network access None buttonGroup System proxy buttonGroup Custom proxy buttonGroup Host false localhost Port false 3128 Qt::Vertical 20 40 Proxy authentication true false Username QLineEdit::Normal Password QLineEdit::Password Behavior Format Style Qt::Vertical 20 40 6 72 0 1 border-bottom: 1px solid; border-bottom-color: rgb(0, 0, 0, 96); Use dark style Format extra paramters: 0 0 Log View Font 0 1 border-bottom: 1px solid; border-bottom-color: rgb(0, 0, 0, 96); 0 0 QFontComboBox::MonospacedFonts Use in-progress/development characteristics Check for project templates updates at start up true Current language Number of jobs in Makefile 1 Optimal QDialogButtonBox::Cancel|QDialogButtonBox::Ok CPPTextEditor QWidget
    cpptexteditor.h
    1
    TemplateManager QWidget
    templatemanager.h
    1
    buttonBox accepted() ConfigWidget accept() 181 457 183 480 buttonBox rejected() ConfigWidget reject() 272 455 272 483 userProxy toggled(bool) proxyHost setEnabled(bool) 119 76 119 76 userProxy toggled(bool) proxyPort setEnabled(bool) 119 76 119 76 userProxy toggled(bool) useAutentication setEnabled(bool) 57 76 84 76 editorDetectIdent toggled(bool) editorTabWidth setDisabled(bool) 43 62 94 65 numberOfJobsOptimal toggled(bool) numberOfJobs setDisabled(bool) 578 303 477 301
    ================================================ FILE: ide/consoleinterceptor.cpp ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "appconfig.h" #include "consoleinterceptor.h" #include "processmanager.h" #include #include #include #include #include #include ConsoleInterceptor::ConsoleInterceptor(QTextBrowser *textBrowser, QObject *parent) : QObject(parent), browser(textBrowser) { const auto size = QSize(16, 16); auto gl = new QGridLayout(textBrowser); m_clearButton = new QToolButton(textBrowser); m_clearButton->setIcon(QIcon(AppConfig::resourceImage({ "actions", "edit-clear" }))); m_clearButton->setAutoRaise(true); m_clearButton->setIconSize(size); m_clearButton->setToolTip(tr("Clear Console")); m_killButton = new QToolButton(textBrowser); m_killButton->setEnabled(false); m_killButton->setIcon(QIcon(AppConfig::resourceImage({ "actions", "window-close" }))); m_killButton->setAutoRaise(true); m_killButton->setIconSize(size); m_killButton->setToolTip(tr("Stop Current Process")); gl->addWidget(m_clearButton, 0, 1); gl->addWidget(m_killButton, 0, 2); gl->setColumnStretch(0, 1); gl->setRowStretch(1, 1); gl->setContentsMargins(0, 0, textBrowser->verticalScrollBar()->sizeHint().width(), 0); gl->setSpacing(0); textBrowser->setFont(QFont("Courier")); connect(&AppConfig::instance(), &AppConfig::configChanged, [textBrowser](AppConfig *conf) { textBrowser->setFont(conf->loggerFont()); }); } ConsoleInterceptor::~ConsoleInterceptor() {} void ConsoleInterceptor::writeMessageTo(QTextBrowser *browser, const QString &message, const QColor &color) { QTextCharFormat fmt; fmt.setForeground(color.isValid()? color : browser->palette().text().color()); writeMessageTo(browser, message, fmt); } void ConsoleInterceptor::writeMessageTo(QTextBrowser *browser, const QString &message, const QTextCharFormat &fmt) { auto cursor = browser->textCursor(); cursor.beginEditBlock(); cursor.movePosition(QTextCursor::End); cursor.setCharFormat(fmt); cursor.insertText(message); cursor.endEditBlock(); browser->verticalScrollBar()->setValue(browser->verticalScrollBar()->maximum()); } void ConsoleInterceptor::writeHtmlTo(QTextBrowser *browser, const QString &html) { auto cursor = browser->textCursor(); cursor.beginEditBlock(); cursor.movePosition(QTextCursor::End); cursor.insertHtml(html); cursor.endEditBlock(); browser->verticalScrollBar()->setValue(browser->verticalScrollBar()->maximum()); } void ConsoleInterceptor::appendToConsole(QProcess::ProcessChannel s, QProcess *p, const QString &text) { Q_UNUSED(p) const auto& filters = s == QProcess::StandardError? stderrFilters : stdoutFilters; QString processedText{ text }; for(const auto& c: filters) if (c(browser, processedText)) return; writeMessage(processedText, browser->palette().text().color()); } void ConsoleInterceptor::writeMessage(const QString &message, const QColor &color) { writeMessageTo(browser, message, color); } void ConsoleInterceptor::writeFmtMessage(const QString &message, const QTextCharFormat &fmt) { writeMessageTo(browser, message, fmt); } void ConsoleInterceptor::writeHtml(const QString &html) { writeHtmlTo(browser, html); } ================================================ FILE: ide/consoleinterceptor.h ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef CONSOLEINTERCEPTOR_H #define CONSOLEINTERCEPTOR_H #include #include #include #include #include class QTextBrowser; class QProcess; class ProcessManager; using ConsoleInterceptorFilter = std::function; class ConsoleInterceptor : public QObject { Q_OBJECT public: explicit ConsoleInterceptor(QTextBrowser *textBrowser, QObject *parent = nullptr); virtual ~ConsoleInterceptor(); QToolButton *killButton() { return m_killButton; } QToolButton *clearButton() { return m_clearButton; } static void writeMessageTo(QTextBrowser *browser, const QString& message, const QColor& color={}); static void writeMessageTo(QTextBrowser *browser, const QString& message, const QTextCharFormat &fmt); static void writeHtmlTo(QTextBrowser *browser, const QString& html); void addStdOutFilter(const ConsoleInterceptorFilter& f) { stdoutFilters.append(f); } void addStdErrFilter(const ConsoleInterceptorFilter& f) { stderrFilters.append(f); } void appendToConsole(QProcess::ProcessChannel s, QProcess *p, const QString& text); signals: public slots: void writeMessage(const QString& message, const QColor& color={}); void writeFmtMessage(const QString& message, const QTextCharFormat& fmt); void writeHtml(const QString& html); private: QToolButton *m_killButton; QToolButton *m_clearButton; QTextBrowser *browser; QList stdoutFilters; QList stderrFilters; }; #endif // CONSOLEINTERCEPTOR_H ================================================ FILE: ide/cpptexteditor.cpp ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "appconfig.h" #include "cpptexteditor.h" #include "filereferencesdialog.h" #include "icodemodelprovider.h" #include "textmessagebrocker.h" #include #include #include #include #include #include #include #include #include static const QStringList C_CXX_EXTENSIONS = { "c", "cpp", "h", "hpp", "cc", "hh", "hxx", "cxx", "c++", "h++" }; static const QStringList C_MIMETYPE = { "text/x-c++src", "text/x-c++hdr" }; static const QStringList CXX_MIMETYPE = { "text/x-c", "text/x-csrc", "text/x-chdr" }; static inline void triggerFindAndOpen(const QString& filePath) { TextMessageBrocker::instance().publish("findAndOpen", filePath); } class MyQsciLexerCPP: public QsciLexerCPP { private: QLatin1String keywordList; public: MyQsciLexerCPP(QObject *parent = nullptr, bool caseInsensitiveKeywords = false) : QsciLexerCPP(parent, caseInsensitiveKeywords) { setFoldCompact(false); } ~MyQsciLexerCPP() override = default; void refreshProperties() override { QsciLexerCPP::refreshProperties(); emit propertyChanged("lexer.cpp.track.preprocessor", "0"); } const char *keywords(int set) const override { //if (set == 5) { // updateKeywordList(); // return keywordList.data(); //} else { return QsciLexerCPP::keywords(set); //} } private: void updateKeywordList() const { auto c = qobject_cast(editor()); if (c) { // keywordList = c->keywordList(); } } }; CPPTextEditor::CPPTextEditor(QWidget *parent) : CodeTextEditor(parent) { setProperty("isCXX", false); setAutoCompletionSource(AcsNone); connect(new QShortcut(QKeySequence("Ctrl+Return"), this), &QShortcut::activated, this, &CPPTextEditor::findReference); connect(new QShortcut(QKeySequence("Ctrl+i"), this), &QShortcut::activated, this, &CPPTextEditor::formatCode); connect(new QShortcut(QKeySequence("Ctrl+Shift+i"), this), &QShortcut::activated, this, &CPPTextEditor::openIncludeInCursor); } CPPTextEditor::~CPPTextEditor() = default; bool CPPTextEditor::load(const QString &path) { if (codeModel()) codeModel()->startIndexingFile(path, [] {}); return CodeTextEditor::load(path); } class CPPEditorCreator: public IDocumentEditorCreator { public: ~CPPEditorCreator() override = default; static bool in(const QMimeType& t, const QStringList& list) { for(const auto& mtype: list) if (t.inherits(mtype)) return true; return false; } bool canHandleExtentions(const QStringList &suffixes) const override { for(const auto& suffix: suffixes) if (C_CXX_EXTENSIONS.contains(suffix)) return true; return false; } bool canHandleMime(const QMimeType &mime) const override { if (in(mime, C_MIMETYPE)) return true; if (in(mime, CXX_MIMETYPE)) return true; return false; } IDocumentEditor *create(QWidget *parent = nullptr) const override { return new CPPTextEditor(parent); } }; IDocumentEditorCreator *CPPTextEditor::creator() { return IDocumentEditorCreator::staticCreator(); } void CPPTextEditor::findReference() { if (codeModel()) { auto word = wordUnderCursor(); codeModel()->referenceOf(word, [this, word](const ICodeModelProvider::FileReferenceList& refs) { FileReferencesDialog d(refs, window()); connect(&d, &FileReferencesDialog::itemClicked, [this](const QString& path, int line) { qDebug() << "open" << path << "at" << line; documentManager()->openDocumentHere(path, line, 0); }); d.exec(); }); } else qDebug() << "No code model defined"; } static STDCALL char* tempMemoryAllocation(unsigned long memoryNeeded) { char* buffer = new char[memoryNeeded]; return buffer; } static STDCALL void tempError(int errorNumber, const char* errorMessage) { qDebug() << errorNumber << errorMessage; TextMessageBrocker::instance().publish(TextMessages::STDERR_LOG, QString("%1: %2").arg(errorNumber).arg(errorMessage)); } void CPPTextEditor::formatCode() { int l; int i; getCursorPosition(&l, &i); auto inText = selectedText(); if (inText.isEmpty()) { selectAll(); inText = selectedText(); } auto rawText = inText.toUtf8(); char* utf8In = rawText.data(); auto& cfg = AppConfig::instance(); const auto style = cfg.editorFormatterStyle(); const auto indentType = QString{cfg.editorTabsToSpaces()? "spaces" : "tab"}; const auto indentCount = QString("%1").arg(cfg.editorTabWidth()); auto extraAstyleParams = cfg.editorFormatterExtra(); char* utf8Out = AStyleMain(utf8In, QString("--style=%1 --indent=%2=%3 %4") .arg(style, indentType, indentCount, extraAstyleParams).toLatin1().data(), tempError, tempMemoryAllocation); replaceSelectedText(QString::fromUtf8(utf8Out)); setCursorPosition(l, i); ensureLineVisible(l); } void CPPTextEditor::openIncludeInCursor() { static const QRegularExpression incRe(R"(^\s*\#\s*include(?:_next)?\s+[\<\"](.*)[\>\"])"); auto m = incRe.match(lineUnderCursor()); if (m.hasMatch()) { auto file = m.captured(1); triggerFindAndOpen(file); } } QMenu *CPPTextEditor::createContextualMenu() { auto menu = CodeTextEditor::createContextualMenu(); menu->addAction(QIcon(AppConfig::resourceImage({ "actions", "code-context" })), tr("Find Reference"), this, &CPPTextEditor::findReference) ->setShortcut(QKeySequence("CTRL+ENTER")); static const QRegularExpression incRe(R"(^\s*\#\s*include(?:_next)?\s+[\<\"](.*)[\>\"])"); auto m = incRe.match(lineUnderCursor()); if (m.hasMatch()) { auto file = m.captured(1); menu->addAction(QIcon(AppConfig::resourceImage({"actions", "document-open"})), tr("Open Include"), this, [file]() { triggerFindAndOpen(file); })->setShortcut(QKeySequence("Ctrl+Shift+i")); } return menu; } void CPPTextEditor::triggerAutocompletion() { if (codeModel()) { int line; int index; getCursorPosition(&line, &index); codeModel()->completionAt( ICodeModelProvider::FileReference{ path(), line, index, QString() }, text(), [this](const QStringList& completions) { auto w = wordUnderCursor(); auto filtered = completions.filter(QRegularExpression(QString(R"(^%1)").arg(w))); if (!filtered.isEmpty()) { showUserList(1, filtered); } else // Fallback autocompletion CodeTextEditor::triggerAutocompletion(); }); } else { CodeTextEditor::triggerAutocompletion(); } } QsciLexer *CPPTextEditor::lexerFromFile(const QString &name) { Q_UNUSED(name); setProperty("isCXX", CPPEditorCreator::in(QMimeDatabase().mimeTypeForFile(name), CXX_MIMETYPE)); return new MyQsciLexerCPP(this); } ================================================ FILE: ide/cpptexteditor.h ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef CPPTEXTEDITOR_H #define CPPTEXTEDITOR_H #include "codetexteditor.h" class ICodeModelProvider; class CPPTextEditor : public CodeTextEditor { public: explicit CPPTextEditor(QWidget *parent = nullptr); virtual ~CPPTextEditor() override; bool load(const QString &path) override; static IDocumentEditorCreator *creator(); signals: void queryToOpen(const QString& path); private slots: void findReference(); void formatCode(); void openIncludeInCursor(); protected: QMenu *createContextualMenu() override; void triggerAutocompletion() override; QsciLexer *lexerFromFile(const QString &name) override; }; #endif // CPPTEXTEDITOR_H ================================================ FILE: ide/documentmanager.cpp ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "documentmanager.h" #include "idocumenteditor.h" #include "plaintexteditor.h" #include "projectmanager.h" #include "codetexteditor.h" #include "binaryviewer.h" #include "filesystemmanager.h" #include "cpptexteditor.h" #include "mapfileviewer.h" #include "unsavedfilesdialog.h" #include "textmessagebrocker.h" #include "imageviewer.h" #include "markdowneditor.h" #include "appconfig.h" #include #include #include #include #include #include #include #include #include #include static QString absoluteTo(const QString& path, const QString& file) { return QFileInfo(file).isAbsolute()? file : QDir(path).absoluteFilePath(file); } class DocumentManager::Priv_t { public: QComboBox *combo = nullptr; QStackedLayout *stack = nullptr; QHash mapedWidgets; const ProjectManager *projectManager = nullptr; }; DocumentManager::DocumentManager(QWidget *parent) : QWidget(parent), priv(std::make_unique()) { priv->stack = new QStackedLayout(this); priv->stack->setMargin(0); DocumentEditorFactory::instance()->registerDocumentInterface(CPPTextEditor::creator()); DocumentEditorFactory::instance()->registerDocumentInterface(MarkdownEditor::creator()); DocumentEditorFactory::instance()->registerDocumentInterface(ImageViewer::creator()); DocumentEditorFactory::instance()->registerDocumentInterface(CodeTextEditor::creator()); DocumentEditorFactory::instance()->registerDocumentInterface(PlainTextEditor::creator()); DocumentEditorFactory::instance()->registerDocumentInterface(MapFileViewer::creator()); DocumentEditorFactory::instance()->registerDocumentInterface(BinaryViewer::creator()); auto label = new QLabel(this); label->setPixmap(QPixmap(AppConfig::resourceImage({ "screens", tr("EmbeddedIDE_02") }, "png"))); label->setAlignment(Qt::AlignHCenter | Qt::AlignCenter); priv->stack->addWidget(label); auto shCut = [this](const QString& seq, const auto& functor) { connect(new QShortcut(QKeySequence(seq), window()), &QShortcut::activated, this, functor); }; shCut("CTRL+SHIFT+X", &DocumentManager::closeCurrent); shCut("CTRL+SHIFT+R", &DocumentManager::reloadDocumentCurrent); // SHCUT("CTRL+S", &DocumentManager::saveCurrent); } DocumentManager::~DocumentManager() { } void DocumentManager::setComboBox(QComboBox *cb) { if (priv->combo) priv->combo->disconnect(this); priv->combo = cb; auto model = priv->combo->model(); auto proxy = new QSortFilterProxyModel(priv->combo); model->setParent(proxy); proxy->setSourceModel(model); proxy->setSortRole(Qt::DisplayRole); proxy->setDynamicSortFilter(false); priv->combo->setModel(proxy); connect(priv->combo, QOverload::of(&QComboBox::currentIndexChanged), [this](int idx) { auto path = priv->combo->itemData(idx).toString(); if (!path.isEmpty()) openDocument(path); }); connect(priv->stack, &QStackedLayout::currentChanged, [this](int idx) { auto widget = priv->stack->widget(idx); if (widget) { auto path = widget->windowFilePath(); auto iface = priv->mapedWidgets.value(path, nullptr); if (iface) { int comboIdx = priv->combo->findData(path); if (comboIdx == -1) { priv->combo->addItem(QFileInfo(path).fileName(), path); priv->combo->model()->sort(0); comboIdx = priv->combo->findData(path); priv->combo->setItemIcon(comboIdx, FileSystemManager::iconForFile(QFileInfo(path))); } priv->combo->setCurrentIndex(comboIdx); } } }); } QStringList DocumentManager::unsavedDocuments() const { QStringList unsaved; for(const auto& d: priv->mapedWidgets) if (d->isModified()) unsaved.append(d->path()); return unsaved; } QStringList DocumentManager::documents() const { return priv->mapedWidgets.keys(); } int DocumentManager::documentCount() const { return priv->mapedWidgets.count(); } QString DocumentManager::documentCurrent() const { if (priv->stack->currentWidget()) { auto path = priv->stack->currentWidget()->windowFilePath(); auto iface = priv->mapedWidgets.value(path, nullptr); if (iface) return iface->path(); } return QString(); } IDocumentEditor *DocumentManager::documentEditor(const QString& path) const { return priv->mapedWidgets.value(absoluteTo(priv->projectManager->projectPath(), path), nullptr); } void DocumentManager::setProjectManager(const ProjectManager *projectManager) { priv->projectManager = projectManager; } IDocumentEditor *DocumentManager::openDocument(const QString &filePath) { if (filePath == documentCurrent()) return nullptr; QString path = absoluteTo(priv->projectManager->projectPath(), filePath); if (QFileInfo(path).isDir()) return nullptr; if (!QFileInfo::exists(path)) { TextMessageBrocker::instance().publish(TextMessages::STDERR_LOG, tr("%1 not exist").arg(filePath)); return nullptr; } if (QFileInfo(path).isRelative()) path = QDir(priv->projectManager->projectPath()).absoluteFilePath(path); QWidget *widget = nullptr; auto item = priv->mapedWidgets.value(path, nullptr); if (!item) { item = DocumentEditorFactory::instance()->create(path, this); if (item) { if (priv->projectManager) item->setCodeModel(priv->projectManager->codeModel()); widget = item->widget(); if (!item->load(path)) { item->widget()->deleteLater(); item = nullptr; widget = nullptr; } else { item->setModified(false); priv->mapedWidgets.insert(path, item); priv->stack->addWidget(widget); item->addModifyObserver([this](IDocumentEditor *ed, bool m) { auto path = ed->path(); auto idx = priv->combo->findData(path); if (idx != -1) { priv->combo->setItemIcon(idx, m? QIcon(AppConfig::resourceImage({ "actions", "document-close" })) : FileSystemManager::iconForFile(QFileInfo(path))); } emit documentModified(path, ed, m); }); item->addCursorObserver([this](IDocumentEditor *ed, int line, int col) { emit documentPositionModified(ed->path(), line, col); }); item->setDocumentManager(this); } } } else widget = item->widget(); if (widget) { priv->stack->setCurrentWidget(widget); emit documentFocushed(path); } else emit documentNotFound(path); return item; } void DocumentManager::openDocumentHere(const QString &path, int line, int col) { qDebug() << "open document here" << path << line << col; auto ed = openDocument(path); if (ed) ed->setCursor({ col, line }); } bool DocumentManager::closeDocument(const QString &filePath) { auto path = absoluteTo(priv->projectManager->projectPath(), filePath); // Cannot save due not name on path if (path.isEmpty()) return true; // Cannot save due not in map (not widget interface registered) auto iface = priv->mapedWidgets.value(path); if (!iface) return true; if (iface->widget()->close()) { priv->stack->removeWidget(iface->widget()); if (priv->combo) { int idx = priv->combo->findData(iface->path()); if (idx != -1) priv->combo->removeItem(idx); } iface->widget()->deleteLater(); priv->mapedWidgets.remove(path); emit documentClosed(path); return true; } return false; } bool DocumentManager::closeAll() { const auto keys = priv->mapedWidgets.keys(); for(const auto& path: keys) if (!closeDocument(path)) return false; return true; } bool DocumentManager::aboutToCloseAll() { auto unsaved = unsavedDocuments(); if (unsaved.isEmpty()) { return closeAll(); } UnsavedFilesDialog d(unsaved, this); if (d.exec() == QDialog::Accepted) { saveDocuments(d.checkedForSave()); for(const auto& doc: unsavedDocuments()) { auto iface = documentEditor(doc); if (iface) iface->setModified(false); } return closeAll(); } return false; } void DocumentManager::saveDocument(const QString &path) { if (path.isEmpty()) return; auto iface = priv->mapedWidgets.value(absoluteTo(priv->projectManager->projectPath(), path)); if (!iface) return; iface->save(iface->path()); } void DocumentManager::saveAll() { const auto keys = priv->mapedWidgets.keys(); for(const auto& path: keys) saveDocument(path); } void DocumentManager::reloadDocument(const QString &path) { if (path.isEmpty()) return; auto iface = priv->mapedWidgets.value(absoluteTo(priv->projectManager->projectPath(), path)); if (!iface) return; iface->reload(); } void DocumentManager::focusInEvent(QFocusEvent *event) { Q_UNUSED(event); auto w = priv->stack->currentWidget(); if (w) w->setFocus(); } ================================================ FILE: ide/documentmanager.h ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef DOCUMENTMANAGER_H #define DOCUMENTMANAGER_H #include #include class IDocumentEditor; class ProjectManager; class QComboBox; class DocumentManager : public QWidget { Q_OBJECT public: explicit DocumentManager(QWidget *parent = nullptr); virtual ~DocumentManager() override; void setComboBox(QComboBox *cb); QStringList unsavedDocuments() const; QStringList documents() const; int documentCount() const; QString documentCurrent() const; IDocumentEditor *documentEditor(const QString &path) const; IDocumentEditor *documentEditorCurrent() { return documentEditor(documentCurrent()); } void setProjectManager(const ProjectManager *projectManager); signals: void documentFocushed(const QString& path); void documentNotFound(const QString& path); void documentClosed(const QString& path); void documentModified(const QString& path, IDocumentEditor *iface, bool modify); void documentPositionModified(const QString& path, int line, int col); public slots: IDocumentEditor *openDocument(const QString& filePath); void openDocumentHere(const QString& path, int line, int col); bool closeDocument(const QString& filePath); bool closeCurrent() { return closeDocument(documentCurrent()); } bool closeAll(); bool aboutToCloseAll(); void saveDocument(const QString& path); void saveDocuments(const QStringList& list) { for(const auto& a: list) saveDocument(a); } void saveCurrent() { saveDocument(documentCurrent()); } void saveAll(); void reloadDocument(const QString& path); void reloadDocumentCurrent() { reloadDocument(documentCurrent()); } protected: void focusInEvent(QFocusEvent *event) override; private: class Priv_t; std::unique_ptr priv; }; #endif // DOCUMENTMANAGER_H ================================================ FILE: ide/envinputdialog.cpp ================================================ #include "envinputdialog.h" #include "ui_envinputdialog.h" #include "appconfig.h" #include #include #include template static QAction *mkAction(const QString& actionName, F func) { auto a = new QAction{QIcon{AppConfig::resourceImage({ "actions", actionName })}, actionName}; QObject::connect(a, &QAction::triggered, func); return a; } static QAction *mkAction(const QString& actionName, QMenu *m) { auto a = new QAction{QIcon{AppConfig::resourceImage({ "actions", actionName })}, actionName}; a->setMenu(m); return a; } template static QMenu *menuFromEnv(QWidget *parent, F func) { auto m = new QMenu(parent); auto env = QProcessEnvironment::systemEnvironment(); for (const auto& k: env.keys()) { const auto v = env.value(k); auto a = m->addAction(k); a->setData(k); } QObject::connect(m, &QMenu::triggered, func); return m; } EnvInputDialog::EnvInputDialog(QWidget *parent) : QDialog(parent), ui(new Ui::EnvInputDialog) { ui->setupUi(this); ui->valueEditor->addAction(mkAction("document-open", [this]() { openPathSelect(); }), QLineEdit::TrailingPosition); ui->valueEditor->addAction(mkAction("code-context", menuFromEnv(this, [this](QAction *a) { menuAction(a); })), QLineEdit::TrailingPosition); } EnvInputDialog::~EnvInputDialog() { delete ui; } QString EnvInputDialog::envName() const { return ui->nameEditor->text(); } QString EnvInputDialog::envValue() const { return ui->valueEditor->text(); } void EnvInputDialog::openPathSelect() { auto currPath = ui->valueEditor->text(); if (currPath.isEmpty()) currPath = QDir::homePath(); auto newPath = QFileDialog::getExistingDirectory(window(), tr("Select directory"), currPath); if (!newPath.isEmpty()) { ui->valueEditor->insert(newPath); } } void EnvInputDialog::openVarSelect() { // TODO } void EnvInputDialog::menuAction(QAction *a) { ui->valueEditor->insert(QString("${%1}").arg(a->text())); } ================================================ FILE: ide/envinputdialog.h ================================================ #ifndef ENVINPUTDIALOG_H #define ENVINPUTDIALOG_H #include namespace Ui { class EnvInputDialog; } class EnvInputDialog : public QDialog { Q_OBJECT public: explicit EnvInputDialog(QWidget *parent = nullptr); ~EnvInputDialog(); QString envName() const; QString envValue() const; private: Ui::EnvInputDialog *ui; void openPathSelect(); void openVarSelect(); void menuAction(QAction *a); }; #endif // ENVINPUTDIALOG_H ================================================ FILE: ide/envinputdialog.ui ================================================ EnvInputDialog 0 0 465 151 Environment Variable Name Value Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok Qt::Vertical 20 40 buttonBox accepted() EnvInputDialog accept() 248 254 157 274 buttonBox rejected() EnvInputDialog reject() 316 260 286 274 ================================================ FILE: ide/externaltoolmanager.cpp ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "appconfig.h" #include "externaltoolmanager.h" #include "processmanager.h" #include "buildmanager.h" #include "ui_externaltoolmanager.h" #include "projectmanager.h" #include "buttoneditoritemdelegate.h" #include #include #include #include #include #include #include static QList makeItem(const QString& name=QString(), const QString& command=QString()) { QList l{ new QStandardItem(name), new QStandardItem(command) }; l[1]->setFont(AppConfig::instance().editorFont()); return l; } ExternalToolManager::ExternalToolManager(QWidget *parent) : QDialog(parent), ui(std::make_unique()) { ui->setupUi(this); const struct { QAbstractButton *b; const char *name; } buttonmap[] = { { ui->itemDown, "go-down" }, { ui->itemUp, "go-up" }, { ui->itemDel, "list-remove" }, { ui->itemAdd, "list-add" }, { ui->pushButton_2, "dialog-close" }, { ui->pushButton, "dialog-ok-apply" }, }; for (const auto& e: buttonmap) e.b->setIcon(QIcon{AppConfig::resourceImage({ "actions", e.name })}); auto model = new QStandardItemModel(this); model->setHorizontalHeaderLabels({ tr("Description"), tr("Command") }); ui->tableView->setModel(model); auto delegateFunc = [this, model](const QModelIndex& m) { auto item = model->itemFromIndex(m); if (item) { auto s = QFileDialog::getOpenFileName(window()); if (!s.isEmpty()) item->setText(s); } else { qDebug() << "no item"; } }; auto delegate = new ButtonEditorItemDelegate(tr("Select File"), delegateFunc); ui->tableView->setItemDelegateForColumn(1, delegate); connect(ui->itemAdd, &QToolButton::clicked, [model]() { model->appendRow(makeItem()); }); connect(ui->itemDel, &QToolButton::clicked, [model, this]() { QModelIndexList m = ui->tableView->selectionModel()->selectedRows(); while (!m.isEmpty()){ model->removeRow(m.last().row()); m.removeLast(); } }); connect(ui->itemUp, &QToolButton::clicked, [model, this]() { QModelIndexList m = ui->tableView->selectionModel()->selectedRows(); QList selectable; while (!m.isEmpty()) { QModelIndex e = m.last(); int toRow = e.row() - 1; if (toRow >= 0) { QList items = model->takeRow(e.row()); model->insertRow(toRow, items); } selectable.append(toRow); m.removeLast(); } for(auto row: selectable) ui->tableView->selectRow(row); }); connect(ui->itemDown, &QToolButton::clicked, [model, this]() { QModelIndexList m = ui->tableView->selectionModel()->selectedRows(); QList selectable; while (!m.isEmpty()){ QModelIndex e = m.last(); int toRow = e.row() + 1; if (toRow < model->rowCount()) { QList items = model->takeRow(e.row()); model->insertRow(toRow, items); } selectable.append(toRow); m.removeLast(); } for(auto row: selectable) ui->tableView->selectRow(row); }); auto tools = AppConfig::instance().externalTools(); for(const auto& it: tools) model->appendRow(makeItem(it.first, it.second)); } ExternalToolManager::~ExternalToolManager() { } QMenu *ExternalToolManager::makeMenu(QWidget *parent, ProcessManager *pman, ProjectManager *proj) { auto p = pman->processFor(BuildManager::PROCESS_NAME); auto m = new QMenu(parent); auto t = AppConfig::instance().externalTools(); for(const auto& it: t) { auto cmd = it.second; m->addAction(QIcon(AppConfig::resourceImage({ "actions", "run-build" })), it.first, [p, cmd, proj]() { p->setWorkingDirectory(proj->projectPath()); p->start(AppConfig::replaceWithEnv(cmd)); }); } m->addSeparator(); m->addAction(QIcon(AppConfig::resourceImage({ "actions", "window-new" })), QObject::tr("Open Tool Manager"), [parent]() { ExternalToolManager d(parent); if (d.exec()) { auto model = qobject_cast(d.ui->tableView->model()); QList> map; for(int i=0; irowCount(); i++) { auto name = model->item(i, 0)->text(); auto cmd = model->item(i, 1)->text(); map.append({ name, cmd }); } AppConfig::instance().setExternalTools(map); AppConfig::instance().save(); } }); return m; } ================================================ FILE: ide/externaltoolmanager.h ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef EXTERNALTOOLMANAGER_H #define EXTERNALTOOLMANAGER_H #include #include namespace Ui { class ExternalToolManager; } class QMenu; class ProcessManager; class ProjectManager; class ExternalToolManager : public QDialog { Q_OBJECT Q_DISABLE_COPY(ExternalToolManager) public: explicit ExternalToolManager(QWidget *parent = nullptr); virtual ~ExternalToolManager(); static QMenu *makeMenu(QWidget *parent, ProcessManager *pman, ProjectManager *proj); private: std::unique_ptr ui; }; #endif // EXTERNALTOOLMANAGER_H ================================================ FILE: ide/externaltoolmanager.ui ================================================ ExternalToolManager 0 0 594 391 External Tools Cancel 22 22 QAbstractItemView::SelectRows true false Accept 22 22 true 22 22 true Qt::Horizontal 172 20 22 22 true 22 22 true 22 22 true pushButton clicked() ExternalToolManager accept() 425 429 425 446 pushButton_2 clicked() ExternalToolManager reject() 519 433 517 447 ================================================ FILE: ide/filereferencesdialog.cpp ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "filereferencesdialog.h" #include "ui_filereferencesdialog.h" #include #include FileReferencesDialog::FileReferencesDialog(const ICodeModelProvider::FileReferenceList &refList, QWidget *parent) : QDialog(parent), ui(std::make_unique()) { ui->setupUi(this); connect(ui->listWidget, &QListWidget::itemActivated, [this](QListWidgetItem *item) { auto url = item->data(Qt::UserRole).toUrl(); auto ref = ICodeModelProvider::FileReference::decode(url); emit itemClicked(ref.path, ref.line); accept(); }); if (!refList.isEmpty()) { for(const auto& r: refList) { auto url = r.encode(); auto item = new QListWidgetItem(QString("%1: %2\n%3").arg(r.path).arg(r.line).arg(r.meta)); item->setData(Qt::UserRole, url); ui->listWidget->addItem(item); } ui->listWidget->setMinimumWidth(ui->listWidget->sizeHintForColumn(0) + 2 + ui->listWidget->frameWidth()); resize(minimumWidth(), height()); } } FileReferencesDialog::~FileReferencesDialog() { } ================================================ FILE: ide/filereferencesdialog.h ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FILEREFERENCESDIALOG_H #define FILEREFERENCESDIALOG_H #include #include "icodemodelprovider.h" #include namespace Ui { class FileReferencesDialog; } class FileReferencesDialog : public QDialog { Q_OBJECT public: explicit FileReferencesDialog(const ICodeModelProvider::FileReferenceList& refList, QWidget *parent = nullptr); ~FileReferencesDialog(); signals: void itemClicked(const QString& path, int line); private: std::unique_ptr ui; }; #endif // FILEREFERENCESDIALOG_H ================================================ FILE: ide/filereferencesdialog.ui ================================================ FileReferencesDialog 0 0 256 318 0 0 0 0 0 QAbstractItemView::NoEditTriggers true ================================================ FILE: ide/filesystemmanager.cpp ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "appconfig.h" #include "filesystemmanager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class ProjectIconProvider: public QFileIconProvider { public: ~ProjectIconProvider() override; QIcon icon(const QFileInfo &info) const override { if (info.isDir()) return QIcon(FileSystemManager::mimeIconPath("folder")); QMimeDatabase db; auto t = db.mimeTypeForFile(info); if (t.isValid()) { auto resName = FileSystemManager::mimeIconPath(t.iconName()); if (QFile(resName).exists()) return QIcon(resName); resName = FileSystemManager::mimeIconPath(t.genericIconName()); if (QFile(resName).exists()) return QIcon(resName); } return QFileIconProvider::icon(info); } }; class FileSystemModel: public QFileSystemModel { public: ~FileSystemModel() override; FileSystemModel(QObject *parent = nullptr) : QFileSystemModel(parent) { setFilter(QDir::AllDirs | QDir::NoDotAndDotDot | QDir::Files | QDir::Hidden | QDir::System); setNameFilterDisables(false); setNameFilters({ "*" }); setIconProvider(new ProjectIconProvider); setReadOnly(false); } }; FileSystemManager::FileSystemManager(QTreeView *v, QObject *parent) : QObject(parent), view(v) { connect(view, &QTreeView::activated, [this](const QModelIndex& idx) { auto model = qobject_cast(view->model()); if (model) { auto path = model->filePath(idx); emit requestFileOpen(path); } }); view->setContextMenuPolicy(Qt::CustomContextMenu); connect(view, &QTreeView::customContextMenuRequested, this, &FileSystemManager::customContextMenu); connect(new QShortcut{QKeySequence{"DEL"}, v}, &QShortcut::activated, this, &FileSystemManager::menuItemDelete); } FileSystemManager::~FileSystemManager() = default; QIcon FileSystemManager::iconForFile(const QFileInfo &info) { return ProjectIconProvider().icon(info); } QString FileSystemManager::mimeIconPath(const QString &mimeName) { return AppConfig::resourceImage({ "mimetypes", mimeName }); } void FileSystemManager::openPath(const QString &path) { if (view) { auto model = qobject_cast(view->model()); if (!model) { if (view->model()) view->model()->deleteLater(); view->setModel(model = new FileSystemModel(view)); } view->setRootIndex(model->setRootPath(path)); for(int i = 1; i < model->columnCount(); i++) view->hideColumn(i); view->header()->setSectionResizeMode(QHeaderView::ResizeToContents); view->header()->hide(); } } void FileSystemManager::closePath() { if (view->model()) view->model()->deleteLater(); view->setModel(nullptr); } static bool isExec(const QFileInfo& f) { return f.isExecutable() && !f.isDir(); } void FileSystemManager::customContextMenu(const QPoint &pos) { auto model = qobject_cast(view->model()); if (!model) return; auto index = view->currentIndex(); if (!index.isValid()) index = view->rootIndex(); auto noSelection = view->selectionModel()->selectedRows(0).isEmpty(); auto info = model->fileInfo(index); auto m = new QMenu(view); auto createAction = [this, m](const auto& icon, const auto& text, const auto& slot) { return m->addAction(QIcon(AppConfig::resourceImage({ "actions", icon })), text, this, slot); }; createAction("document-new", tr("File New"), &FileSystemManager::menuNewFile); createAction("folder-new", tr("New Directory"), &FileSystemManager::menuNewDirectory); #ifdef Q_OS_UNIX createAction("insert-link-symbolic", tr("New Symlink"), &FileSystemManager::menuNewSymlink); #endif createAction("run-build-file", tr("Execute"), &FileSystemManager::menuItemExecute)->setEnabled(isExec(info)); createAction("window-new", tr("Open External"), &FileSystemManager::menuItemOpenExternal); m->addSeparator(); createAction("debug-execute-from-cursor", tr("Rename"), &FileSystemManager::menuItemRename)->setDisabled(noSelection); createAction("document-close", tr("Delete"), &FileSystemManager::menuItemDelete)->setDisabled(noSelection); m->exec(view->mapToGlobal(pos)); m->deleteLater(); } static QModelIndex selectedOnView(QTreeView *v) { if (v->selectionModel()->selectedIndexes().isEmpty()) return v->rootIndex(); return v->selectionModel()->selectedIndexes().first(); } void FileSystemManager::menuNewFile() { if (!view->selectionModel()) return; auto m = qobject_cast(view->model()); if (!m) return; auto idx = selectedOnView(view); auto info = m->fileInfo(idx); if (!info.isDir()) { info = QFileInfo(info.absoluteDir().absolutePath()); } auto fileName = QInputDialog::getText(view->window(), tr("File name"), tr("Create file on %1") .arg(info.absoluteFilePath())); if (!fileName.isEmpty()) { QFile f(QDir(info.absoluteFilePath()).absoluteFilePath(fileName)); if (!f.open(QFile::WriteOnly)) { QMessageBox::critical(view->window(), tr("Error creating file"), f.errorString()); } else { f.close(); emit requestFileOpen(fileName); } } } void FileSystemManager::menuNewDirectory() { if (!view->selectionModel()) return; auto m = qobject_cast(view->model()); if (!m) return; auto idx = selectedOnView(view); if (!QFileInfo(m->fileInfo(idx)).isDir()) { idx = idx.parent(); if (!m->fileInfo(idx).isDir()) { qDebug() << "ERROR parent not a dir"; return; } } auto name = QInputDialog::getText(view->window(), tr("Folder name"), tr("Create folder on %1") .arg(m->fileInfo(idx).absoluteFilePath())); if (!name.isEmpty()) { qDebug() << "creating" << name << " on " << m->fileName(idx); m->mkdir(idx, name); } } void FileSystemManager::menuNewSymlink() { #ifdef Q_OS_UNIX if (!view->selectionModel()) return; auto m = qobject_cast(view->model()); if (!m) return; auto idx = selectedOnView(view); if (!QFileInfo(m->fileInfo(idx)).isDir()) { idx = idx.parent(); if (!m->fileInfo(idx).isDir()) { qDebug() << "ERROR parent not a dir"; return; } } QDir targetDir(m->fileInfo(idx).absoluteFilePath()); QFileDialog dialog(view->window()); dialog.setWindowTitle(tr("Link target")); dialog.setAcceptMode(QFileDialog::AcceptOpen); dialog.setDirectory(targetDir.absolutePath()); dialog.setFileMode(QFileDialog::Directory); if (dialog.exec() != QDialog::Accepted || dialog.selectedFiles().isEmpty()) return; QFile target(dialog.selectedFiles().first()); auto linkName = targetDir.absoluteFilePath(QFileInfo(target.fileName()).fileName()); if (!target.link(linkName)) QMessageBox::critical(view->window(), tr("Link creation fail"), tr("ERROR: %1").arg(target.errorString())); #else QMessageBox::critical(view->window(), tr("Link creation fail"), tr("ERROR: Not implemented")); #endif } void FileSystemManager::menuItemExecute() { if (!view->selectionModel()) return; auto m = qobject_cast(view->model()); if (!m) return; auto idx = selectedOnView(view); auto info = m->fileInfo(idx); #ifdef Q_OS_WIN QDesktopServices::openUrl(QUrl::fromLocalFile(info.absoluteFilePath())); #else QProcess::execute(info.absoluteFilePath()); #endif } void FileSystemManager::menuItemOpenExternal() { if (!view->selectionModel()) return; auto m = qobject_cast(view->model()); if (!m) return; auto info = m->fileInfo(selectedOnView(view)); QDesktopServices::openUrl(QUrl::fromLocalFile(info.absoluteFilePath())); } void FileSystemManager::menuItemRename() { view->edit(selectedOnView(view)); } void FileSystemManager::menuItemDelete() { if (!view->selectionModel()) return; auto m = qobject_cast(view->model()); if (!m) return; auto items = view->selectionModel()->selectedRows(0); QMessageBox msg(view->window()); msg.setWindowTitle(tr("Delete files")); msg.setIcon(QMessageBox::Warning); msg.addButton(QMessageBox::Yes); msg.addButton(QMessageBox::No); if (items.count() > 1) { auto forAll = new QCheckBox(tr("Do this operation for all items"), &msg); forAll->setCheckState(Qt::Unchecked); msg.setCheckBox(forAll); msg.addButton(QMessageBox::Cancel); } int last = -1; for(const auto& idx: items) { auto parent = idx.parent(); auto name = m->filePath(idx); bool doForAll = false; if (msg.checkBox()) doForAll = (msg.checkBox()->checkState() == Qt::Checked); if (!doForAll) { msg.setText(tr("Realy remove %1").arg(name)); last = msg.exec(); } switch(last) { case QMessageBox::Yes: if (m->fileInfo(idx).isDir()) { QDir(m->fileInfo(idx).absoluteFilePath()).removeRecursively(); } else { m->remove(idx); } view->update(parent); break; case QMessageBox::No: break; case QMessageBox::Cancel: return; } } } ProjectIconProvider::~ProjectIconProvider() = default; FileSystemModel::~FileSystemModel() = default; ================================================ FILE: ide/filesystemmanager.h ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FILESYSTEMMANAGER_H #define FILESYSTEMMANAGER_H #include #include #include class QTreeView; class FileSystemManager : public QObject { Q_OBJECT Q_DISABLE_COPY(FileSystemManager) public: explicit FileSystemManager(QTreeView *v, QObject *parent = nullptr); virtual ~FileSystemManager(); static QIcon iconForFile(const QFileInfo &info); static QString mimeIconPath(const QString& mimeName); signals: void requestFileOpen(const QString& path); public slots: void openPath(const QString& path); void closePath(); private slots: void customContextMenu(const QPoint& pos); void menuNewFile(); void menuNewDirectory(); void menuNewSymlink(); void menuItemExecute(); void menuItemOpenExternal(); void menuItemRename(); void menuItemDelete(); private: QTreeView *view; }; #endif // FILESYSTEMMANAGER_H ================================================ FILE: ide/findandopenfiledialog.cpp ================================================ #include "findandopenfiledialog.h" #include "ui_findandopenfiledialog.h" #include #include #include #include FindAndOpenFileDialog::FindAndOpenFileDialog(QWidget *parent) : QDialog(parent), ui(new Ui::FindAndOpenFileDialog) { ui->setupUi(this); ui->buttonBox->button(QDialogButtonBox::Open)->setDisabled(true); connect(ui->fileList, &QAbstractItemView::activated, this, &QDialog::accept); } FindAndOpenFileDialog::~FindAndOpenFileDialog() { delete ui; } void FindAndOpenFileDialog::setFileList(const QString& prefix, const QStringList &list) { if (ui->fileList->model()) ui->fileList->model()->deleteLater(); auto m = new QStandardItemModel(this); for (const auto& e: list) { auto item = new QStandardItem{"..." + QString{e}.remove(prefix)}; item->setData(e, Qt::UserRole + 1); m->appendRow(item); } ui->fileList->setModel(m); ui->buttonBox->button(QDialogButtonBox::Open)->setDisabled(list.isEmpty()); } QString FindAndOpenFileDialog::selectedFile() const { auto idx = ui->fileList->currentIndex(); return ui->fileList->model()->data(idx, Qt::UserRole + 1).toString(); } QStringList FindAndOpenFileDialog::findFilesInPath(const QString &file, const QString &path) { QStringList list; QDirIterator it(path, { file }, QDir::NoDotAndDotDot | QDir::Files, QDirIterator::Subdirectories); while (it.hasNext()) { list.append(it.next()); } return list; } ================================================ FILE: ide/findandopenfiledialog.h ================================================ #ifndef FINDANDOPENFILEDIALOG_H #define FINDANDOPENFILEDIALOG_H #include namespace Ui { class FindAndOpenFileDialog; } class FindAndOpenFileDialog : public QDialog { Q_OBJECT public: explicit FindAndOpenFileDialog(QWidget *parent = nullptr); ~FindAndOpenFileDialog(); void setFileList(const QString &prefix, const QStringList &list); QString selectedFile() const; static QStringList findFilesInPath(const QString& file, const QString& path); private: Ui::FindAndOpenFileDialog *ui; }; #endif // FINDANDOPENFILEDIALOG_H ================================================ FILE: ide/findandopenfiledialog.ui ================================================ FindAndOpenFileDialog 0 0 582 355 Select file to open QAbstractItemView::NoEditTriggers true Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Open buttonBox accepted() FindAndOpenFileDialog accept() 248 254 157 274 buttonBox rejected() FindAndOpenFileDialog reject() 316 260 286 274 ================================================ FILE: ide/findinfilesdialog.cpp ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "appconfig.h" #include "findinfilesdialog.h" #include "ui_findinfilesdialog.h" #include #include #include #include #include #include #include #include struct FilePos { int line{ 0 }; int column{ 0 }; QString path; FilePos() = default; FilePos(int l, int c, QString p) : line(l), column(c), path(std::move(p)) {} }; Q_DECLARE_METATYPE(FilePos) const QStringList STANDARD_FILTERS = { "*.*", "*.c", "*.cpp", "*.h", "*.hpp", "*.txt" }; FindInFilesDialog::FindInFilesDialog(QWidget *parent) : QDialog(parent), ui(std::make_unique()) { ui->setupUi(this); const struct { QAbstractButton *b; const char *name; } buttonmap[]={ { ui->buttonChoseDirectory, "document-open" }, { ui->buttonSelectfilePattern, "application-menu" }, { ui->buttonFind, "edit-find" }, { ui->buttonStop, "dialog-cancel" }, }; for (const auto& e: buttonmap) e.b->setIcon(QIcon{AppConfig::resourceImage({ "actions", e.name })}); auto model = new QStandardItemModel(this); ui->treeView->setModel(model); auto protoItem = new QStandardItem(); protoItem->setFont(AppConfig::instance().loggerFont()); model->setItemPrototype(protoItem); ui->textToFind->addMenuActions(QHash{ { tr("Regular Expression"), "regex" }, { tr("Case Sensitive"), "case" }, { tr("Wole Words"), "wword" }, }); ui->labelStatus->setText(tr("Ready")); auto filterMenu = new QMenu(this); auto listViewAction = new QWidgetAction(filterMenu); auto filterListView = new QListView(this); filterListView->setEditTriggers(QListView::NoEditTriggers); auto filterModel = new QStandardItemModel(filterListView); for(const auto& e: STANDARD_FILTERS) { auto i = new QStandardItem(e); i->setCheckable(true); filterModel->appendRow(i); } connect(filterModel, &QStandardItemModel::itemChanged, [this](QStandardItem *item) { QString filter = ui->textFilePattern->text(); if (item->checkState() == Qt::Checked) { if (!filter.isEmpty()) filter.append(", "); filter.append(item->text()); } else { filter.remove(QRegularExpression(QString(R"(\,?\s?%1,?)") .arg(QRegularExpression::escape(item->text())))); } ui->textFilePattern->setText(filter.trimmed()); }); filterModel->item(0, 0)->setCheckState(Qt::Checked); filterListView->setModel(filterModel); listViewAction->setDefaultWidget(filterListView); filterMenu->addAction(listViewAction); ui->buttonSelectfilePattern->setMenu(filterMenu); ui->buttonSelectfilePattern->setPopupMode(QToolButton::InstantPopup); connect(ui->treeView, &QTreeView::activated, [this, model](const QModelIndex& index) { auto item = model->itemFromIndex(index); if (item) { auto pos = item->data().value(); if (!pos.path.isEmpty()) { emit queryToOpen(pos.path, pos.line, pos.column); accept(); } } }); connect(ui->buttonExpandAll, &QToolButton::toggled, [this](bool b) { if (b) { ui->buttonExpandAll->setText(tr("Collapse All")); ui->treeView->expandAll(); } else { ui->buttonExpandAll->setText(tr("Expand All")); ui->treeView->collapseAll(); } }); auto doc = new QsciScintilla(this); doc->hide(); connect(ui->buttonFind, &QToolButton::clicked, [this, model, doc]() { setProperty("onProcessLoop", true); model->clear(); ui->buttonStop->setEnabled(true); ui->buttonFind->setDisabled(true); QStringList filters = ui->textFilePattern->text().split(",") .replaceInStrings(QRegularExpression(R"(^\s+)"), QString()) .replaceInStrings(QRegularExpression(R"(\s+$)"), QString()); QString textToFind = ui->textToFind->text(); bool isRegex = ui->textToFind->isPropertyChecked("regex"); bool isCS = ui->textToFind->isPropertyChecked("case"); bool isWWord = ui->textToFind->isPropertyChecked("wword"); QDirIterator it(ui->textDirectory->text(), filters, QDir::NoFilter, QDirIterator::Subdirectories); while (it.hasNext() && property("onProcessLoop").toBool()) { QCoreApplication::processEvents(); if (!property("onProcessLoop").toBool()) break; ui->labelStatus->setText(tr("Scanning file:")); ui->labelFilename->setText(QFontMetrics(ui->labelStatus->font()) .elidedText(it.next(), Qt::ElideLeft, ui->labelStatus->width())); auto info = it.fileInfo(); if (!info.isFile()) continue; QFile file(info.absoluteFilePath()); if (!file.open(QFile::ReadOnly)) continue; if (!doc->read(&file)) continue; if (!doc->findFirst(textToFind, isRegex, isCS, isWWord, false, true, -1, -1, false, false)) continue; auto fileItem = model->itemPrototype()->clone(); fileItem->setText(info.absoluteFilePath().remove(ui->textDirectory->text() + QDir::separator())); model->appendRow(fileItem); do { int line; int column; doc->getCursorPosition(&line, &column); auto textInFile = doc->text(line); auto posItem = model->itemPrototype()->clone(); constexpr auto JUSTIFY = 8; posItem->setText(tr("Line %1 Char %2: %3").arg( QString("%1").arg(line).leftJustified(JUSTIFY, ' '), QString("%1").arg(column).leftJustified(JUSTIFY, ' '), textInFile)); posItem->setData(QVariant::fromValue(FilePos{ line, column, info.absoluteFilePath() })); posItem->setData(Qt::AlignBaseline, Qt::TextAlignmentRole); fileItem->appendRow(posItem); QCoreApplication::processEvents(); } while (doc->findNext() && property("onProcessLoop").toBool()); } ui->buttonStop->setDisabled(true); ui->buttonFind->setEnabled(true); // ui->treeView->expandAll(); ui->labelStatus->setText(tr("Done")); ui->labelFilename->clear(); setProperty("onProcessLoop", false); }); connect(this, &QDialog::finished, [this]() { setProperty("onProcessLoop", false); }); connect(ui->buttonStop, &QToolButton::clicked, [this]() { setProperty("onProcessLoop", false); }); connect(ui->buttonChoseDirectory, &QToolButton::clicked, [this]() { QString path = QFileDialog::getExistingDirectory(this, tr("Select directory"), ui->textDirectory->text()); if (!path.isEmpty()) ui->textDirectory->setText(path); }); } FindInFilesDialog::~FindInFilesDialog() { } QString FindInFilesDialog::findPath() const { return ui->textDirectory->text(); } void FindInFilesDialog::setFindPath(const QString &path) { ui->textDirectory->setText(path); } void FindInFilesDialog::closeEvent(QCloseEvent *) { setProperty("onProcessLoop", false); } ================================================ FILE: ide/findinfilesdialog.h ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FINDINFILESDIALOG_H #define FINDINFILESDIALOG_H #include #include #include #include class ProjectView; class DocumentArea; namespace Ui { class FindInFilesDialog; } class QStandardItemModel; class QStandardItem; class FindInFilesDialog : public QDialog { Q_OBJECT public: explicit FindInFilesDialog(QWidget *parent = nullptr); virtual ~FindInFilesDialog() override; QString findPath() const; public slots: void setFindPath(const QString& path); signals: void queryToOpen(const QString& path, int line, int column); protected: virtual void closeEvent(QCloseEvent *) override; private: std::unique_ptr ui; }; #endif // FINDINFILESDIALOG_H ================================================ FILE: ide/findinfilesdialog.ui ================================================ FindInFilesDialog 0 0 609 372 Find in files Directory Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 .. File pattern Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 .. Text to find Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 .. QAbstractItemView::NoEditTriggers true true true true expand all true 0 0 false .. FindLineEdit QLineEdit
    findlineedit.h
    textToFind treeView buttonFind buttonSelectfilePattern textDirectory textFilePattern buttonChoseDirectory buttonStop textToFind returnPressed() buttonFind click() 321 79 427 87
    ================================================ FILE: ide/findlineedit.cpp ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "appconfig.h" #include "findlineedit.h" #include #include #include #include #include static const QString INNER_BUTTON_STYLE = "background: transparent; border: none;"; FindLineEdit::FindLineEdit(QWidget *parent) : QLineEdit(parent) { auto layout = new QHBoxLayout(this); auto clearButton = new QToolButton(this); optionsButton = new QToolButton(this); optionsButton->setIcon(QIcon(AppConfig::resourceImage({ "actions", "application-menu" }))); clearButton->setIcon(QIcon(AppConfig::resourceImage({ "actions", "edit-clear" }))); optionsButton->setFocusPolicy(Qt::NoFocus); optionsButton->setPopupMode(QToolButton::InstantPopup); optionsButton->setCursor(Qt::ArrowCursor); clearButton->setFocusPolicy(Qt::NoFocus); clearButton->setCursor(Qt::ArrowCursor); optionsButton->setStyleSheet(INNER_BUTTON_STYLE); clearButton->setStyleSheet(INNER_BUTTON_STYLE); int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); setStyleSheet(QString("QLineEdit { padding-right: %1px; padding-left: %2px }\n" "QToolButton::menu-indicator { image: none; }") .arg(clearButton->sizeHint().width() + frameWidth + 1) .arg(optionsButton->sizeHint().width() + frameWidth + 1)); layout->addWidget(optionsButton, 0, Qt::AlignLeft); layout->addWidget(clearButton, 0, Qt::AlignRight); layout->setMargin(0); const auto margin = frameWidth * 2 + 2; layout->setContentsMargins(margin, 0, margin, 0); layout->setSpacing(0); connect(clearButton, SIGNAL(clicked()), this, SLOT(clear())); QSize msz = minimumSizeHint(); setMinimumSize(qMax(msz.width(), clearButton->sizeHint().height() + margin), qMax(msz.height(), clearButton->sizeHint().height() + margin)); } void FindLineEdit::addMenuActions(const QHash &actionList) { auto menu = new QMenu(this); QHash::const_iterator it; for (it = actionList.constBegin(); it != actionList.constEnd(); ++it) { const auto& actionName = it.key(); const auto& propertyName = it.value(); auto action = menu->addAction(actionName); action->setCheckable(true); setProperty(propertyName.toLatin1().constData(), false); actionMap.insert(propertyName, action); connect(action, &QAction::triggered, [this, propertyName](bool ck) { setProperty(propertyName.toLatin1().constData(), QVariant(ck)); emit menuActionClicked(propertyName, ck); }); } optionsButton->setMenu(menu); } void FindLineEdit::setPropertyChecked(const QString& propertyName, bool state) { setProperty(propertyName.toLatin1().constData(), state); if (actionMap.contains(propertyName)) actionMap.value(propertyName)->setChecked(state); } ================================================ FILE: ide/findlineedit.h ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FINDLINEEDIT_H #define FINDLINEEDIT_H #include class QToolButton; class FindLineEdit : public QLineEdit { Q_OBJECT public: explicit FindLineEdit(QWidget *parent = nullptr); virtual ~FindLineEdit() {} void addMenuActions(const QHash& actionList); void setPropertyChecked(const QString &propertyName, bool state); bool isPropertyChecked(const char *name) const { return property(name).toBool(); } signals: void menuActionClicked(const QString& prop, bool status); private: QToolButton *optionsButton; QHash actionMap; }; #endif // FINDLINEEDIT_H ================================================ FILE: ide/findmakefiledialog.cpp ================================================ #include "findmakefiledialog.h" #include "ui_findmakefiledialog.h" #include #include #include #include QString find_root(const QStringList& list) { QString root = list.front(); for(const auto& item : list) { if (root.length() > item.length()) root.truncate(item.length()); for(int i = 0; i < root.length(); ++i) if (root[i] != item[i]) { root.truncate(i); break; } } return root; } static QStringList extractPrefix(const QStringList& list, const QString& root) { QStringList copy; for (const auto& e: list) copy.append("..." + QString(e).remove(root)); return copy; } FindMakefileDialog::FindMakefileDialog(const QString &path, QWidget *parent) : QDialog(parent), ui(new Ui::FindMakefileDialog) { ui->setupUi(this); setProperty("path", path); QTimer::singleShot(0, [this, path](){ auto model = new QStringListModel(this); ui->makefileList->setModel(model); ui->buttonOpen->setDisabled(true); model->setStringList(extractPrefix(findInPath(path), path)); ui->buttonOpen->setDisabled(false); if (model->rowCount() > 0) { ui->makefileList->setCurrentIndex(model->index(0, 0)); } }); setProperty("canceling", false); connect(ui->buttonCancel, &QAbstractButton::clicked, [this]() { reject(); setProperty("canceling", true); }); connect(ui->buttonOpen, &QAbstractButton::clicked, this, &QDialog::accept); connect(ui->makefileList, &QAbstractItemView::activated, this, &QDialog::accept); } FindMakefileDialog::~FindMakefileDialog() { delete ui; } QString FindMakefileDialog::fileName() const { auto model = qobject_cast(ui->makefileList->model()); if (!model) return {}; auto text = model->stringList().at(ui->makefileList->currentIndex().row()); return property("path").toString() + QDir::separator() + text.remove(0, 3); } QStringList FindMakefileDialog::findInPath(const QString &path) { QStringList list; QDirIterator it(path, QDir::AllEntries | QDir::NoSymLinks | QDir::NoDotAndDotDot, QDirIterator::Subdirectories); while (it.hasNext()) { auto name = it.next(); ui->labelLog->setText(tr("Finding in ...%1").arg(name.rightRef(30))); if (QFileInfo(name).fileName() == "Makefile") { list += name; } QCoreApplication::processEvents(); if (property("canceling").toBool()) { break; } } ui->labelLog->setText(tr("Done.")); return list; } ================================================ FILE: ide/findmakefiledialog.h ================================================ #ifndef FINDMAKEFILEDIALOG_H #define FINDMAKEFILEDIALOG_H #include namespace Ui { class FindMakefileDialog; } class FindMakefileDialog : public QDialog { Q_OBJECT public: explicit FindMakefileDialog(const QString& path, QWidget *parent = nullptr); ~FindMakefileDialog(); QString fileName() const; private: Ui::FindMakefileDialog *ui; QStringList findInPath(const QString& path); }; #endif // FINDMAKEFILEDIALOG_H ================================================ FILE: ide/findmakefiledialog.ui ================================================ FindMakefileDialog 0 0 463 266 Dialog QAbstractItemView::NoEditTriggers Cancel Qt::Horizontal 40 20 Open ================================================ FILE: ide/formfindreplace.cpp ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "formfindreplace.h" #include "Qsci/qsciscintilla.h" #include "appconfig.h" #include "ui_formfindreplace.h" #include #include #include FormFindReplace::FormFindReplace(QsciScintilla *ed) : QWidget(ed->viewport()), ui(std::make_unique()), editor(ed) { ui->setupUi(this); const struct { QAbstractButton *b; const char *name; } buttonmap[]={ { ui->buttonFind, "edit-find" }, { ui->buttonFindPrev_2, "dialog-close" }, { ui->buttonReplace, "edit-find-replace" }, }; for (const auto& e: buttonmap) e.b->setIcon(QIcon{AppConfig::resourceImage({ "actions", e.name })}); ui->textToFind->addMenuActions({ { tr("Regular Expression"), "regex" }, { tr("Case Sensitive"), "case" }, { tr("Wole Words"), "wword" }, { tr("Selection Only"), "selonly" }, { tr("Wrap search"), "wrap" }, { tr("Backward search"), "backward" }, }); ui->textToFind->setPropertyChecked("wrap", true); ui->textToReplace->addMenuActions({ { tr("Replace All"), "replaceAll" } }); connect(ui->textToFind, &FindLineEdit::menuActionClicked, [this]() { setProperty("isFirst", true); }); auto layout = new QGridLayout(ed->viewport()); layout->setRowStretch(0, 1); layout->addWidget(this, 1, 0); layout->setMargin(0); layout->setContentsMargins(0, 0, 0, 0); editor->installEventFilter(this); } FormFindReplace::~FormFindReplace() { } void FormFindReplace::showEvent(QShowEvent *event) { QWidget::showEvent(event); ui->textToFind->setFocus(); ui->textToFind->setText(editor->selectedText()); setProperty("isFirst", true); } void FormFindReplace::hideEvent(QHideEvent *event) { QWidget::hideEvent(event); editor->setFocus(); } void FormFindReplace::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Escape) { hide(); event->accept(); } else QWidget::keyPressEvent(event); } bool FormFindReplace::eventFilter(QObject *watched, QEvent *event) { Q_UNUSED(watched) switch (event->type()) { case QEvent::KeyPress: if (isVisible()) { if (!ui->textToFind->hasFocus()) ui->textToFind->setFocus(); this->event(event); return true; } break; default: break; } return false; } bool FormFindReplace::on_buttonFind_clicked() { auto found = false; auto expr = ui->textToFind->text(); if (property("isFirst").toBool()) { bool re = ui->textToFind->isPropertyChecked("regex"); bool cs = ui->textToFind->isPropertyChecked("case"); bool wo = ui->textToFind->isPropertyChecked("wword"); bool wrap = ui->textToFind->isPropertyChecked("wrap"); bool forward = !ui->textToFind->isPropertyChecked("backward"); int line = -1; int index = -1; bool show = true; bool posix = false; bool selonly = ui->textToFind->isPropertyChecked("selonly"); if (selonly) found = editor->findFirstInSelection(expr, re, cs, wo, forward, show, posix); else found = editor->findFirst(expr, re, cs, wo, wrap, forward, line, index, show, posix); setProperty("isFirst", false); } else found = editor->findNext(); auto pal = ui->textToFind->palette(); pal.setBrush(QPalette::Base, found? palette().base() : QBrush(QColor(Qt::red).lighter())); ui->textToFind->setPalette(pal); return found; } void FormFindReplace::on_buttonReplace_clicked() { while (on_buttonFind_clicked()) { auto replaceText = ui->textToReplace->text(); editor->replace(replaceText); if (!ui->textToReplace->isPropertyChecked("replaceAll")) break; } } void FormFindReplace::on_textToFind_textChanged(const QString &text) { Q_UNUSED(text) int line; int pos; auto p = property("currentPos").toPoint(); if (!p.isNull()) { line = p.x(); pos = p.y(); editor->setCursorPosition(line, pos); } editor->getCursorPosition(&line, &pos); setProperty("currentPos", QPoint(line, pos)); setProperty("isFirst", true); on_buttonFind_clicked(); } void FormFindReplace::on_textToFind_returnPressed() { setProperty("currentPos", QPoint()); setProperty("isFirst", false); on_buttonFind_clicked(); } ================================================ FILE: ide/formfindreplace.h ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FORMFINDREPLACE_H #define FORMFINDREPLACE_H #include #include namespace Ui { class FormFindReplace; } class QsciScintilla; class FormFindReplace : public QWidget { Q_OBJECT public: explicit FormFindReplace(QsciScintilla *ed); virtual ~FormFindReplace() override; protected: void showEvent(QShowEvent *event) override; void hideEvent(QHideEvent *event) override; void keyPressEvent(QKeyEvent *event) override; bool eventFilter(QObject *watched, QEvent *event) override; private slots: bool on_buttonFind_clicked(); void on_buttonReplace_clicked(); void on_textToFind_textChanged(const QString &text); void on_textToFind_returnPressed(); private: std::unique_ptr ui; QsciScintilla *editor; }; #endif // FORMFINDREPLACE_H ================================================ FILE: ide/formfindreplace.ui ================================================ FormFindReplace 0 0 210 56 true 2 2 2 2 2 Find Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 .. .. Replace 0 0 .. FindLineEdit QLineEdit
    findlineedit.h
    buttonFindPrev_2 clicked() FormFindReplace hide() 498 22 466 9
    ================================================ FILE: ide/icodemodelprovider.cpp ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "icodemodelprovider.h" #include ICodeModelProvider::FileReference::FileReference(const QString &p, int l, int c, const QString &m): path(p), line(l), column(c), meta(m) { } QUrl ICodeModelProvider::FileReference::encode() const { auto url = QUrl::fromLocalFile(path); QUrlQuery q; q.addQueryItem("line", QString("%1").arg(line)); q.addQueryItem("column", QString("%1").arg(column)); q.addQueryItem("meta", meta); url.setQuery(q); return url; } bool ICodeModelProvider::FileReference::isEmpty() const { return path.isEmpty() && meta.isEmpty() && line == -1 && column == -1; } ICodeModelProvider::FileReference ICodeModelProvider::FileReference::decode(const QUrl &url) { QUrlQuery q(url.query()); return { url.toLocalFile(), q.queryItemValue("line").toInt(), 0, q.queryItemValue("meta") }; } bool ICodeModelProvider::FileReference::operator ==(const ICodeModelProvider::FileReference &other) const { return path == other.path && meta == other.meta && line == other.line && column == other.column; } ICodeModelProvider::~ICodeModelProvider() {} QString ICodeModelProvider::Symbol::toString() const { return QString{"%1, %2, %3, %4, %5"}.arg(name, expression, lang, type, ref.encode().toString()); } ================================================ FILE: ide/icodemodelprovider.h ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef ICPPCODEMODELPROVIDER_H #define ICPPCODEMODELPROVIDER_H #include #include #include #include class ICodeModelProvider { public: virtual ~ICodeModelProvider(); struct FileReference { QString path; int line = -1; int column = -1; QString meta; FileReference() {} FileReference(const QString& p, int l, int c, const QString& m); QUrl encode() const; bool isEmpty() const; static FileReference decode(const QUrl& url); bool operator ==(const FileReference& other) const; }; struct Symbol { QString name; QString expression; QString lang; QString type; FileReference ref; bool operator ==(const Symbol& other) const { return toString() == other.toString(); } QString toString() const; }; using SymbolList = QList; using FileReferenceList = QList; using SymbolSet = QSet; using SymbolSetMap = QHash; using FindReferenceCallback_t = std::function; using CompletionCallback_t = std::function; using SymbolRequestCallback_t = std::function; using FinishIndexProjectCallback_t = std::function; using FinishIndexFileCallback_t = std::function; virtual void startIndexingProject(const QString& path, FinishIndexProjectCallback_t cb) = 0; virtual void startIndexingFile(const QString& path, FinishIndexFileCallback_t cb) = 0; virtual void referenceOf(const QString& entity, FindReferenceCallback_t cb) = 0; virtual void completionAt(const FileReference& ref, const QString& unsaved, CompletionCallback_t cb) = 0; virtual void requestSymbolForFile(const QString& path, SymbolRequestCallback_t cb) = 0; }; Q_DECLARE_METATYPE(ICodeModelProvider::FileReference) Q_DECLARE_METATYPE(ICodeModelProvider::Symbol) inline uint qHash(const ICodeModelProvider::FileReference &t, uint seed = 0) { return qHash(t.encode(), seed); } inline uint qHash(const ICodeModelProvider::Symbol &t, uint seed = 0) { return qHash(t.toString(), seed); } #endif // ICPPCODEMODELPROVIDER_H ================================================ FILE: ide/ide.pro ================================================ DESTDIR = ../build QT += core gui widgets svg xml network concurrent uitools CONFIG += qscintilla2 CONFIG += c++14 CONFIG += warn_off greaterThan(QT_MINOR_VERSION, 11) { CONFIG += lrelease embed_translations } TARGET = embedded-ide TEMPLATE = app TRANSLATIONS = translations/es.ts DEFINES += QT_DEPRECATED_WARNINGS # You can also make your code fail to compile if you use deprecated APIs. # In order to do so, uncomment the following line. # You can also select to disable deprecated APIs only up to a certain version of Qt. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 include($$PWD/../mapview/mapview.pri) include($$PWD/../3rdpart/qhexview/qhexview.pri) include($$PWD/../3rdpart/astyle/astyle.pri) include($$PWD/../3rdpart/qdarkstyle/qdarkstype.pri) include($$PWD/../3rdpart/hoedown/hoedown.pri) include($$PWD/../3rdpart/qt-mustache-master/qt-mustache.pri) #linux:!android: include(3rdpart/backward/backward.pri) #include(3rdpart/qt-promise/qt-promise.pri) INCLUDEPATH += $$PWD/../3rdpart SOURCES += \ envinputdialog.cpp \ findandopenfiledialog.cpp \ findmakefiledialog.cpp \ main.cpp \ mainwindow.cpp \ markdowneditor.cpp \ markdownview.cpp \ newprojectfromremotedialog.cpp \ processlinebufferizer.cpp \ projectmanager.cpp \ documentmanager.cpp \ idocumenteditor.cpp \ plaintexteditor.cpp \ filesystemmanager.cpp \ templatefile.cpp \ unsavedfilesdialog.cpp \ processmanager.cpp \ consoleinterceptor.cpp \ buildmanager.cpp \ binaryviewer.cpp \ codetexteditor.cpp \ findlineedit.cpp \ formfindreplace.cpp \ cpptexteditor.cpp \ appconfig.cpp \ configwidget.cpp \ externaltoolmanager.cpp \ version.cpp \ newprojectdialog.cpp \ findinfilesdialog.cpp \ icodemodelprovider.cpp \ templatemanager.cpp \ templateitemwidget.cpp \ clangautocompletionprovider.cpp \ childprocess.cpp \ filereferencesdialog.cpp \ mapfileviewer.cpp \ textmessagebrocker.cpp \ regexhtmltranslator.cpp \ imageviewer.cpp HEADERS += \ buttoneditoritemdelegate.h \ envinputdialog.h \ findandopenfiledialog.h \ findmakefiledialog.h \ mainwindow.h \ markdowneditor.h \ markdownview.h \ newprojectfromremotedialog.h \ processlinebufferizer.h \ projectmanager.h \ documentmanager.h \ idocumenteditor.h \ plaintexteditor.h \ filesystemmanager.h \ tar.h \ templatefile.h \ unsavedfilesdialog.h \ processmanager.h \ consoleinterceptor.h \ buildmanager.h \ binaryviewer.h \ codetexteditor.h \ findlineedit.h \ formfindreplace.h \ cpptexteditor.h \ appconfig.h \ configwidget.h \ externaltoolmanager.h \ version.h \ newprojectdialog.h \ findinfilesdialog.h \ icodemodelprovider.h \ templatemanager.h \ templateitemwidget.h \ clangautocompletionprovider.h \ childprocess.h \ filereferencesdialog.h \ mapfileviewer.h \ textmessagebrocker.h \ regexhtmltranslator.h \ imageviewer.h FORMS += \ envinputdialog.ui \ findandopenfiledialog.ui \ findmakefiledialog.ui \ mainwindow.ui \ newprojectfromremotedialog.ui \ unsavedfilesdialog.ui \ formfindreplace.ui \ configwidget.ui \ externaltoolmanager.ui \ newprojectdialog.ui \ findinfilesdialog.ui \ templatemanager.ui \ templateitemwidget.ui \ filereferencesdialog.ui CONFIG += mobility MOBILITY = RESOURCES += \ resources/fonts.qrc \ resources/iconactions.qrc \ resources/kinds.qrc \ resources/mimetypes.qrc \ resources/resources.qrc \ resources/styles.qrc #QMAKE_LFLAGS += -lqscintilla2_qt5 LIBS += -lz win32 { QMAKE_CXXFLAGS += -g3 QMAKE_CFLAGS += -g3 } unix { QMAKE_LFLAGS_RELEASE += -static-libstdc++ -static-libgcc QMAKE_LFLAGS_DEBUG += -static-libstdc++ -static-libgcc isEmpty(PREFIX) { PREFIX = /usr } target.path = $$PREFIX/bin desktopfile.files = skeleton/embedded-ide.desktop desktopfile.path = $$PREFIX/share/applications iconfiles.files = resources/images/light/embedded-ide.svg resources/light/images/embedded-ide.png iconfiles.path = $$PREFIX/share/icons/default/256x256/apps/ scripts.path = $$PREFIX/bin scripts.files = skeleton/desktop-integration.sh skeleton/ftdi-tools.sh hardconf.path = $$PREFIX/share/embedded-ide hardconf.files = skeleton/embedded-ide.hardconf INSTALLS += desktopfile INSTALLS += iconfiles INSTALLS += scripts INSTALLS += hardconf INSTALLS += target } DISTFILES += \ skeleton/desktop-integration.sh \ skeleton/embedded-ide.sh \ skeleton/embedded-ide.sh.wrapper \ skeleton/ftdi-tools.sh \ skeleton/embedded-ide.hardconf \ skeleton/embedded-ide.desktop \ translations/es.ts ================================================ FILE: ide/idocumenteditor.cpp ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "idocumenteditor.h" #include #include #include DocumentEditorFactory::DocumentEditorFactory() = default; DocumentEditorFactory *DocumentEditorFactory::instance() { static DocumentEditorFactory *staticInstance = nullptr; if (!staticInstance) staticInstance = new DocumentEditorFactory(); return staticInstance; } void DocumentEditorFactory::registerDocumentInterface(IDocumentEditorCreator *creator) { creators << creator; } IDocumentEditor *DocumentEditorFactory::create(const QString &path, QWidget *parent) { QMimeDatabase db; auto mime = db.mimeTypeForFile(path); auto info = QFileInfo(path); auto suffixes = QStringList(info.suffix()) << mime.suffixes(); if (info.size() == 0) { // FIXME: Force the content type of empty files to plain-text mime = db.mimeTypeForData(QByteArray{"\n"}); } // Try first from suffix for(auto c: creators) if (c->canHandleExtentions(suffixes)) return c->create(parent); // Try second from mimetype for(auto c: creators) if (c->canHandleMime(mime)) return c->create(parent); return nullptr; } IDocumentEditor::~IDocumentEditor() = default; IDocumentEditorCreator::~IDocumentEditorCreator() = default; ================================================ FILE: ide/idocumenteditor.h ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef IDOCUMENTEDITOR_H #define IDOCUMENTEDITOR_H #include "documentmanager.h" #include #include #include #include #include #include class ICodeModelProvider; class IDocumentEditor { public: using ModifyObserver_t = std::function; using CursorObserver_t = std::function; virtual ~IDocumentEditor(); virtual const QWidget *widget() const = 0; virtual QWidget *widget() = 0; virtual bool load(const QString& path) = 0; virtual bool save(const QString& path) = 0; virtual void reload() = 0; virtual QString path() const { return widget()->windowFilePath(); } virtual void setPath(const QString& path) { widget()->setWindowFilePath(path); } virtual bool isReadonly() const = 0; virtual void setReadonly(bool rdOnly) = 0; virtual bool isModified() const = 0; virtual void setModified(bool m) = 0; virtual QPoint cursor() const = 0; virtual void setCursor(const QPoint& pos) = 0; void setDocumentManager(DocumentManager *man) { this->man = man; } DocumentManager *documentManager() const { return this->man; } void addModifyObserver(ModifyObserver_t fptr) { modifyObserverList.append(fptr); } void addCursorObserver(CursorObserver_t fptr) { cursorObserverList.append(fptr); } void setCodeModel(ICodeModelProvider *m) { _codeModel = m; } ICodeModelProvider *codeModel() const { return _codeModel; } protected: void notifyModifyObservers() { for(auto& a: modifyObserverList) a(this, isModified()); } void notifyCursorOvserver(int line, int col) { for(auto& a: cursorObserverList) a(this, line, col); } private: QList modifyObserverList; QList cursorObserverList; ICodeModelProvider *_codeModel = nullptr; DocumentManager *man = nullptr; }; class IDocumentEditorCreator { public: virtual ~IDocumentEditorCreator(); virtual bool canHandleExtentions(const QStringList&) const { return false; } virtual bool canHandleMime(const QMimeType&) const { return false; } virtual IDocumentEditor *create(QWidget *parent = nullptr) const = 0; template static IDocumentEditorCreator *staticCreator() { static IDocumentEditorCreator *singleton = nullptr; if (!singleton) singleton = new T; return singleton; } }; class DocumentEditorFactory: public QObject { Q_OBJECT Q_DISABLE_COPY(DocumentEditorFactory) private: DocumentEditorFactory(); QList creators; public: static DocumentEditorFactory* instance(); void registerDocumentInterface(IDocumentEditorCreator *creator); IDocumentEditor *create(const QString& path, QWidget *parent = nullptr); }; #endif // IDOCUMENTEDITOR_H ================================================ FILE: ide/imageviewer.cpp ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "appconfig.h" #include "imageviewer.h" #include #include #include #include #include #include static constexpr auto ICON_SIZE = QSize{22, 22}; static constexpr auto ZOOMOUT_FACTOR = 1.1; static constexpr auto ZOOMIN_FACTOR = 0.99; template static QToolButton *createButton(const QString& name, QWidget *parent, F& func) { auto b = new QToolButton(parent); b->setIcon(QIcon(AppConfig::resourceImage({ "actions", name }))); b->setIconSize(ICON_SIZE); b->setAutoRaise(true); QObject::connect(b, &QToolButton::clicked, func); return b; } template static QToolButton *createToggleButton(const QString& name, QWidget *parent, F& func) { auto b = new QToolButton(parent); b->setIcon(QIcon(AppConfig::resourceImage({ "actions", name }))); b->setIconSize(ICON_SIZE); b->setAutoRaise(true); b->setCheckable(true); QObject::connect(b, &QToolButton::toggled, func); return b; } class AspectRatioLabel : public QLabel { public: explicit AspectRatioLabel(QWidget* parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()) : QLabel(parent, f) {} ~AspectRatioLabel() override; public slots: void setPixmap(const QPixmap& pm) { pixmapWidth = pm.width(); pixmapHeight = pm.height(); updateMargins(); QLabel::setPixmap(pm); } protected: void resizeEvent(QResizeEvent* event) override { updateMargins(); QLabel::resizeEvent(event); } private: void updateMargins() { if (pixmapWidth <= 0 || pixmapHeight <= 0) return; int w = this->width(); int h = this->height(); if (w <= 0 || h <= 0) return; if (w * pixmapHeight > h * pixmapWidth) { int m = (w - (pixmapWidth * h / pixmapHeight)) / 2; setContentsMargins(m, 0, m, 0); } else { int m = (h - (pixmapHeight * w / pixmapWidth)) / 2; setContentsMargins(0, m, 0, m); } } int pixmapWidth = 0; int pixmapHeight = 0; }; AspectRatioLabel::~AspectRatioLabel() = default; ImageViewer::ImageViewer(QWidget *parent) : QWidget(parent) { auto area = new QScrollArea(this); auto label = new AspectRatioLabel(this); label->setObjectName("image"); label->setBackgroundRole(QPalette::Base); label->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); label->setScaledContents(true); area->setWidget(label); auto h = new QGridLayout(area); setProperty("factor", 1.0); auto doScale = [label](double f) { label->resize(label->size() * f); }; auto zoomIn = [doScale]() { doScale(ZOOMOUT_FACTOR); }; auto zoomOut = [doScale]() { doScale(ZOOMIN_FACTOR); }; auto zoomFit = [area, label](bool check) { area->setWidgetResizable(check); if (!check) { label->resize(label->pixmap()->size()); } }; h->setContentsMargins(0, 0, 0, 0); h->setSpacing(0); h->addWidget(createToggleButton("zoom-fit-best", area, zoomFit), 0, 1); h->addWidget(createButton("zoom-in", area, zoomIn), 0, 2); h->addWidget(createButton("zoom-out", area, zoomOut), 0, 3); h->setColumnStretch(0, 1); h->setColumnStretch(1, 0); h->setColumnStretch(2, 0); h->setColumnStretch(3, 0); h->setColumnStretch(4, 1); h->setRowStretch(0, 0); h->setRowStretch(1, 1); auto l = new QHBoxLayout(this); l->setContentsMargins(0, 0, 0, 0); l->addWidget(area); } bool ImageViewer::load(const QString &path) { QPixmap p; if (!p.load(path)) return false; auto label = findChild("image"); label->setPixmap(p); label->resize(label->pixmap()->size()); setProperty("factor", 1.0); setWindowFilePath(path); return true; } class ImageViewerCreator: public IDocumentEditorCreator { public: ~ImageViewerCreator() override; bool canHandleMime(const QMimeType &mime) const override { auto supportedMimes = QImageReader::supportedMimeTypes(); for (const auto& m: supportedMimes) if (mime.inherits(m)) return true; return false; } IDocumentEditor *create(QWidget *parent = nullptr) const override { return new ImageViewer(parent); } }; IDocumentEditorCreator *ImageViewer::creator() { return IDocumentEditorCreator::staticCreator(); } ImageViewerCreator::~ImageViewerCreator() = default; ================================================ FILE: ide/imageviewer.h ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef IMAGEVIEWER_H #define IMAGEVIEWER_H #include "idocumenteditor.h" #include class ImageViewer : public IDocumentEditor, public QWidget { public: explicit ImageViewer(QWidget *parent = nullptr); virtual ~ImageViewer() override {} virtual const QWidget *widget() const override { return this; } virtual QWidget *widget() override { return this; } virtual bool load(const QString& path) override; virtual bool save(const QString& path) override { Q_UNUSED(path); return false; } virtual void reload() override { load(path()); } virtual QString path() const override { return widget()->windowFilePath(); } virtual void setPath(const QString& path) override { widget()->setWindowFilePath(path); } virtual bool isReadonly() const override { return true; } virtual void setReadonly(bool rdOnly) override { Q_UNUSED(rdOnly); } virtual bool isModified() const override { return false; } virtual void setModified(bool m) override { Q_UNUSED(m); } virtual QPoint cursor() const override { return QPoint(); } virtual void setCursor(const QPoint& pos) override { Q_UNUSED(pos); } static IDocumentEditorCreator *creator(); }; #endif // IMAGEVIEWER_H ================================================ FILE: ide/main.cpp ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "appconfig.h" #include "mainwindow.h" #include #include #include #include #include #include #include #include #include #include #include int main(int argc, char *argv[]) { QCoreApplication::setApplicationName("Embedded IDE"); QCoreApplication::setOrganizationName("none"); QCoreApplication::setOrganizationDomain("unknown.tk"); QApplication app(argc, argv); QApplication::setWindowIcon(QIcon(AppConfig::resourceImage("embedded-ide" ))); QTranslator tr; for(const auto& p: AppConfig::langPaths()) { if (tr.load(QLocale::system().name(), p)) { if (QApplication::installTranslator(&tr)) { qDebug() << "load translations" << QLocale::system().name(); break; } } } auto checkConfig = [&app, &tr](AppConfig *config) { app.setStyleSheet( config->useDarkStyle()? AppConfig::readEntireTextFile(":/qdarkstyle/style.qss"): "" ); auto selectedLang = config->language(); if (!selectedLang.isEmpty()) { for(const auto& p: AppConfig::langPaths()) { if (tr.load(selectedLang, p)) { if (QApplication::installTranslator(&tr)) { qDebug() << "load translations" << QLocale::system().name(); break; } } } } }; QObject::connect(&AppConfig::instance(), &AppConfig::configChanged, [&checkConfig](AppConfig *config) { checkConfig(config); switch (config->networkProxyType()) { case AppConfig::NetworkProxyType::None: QNetworkProxy::setApplicationProxy(QNetworkProxy::NoProxy); break; case AppConfig::NetworkProxyType::System: QNetworkProxyFactory::setUseSystemConfiguration(true); break; case AppConfig::NetworkProxyType::Custom: QNetworkProxy proxy( QNetworkProxy::HttpProxy, config->networkProxyHost(), static_cast(config->networkProxyPort().toInt())); if (config->networkProxyUseCredentials()) { proxy.setUser(config->networkProxyUsername()); proxy.setPassword(config->networkProxyPassword()); } QNetworkProxy::setApplicationProxy(proxy); break; } }); AppConfig::instance().load(); checkConfig(&AppConfig::instance()); QCommandLineParser opt; opt.addHelpOption(); opt.addVersionOption(); opt.addPositionalArgument("filename", "Makefile filename"); opt.addOptions({ { { "e", "exec" }, "Execute stript or file", "execname" }, { { "d", "debug"}, "Enable debug" }, { { "s", "stacktrace" }, "Add stack trace to debug" } }); opt.process(app); if (opt.isSet("exec")) { QString execname = opt.value("exec"); QProcess::startDetached(execname); return 0; } if (opt.isSet("debug")) { QString debugString = "[%{type}] %{appname} (%{file}:%{line}) - %{message}"; if (opt.isSet("stacktrace")) debugString += "\n\t%{backtrace separator=\"\n\t\"}"; qSetMessagePattern(debugString); } else qSetMessagePattern(""); MainWindow w; w.show(); if (!opt.positionalArguments().isEmpty()) { QString path = opt.positionalArguments().first(); QTimer::singleShot(0, [path, &w]() { w.openProject(path); }); } return QApplication::exec(); } ================================================ FILE: ide/mainwindow.cpp ================================================ /* * This file is part of Embedded-IDE * * Copyright 2019 Martin Ribelotta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "mainwindow.h" #include "ui_mainwindow.h" #include "appconfig.h" #include "buildmanager.h" #include "consoleinterceptor.h" #include "filesystemmanager.h" #include "idocumenteditor.h" #include "externaltoolmanager.h" #include "processmanager.h" #include "projectmanager.h" #include "unsavedfilesdialog.h" #include "version.h" #include "newprojectdialog.h" #include "configwidget.h" #include "findinfilesdialog.h" #include "clangautocompletionprovider.h" #include "textmessagebrocker.h" #include "regexhtmltranslator.h" #include "templatemanager.h" #include "templateitemwidget.h" #include "templatefile.h" #include "processlinebufferizer.h" #include "newprojectfromremotedialog.h" #include "findmakefiledialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include struct LineRange { int first, second, idx; }; Q_DECLARE_METATYPE(LineRange) using LineRangeList = QList; class MainWindow::Priv_t { public: ProjectManager *projectManager; FileSystemManager *fileManager; ProcessManager *pman; ConsoleInterceptor *console; BuildManager *buildManager; LineRangeList lineRanges; QString lastDir; bool documentOnly = false; QByteArray topSplitterState; QByteArray docSplitterState; QVector trackedBuildPath; }; static constexpr auto MAINWINDOW_SIZE = QSize{900, 600}; static QString kindToIcon(const QString& kind) { static const QHash map{ { "array", "variable" }, { "boolean", "variable" }, { "chapter", "variable" }, { "enum", "enum" }, { "enumerator", "enum" }, { "externvar", "variable" }, { "function", "function" }, { "macro", "macro" }, { "object", "unknown" }, { "prototype", "function" }, { "section", "unknown" }, { "struct", "class" }, { "symbol", "class" }, { "typedef", "class" }, { "union", "class" }, { "variable", "variable" }, }; return map.value(kind, "unknown"); } MainWindow::MainWindow(QWidget *parent) : QWidget(parent), ui(std::make_unique()), priv(std::make_unique()) { ui->setupUi(this); ui->stackedWidget->setCurrentWidget(ui->welcomePage); ui->bottomLeftStack->setCurrentWidget(ui->actionViewer); const struct { QToolButton *b; const char *icon; } buttonmap[] = { { ui->buttonDocumentCloseAll, "document-close-all" }, { ui->buttonReload, "view-refresh" }, { ui->buttonCloseProject, "document-close" }, { ui->buttonExport, "document-export" }, { ui->buttonFindAll, "edit-find-replace" }, { ui->buttonTools, "run-build-configure" }, { ui->buttonConfigurationMain, "configure" }, { ui->buttonDebugLaunch, "debug-init" }, { ui->buttonDebugRun, "debug-run-v2" }, { ui->buttonDebugStepOver, "debug-step-over-v2" }, { ui->buttonDebugStepInto, "debug-step-into-v2" }, { ui->buttonDebugRunToEOF, "debug-step-out-v2" }, { ui->buttonDocumentClose, "document-close" }, { ui->buttonDocumentSave, "document-save" }, { ui->buttonDocumentCloseAll, "document-close-all" }, { ui->buttonDocumentSaveAll, "document-save-all" }, { ui->buttonDocumentReload, "view-refresh" }, { ui->updateAvailable, "view-refresh" }, { ui->buttonQuit, "application-exit" }, { ui->buttonConfiguration, "configure" }, // { ui->buttonExternalTools, "run-build-configure" }, { ui->buttonOpenProject, "document-open" }, { ui->buttonNewProject, "document-new" }, { ui->buttonNewProjectFromRemote, "document-new" }, }; auto loadIcons = [this, buttonmap]() { for (const auto& e: buttonmap) e.b->setIcon(QIcon{AppConfig::resourceImage({ "actions", e.icon })}); auto l = QWidgetList{ ui->labelConfiguration, ui->labelExit, ui->labelNewProject, ui->labelOpenProject }; for (QWidget *e: l) { auto p = e->palette(); if (AppConfig::instance().useDarkStyle()) p.setColor(QPalette::Text, p.color(QPalette::Text).lighter()); else p.setColor(QPalette::Text, p.color(QPalette::Text).darker()); e->setPalette(p); } }; loadIcons(); connect(&AppConfig::instance(), &AppConfig::configChanged, loadIcons); ui->buttonReload->setIcon(QIcon(AppConfig::resourceImage({"actions", "view-refresh"}))); ui->buttonDebugLaunch->setVisible(AppConfig::instance().useDevelopMode()); ui->updateAvailable->setVisible(false); auto tman = new TemplateManager(); tman->setRepositoryUrl(AppConfig::instance().templatesUrl()); connect(tman, &TemplateManager::message, [](const QString& msg) { qDebug() << "tman msg:" << msg; }); connect(tman, &TemplateManager::errorMessage, [](const QString& msg) { qDebug() << "tman err:" << msg; }); connect(tman, &TemplateManager::haveMetadata, [this, tman]() { bool canUpdate = false; auto list = tman->itemWidgets(); for(auto *witem: list) { const auto& item = witem->templateItem(); canUpdate = canUpdate || (item.state() == TemplateItem::State::Updatable) || (item.state() == TemplateItem::State::New); } ui->updateAvailable->setVisible(canUpdate); }); connect(ui->updateAvailable, &QToolButton::clicked, [this, tman]() { static constexpr auto SIZE_GROW = 0.9; QDialog d(this); d.resize(this->size().scaled(this->size() * SIZE_GROW, Qt::KeepAspectRatio)); auto l = new QVBoxLayout(&d); auto bb = new QDialogButtonBox(QDialogButtonBox::Close, &d); l->addWidget(tman); l->addWidget(bb); connect(bb, &QDialogButtonBox::accepted, &d, &QDialog::accept); connect(bb, &QDialogButtonBox::rejected, &d, &QDialog::reject); d.exec(); ui->updateAvailable->hide(); tman->setParent(nullptr); tman->startUpdate(); }); tman->startUpdate(); ui->stackedWidget->setCurrentWidget(ui->welcomePage); ui->documentContainer->setComboBox(ui->documentSelector); auto version = tr("%1 build at %2").arg(VERSION, BUILD_DATE); ui->labelVersion->setText(ui->labelVersion->text().replace("{{version}}", version)); resize(MAINWINDOW_SIZE); priv->pman = new ProcessManager(this); priv->console = new ConsoleInterceptor(ui->logView, this); auto currentPathTracker = [this](QTextBrowser *b, QString& s) -> bool { Q_UNUSED(b) auto &stack = priv->trackedBuildPath; static const QRegularExpression re(R"(make\[(\d+)\]\: (Entering|Leaving) directory \'([^\']*)\')"); auto m = re.match(s); if (m.hasMatch()) { auto level = m.captured(1).toInt(); auto path = m.captured(3); if (stack.size() < level) { stack.resize(level); } stack[level - 1] = path; } return false; }; auto errorStringFinder = [this](QTextBrowser *b, QString& s) -> bool { Q_UNUSED(b) static const QRegularExpression errRe( R"(^(?.*?):(?\d+):(?\d+)?(?:?)(?.*?)$)", QRegularExpression::MultilineOption ); auto mit = errRe.globalMatch(s); int count = 0; int pos = 0; while (mit.hasNext()) { auto mm = mit.next(); int reStart = mm.capturedStart(); if (reStart > pos) { ConsoleInterceptor::writeMessageTo(b, s.mid(pos, reStart - pos)); } QString file = mm.captured("file"); if (!priv->trackedBuildPath.isEmpty() && !QFileInfo(file).isAbsolute()) file.prepend(priv->trackedBuildPath.last() + QDir::separator()); auto line = mm.captured("line"); auto col = mm.captured("col"); auto ddot = mm.captured("ddot"); auto msg = mm.captured("msg"); auto url = ICodeModelProvider::FileReference(file, line.toInt(), col.toInt(), msg).encode(); auto err = QString("%2:%3:%4%5 ").arg(file, line, col, ddot); ConsoleInterceptor::writeMessageTo(b, err, Qt::red); QTextCharFormat linkFmt; linkFmt.setAnchor(true); linkFmt.setAnchorHref(url.toString()); linkFmt.setForeground(b->palette().link().color()); ConsoleInterceptor::writeMessageTo(b, msg, linkFmt); pos = mm.capturedEnd(); count++; } if (count > 0 && pos < s.length()) { ConsoleInterceptor::writeMessageTo(b, s.mid(pos)); return true; } return false; }; priv->console->addStdErrFilter(currentPathTracker); priv->console->addStdOutFilter(currentPathTracker); priv->console->addStdErrFilter(errorStringFinder); priv->console->addStdOutFilter(errorStringFinder); connect(priv->console->clearButton(), &QToolButton::clicked, ui->logView, &QTextBrowser::clear); auto makeProc = priv->pman->processFor(BuildManager::PROCESS_NAME); connect(makeProc, &QProcess::stateChanged, [this](QProcess::ProcessState state) { priv->console->killButton()->setEnabled(state == QProcess::Running); }); auto makeLinerize = new ProcessLineBufferizer(ProcessLineBufferizer::MergedChannel, makeProc); connect(makeLinerize, &ProcessLineBufferizer::haveLine, this, [this, makeProc](const QString& line) { priv->console->appendToConsole(QProcess::StandardError, makeProc, line); }); connect(priv->buildManager, &BuildManager::buildTerminated, makeLinerize, &ProcessLineBufferizer::flush); priv->projectManager = new ProjectManager(ui->actionViewer, priv->pman, this); priv->projectManager->setCodeModelProvider(new ClangAutocompletionProvider(priv->projectManager, this)); ui->documentContainer->setProjectManager(priv->projectManager); priv->buildManager = new BuildManager(priv->projectManager, priv->pman, this); connect(priv->console->killButton(), &QToolButton::clicked, priv->buildManager, &BuildManager::cancelBuild); priv->fileManager = new FileSystemManager(ui->fileViewer, this); connect(ui->logView, &QTextBrowser::anchorClicked, [this](const QUrl& url) { auto ref = ICodeModelProvider::FileReference::decode(url); ui->documentContainer->openDocumentHere(ref.path, ref.line, ref.column); ui->documentContainer->setFocus(); }); TextMessageBrocker::instance().subscribe(TextMessages::STDERR_LOG, [this](const QString& msg) { priv->console->writeMessage(msg); }); TextMessageBrocker::instance().subscribe(TextMessages::STDOUT_LOG, [this](const QString& msg) { priv->console->writeMessage(msg); }); connect(priv->buildManager, &BuildManager::buildStarted, [this]() { priv->trackedBuildPath.clear(); ui->actionViewer->setEnabled(false); }); connect(priv->buildManager, &BuildManager::buildTerminated, [this]() { ui->actionViewer->setEnabled(true); }); connect(priv->projectManager, &ProjectManager::targetTriggered, [this](const QString& target) { ui->logView->clear(); auto unsaved = ui->documentContainer->unsavedDocuments(); if (!unsaved.isEmpty()) { UnsavedFilesDialog d(unsaved, this); if (d.exec() == QDialog::Rejected) return; ui->documentContainer->saveDocuments(d.checkedForSave()); } priv->buildManager->startBuild(target); }); connect(priv->fileManager, &FileSystemManager::requestFileOpen, ui->documentContainer, &DocumentManager::openDocument); auto showMessageCallback = [this](const QString& msg) { priv->console->writeMessage(msg, Qt::darkGreen); }; auto clearMessageCallback = [this]() { ui->logView->clear(); }; connect(priv->projectManager, &ProjectManager::exportFinish, showMessageCallback); ui->recentProjectsView->setModel(new QStandardItemModel(ui->recentProjectsView)); auto makeRecentProjects = [this]() { auto m = dynamic_cast(ui->recentProjectsView->model()); m->clear(); for(const auto& e: AppConfig::instance().recentProjects()) { auto item = new QStandardItem( QIcon(FileSystemManager::mimeIconPath("text-x-makefile")), e.dir().dirName()); item->setData(e.absoluteFilePath()); item->setToolTip(e.absoluteFilePath()); m->appendRow(item); } }; makeRecentProjects(); connect(new QFileSystemWatcher({AppConfig::instance().projectsPath()}, this), &QFileSystemWatcher::directoryChanged, makeRecentProjects); connect(ui->recentProjectsView, &QListView::activated, [this](const QModelIndex& m) { openProject(dynamic_cast(m.model())->data(m, Qt::UserRole + 1).toString()); }); auto openProjectCallback = [this]() { auto lastDir = priv->lastDir; if (lastDir.isEmpty()) lastDir = AppConfig::instance().projectsPath(); auto path = QFileDialog::getOpenFileName(this, tr("Open Project"), lastDir, tr("Makefile (Makefile);;All files (*)")); if (!path.isEmpty()) openProject(path); }; auto newProjectCallback = [this]() { NewProjectDialog d(this); if (d.exec() == QDialog::Accepted) { if (d.isTemplate()) { priv->projectManager->createProject(d.absoluteProjectPath(), d.templateFile()); } else { priv->projectManager->createProjectFromTGZ(d.absoluteProjectPath(), d.selectedTemplateFile().absoluteFilePath()); } } }; auto newProjectFromRemoteCallback = [this, makeRecentProjects]() { NewProjectFromRemoteDialog d(this); if (d.exec()) { FindMakefileDialog fd(d.projectPath(), this); if (fd.exec()) { auto projectFile = fd.fileName(); openProject(projectFile); } } makeRecentProjects(); }; auto openConfigurationCallback = [this]() { ConfigWidget d(this); if (d.exec()) d.save(); }; auto exportCallback = [this, clearMessageCallback]() { auto path = QFileDialog::getSaveFileName(this, tr("New File"), AppConfig::instance().templatesPath(), TemplateFile::TEMPLATE_FILEDIALOG_FILTER); if (!path.isEmpty()) { if (QFileInfo(path).suffix().isEmpty()) path.append(".template"); clearMessageCallback(); priv->projectManager->exportCurrentProjectTo(path); } }; connect(ui->buttonOpenProject, &QToolButton::clicked, openProjectCallback); connect(ui->buttonExport, &QToolButton::clicked, exportCallback); connect(ui->buttonNewProject, &QToolButton::clicked, newProjectCallback); connect(ui->buttonNewProjectFromRemote, &QToolButton::clicked, newProjectFromRemoteCallback); connect(ui->buttonConfiguration, &QToolButton::clicked, openConfigurationCallback); connect(ui->buttonConfigurationMain, &QToolButton::clicked, openConfigurationCallback); connect(ui->buttonCloseProject, &QToolButton::clicked, priv->projectManager, &ProjectManager::closeProject); connect(new QShortcut(QKeySequence("CTRL+N"), this), &QShortcut::activated, newProjectCallback); connect(new QShortcut(QKeySequence("CTRL+O"), this), &QShortcut::activated, openProjectCallback); connect(new QShortcut(QKeySequence("CTRL+SHIFT+P"), this), &QShortcut::activated, openConfigurationCallback); connect(new QShortcut(QKeySequence("CTRL+SHIFT+Q"), this), &QShortcut::activated, priv->projectManager, &ProjectManager::closeProject); priv->documentOnly = false; connect(new QShortcut(QKeySequence("CTRL+SHIFT+C"), this), &QShortcut::activated, [this]() { auto state = priv->documentOnly; if (state) { ui->horizontalSplitterTop->restoreState(priv->topSplitterState); ui->splitterDocumentViewer->restoreState(priv->docSplitterState); } else { priv->topSplitterState = ui->horizontalSplitterTop->saveState(); priv->docSplitterState = ui->splitterDocumentViewer->saveState(); constexpr auto DEFAULT_SPLITTER_SIZE = 100; ui->horizontalSplitterTop->setSizes({ 0, DEFAULT_SPLITTER_SIZE }); ui->splitterDocumentViewer->setSizes({ DEFAULT_SPLITTER_SIZE, 0 }); ui->documentContainer->setFocus(); } priv->documentOnly = !state; }); connect(ui->buttonReload, &QToolButton::clicked, priv->projectManager, &ProjectManager::reloadProject); connect(priv->projectManager, &ProjectManager::projectOpened, [this](const QString& makefile) { for(auto& btn: ui->projectButtons->buttons()) btn->setEnabled(true); QFileInfo mkInfo(makefile); auto filepath = mkInfo.absoluteFilePath(); auto dirpath = mkInfo.absolutePath(); ui->stackedWidget->setCurrentWidget(ui->mainPage); priv->fileManager->openPath(dirpath); AppConfig::instance().appendToRecentProjects(filepath); AppConfig::instance().save(); qputenv("CURRENT_PROJECT_FILE", filepath.toLocal8Bit()); qputenv("CURRENT_PROJECT_DIR", dirpath.toLocal8Bit()); }); connect(priv->projectManager, &ProjectManager::projectClosed, [this, makeRecentProjects]() { qputenv("CURRENT_PROJECT_FILE", ""); qputenv("CURRENT_PROJECT_DIR", ""); bool ok = ui->documentContainer->aboutToCloseAll(); qDebug() << "can close" << ok; if (ok) { for(auto& btn: ui->projectButtons->buttons()) btn->setEnabled(false); makeRecentProjects(); ui->stackedWidget->setCurrentWidget(ui->welcomePage); priv->fileManager->closePath(); } }); auto enableEdition = [this]() { auto haveDocuments = ui->documentContainer->documentCount() > 0; auto current = ui->documentContainer->documentEditorCurrent(); auto isModified = current? current->isModified() : false; ui->documentSelector->setEnabled(haveDocuments); ui->buttonDocumentClose->setEnabled(haveDocuments); ui->buttonDocumentCloseAll->setEnabled(haveDocuments); ui->buttonDocumentReload->setEnabled(haveDocuments); ui->buttonDocumentSave->setEnabled(isModified); ui->buttonDocumentSaveAll->setEnabled(ui->documentContainer->unsavedDocuments().count() > 0); ui->symbolSelector->setEnabled(false); ui->symbolSelector->clear(); if (current) { auto m = qobject_cast(ui->fileViewer->model()); if (m) { auto i = m->index(current->path()); if (i.isValid()) { ui->fileViewer->setCurrentIndex(i); } } } }; connect(ui->documentContainer, &DocumentManager::documentModified, [this](const QString& path, IDocumentEditor *iface, bool modify){ Q_UNUSED(path) Q_UNUSED(iface) ui->buttonDocumentSave->setEnabled(modify); ui->buttonDocumentSaveAll->setEnabled(ui->documentContainer->unsavedDocuments().count() > 0); }); connect(ui->symbolSelector, qOverload(&QComboBox::activated), [this](int idx) { auto var = ui->symbolSelector->itemData(idx); if (var.isNull()) return; auto meta = var.value(); auto ed = ui->documentContainer->documentEditorCurrent(); if (ed) { ed->setCursor({ meta.ref.column, meta.ref.line }); ed->widget()->setFocus(); } }); connect(ui->documentContainer, &DocumentManager::documentPositionModified, [this](const QString& path, int l, int c) { Q_UNUSED(c) Q_UNUSED(path) int idx = 0; for (const auto& e: priv->lineRanges) { if (l >= e.first && l < e.second) { idx = e.idx; break; } } auto b = ui->symbolSelector->blockSignals(true); ui->symbolSelector->setCurrentIndex(idx); ui->symbolSelector->blockSignals(b); }); auto requestSymbolsForFile = [this](const QString& path) { priv->projectManager->codeModel()->requestSymbolForFile( path, [this](const ICodeModelProvider::SymbolSetMap& items) { ui->symbolSelector->clear(); priv->lineRanges.clear(); if (items.isEmpty()) return; auto b = ui->symbolSelector->blockSignals(true); auto& lineNumbers = priv->lineRanges; ui->symbolSelector->addItem(tr("