Compare commits

...

333 Commits

Author SHA1 Message Date
Jørgen Lien Sellæg e65c91d24d Merge branch 'python-refactor' into 'master' 7 months ago
eidheim f8f112f3fc Made variable sized array constant size 7 months ago
eidheim 659dc0d49a Improved extend selection for latex 7 months ago
Jørgen Lien Sellæg a2e4868893 Merge branch 'master' into python-refactor 7 months ago
Jørgen Lien Sellæg 1223a72908 Merge branch 'master' into python-refactor 7 months ago
eidheim 14b3317eee Select all now works in terminal as well 8 months ago
eidheim 1f94ae9a80 Added comment char for prolog 9 months ago
eidheim eb9fc4d9e6 Fixed isysroot settings on MacOS 9 months ago
eidheim e21a641d26 Fixed build issue on MacOS 9 months ago
eidheim bd2cb0a288 Escape key now closes tooltips 10 months ago
eidheim 1944bf78bb v1.8.1 11 months ago
eidheim 0984889b98 Fixed boost cmake warning 11 months ago
eidheim 332bf8b627 Updated cmake_minimum_required 11 months ago
eidheim d473c95af0 Fixed fontconfig cmake search and libclang isysroot issue on MacOS 11 months ago
eidheim 9b0e4555ab Updated libclangmm 11 months ago
eidheim 617e5d96cb Minor improvement to space drawing code 1 year ago
eidheim 3be4752a64 Fixed space drawing 1 year ago
eidheim 4dc64b8cce Added cmake check for gtksourceviewmm-4 1 year ago
eidheim 14f37e4544 Fixed gtk_source_search functions for all gtksourceview versions 1 year ago
eidheim 261660c715 Added support for new space drawer in gtksourceview 1 year ago
eidheim de56bb1111 Fixed control-click to run source files on Linux 1 year ago
eidheim f4a1498c99 Reversed control-click to run source files on Linux for the time being 1 year ago
eidheim 54400ccf83 Cleanup of clang language server commits 1 year ago
Shivang Gangadia dfd4ef7f83 Restored C++14, made C++ language server access through symbolic linnk. Updated documentation. 1 year ago
Shivang Gangadia ffdf01c1ab Fixed C++20 ambiguous operator overload warning 1 year ago
Shivang Gangadia aedd819fbe Added support for optionally using LSP for c-like languages. Also bumped c++ version to 20 for project 1 year ago
eidheim a8163f0d84 Cleanup of menu inconsistency fix 1 year ago
eidheim c7dbbcd962 Merge branch 'juci_menu_inconsistent' of https://gitlab.com/shivangsgangadia/jucipp-clangd-support 1 year ago
eidheim efc98b5b13 Space between lines cleanup 1 year ago
eidheim 27a4c47f5a Merge branch 'space_between_lines_option' of https://gitlab.com/shivangsgangadia/jucipp-clangd-support 1 year ago
Shivang Gangadia b4d6286460 Removed trailing app menu variable 1 year ago
Shivang Gangadia a5cf0529d5 Moved app menu to the menu bar to ensure consistent appearance across desktops. 1 year ago
Shivang Gangadia 69cec55f5a Added option to configure spacing between lines in editor 1 year ago
Shivang Gangadia 50adce1c4a Added support for configuration of space between lines in editor. Updated IsClangType to use a set. 1 year ago
eidheim 1289f45e59 Can now use control/command click to run source files directly from the directory view 1 year ago
eidheim 20e74bd468 Updated libclangmm 1 year ago
eidheim 9d2604aa4b Updated libclangmm 1 year ago
eidheim 37e5f049d9 Updated libclangmm 1 year ago
eidheim cc3ecab010 Added project.open_with_default_application to preferences 2 years ago
eidheim 5bc8dde6de Language protocol: added sorting for rename items in case server does not return sorted changes 2 years ago
eidheim 2bff28a844 Added possibility to label run commands in the commands.json 2 years ago
eidheim fe16979289 ctags: location.symbol and location.source should now correspond with respect to escaping 2 years ago
eidheim f6aeb314f0 Added workaround for Invalid cross-device link error 2 years ago
eidheim 9e080d0f77 Improved indentation when inserting snippets 2 years ago
eidheim fccfd34771 Fixed boost::archive exception 2 years ago
eidheim 86b77fcff1 Replaced deprecated boost::filesystem::copy_option 2 years ago
eidheim 469d8c8cb0 Replaced deprecated boost::filesystem::iterator::no_push 2 years ago
eidheim 0889806b52 Removed use of deprecated boost::property_tree::get_child 2 years ago
eidheim 284cc068ff No longer considers keys pressed with control/command active as keyvals 2 years ago
Sven Otto ee90cfe3df Update install.md to cover OpenSuSE Leap as well, when i tried to compile it was missing some dependencies, now it builds successfully. You would probably have to check as there is still a Warning shown when running the IDE: "Could not find a supported Build System" 2 years ago
eidheim 9305e35f7a Updated LICENSE 2 years ago
eidheim 91425d9c74 Cleanup of clang resource directory lookup 2 years ago
eidheim 546d5810be Merge branch 'clang_resource_dir_fix' of https://gitlab.com/doe300/jucipp 2 years ago
doe300 481a728a59 Fix Clang resource directory lookup for Fedora with LLVM 17+ 2 years ago
eidheim 2b7fc1e7d6 Slight improvement to paste indentation for Python buffers 2 years ago
eidheim 043a105dc3 Search entry histories are now updated correctly 2 years ago
eidheim de7763e555 Fixed CMake and Meson project detection for dos newlines 2 years ago
eidheim f8cb78897a Updated CMake version 2 years ago
eidheim 23ebfaa509 v1.8.0 2 years ago
eidheim ac3f17218a Updated cmake version when creating C/C++ projects 2 years ago
eidheim dd2f762924 Added support for prettier version 3 2 years ago
eidheim bdbcd22ca4 Updated submodules 2 years ago
eidheim 43f4e9b30b Improved version extraction from clang_getClangVersion() 2 years ago
eidheim b269119622 Minor addition to source.tooltip_top_offset setting 2 years ago
eidheim f6f2610098 Made it possible to adjust tooltip top offset in configuration item source.tooltip_top_offset 2 years ago
eidheim ba3ee99d11 Added another attempt to find libclang resource path 3 years ago
eidheim 66b98f18f4 Platform independent way to find clang resource path 3 years ago
eidheim 70b01ba867 Further improvements to libclang's resource path 3 years ago
eidheim ddd53d98fb Improved resource path detection on systems without clang/clang++ installed 3 years ago
eidheim e57ede474c Updated rust language server instructions 3 years ago
eidheim 8f4fafd23d Fixed process_test for systems without bash installed 3 years ago
eidheim a5858199df Fixed rust-analyzer installation 3 years ago
eidheim 96383e11f9 Updated libclangmm submodule 3 years ago
eidheim 6cbaf89802 Cleanup of state handling and parsing in order to limit while loops with sleeps, and removed standard c++ version from libclang arguments due to C header crashes 3 years ago
eidheim c2d31f20ff Fixed autocomplete on for instance '.' after newline 3 years ago
eidheim 5370e68479 Added support for compile_commands.json file in project root folder 3 years ago
eidheim 082cee92ca Cleanup of CompileCommands class 3 years ago
eidheim 6603f52be9 Minor cleanup of clang -resource-dir setting 3 years ago
eidheim 8913a4a485 Fixed clang -resource-dir setting for libclang 3 years ago
eidheim ef96031022 Fixed scrolling to selected element in selection dialogs 3 years ago
eidheim 1eb1209f4e Tooltip workaround no longer needed for gtk versions 3.24.38 and above. Also updated libclangmm submodule 3 years ago
eidheim ba9203959a Cleanup of GTK_VERSION_GT_MICRO replacement macro 3 years ago
eidheim cbda10a798 Fixed autocomplete initialization on text removal 3 years ago
eidheim 7e15a8a398 Fixed GTK_VERSION_GT_MICRO check 3 years ago
eidheim 7ae617d264 Cleanup and corrections to Window::set_title() and its usages: title is now correctly set when opening juCi++ for the first time, and when closing a folder 3 years ago
Jørgen Lien Sellæg 73977fa1ad set main window title based on open directory in tree view 3 years ago
eidheim 42c1103001 Added workaround MacOS missing resource-dir 3 years ago
eidheim 40536fefc8 Updated libclangmm submodule 3 years ago
eidheim 1c82115576 Removed unused variables 3 years ago
eidheim c43b5fff96 Added workaround for the crash described in https://gitlab.gnome.org/GNOME/gtk/-/issues/5593 3 years ago
eidheim 0e4fc91c48 Language client: corrected default workspace/configuration response 3 years ago
eidheim 18f2ebceff Merge https://gitlab.com/cppit/jucipp 3 years ago
eidheim 89efcacb4f Language client: improved support for autocomplete text edits 3 years ago
Jørgen Lien Sellæg fd836b0ecf skip very long lines as they probably arent the correct path and it fixes a crash when the regex match is to big 3 years ago
eidheim 9d902e2354 Made the entry boxes, for instance used in Find, larger 3 years ago
eidheim a7483981bf Directory view: unknown files are now made italic even when not showing a git repository 3 years ago
eidheim 85405c36bc Cleanup of Source::guess_language 3 years ago
eidheim 44e31bc0c4 Merge branch 'bak_in_detection' of https://gitlab.com/doe300/jucipp 3 years ago
eidheim 45c9f5b96d Merge branch 'xml_prologue' of https://gitlab.com/doe300/jucipp 3 years ago
doe300 4307048ed8 Implemented removal of .bak .in file extensions for language detection 3 years ago
doe300 c021fad812 Added detection of XML-derived file types by peeking the file contents 3 years ago
eidheim e9044e386b Language client: now adds diagnostics to codeAction requests, and supports adding configuration responses. Also now uses buffer completions when completion is not provided by server. 3 years ago
eidheim 1e8f4681c5 Updated tiny-process-library submodule 3 years ago
eidheim f212fa5936 Improved newline support for markdown annotated tooltips: two spaces followed by newline will now produce newline within a paragraph 3 years ago
eidheim e2201df1f7 Replaced unnecessary use of get_source_buffer() with get_buffer() 3 years ago
eidheim d11946c75c No longer copies indentation when copying within one line 3 years ago
eidheim 88f8934d32 Updated tiny-process-library submodule 3 years ago
eidheim e40ae4bfef Removed python specific paste() code 3 years ago
eidheim 50291c2fbe Slight cleanup of copy and cut text 3 years ago
eidheim c6d6cfec43 Improved paste indentation by coping or cutting with starting indentation if possible 3 years ago
eidheim c2b824e51e Force newline at end of file on prettier style format 3 years ago
eidheim e566cb8487 Temporary solution to enable prettier plugins: add a file, for instance .prettier-sql, to enable prettier for sql files in that directory and its subdirectories 3 years ago
eidheim d28dcf6f7a Slight improvement of Natural::compare, for instance: t1.txt now comes after t.txt 3 years ago
eidheim 6def62ad21 Added line comment for markdown 3 years ago
eidheim bfbb7a3ab1 Updated tiny-process-library submodule 3 years ago
eidheim d0c0107afc Merge branch 'flatpak' of https://gitlab.com/doe300/jucipp 3 years ago
eidheim 2fe8f1cb46 Fixed llvm 15 crash when trying to parse C header as C++ header in a C project 3 years ago
eidheim bab7e16a90 Temporary fix for llvm 15 where the TranslationUnit cursor kind was changed from 300 to 350 3 years ago
eidheim 20530fab7e Made it possible to enable dark MacOS titlebar through the C definition MACOS_DARK_TITLEBAR 3 years ago
eidheim 0ecf381ab5 Added code, although deactivated currently, to make title bar dark in MacOS 3 years ago
eidheim 1f2aef713d Fixed setting icon on MacOS 3 years ago
eidheim 5f26ec870e Corrected placement of tooltips 3 years ago
doe300 69a30f271d Flatpak, use host system headers and run external programs outside of sandbox 3 years ago
doe300 b9fcb0195b Add flatpak manifest 3 years ago
eidheim b8bda75048 Added instructions on how to use the pyright language server 3 years ago
eidheim 692f3de1f0 v1.7.2 3 years ago
eidheim bd930063c2 Fixed Project::CMakeBuild::is_valid for MSYS2 3 years ago
eidheim 69442af3e9 Added fstream include to filesystem.hpp as suggested in https://gitlab.com/cppit/jucipp/-/issues/453 to fix FreeBSD compilation 3 years ago
eidheim 5611ff5b21 Ignore arguments in compile commands after and including -- 3 years ago
eidheim 847dcbf424 Merge branch 'stop-ignoring-some-compile-commands' of https://gitlab.com/cppit/jucipp 3 years ago
eidheim 729c783d69 Updated libclangmm 3 years ago
eidheim d703454f5f C/C++: create build folder, if not already created, prior to getting run arguments in order to extract executable from build 3 years ago
Jørgen Lien Sellæg 99388cc472 fixes #452. I don't know why the compile commands insterts these? 3 years ago
eidheim de242f310b Minor cleanup of CMake::get_executable 3 years ago
eidheim 350da301ec Cleanup and slight improvement of cmake_file_api code 3 years ago
eidheim 9ee1d4dade Merge branch 'cmake_file_api' of https://gitlab.com/doe300/jucipp 3 years ago
doe300 d9fc483332 Add missing headers required for GCC 11 compilation 4 years ago
doe300 6c4de943ec Use CMake file API to find executables for source file 4 years ago
Jørgen Lien Sellæg 24ff43d654 add svelte files 4 years ago
eidheim d9db8cb2f8 Temporarily disable warnings as errors due to GCC bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105329 4 years ago
Jørgen Sverre Lien Sellæg e81b8f78b4 use space before comment 4 years ago
Jørgen Sverre Lien Sellæg 8f3761cb7a update license 4 years ago
eidheim a8021a1865 Minor cleanup of Source::guess_language 4 years ago
doe300 a07a4605ca Add support for Groovy language and Jenkins files 4 years ago
eidheim 90af1f14cf Slightly safer Natural::compare() 4 years ago
eidheim 103e5519bc Added natural sort to Find File 4 years ago
eidheim 625cecd305 Updated tiny-process-library submodule 4 years ago
eidheim 3e1eca11fc Improved build error messages 4 years ago
eidheim ec1d82e59b Fixed lldb_test on MacOS 4 years ago
eidheim 577c832a61 Update libclangmm 4 years ago
eidheim ae669bfcdf Slightly improved C++ include fixits 4 years ago
eidheim 3b0412e870 Fixed deadlock when inserting snippets when using autocomplete keys 4 years ago
eidheim 42b3f00980 Added extra checks for valid CMake builds, and fixed previous check for correct project path in CMakeCache.txt 4 years ago
eidheim 686649e67d Dollar sign now adds dollar signs around selected text in latex buffers 4 years ago
eidheim 77cab2ccbe Updated libclangmm 4 years ago
eidheim 485a7f5eb2 Fixed latexindent selection formatting 4 years ago
eidheim d3d54b48b6 Added -m switch to latexindent 4 years ago
eidheim fedb5c2c96 Added support for latexindent 4 years ago
vazub 58abd35e4e NetBSD build instructions 4 years ago
vazub 8d03762695 Build for NetBSD and include proper RPATH 4 years ago
eidheim 228056820e Language protocol: no longer show tooltips when no completion is selected 4 years ago
eidheim eb4b1eaf8c Language client: cleanup of client capabilities 4 years ago
eidheim 00a696d19c Language client: now supports multiple declarations and type declarations 4 years ago
eidheim 56a655f096 Changed preference item source.cleanup_whitespace_characters into two seperate items: add_missing_newline_at_end_of_file_on_save and remove_trailing_whitespace_characters_on_save 4 years ago
eidheim 0a3102f13e Language client: added pyright workarounds 4 years ago
eidheim cf695def6a Added yapf style format for python files when not using language server or when language server does not support style formatting, and no longer use ranged formatting on save 4 years ago
eidheim 1a1cffb039 Minor fix to toggle zen mode 4 years ago
eidheim 85f40a76d2 Language client: removed unecessary event flush 4 years ago
eidheim aed4e5723a Language client: added workaround for pyright, and cleanup of rename(). Also added log mutex for cleaner log output. 4 years ago
eidheim be5c8228d5 Language client: various cleanups 4 years ago
eidheim a99755b0e9 Language client: improved error message on JSON parse error 4 years ago
eidheim 06b9f63ad5 Language client: no longer save after code action, and saving after rename is now performed after all rename edits 4 years ago
eidheim f119f17341 Language client: accept requests from server with text ids 4 years ago
eidheim 46bc3de9d0 Language client: added support for RenameFile in textDocument/rename response 4 years ago
eidheim d66c8e16d6 No longer move cursor up/down paragraph when using control+alt+up/down 4 years ago
eidheim 53809dc93e Two or more menu actions can now have the same keybinding 4 years ago
eidheim 561ae473ba Simplified getting default Gtk::Application object 4 years ago
eidheim a8b2d1037b Updated LICENSE 4 years ago
eidheim b3b5182258 v1.7.1 4 years ago
eidheim 8b134a091a Added another doxygen test 4 years ago
eidheim d5001438c8 Fixes #446 : buffer overread in Tooltip::insert_doxygen(), and added _GLIBCXX_ASSERTIONS to tests 4 years ago
eidheim efb6a29e0c Can now unset keybindings in Preferences 4 years ago
eidheim 4525a596e5 Added Toggle Status to Window menu 4 years ago
eidheim 7d55b1f374 Removed dashed lines surrounding tooltips on some gtk themes 4 years ago
eidheim ba995b168a Tooltips: now adds scrolling to all tooltip windows that are placed partially outside of screen 4 years ago
eidheim d65eb9eaf2 Fixed potential compilation error on MSYS2 4 years ago
eidheim 1a2263f6db Preference item search_for_selection now also applies to Find Pattern 4 years ago
eidheim 5332fa624f Fixed indentation on enter after for instance {\n 1,\n 2,\n} 4 years ago
eidheim c3b62cb647 v1.7.0 4 years ago
eidheim 140e40b300 Reversed save modified dialog change: answering no now again closes buffer 4 years ago
eidheim c1525c0296 Store find_pattern options in last_session.json 4 years ago
eidheim 2035dba47e Added Close Other Files to File menu, and fixed save_modified_dialog when choosing No 4 years ago
eidheim c837f49092 Store search options in last_session.json 4 years ago
eidheim 56d77ef32d Added Switch File Type to File menu, and moved Find File from Source to File menu 4 years ago
eidheim 7f62bd6802 File path in status bar is now relative to opened folder if file is within this folder 4 years ago
eidheim 4d18245743 Language client: corrected workspace/workspaceFolders response 4 years ago
eidheim 709e2aeb18 Language client: additional support for workspaceFolders 4 years ago
eidheim 462534cb64 Language client: added support for workspaceFolders (needed by pyright) 4 years ago
eidheim bad362b248 Added preference instructions in python language server setup 4 years ago
eidheim 2ba8fea018 Slight modification of new C/C++ hello world source files 4 years ago
eidheim a8169628c2 Added compile flags -Wstring-conversion and -Wliteral-conversion for clang++ 4 years ago
eidheim 216c7e3a0a Added support for docstring :: code blocks 4 years ago
eidheim 24c795b41f Language client: added codeAction.resolveSupport to initialize request 4 years ago
eidheim 6cb1094ee5 Suggest using python-lsp-server instead of python-language-server, since the latter is no longer updated 4 years ago
eidheim 91efe29a52 Language client: added completionItem.resolveSupport to initialize request 4 years ago
eidheim 0a28c1822f Removed unnecessary mutex from GenericView 4 years ago
eidheim b9c2e2a019 Added spellcheck_all flag to html and xml 4 years ago
eidheim 584eb1e64e Added additional link to docs/language_servers.md in README 4 years ago
eidheim a4db3cc621 Autocomplete is now run without threads in GenericView 4 years ago
eidheim df51554ca0 Cleanup of spellcheck_all boolean used to activate spellchecking on for instance markdown text 4 years ago
eidheim 80cb893f0f Further cleanup of is_token_char 4 years ago
eidheim 91c1d2716d Cleanup of Source::BaseView::is_token_char 4 years ago
eidheim 821b5e845e Updated installation doc: universal-ctags is now an official homebrew package 4 years ago
eidheim d86355b88f Removed unnecessary line 4 years ago
eidheim 1f5973b5ff No longer shows prettier missing message in terminal when opening for instance preferences 4 years ago
eidheim 0a989504db Fixed complete insertion of dollar signs in generic views 4 years ago
eidheim ce99fe4f4b Added dialog icons and added rounded edges on message dialog 4 years ago
eidheim 2305dfb42b Reshow Rust and rust-analyzer dialogs if installation was cancelled 4 years ago
eidheim 9e534f053d Improved spellcheck inside '' 4 years ago
eidheim bc743b4a47 Language server on MSYS2: fixed URIs for Windows paths 4 years ago
eidheim dc57a8749c Added version_compare and adds parallel to default value in preference item project.cmake.compile_command 4 years ago
eidheim da8b1f7f98 Minor cleanup of rustup PATH modification 4 years ago
eidheim b67b919394 Additional utility tests 4 years ago
eidheim 8bf8148c63 Fixed filesystem_test for MSYS2 4 years ago
eidheim c3afe1009b Added filesystem::is_executable 4 years ago
eidheim af652278c3 Further improvements to MSYS2 Rust installer 4 years ago
eidheim b2aade877b Fixed Rust installation on MSYS2 4 years ago
eidheim d18e26ba80 Improved entry in selection dialog on MacOS 4 years ago
eidheim 1dd8cb3e36 Added Rust installer 4 years ago
eidheim a0a587b8fd Added cancellation dialog to rust-analyzer installation 4 years ago
eidheim 09d569873f Cleanup of rust-analyzer installation 4 years ago
eidheim e9b5548b57 Improved installer for rust-analyzer 4 years ago
eidheim 37341bf12e Added automatic installation of rust-analyzer through rustup if component is found 4 years ago
eidheim 7e56744972 Language protocol: client message logs are now formatted 4 years ago
eidheim 9fe68e4891 Improvement of error message when background prettier process exits prematurely 4 years ago
eidheim 6a5b74b87b Added error message if background prettier process exited prematurely 4 years ago
eidheim aa44fc0a50 Reset stdout_buffer and related variables if prettier background process has to be recreated due to failure 4 years ago
eidheim 5adfeaa1c1 Use stringstream reconstruction instead of using clear() 4 years ago
eidheim 257668730c Improvement to prettier library output parsing 4 years ago
eidheim d5eabfd4e7 Made use of prettier library when possible to speed up prettier formatting 4 years ago
eidheim 10a1ccba3c Reduced width of C/C++ completions slightly 4 years ago
eidheim 603835abdc Added style class to selection dialogs 4 years ago
eidheim 40905d54ed Made similar symbol tags more visibile when using dark themes 4 years ago
eidheim 8fb81c11d9 Improved placement of tooltips: now places tooltips below cursor if no space above or right 4 years ago
eidheim db63cedcd6 Improved positioning of tooltips through more exactly computing the size of the tooltip to be shown 4 years ago
eidheim 0cefe148fa Made use of the nlohmann/json library due to lacking JSON capabilities in Boost::PropertyTree 4 years ago
eidheim 3abc46c17b Language client: always add params object when sending notifications (gopls requires this) 5 years ago
eidheim c7340b709d Added JSON::write_json, and some various cleanup 5 years ago
eidheim 32b6436fe8 Minor cleanup of show_or_hide() 5 years ago
eidheim 6d46110907 Added support for Julia's end keywork in show_or_hide() 5 years ago
eidheim 30ce9ca3a4 Added file path escaping to language_protocol_server_test.cpp 5 years ago
eidheim 2b9491565f Language client: now supports CompletionItem.additionalTextEdits 5 years ago
eidheim 66abb09262 Language client: added support for refactor code actions at cursor position using Apply Fix-its menu item. Availability of refactorings is not shown to keep visual noise to a minimum. 5 years ago
eidheim 149e82a279 Additional language client tests 5 years ago
eidheim e6069f00bc Minor improvement to extend_selection(), and correction of cursor movement on functions with no arguments 5 years ago
eidheim c89238b9e5 Improvements to indentaion detection 5 years ago
eidheim 4e121ffd00 Improved C/C++ include fixits 5 years ago
eidheim a95cb94b79 Language client: move cursor forward if no arguments in completed function and if cursor is still inside () 5 years ago
eidheim b6e8e5d441 Language client: added completionItem/resolve request to fetch details and documentation of completion items 5 years ago
eidheim 70d9818772 Language client: added support for type declaration and implementation location. Also fixes to utf-8 byte count and regular utf-16 offsets, and cleaned up write_request and write_notification calls 5 years ago
eidheim 2e34b4ba6c Updated libclangmm 5 years ago
eidheim c51ad74811 Added link to documentation on Language Protocol extension offsetEncoding 5 years ago
eidheim 0c42dd0fa9 Language client: use proper utf-16 offsets when getting iters and offsetEncoding is not set to utf-8 5 years ago
eidheim be5e36627d Language client: improved support for both UTF-16 offsets and offsetEncoding set to utf-8 5 years ago
eidheim 5aeb065f4b Language client: cleanup of embolden_token 5 years ago
eidheim 5164f3f956 Another fix to MSYS2 test failure 5 years ago
eidheim 8acf82bb75 Removed unnecessary .raw() uses 5 years ago
eidheim 47c38ef227 Another fix to MSYS2 test failure 5 years ago
eidheim 300cac6589 Fixed MSYS2 test failure 5 years ago
eidheim aa91e38bfa Added language protocol tests 5 years ago
Jørgen Lien Sellæg 4e6c2c2d95 bind up tiny process lib and add test should also fix terminal warnings 5 years ago
Jørgen Lien Sellæg 1a11a5736b update lldb test 5 years ago
Jørgen Lien Sellæg 92679161f4 update bindings of ctags, but disable test of it for now 5 years ago
Jørgen Lien Sellæg 9ad7c21407 update config test to not fail. Needs updating according to new config options 5 years ago
Jørgen Lien Sellæg 8a7dd41b73 initialize gtksourceviewmm since terminal uses that now and calls to terminal will cause an null value of the sourcebuffer 5 years ago
Jørgen Lien Sellæg 5139d6e127 add new line to terminal call 5 years ago
Jørgen Lien Sellæg 5b20c32754 remove enabled print 5 years ago
Jørgen Lien Sellæg 32a70dd5b0 add basic docs 5 years ago
Jørgen Lien Sellæg 19c3e90cca hook into juci after window has been loaded 5 years ago
Jørgen Lien Sellæg c7e67a70b4 handle creation of plugins directory if it doesn't exist, remove calls to terminal as it might not have been loaded yet at this point 5 years ago
Jørgen Lien Sellæg a994405c71 load config and plugins early 5 years ago
Jørgen Lien Sellæg ad60559d97 load plugins before application, we might want to do changes before the application starts 5 years ago
Jørgen Lien Sellæg 69d5297d88 bump version number so that config file updates 5 years ago
Jørgen Lien Sellæg c72aa8cdc4 Compiling again 5 years ago
Jørgen Lien Sellæg 03cf0990f2 fix merge mistakes in tests 5 years ago
Jørgen Lien Sellæg ecc2a4d642 remove duplicate include 5 years ago
Jørgen Lien Sellæg c5d40c1d0e Add files 5 years ago
Jørgen Lien Sellæg ff0c52cc45 add debug lldb test and fix bug with functional header missing 5 years ago
Jørgen Lien Sellæg 86e6a12180 add ctags test 5 years ago
Jørgen Lien Sellæg f8a607a8ab fix some formatting and check for errors in suite 5 years ago
Jørgen Lien Sellæg c3b4f21735 fix reference count bug in type caster 5 years ago
Jørgen Lien Sellæg ca7d9830b2 extract common method 5 years ago
Jørgen Lien Sellæg be76f94374 small cleanup 5 years ago
Jørgen Lien Sellæg b736400807 fix remaining tests on windows 5 years ago
Jørgen Lien Sellæg cdf8b4548f fix tests in compile_commands for windows and fix a bug in compile_commands.cc 5 years ago
Jørgen Lien Sellæg c9055243c3 organize imports 5 years ago
Jørgen Lien Sellæg ce37b55d0b move terminal target to top 5 years ago
Jørgen Lien Sellæg 6afde6fe37 test all config options and fix two bugs in module 5 years ago
Jørgen Lien Sellæg 6d04f7cff6 add default contstructor for all subclasses of config 5 years ago
Jørgen Lien Sellæg 333fe0aa81 fix assignment bug and add default constructor 5 years ago
Jørgen Lien Sellæg 53f1c755b7 add basic config test 5 years ago
Jørgen Lien Sellæg 3cf14b7841 add tests for compile commands and fix some bugs 5 years ago
Jørgen Lien Sellæg f8127a76d2 move cmake project one level up 5 years ago
Jørgen Lien Sellæg 3368b4ffc2 test cmake bindings 5 years ago
Jørgen Lien Sellæg 95f15ef264 move python interpreter test to python module test 5 years ago
Jørgen Lien Sellæg 7778752da0 disable lldb tests on debian due to an bug in python 5 years ago
Jørgen Lien Sellæg 76e8e5dc54 add paths for windows in test environment 5 years ago
Jørgen Lien Sellæg 7307307846 compile target with files instead of linking 5 years ago
Jørgen Lien Sellæg 05b5b57997 remove references to interpreter 5 years ago
Jørgen Lien Sellæg c519c9dd94 remove interpreter wrapping 5 years ago
Jørgen Lien Sellæg 32b5df1060 remove lldb tests for build check 5 years ago
Jørgen Lien Sellæg 00e1282008 add very basic CMake test 5 years ago
Jørgen Lien Sellæg 91ba2234fe refactor test suite to make room for tests of all bindings 5 years ago
Jørgen Lien Sellæg b898db575f make sure boost filesystem is type casted consistlty 5 years ago
Jørgen Lien Sellæg 8061731851 fix bug where two classes got the same name 5 years ago
Jørgen Lien Sellæg 57a42c8f9e bind some of menu 5 years ago
Jørgen Lien Sellæg 0f4c4a7ce7 fix tests after moving pybind code 5 years ago
Jørgen Lien Sellæg ba24942106 bind git 5 years ago
Jørgen Lien Sellæg e08504e250 bind cppreference 5 years ago
Jørgen Lien Sellæg f583644a89 move binding code to respective files 5 years ago
Jørgen Lien Sellæg 891edadc48 bind dialogs module 5 years ago
Jørgen Lien Sellæg 8e6eaec757 wrap with string to ensure windows support 5 years ago
Jørgen Lien Sellæg 6ae2d94dd1 partially bind lldb debugger 5 years ago
Jørgen Lien Sellæg 3879f9b0d8 bind ctags class 5 years ago
Jørgen Lien Sellæg ee8226d224 small cleanup 5 years ago
Jørgen Lien Sellæg e55c075981 bind compile commands class 5 years ago
Jørgen Lien Sellæg 4763f50e25 bind cmake class 5 years ago
Jørgen Lien Sellæg bcd3d0eb96 bind config classes 5 years ago
Jørgen Lien Sellæg 22bf01be44 move plugin implementation to own file 5 years ago
Jørgen Lien Sellæg 1244f1a32f fix python home and path on windows 5 years ago
Jørgen Lien Sellæg 0be5d31d13 add test for terminal bindings 5 years ago
Jørgen Lien Sellæg 63c822c887 bind terminal and add type caster for filesystem path 5 years ago
Jørgen Lien Sellæg d7b60adc62 find and include python plugin dependencies 5 years ago
  1. 2
      .appveyor.yml
  2. 2
      .gitlab-ci.yml
  3. 3
      .gitmodules
  4. 94
      CMakeLists.txt
  5. 3
      LICENSE
  6. 11
      README.md
  7. 4
      docs/custom_styling.md
  8. 50
      docs/install.md
  9. 57
      docs/language_servers.md
  10. 14
      docs/plugins.md
  11. 234
      jucipp.yaml
  12. 84
      lib/json/.clang-format
  13. 21
      lib/json/LICENSE.MIT
  14. 73
      lib/json/include/nlohmann/adl_serializer.hpp
  15. 166
      lib/json/include/nlohmann/byte_container_with_subtype.hpp
  16. 453
      lib/json/include/nlohmann/detail/conversions/from_json.hpp
  17. 1103
      lib/json/include/nlohmann/detail/conversions/to_chars.hpp
  18. 382
      lib/json/include/nlohmann/detail/conversions/to_json.hpp
  19. 421
      lib/json/include/nlohmann/detail/exceptions.hpp
  20. 121
      lib/json/include/nlohmann/detail/hash.hpp
  21. 2461
      lib/json/include/nlohmann/detail/input/binary_reader.hpp
  22. 476
      lib/json/include/nlohmann/detail/input/input_adapters.hpp
  23. 711
      lib/json/include/nlohmann/detail/input/json_sax.hpp
  24. 1623
      lib/json/include/nlohmann/detail/input/lexer.hpp
  25. 492
      lib/json/include/nlohmann/detail/input/parser.hpp
  26. 27
      lib/json/include/nlohmann/detail/input/position_t.hpp
  27. 25
      lib/json/include/nlohmann/detail/iterators/internal_iterator.hpp
  28. 646
      lib/json/include/nlohmann/detail/iterators/iter_impl.hpp
  29. 181
      lib/json/include/nlohmann/detail/iterators/iteration_proxy.hpp
  30. 51
      lib/json/include/nlohmann/detail/iterators/iterator_traits.hpp
  31. 119
      lib/json/include/nlohmann/detail/iterators/json_reverse_iterator.hpp
  32. 123
      lib/json/include/nlohmann/detail/iterators/primitive_iterator.hpp
  33. 934
      lib/json/include/nlohmann/detail/json_pointer.hpp
  34. 68
      lib/json/include/nlohmann/detail/json_ref.hpp
  35. 302
      lib/json/include/nlohmann/detail/macro_scope.hpp
  36. 23
      lib/json/include/nlohmann/detail/macro_unscope.hpp
  37. 154
      lib/json/include/nlohmann/detail/meta/cpp_future.hpp
  38. 58
      lib/json/include/nlohmann/detail/meta/detected.hpp
  39. 10
      lib/json/include/nlohmann/detail/meta/identity_tag.hpp
  40. 149
      lib/json/include/nlohmann/detail/meta/is_sax.hpp
  41. 436
      lib/json/include/nlohmann/detail/meta/type_traits.hpp
  42. 13
      lib/json/include/nlohmann/detail/meta/void_t.hpp
  43. 1594
      lib/json/include/nlohmann/detail/output/binary_writer.hpp
  44. 129
      lib/json/include/nlohmann/detail/output/output_adapters.hpp
  45. 954
      lib/json/include/nlohmann/detail/output/serializer.hpp
  46. 63
      lib/json/include/nlohmann/detail/string_escape.hpp
  47. 81
      lib/json/include/nlohmann/detail/value_t.hpp
  48. 8942
      lib/json/include/nlohmann/json.hpp
  49. 78
      lib/json/include/nlohmann/json_fwd.hpp
  50. 190
      lib/json/include/nlohmann/ordered_map.hpp
  51. 2044
      lib/json/include/nlohmann/thirdparty/hedley/hedley.hpp
  52. 150
      lib/json/include/nlohmann/thirdparty/hedley/hedley_undef.hpp
  53. 2
      lib/libclangmm
  54. 1
      lib/pybind11
  55. 2
      lib/tiny-process-library
  56. 26
      share/set_icon.sh
  57. 4
      share/set_icon_macos.py
  58. 27
      src/CMakeLists.txt
  59. 54
      src/autocomplete.cpp
  60. 12
      src/autocomplete.hpp
  61. 116
      src/cmake.cpp
  62. 10
      src/cmake.hpp
  63. 20
      src/commands.cpp
  64. 1
      src/commands.hpp
  65. 295
      src/compile_commands.cpp
  66. 20
      src/compile_commands.hpp
  67. 758
      src/config.cpp
  68. 36
      src/config.hpp
  69. 116
      src/config_module.cc
  70. 68
      src/ctags.cpp
  71. 2
      src/ctags.hpp
  72. 113
      src/debug_lldb.cpp
  73. 9
      src/debug_lldb.hpp
  74. 52
      src/dialog.cpp
  75. 2
      src/dialog.hpp
  76. 301
      src/directories.cpp
  77. 3
      src/directories.hpp
  78. 9
      src/dispatcher.cpp
  79. 3
      src/dispatcher.hpp
  80. 5
      src/documentation.cpp
  81. 2
      src/documentation.hpp
  82. 21
      src/entrybox.cpp
  83. 5
      src/entrybox.hpp
  84. 55
      src/files.hpp
  85. 159
      src/filesystem.cpp
  86. 37
      src/filesystem.hpp
  87. 65
      src/git.cpp
  88. 6
      src/git.hpp
  89. 9
      src/info.cpp
  90. 497
      src/json.cpp
  91. 128
      src/json.hpp
  92. 30
      src/juci.cpp
  93. 9
      src/juci.hpp
  94. 151
      src/menu.cpp
  95. 3
      src/menu.hpp
  96. 26
      src/meson.cpp
  97. 214
      src/notebook.cpp
  98. 3
      src/notebook.hpp
  99. 79
      src/plugins.cc
  100. 18
      src/plugins.h
  101. Some files were not shown because too many files have changed in this diff Show More

2
.appveyor.yml

@ -12,7 +12,7 @@ cache:
before_build:
- git submodule update --init --recursive
# - C:\msys64\usr\bin\bash -lc "pacman --noconfirm -Syyuu"
- C:\msys64\usr\bin\bash -lc "pacman --noconfirm --needed -S make mingw-w64-x86_64-{cmake,toolchain,clang,gtkmm3,gtksourceviewmm3,boost,aspell,aspell-en,libgit2,universal-ctags-git,libffi}"
- C:\msys64\usr\bin\bash -lc "pacman --noconfirm --needed -S make mingw-w64-x86_64-{cmake,toolchain,clang,gtkmm3,gtksourceviewmm3,boost,aspell,aspell-en,libgit2,universal-ctags-git,libffi,pygobject-devel}"
build_script:
- C:\msys64\usr\bin\bash -lc "cd $APPVEYOR_BUILD_FOLDER && mkdir build && cd build && cmake -G\"MSYS Makefiles\" -DCMAKE_INSTALL_PREFIX=/mingw64 -DBUILD_TESTING=1 .. && make -j$(nproc) && make test"

2
.gitlab-ci.yml

@ -10,7 +10,7 @@ stages:
stage: test
script:
- mkdir build && cd build
- CXXFLAGS=-Werror cmake -DBUILD_TESTING=1 ..
- CXXFLAGS="-D_GLIBCXX_ASSERTIONS" cmake -DBUILD_TESTING=1 ..
- make -j$(nproc)
- broadwayd & CTEST_OUTPUT_ON_FAILURE=1 make test

3
.gitmodules vendored

@ -4,3 +4,6 @@
[submodule "lib/libclangmm"]
path = lib/libclangmm
url = https://gitlab.com/cppit/libclangmm
[submodule "lib/pybind11"]
path = lib/pybind11
url = https://github.com/pybind/pybind11

94
CMakeLists.txt

@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.1)
cmake_minimum_required(VERSION 3.10)
project(juci)
set(JUCI_VERSION "1.6.3.1")
set(JUCI_VERSION "1.8.1")
set(CPACK_PACKAGE_NAME "jucipp")
set(CPACK_PACKAGE_CONTACT "Ole Christian Eidheim <eidheim@gmail.com>")
@ -28,7 +28,7 @@ include(CPack)
set(CMAKE_CXX_STANDARD 14)
add_compile_options(-pthread -Wall -Wextra -Wno-unused-parameter -Wno-deprecated-declarations)
add_compile_options(-pthread -Wall -Wextra -Wno-unused-parameter -Wno-deprecated-declarations -fvisibility=hidden)
add_definitions(-DJUCI_VERSION="${JUCI_VERSION}")
if(CMAKE_BUILD_TYPE STREQUAL "")
add_compile_options(-O3)
@ -39,7 +39,7 @@ if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
add_compile_options(-Wno-cpp)
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options("-Wno-#warnings")
add_compile_options(-Wthread-safety -Wno-deprecated)
add_compile_options(-Wthread-safety -Wno-deprecated -Wstring-conversion -Wliteral-conversion)
endif()
if(APPLE)
@ -59,23 +59,93 @@ if(APPLE)
include_directories(/usr/local/opt/gettext/include)
set(CMAKE_MACOSX_RPATH 1)
set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/usr/local/lib/pkgconfig:/opt/X11/lib/pkgconfig:/usr/local/opt/libffi/lib/pkgconfig:/usr/local/opt/libsigc++@2/lib/pkgconfig:/usr/local/opt/zlib/lib/pkgconfig:/usr/local/opt/libxml2/lib/pkgconfig")
set(ENV{PKG_CONFIG_PATH} "/opt/homebrew/opt/cairo/lib/pkgconfig:/opt/homebrew/opt/fontconfig/lib/pkgconfig:/usr/local/opt/fontconfig/lib/pkgconfig:$ENV{PKG_CONFIG_PATH}:/usr/local/lib/pkgconfig:/opt/X11/lib/pkgconfig:/usr/local/opt/libffi/lib/pkgconfig:/usr/local/opt/libsigc++@2/lib/pkgconfig:/usr/local/opt/zlib/lib/pkgconfig:/usr/local/opt/libxml2/lib/pkgconfig")
endif()
if(${CMAKE_SYSTEM_NAME} MATCHES FreeBSD)
link_directories(/usr/local/lib)
link_directories(${CMAKE_INSTALL_PREFIX}/lib)
endif()
if(${CMAKE_SYSTEM_NAME} MATCHES NetBSD)
link_directories(/usr/pkg/lib)
link_directories(${CMAKE_INSTALL_PREFIX}/lib)
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
endif()
option(BUILD_TESTING "Build tests")
option(BUILD_FUZZING "Build tests")
option(LIBCLANG_PATH "Use custom path for libclang")
option(LIBLLDB_PATH "Use custom path for liblldb")
option(FLATPAK_SANDBOX "Runs from within a flatpak sandbox")
if(${CMAKE_VERSION} VERSION_GREATER 3.11.4)
if (${CMAKE_HOST_WIN32})
if("$ENV{APPVEYOR}" STREQUAL "True")
set(MINGW_PATH "C:/msys64/mingw64")
else()
set(MINGW_PATH "$ENV{MSYSTEM_PREFIX}")
endif()
set(MINGW_PYTHON_VERSION 3.7)
set(MINGW_LIBRARY_DIR "${MINGW_PATH}/lib")
set(MINGW_INCLUDE_DIR "${MINGW_PATH}/include")
find_file(Python3_LIBRARIES "libpython${MINGW_PYTHON_VERSION}m.dll.a" HINTS ${MINGW_LIBRARY_DIR} NO_DEFAULT_PATH)
find_path(Python3_INCLUDE_DIRS "Python.h" HINTS "${MINGW_INCLUDE_DIR}/python${MINGW_PYTHON_VERSION}m" NO_DEFAULT_PATH)
if (EXISTS ${Python3_LIBRARIES} AND EXISTS ${Python3_INCLUDE_DIRS})
set(Python3_FOUND True)
set(Python3_LIBRARY_DIRS ${MINGW_LIBRARY_DIR})
set(Python3_VERSION_MAJOR 3)
set(Python3_VERSION_MINOR 7)
endif()
else()
find_package(Python3 COMPONENTS Development)
endif()
else()
find_package(PythonLibs 3)
message(${PYTHONLIBS_VERSION_STRING})
endif()
if(${Python3_FOUND})
if(${CMAKE_HOST_WIN32})
set(PYTHON_MODULE_EXTENSION True)
endif()
set(PYTHONLIBS_FOUND ${Python3_FOUND})
set(PYTHON_LIBRARIES ${Python3_LIBRARIES})
set(PYTHON_INCLUDE_DIRS ${Python3_INCLUDE_DIRS})
set(PYTHON_LIBRARY_DIR ${Python3_LIBRARY_DIRS}/python${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR})
endif()
include(FindPkgConfig)
set(PYGOBJECT_FOUND False)
if(${PYTHONLIBS_FOUND})
add_subdirectory(lib/pybind11)
pkg_check_modules(PYGOBJECT pygobject-3.0)
if("${PYGOBJECT_FOUND}" STREQUAL "")
set(PYGOBJECT_FOUND False)
endif()
endif()
if(${PYGOBJECT_FOUND})
add_definitions(-DPYTHON_HOME_DIR=L"${PYTHON_LIBRARY_DIR}")
endif()
set(BUILD_TESTING_SAVED ${BUILD_TESTING})
set(BUILD_TESTING OFF CACHE BOOL "Disable sub-project tests" FORCE)
set(BUILD_TESTING ${BUILD_TESTING_SAVED} CACHE BOOL "Set to previous value" FORCE)
if(POLICY CMP0167)
cmake_policy(SET CMP0167 NEW)
endif()
find_package(Boost 1.54 COMPONENTS REQUIRED filesystem serialization)
find_package(ASPELL REQUIRED)
include(FindPkgConfig)
pkg_check_modules(GTKMM gtkmm-3.0 REQUIRED)
pkg_check_modules(GTKSVMM gtksourceviewmm-3.0 REQUIRED)
pkg_check_modules(GTKSVMM gtksourceviewmm-3.0)
if(NOT GTKSVMM_FOUND)
pkg_check_modules(GTKSVMM gtksourceviewmm-4.0 REQUIRED)
endif()
pkg_check_modules(LIBGIT2 libgit2 REQUIRED)
include(FindPackageHandleStandardArgs)
@ -93,6 +163,8 @@ else()
find_package(LibClang REQUIRED)
endif()
add_definitions(-DLIBCLANG_LIBRARY_DIR="${LIBCLANG_LIBRARY_DIR}")
set(BUILD_TESTING_SAVED ${BUILD_TESTING})
set(BUILD_TESTING OFF CACHE BOOL "Disable sub-project tests" FORCE)
add_subdirectory(lib/tiny-process-library)
@ -122,6 +194,10 @@ else()
message("liblldb not found. Building juCi++ without debugging support")
endif()
if(FLATPAK_SANDBOX)
add_definitions(-DJUCI_FLATPAK_SANDBOX)
endif()
if(CMAKE_SYSTEM_NAME MATCHES .*BSD|DragonFly)
add_definitions(-DJUCI_USE_UCTAGS) # See https://svnweb.freebsd.org/ports?view=revision&revision=452957
add_definitions(-DJUCI_USE_GREP_EXCLUDE) # --exclude-dir is not an argument in bsd grep
@ -135,6 +211,10 @@ include_directories(
${LIBCLANG_INCLUDE_DIRS}
${ASPELL_INCLUDE_DIR}
${LIBGIT2_INCLUDE_DIRS}
${PYTHON_INCLUDE_DIRS}
${PYGOBJECT_INCLUDE_DIRS}
${CMAKE_SOURCE_DIR}/lib/pybind11/include
${CMAKE_SOURCE_DIR}/lib/json/include
)
add_subdirectory("src")

3
LICENSE

@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2015-2020 cppit
Copyright (c) 2015-2024 cppit
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@ -19,4 +19,3 @@ 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.

11
README.md

@ -4,9 +4,10 @@
In 2015, juCi++ was one of the first IDEs to utilize libclang for improved C/C++ tooling. The
integrated C/C++ support has since then improved steadily, and support for other languages has been
made possible through the language server protocol. The main goals of juCi++ is effective resource
usage, stability, and ease of use. Instead of relying on 3rd party addons, features expected in an
IDE is instead integrated directly into juCi++.
made possible through the language server protocol (see
[setup of tested language servers](docs/language_servers.md)). The main goals of juCi++ is effective
resource usage, stability, and ease of use. Instead of relying on 3rd party addons, features
expected in an IDE is instead integrated directly into juCi++.
For effective development, juCi++ is primarily written for Unix/Linux systems. However, Windows
users can use juCi++ through POSIX compatibility layers such as MSYS2.
@ -38,7 +39,7 @@ See [installation guide](docs/install.md).
- Non-C/C++ files are supported through the Language Server Protocol, which is enabled if an
`[language identifier]-language-server` executable is found. This executable can be a symbolic
link to one of your installed language server binaries.
- For additional instructions, see: [setup of tested language servers](docs/language_servers.md)
- For additional instructions, see [setup of tested language servers](docs/language_servers.md)
- Non-C/C++ projects are also supported, such as JavaScript, Python, Rust, and Go projects
- Git support through libgit2
- Find symbol through Ctags ([Universal Ctags](https://github.com/universal-ctags/ctags) is
@ -98,6 +99,8 @@ See [custom styling](docs/custom_styling.md).
need to install)
- [tiny-process-library](http://gitlab.com/eidheim/tiny-process-library/) (downloaded directly with
git --recursive, no need to install)
- [JSON for Modern C++](https://github.com/nlohmann/json) (included in repository, no need to
install)
## Documentation

4
docs/custom_styling.md

@ -38,6 +38,10 @@ was made with the following ~/.config/gtk-3.0/gtk.css:
background: transparent;
}
.juci_notebook {
background: transparent;
}
.juci_notebook :not(slider) {
background-color: rgba(255, 255, 255, 0.8);
}

50
docs/install.md

@ -6,9 +6,11 @@
- [Arch Linux/Manjaro Linux](#arch-linuxmanjaro-linux)
- [Fedora](#fedora)
- [Mageia](#mageia)
- [OpenSUSE Tumbleweed](#opensuse-tumbleweed)
- [OpenSuSE Tumbleweed](#opensuse-tumbleweed)
- [OpenSuSE Leap](#opensuse-leap)
- [GNU Guix/GuixSD](#gnu-guixguixsd)
- [FreeBSD](#freebsd)
- [NetBSD](#netbsd)
- MacOS
- [Homebrew](#macos-with-homebrew-httpbrewsh)
- Windows
@ -103,7 +105,7 @@ make
sudo make install
```
## OpenSUSE Tumbleweed
## OpenSuSE Tumbleweed
Install dependencies:
@ -122,6 +124,25 @@ make
sudo make install
```
## OpenSuSE Leap
Install dependencies:
```sh
sudo zypper install git-core cmake gcc-c++ boost-devel libboost_filesystem-devel libboost_serialization-devel clang-devel lldb-devel lldb gtksourceviewmm3_0-devel aspell-devel aspell-en libgit2-devel ctags libboost_filesystem1_66_0 libboost_filesystem1_66_0-devel libboost_serialization1_66_0 libboost_serialization1_66_0-devel aspell gtksourceviewmm3_0-devel
```
Get juCi++ source, compile and install:
```sh
git clone --recursive https://gitlab.com/cppit/jucipp
mkdir jucipp/build
cd jucipp/build
cmake -DCMAKE_CXX_COMPILER=g++ ..
make
sudo make install
```
## GNU Guix/GuixSD
Simply install juCi++ from the official package definition
@ -134,13 +155,32 @@ guix install jucipp
On FreeBSD, latest release of juCi++ is available through the port: jucipp.
## NetBSD
Install dependencies:
```sh
pkgin -y install lldb ccache doxygen pkg-config aspell-en libgit2 gtksourceviewmm exctags boost
```
Get juCi++ source, compile and install:
```sh
git clone --recursive https://gitlab.com/cppit/jucipp
mkdir -p jucipp/build
cd jucipp/build
cmake -DCMAKE_INSTALL_PREFIX=/usr/pkg ..
make
su -m
make install
```
## MacOS with Homebrew (http://brew.sh/)
Install dependencies:
```sh
brew install cmake pkg-config boost gtksourceviewmm3 gnome-icon-theme aspell llvm clang-format libgit2 zlib libxml2
brew install --HEAD universal-ctags/universal-ctags/universal-ctags # Recommended Ctags package
brew install cmake pkg-config boost gtksourceviewmm3 gnome-icon-theme aspell llvm clang-format libgit2 zlib libxml2 universal-ctags
```
Mojave users might need to install headers:
@ -167,7 +207,7 @@ make install
Install dependencies (replace `x86_64` with `i686` for 32-bit MSYS2 installs):
```sh
pacman -S git mingw-w64-x86_64-cmake make mingw-w64-x86_64-toolchain mingw-w64-x86_64-clang mingw-w64-x86_64-gtkmm3 mingw-w64-x86_64-gtksourceviewmm3 mingw-w64-x86_64-boost mingw-w64-x86_64-aspell mingw-w64-x86_64-aspell-en mingw-w64-x86_64-libgit2 mingw-w64-x86_64-universal-ctags-git
pacman -S git make mingw-w64-x86_64-{cmake,toolchain,clang,gtkmm3,gtksourceviewmm3,boost,aspell,aspell-en,libgit2,universal-ctags-git,pygobject-devel}
```
Note that juCi++ must be built and run in a MinGW Shell (for instance MinGW-w64 Win64 Shell).

57
docs/language_servers.md

@ -6,6 +6,7 @@
- [Go](#go)
- [Julia](#julia)
- [GLSL](#glsl)
- [C/C++](#cc)
## JavaScript/TypeScript
@ -58,14 +59,17 @@ cp /usr/local/bin/javascript-language-server /usr/local/bin/typescriptreact-lang
- Prerequisites:
- Python3
- In juCi++ preferences, set `project.python_command` to `python3 -u`
### Python LSP Server
Install language server, and create symbolic link to enable server in juCi++:
```sh
pip3 install python-language-server[rope,pycodestyle,yapf]
pip3 install python-lsp-server[pycodestyle,yapf]
# Usually as root:
ln -s `which pyls` /usr/local/bin/python-language-server
ln -s `which pylsp` /usr/local/bin/python-language-server
```
- Additional setup within a Python project:
@ -73,22 +77,38 @@ ln -s `which pyls` /usr/local/bin/python-language-server
`printf '[pycodestyle]\nmax-line-length = 120\n\n[yapf]\nCOLUMN_LIMIT = 120\n' > setup.cfg`
- Add an empty `.python-format` file to enable style format on save
### Pyright
- Prerequisite:
- Node.js
- Recommended:
- [YAPF](https://github.com/google/yapf) for style format (`pip3 install yapf`)
Install language server, and create executable to enable server in juCi++:
```sh
npm install -g pyright
# Usually as root:
echo '#!/bin/sh
pyright-langserver --stdio' > /usr/local/bin/python-language-server
chmod 755 /usr/local/bin/python-language-server
```
- Additional setup within a Python project:
- Add Pyright configuration file: `echo '{}' > pyrightconfig.json`
- Add style format file to enable format on save, for instance:
`printf '[style]\nCOLUMN_LIMIT = 120\n' > .style.yapf`
## Rust
- Prerequisites:
- [Rust](https://www.rust-lang.org/tools/install)
Install language server, and create symbolic link to enable server in juCi++:
Install language server:
```sh
rustup component add rust-src
git clone https://github.com/rust-analyzer/rust-analyzer
cd rust-analyzer
cargo xtask install --server
# Usually as root:
ln -s ~/.cargo/bin/rust-analyzer /usr/local/bin/rust-language-server
rustup component add rust-analyzer
```
- Additional setup within a Rust project:
@ -156,3 +176,18 @@ echo '#!/bin/sh
/usr/local/bin/glslls --stdin' > /usr/local/bin/glsl-language-server
chmod 755 /usr/local/bin/glsl-language-server
```
## C/C++
**Note that we recommend using the builtin C/C++ support in juCi++ instead.**
- Prerequisites:
- A language server installed on your system, for example clangd or ccls
Create symbolic link to enable language server in juCi++. For example, if you have the clangd
installed and available on path:
```sh
# Usually as root:
ln -s `which clangd` /usr/local/bin/clang-language-server
```

14
docs/plugins.md

@ -0,0 +1,14 @@
# Plugins
## Getting started
Plugins are stored in .juci/plugins
### Basic hello world
from Jucipp import Terminal
print("Hello, world! From before the application is loaded")
def init_hook():
t = Terminal()
t.print("Hello, world! From after the application is started.\n")

234
jucipp.yaml

@ -0,0 +1,234 @@
id: com.gitlab.cppit.jucipp
command: juci
# Install via: flatpak install flathub org.gnome.Platform//42 org.gnome.Sdk//42
runtime: org.gnome.Platform
runtime-version: '42'
sdk: org.gnome.Sdk
sdk-extensions:
# Install via: flatpak install flathub org.freedesktop.Sdk.Extension.llvm14
- org.freedesktop.Sdk.Extension.llvm14
rename-desktop-file: juci.desktop
rename-icon: juci
finish-args:
- --socket=x11
- --share=ipc # somewhat required by X11
- --socket=wayland
- --share=network # Unix sockets are e.g. used by X11
- --filesystem=host:rw # access all files on the host
- --filesystem=home:rw # access all files on the host
- --talk-name=org.freedesktop.Flatpak # allows to run processes outside of flatpak sandbox
- --persist=.juci # persist configuration
modules:
- name: boost
buildsystem: simple
sources:
- type: archive
url: https://boostorg.jfrog.io/artifactory/main/release/1.79.0/source/boost_1_79_0.tar.bz2
sha256: 475d589d51a7f8b3ba2ba4eda022b170e562ca3b760ee922c146b6c65856ef39
build-commands:
- ./bootstrap.sh --prefix="${FLATPAK_DEST}" --with-libraries=filesystem,serialization
- ./b2 -j $FLATPAK_BUILDER_N_JOBS install
cleanup:
- /include
- '/lib/libboost_*.a'
- /lib/cmake
- /lib/debug
- /lib/pkgconfig
- name: mm-common # build-dependency of gtkmm
sources:
- type: archive
url: https://download.gnome.org/sources/mm-common/1.0/mm-common-1.0.4.tar.xz
sha256: e954c09b4309a7ef93e13b69260acdc5738c907477eb381b78bb1e414ee6dbd8
cleanup:
- '*'
- name: sigc++-2 # dependency of gtkmm
buildsystem: autotools
config-opts:
- --disable-documentation
sources:
- type: archive
url: https://download.gnome.org/sources/libsigc%2B%2B/2.10/libsigc%2B%2B-2.10.8.tar.xz
sha256: 235a40bec7346c7b82b6a8caae0456353dc06e71f14bc414bcc858af1838719a
cleanup:
- /include
- /lib/debug
- /lib/sigc++-2.0
- /lib/pkgconfig
- /lib/libsigc-2.0.la
- name: glibmm # dependency of gtkmm
buildsystem: meson
sources:
- type: archive
url: https://download.gnome.org/sources/glibmm/2.66/glibmm-2.66.2.tar.xz
sha256: b2a4cd7b9ae987794cbb5a1becc10cecb65182b9bb841868625d6bbb123edb1d
cleanup:
- /include
- /lib/debug
- /lib/giomm-2.4
- /lib/glibmm-2.4
- /lib/pkgconfig
- name: cairomm # dependency of gtkmm
buildsystem: autotools
config-opts:
- --disable-documentation
sources:
- type: archive
url: https://www.cairographics.org/releases/cairomm-1.14.3.tar.xz
sha256: 0d37e067c5c4ca7808b7ceddabfe1932c5bd2a750ad64fb321e1213536297e78
cleanup:
- /include
- /lib/cairomm-1.0
- /lib/debug
- /lib/pkgconfig
- /lib/libcairomm-1.0.la
- name: pangomm # dependency of gtkmm
buildsystem: meson
sources:
- type: archive
url: https://download.gnome.org/sources/pangomm/2.46/pangomm-2.46.2.tar.xz
sha256: 57442ab4dc043877bfe3839915731ab2d693fc6634a71614422fb530c9eaa6f4
cleanup:
- /include
- /lib/debug
- /lib/pangomm-1.4
- /lib/pkgconfig
- name: atkmm # dependency of gtkmm
buildsystem: meson
sources:
- type: archive
url: https://download.gnome.org/sources/atkmm/2.28/atkmm-2.28.2.tar.xz
sha256: a0bb49765ceccc293ab2c6735ba100431807d384ffa14c2ebd30e07993fd2fa4
cleanup:
- /include
- /lib/atkmm-1.6
- /lib/debug
- /lib/pkgconfig
- name: gtkmm
buildsystem: meson
sources:
- type: archive
url: https://download.gnome.org/sources/gtkmm/3.24/gtkmm-3.24.5.tar.xz
sha256: 856333de86689f6a81c123f2db15d85db9addc438bc3574c36f15736aeae22e6
cleanup:
- /include
- /lib/debug
- /lib/gdkmm-3.0
- /lib/gtkmm-3.0
- /lib/pkgconfig
- name: gtksourceview # dependency of gtksourceviewmm
buildsystem: autotools
config-opts:
- --disable-documentation
sources:
- type: archive
url: https://download.gnome.org/sources/gtksourceview/3.24/gtksourceview-3.24.11.tar.xz
sha256: 691b074a37b2a307f7f48edc5b8c7afa7301709be56378ccf9cc9735909077fd
cleanup:
- /include
- /lib/debug
- /lib/pkgconfig
- /lib/libgtksourceview-3.0.la
- /share/gtk-doc
- /share/vala
- name: gtksourceviewmm
buildsystem: autotools
config-opts:
- --disable-documentation
sources:
- type: archive
url: https://download.gnome.org/sources/gtksourceviewmm/3.21/gtksourceviewmm-3.21.3.tar.xz
sha256: dbb00b1c28e0407cc27d8b07a2ed0b4ea22f92e4b3e3006431cbd6726b6256b5
cleanup:
- /include
- /lib/debug
- /lib/gtksourceviewmm-3.0
- /lib/pkgconfig
- /lib/libgtksourceviewmm-3.0.la
- name: ncurses # build-depencendy of aspell (required to build aspell executable)
buildsystem: autotools
sources:
- type: archive
url: https://ftp.gnu.org/gnu/ncurses/ncurses-6.3.tar.gz
sha256: 97fc51ac2b085d4cde31ef4d2c3122c21abc217e9090a43a30fc5ec21684e059
cleanup:
- '*'
- name: aspell
buildsystem: autotools
sources:
- type: archive
url: https://ftp.gnu.org/gnu/aspell/aspell-0.60.8.tar.gz
sha256: f9b77e515334a751b2e60daab5db23499e26c9209f5e7b7443b05235ad0226f2
cleanup:
- /bin
- /include
- /lib/debug
- /lib/pkgconfig
- /lib/libaspell.la
- '/lib/libpspell.*'
- /share/info
- /share/man
- name: aspell-en
buildsystem: simple
sources:
- type: archive
url: https://ftp.gnu.org/gnu/aspell/dict/en/aspell6-en-2020.12.07-0.tar.bz2
sha256: 4c8f734a28a088b88bb6481fcf972d0b2c3dc8da944f7673283ce487eac49fb3
build-commands:
- ./configure
- make -j $FLATPAK_BUILDER_N_JOBS
- make install
- name: libgit2
buildsystem: cmake
config-opts:
- -DBUILD_TESTS=OFF
sources:
- type: archive
url: https://github.com/libgit2/libgit2/archive/refs/tags/v1.4.3.tar.gz
sha256: f48b961e463a9e4e7e7e58b21a0fb5a9b2a1d24d9ba4d15870a0c9b8ad965163
cleanup:
- /include
- /lib/debug
- /lib/pkgconfig
- name: jucipp
buildsystem: simple
build-options:
append-path: /usr/lib/sdk/llvm14/bin
prepend-ld-library-path: /usr/lib/sdk/llvm14/lib
sources:
- type: git
branch: master
url: https://gitlab.com/cppit/jucipp.git
build-commands:
- cp -r /usr/lib/sdk/llvm14/lib/* /app/lib/
- cp -r /usr/lib/sdk/llvm14/include/* /app/include/
- cmake -DCMAKE_INSTALL_PREFIX=${FLATPAK_DEST} -DFLATPAK_SANDBOX=ON .
- cmake --build . -- -j $FLATPAK_BUILDER_N_JOBS
- make install
cleanup:
- /include
- /lib/clang
- /lib/cmake
- /lib/debug
- /lib/libear
- /lib/libscanbuild
- /lib/python3.9
- '/lib/libLTO.*'
- '/lib/libRemarks.*'
- /lib/LLVMgold.so

84
lib/json/.clang-format

@ -0,0 +1,84 @@
#AccessModifierOffset: 2
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
#AlignConsecutiveBitFields: false
AlignConsecutiveDeclarations: false
AlignConsecutiveMacros: false
AlignEscapedNewlines: Right
#AlignOperands: AlignAfterOperator
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: false
AllowAllConstructorInitializersOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: Empty
AllowShortCaseLabelsOnASingleLine: false
#AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: Empty
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: false
BinPackParameters: false
#BitFieldColonSpacing: Both
BreakBeforeBraces: Custom # or Allman
BraceWrapping:
AfterCaseLabel: true
AfterClass: true
AfterControlStatement: Always
AfterEnum: true
AfterFunction: true
AfterNamespace: false
AfterStruct: true
AfterUnion: true
AfterExternBlock: false
BeforeCatch: true
BeforeElse: true
#BeforeLambdaBody: false
#BeforeWhile: false
SplitEmptyFunction: false
SplitEmptyRecord: false
SplitEmptyNamespace: false
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeComma
BreakStringLiterals: false
ColumnLimit: 0
CompactNamespaces: false
ConstructorInitializerIndentWidth: 2
Cpp11BracedListStyle: true
PointerAlignment: Left
FixNamespaceComments: true
IncludeBlocks: Preserve
#IndentCaseBlocks: false
IndentCaseLabels: true
IndentGotoLabels: false
IndentPPDirectives: BeforeHash
IndentWidth: 4
KeepEmptyLinesAtTheStartOfBlocks: false
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ReflowComments: false
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInConditionalStatement: false
SpacesInContainerLiterals: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: c++11
TabWidth: 4
UseTab: Never

21
lib/json/LICENSE.MIT

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2013-2021 Niels Lohmann
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.

73
lib/json/include/nlohmann/adl_serializer.hpp

@ -0,0 +1,73 @@
#pragma once
#include <type_traits>
#include <utility>
#include <nlohmann/detail/conversions/from_json.hpp>
#include <nlohmann/detail/conversions/to_json.hpp>
#include <nlohmann/detail/meta/identity_tag.hpp>
#include <nlohmann/detail/meta/type_traits.hpp>
namespace nlohmann
{
template<typename ValueType, typename>
struct adl_serializer
{
/*!
@brief convert a JSON value to any value type
This function is usually called by the `get()` function of the
@ref basic_json class (either explicit or via conversion operators).
@note This function is chosen for default-constructible value types.
@param[in] j JSON value to read from
@param[in,out] val value to write to
*/
template<typename BasicJsonType, typename TargetType = ValueType>
static auto from_json(BasicJsonType && j, TargetType& val) noexcept(
noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), val)))
-> decltype(::nlohmann::from_json(std::forward<BasicJsonType>(j), val), void())
{
::nlohmann::from_json(std::forward<BasicJsonType>(j), val);
}
/*!
@brief convert a JSON value to any value type
This function is usually called by the `get()` function of the
@ref basic_json class (either explicit or via conversion operators).
@note This function is chosen for value types which are not default-constructible.
@param[in] j JSON value to read from
@return copy of the JSON value, converted to @a ValueType
*/
template<typename BasicJsonType, typename TargetType = ValueType>
static auto from_json(BasicJsonType && j) noexcept(
noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {})))
-> decltype(::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {}))
{
return ::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {});
}
/*!
@brief convert any value type to a JSON value
This function is usually called by the constructors of the @ref basic_json
class.
@param[in,out] j JSON value to write to
@param[in] val value to read from
*/
template<typename BasicJsonType, typename TargetType = ValueType>
static auto to_json(BasicJsonType& j, TargetType && val) noexcept(
noexcept(::nlohmann::to_json(j, std::forward<TargetType>(val))))
-> decltype(::nlohmann::to_json(j, std::forward<TargetType>(val)), void())
{
::nlohmann::to_json(j, std::forward<TargetType>(val));
}
};
} // namespace nlohmann

166
lib/json/include/nlohmann/byte_container_with_subtype.hpp

@ -0,0 +1,166 @@
#pragma once
#include <cstdint> // uint8_t
#include <tuple> // tie
#include <utility> // move
namespace nlohmann
{
/*!
@brief an internal type for a backed binary type
This type extends the template parameter @a BinaryType provided to `basic_json`
with a subtype used by BSON and MessagePack. This type exists so that the user
does not have to specify a type themselves with a specific naming scheme in
order to override the binary type.
@tparam BinaryType container to store bytes (`std::vector<std::uint8_t>` by
default)
@since version 3.8.0
*/
template<typename BinaryType>
class byte_container_with_subtype : public BinaryType
{
public:
/// the type of the underlying container
using container_type = BinaryType;
byte_container_with_subtype() noexcept(noexcept(container_type()))
: container_type()
{}
byte_container_with_subtype(const container_type& b) noexcept(noexcept(container_type(b)))
: container_type(b)
{}
byte_container_with_subtype(container_type&& b) noexcept(noexcept(container_type(std::move(b))))
: container_type(std::move(b))
{}
byte_container_with_subtype(const container_type& b, std::uint8_t subtype_) noexcept(noexcept(container_type(b)))
: container_type(b)
, m_subtype(subtype_)
, m_has_subtype(true)
{}
byte_container_with_subtype(container_type&& b, std::uint8_t subtype_) noexcept(noexcept(container_type(std::move(b))))
: container_type(std::move(b))
, m_subtype(subtype_)
, m_has_subtype(true)
{}
bool operator==(const byte_container_with_subtype& rhs) const
{
return std::tie(static_cast<const BinaryType&>(*this), m_subtype, m_has_subtype) ==
std::tie(static_cast<const BinaryType&>(rhs), rhs.m_subtype, rhs.m_has_subtype);
}
bool operator!=(const byte_container_with_subtype& rhs) const
{
return !(rhs == *this);
}
/*!
@brief sets the binary subtype
Sets the binary subtype of the value, also flags a binary JSON value as
having a subtype, which has implications for serialization.
@complexity Constant.
@exceptionsafety No-throw guarantee: this member function never throws
exceptions.
@sa see @ref subtype() -- return the binary subtype
@sa see @ref clear_subtype() -- clears the binary subtype
@sa see @ref has_subtype() -- returns whether or not the binary value has a
subtype
@since version 3.8.0
*/
void set_subtype(std::uint8_t subtype_) noexcept
{
m_subtype = subtype_;
m_has_subtype = true;
}
/*!
@brief return the binary subtype
Returns the numerical subtype of the value if it has a subtype. If it does
not have a subtype, this function will return size_t(-1) as a sentinel
value.
@return the numerical subtype of the binary value
@complexity Constant.
@exceptionsafety No-throw guarantee: this member function never throws
exceptions.
@sa see @ref set_subtype() -- sets the binary subtype
@sa see @ref clear_subtype() -- clears the binary subtype
@sa see @ref has_subtype() -- returns whether or not the binary value has a
subtype
@since version 3.8.0
*/
constexpr std::uint8_t subtype() const noexcept
{
return m_subtype;
}
/*!
@brief return whether the value has a subtype
@return whether the value has a subtype
@complexity Constant.
@exceptionsafety No-throw guarantee: this member function never throws
exceptions.
@sa see @ref subtype() -- return the binary subtype
@sa see @ref set_subtype() -- sets the binary subtype
@sa see @ref clear_subtype() -- clears the binary subtype
@since version 3.8.0
*/
constexpr bool has_subtype() const noexcept
{
return m_has_subtype;
}
/*!
@brief clears the binary subtype
Clears the binary subtype and flags the value as not having a subtype, which
has implications for serialization; for instance MessagePack will prefer the
bin family over the ext family.
@complexity Constant.
@exceptionsafety No-throw guarantee: this member function never throws
exceptions.
@sa see @ref subtype() -- return the binary subtype
@sa see @ref set_subtype() -- sets the binary subtype
@sa see @ref has_subtype() -- returns whether or not the binary value has a
subtype
@since version 3.8.0
*/
void clear_subtype() noexcept
{
m_subtype = 0;
m_has_subtype = false;
}
private:
std::uint8_t m_subtype = 0;
bool m_has_subtype = false;
};
} // namespace nlohmann

453
lib/json/include/nlohmann/detail/conversions/from_json.hpp

@ -0,0 +1,453 @@
#pragma once
#include <algorithm> // transform
#include <array> // array
#include <forward_list> // forward_list
#include <iterator> // inserter, front_inserter, end
#include <map> // map
#include <string> // string
#include <tuple> // tuple, make_tuple
#include <type_traits> // is_arithmetic, is_same, is_enum, underlying_type, is_convertible
#include <unordered_map> // unordered_map
#include <utility> // pair, declval
#include <valarray> // valarray
#include <nlohmann/detail/exceptions.hpp>
#include <nlohmann/detail/macro_scope.hpp>
#include <nlohmann/detail/meta/cpp_future.hpp>
#include <nlohmann/detail/meta/identity_tag.hpp>
#include <nlohmann/detail/meta/type_traits.hpp>
#include <nlohmann/detail/value_t.hpp>
namespace nlohmann
{
namespace detail
{
template<typename BasicJsonType>
void from_json(const BasicJsonType& j, typename std::nullptr_t& n)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_null()))
{
JSON_THROW(type_error::create(302, "type must be null, but is " + std::string(j.type_name()), j));
}
n = nullptr;
}
// overloads for basic_json template parameters
template < typename BasicJsonType, typename ArithmeticType,
enable_if_t < std::is_arithmetic<ArithmeticType>::value&&
!std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
int > = 0 >
void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
{
switch (static_cast<value_t>(j))
{
case value_t::number_unsigned:
{
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
break;
}
case value_t::number_integer:
{
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
break;
}
case value_t::number_float:
{
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());
break;
}
default:
JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()), j));
}
}
template<typename BasicJsonType>
void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_boolean()))
{
JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name()), j));
}
b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();
}
template<typename BasicJsonType>
void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
{
JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j));
}
s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
}
template <
typename BasicJsonType, typename ConstructibleStringType,
enable_if_t <
is_constructible_string_type<BasicJsonType, ConstructibleStringType>::value&&
!std::is_same<typename BasicJsonType::string_t,
ConstructibleStringType>::value,
int > = 0 >
void from_json(const BasicJsonType& j, ConstructibleStringType& s)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
{
JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j));
}
s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
}
template<typename BasicJsonType>
void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val)
{
get_arithmetic_value(j, val);
}
template<typename BasicJsonType>
void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val)
{
get_arithmetic_value(j, val);
}
template<typename BasicJsonType>
void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val)
{
get_arithmetic_value(j, val);
}
template<typename BasicJsonType, typename EnumType,
enable_if_t<std::is_enum<EnumType>::value, int> = 0>
void from_json(const BasicJsonType& j, EnumType& e)
{
typename std::underlying_type<EnumType>::type val;
get_arithmetic_value(j, val);
e = static_cast<EnumType>(val);
}
// forward_list doesn't have an insert method
template<typename BasicJsonType, typename T, typename Allocator,
enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j));
}
l.clear();
std::transform(j.rbegin(), j.rend(),
std::front_inserter(l), [](const BasicJsonType & i)
{
return i.template get<T>();
});
}
// valarray doesn't have an insert method
template<typename BasicJsonType, typename T,
enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
void from_json(const BasicJsonType& j, std::valarray<T>& l)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j));
}
l.resize(j.size());
std::transform(j.begin(), j.end(), std::begin(l),
[](const BasicJsonType & elem)
{
return elem.template get<T>();
});
}
template<typename BasicJsonType, typename T, std::size_t N>
auto from_json(const BasicJsonType& j, T (&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
-> decltype(j.template get<T>(), void())
{
for (std::size_t i = 0; i < N; ++i)
{
arr[i] = j.at(i).template get<T>();
}
}
template<typename BasicJsonType>
void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/)
{
arr = *j.template get_ptr<const typename BasicJsonType::array_t*>();
}
template<typename BasicJsonType, typename T, std::size_t N>
auto from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr,
priority_tag<2> /*unused*/)
-> decltype(j.template get<T>(), void())
{
for (std::size_t i = 0; i < N; ++i)
{
arr[i] = j.at(i).template get<T>();
}
}
template<typename BasicJsonType, typename ConstructibleArrayType,
enable_if_t<
std::is_assignable<ConstructibleArrayType&, ConstructibleArrayType>::value,
int> = 0>
auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/)
-> decltype(
arr.reserve(std::declval<typename ConstructibleArrayType::size_type>()),
j.template get<typename ConstructibleArrayType::value_type>(),
void())
{
using std::end;
ConstructibleArrayType ret;
ret.reserve(j.size());
std::transform(j.begin(), j.end(),
std::inserter(ret, end(ret)), [](const BasicJsonType & i)
{
// get<BasicJsonType>() returns *this, this won't call a from_json
// method when value_type is BasicJsonType
return i.template get<typename ConstructibleArrayType::value_type>();
});
arr = std::move(ret);
}
template<typename BasicJsonType, typename ConstructibleArrayType,
enable_if_t<
std::is_assignable<ConstructibleArrayType&, ConstructibleArrayType>::value,
int> = 0>
void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr,
priority_tag<0> /*unused*/)
{
using std::end;
ConstructibleArrayType ret;
std::transform(
j.begin(), j.end(), std::inserter(ret, end(ret)),
[](const BasicJsonType & i)
{
// get<BasicJsonType>() returns *this, this won't call a from_json
// method when value_type is BasicJsonType
return i.template get<typename ConstructibleArrayType::value_type>();
});
arr = std::move(ret);
}
template < typename BasicJsonType, typename ConstructibleArrayType,
enable_if_t <
is_constructible_array_type<BasicJsonType, ConstructibleArrayType>::value&&
!is_constructible_object_type<BasicJsonType, ConstructibleArrayType>::value&&
!is_constructible_string_type<BasicJsonType, ConstructibleArrayType>::value&&
!std::is_same<ConstructibleArrayType, typename BasicJsonType::binary_t>::value&&
!is_basic_json<ConstructibleArrayType>::value,
int > = 0 >
auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr)
-> decltype(from_json_array_impl(j, arr, priority_tag<3> {}),
j.template get<typename ConstructibleArrayType::value_type>(),
void())
{
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j));
}
from_json_array_impl(j, arr, priority_tag<3> {});
}
template < typename BasicJsonType, typename T, std::size_t... Idx >
std::array<T, sizeof...(Idx)> from_json_inplace_array_impl(BasicJsonType&& j,
identity_tag<std::array<T, sizeof...(Idx)>> /*unused*/, index_sequence<Idx...> /*unused*/)
{
return { { std::forward<BasicJsonType>(j).at(Idx).template get<T>()... } };
}
template < typename BasicJsonType, typename T, std::size_t N >
auto from_json(BasicJsonType&& j, identity_tag<std::array<T, N>> tag)
-> decltype(from_json_inplace_array_impl(std::forward<BasicJsonType>(j), tag, make_index_sequence<N> {}))
{
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j));
}
return from_json_inplace_array_impl(std::forward<BasicJsonType>(j), tag, make_index_sequence<N> {});
}
template<typename BasicJsonType>
void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_binary()))
{
JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(j.type_name()), j));
}
bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>();
}
template<typename BasicJsonType, typename ConstructibleObjectType,
enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>
void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_object()))
{
JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name()), j));
}
ConstructibleObjectType ret;
const auto* inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
using value_type = typename ConstructibleObjectType::value_type;
std::transform(
inner_object->begin(), inner_object->end(),
std::inserter(ret, ret.begin()),
[](typename BasicJsonType::object_t::value_type const & p)
{
return value_type(p.first, p.second.template get<typename ConstructibleObjectType::mapped_type>());
});
obj = std::move(ret);
}
// overload for arithmetic types, not chosen for basic_json template arguments
// (BooleanType, etc..); note: Is it really necessary to provide explicit
// overloads for boolean_t etc. in case of a custom BooleanType which is not
// an arithmetic type?
template < typename BasicJsonType, typename ArithmeticType,
enable_if_t <
std::is_arithmetic<ArithmeticType>::value&&
!std::is_same<ArithmeticType, typename BasicJsonType::number_unsigned_t>::value&&
!std::is_same<ArithmeticType, typename BasicJsonType::number_integer_t>::value&&
!std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value&&
!std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
int > = 0 >
void from_json(const BasicJsonType& j, ArithmeticType& val)
{
switch (static_cast<value_t>(j))
{
case value_t::number_unsigned:
{
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
break;
}
case value_t::number_integer:
{
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
break;
}
case value_t::number_float:
{
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());
break;
}
case value_t::boolean:
{
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::boolean_t*>());
break;
}
default:
JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()), j));
}
}
template<typename BasicJsonType, typename... Args, std::size_t... Idx>
std::tuple<Args...> from_json_tuple_impl_base(BasicJsonType&& j, index_sequence<Idx...> /*unused*/)
{
return std::make_tuple(std::forward<BasicJsonType>(j).at(Idx).template get<Args>()...);
}
template < typename BasicJsonType, class A1, class A2 >
std::pair<A1, A2> from_json_tuple_impl(BasicJsonType&& j, identity_tag<std::pair<A1, A2>> /*unused*/, priority_tag<0> /*unused*/)
{
return {std::forward<BasicJsonType>(j).at(0).template get<A1>(),
std::forward<BasicJsonType>(j).at(1).template get<A2>()};
}
template<typename BasicJsonType, typename A1, typename A2>
void from_json_tuple_impl(BasicJsonType&& j, std::pair<A1, A2>& p, priority_tag<1> /*unused*/)
{
p = from_json_tuple_impl(std::forward<BasicJsonType>(j), identity_tag<std::pair<A1, A2>> {}, priority_tag<0> {});
}
template<typename BasicJsonType, typename... Args>
std::tuple<Args...> from_json_tuple_impl(BasicJsonType&& j, identity_tag<std::tuple<Args...>> /*unused*/, priority_tag<2> /*unused*/)
{
return from_json_tuple_impl_base<BasicJsonType, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});
}
template<typename BasicJsonType, typename... Args>
void from_json_tuple_impl(BasicJsonType&& j, std::tuple<Args...>& t, priority_tag<3> /*unused*/)
{
t = from_json_tuple_impl_base<BasicJsonType, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});
}
template<typename BasicJsonType, typename TupleRelated>
auto from_json(BasicJsonType&& j, TupleRelated&& t)
-> decltype(from_json_tuple_impl(std::forward<BasicJsonType>(j), std::forward<TupleRelated>(t), priority_tag<3> {}))
{
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j));
}
return from_json_tuple_impl(std::forward<BasicJsonType>(j), std::forward<TupleRelated>(t), priority_tag<3> {});
}
template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator,
typename = enable_if_t < !std::is_constructible <
typename BasicJsonType::string_t, Key >::value >>
void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>& m)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j));
}
m.clear();
for (const auto& p : j)
{
if (JSON_HEDLEY_UNLIKELY(!p.is_array()))
{
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()), j));
}
m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());
}
}
template < typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator,
typename = enable_if_t < !std::is_constructible <
typename BasicJsonType::string_t, Key >::value >>
void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& m)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j));
}
m.clear();
for (const auto& p : j)
{
if (JSON_HEDLEY_UNLIKELY(!p.is_array()))
{
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()), j));
}
m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());
}
}
struct from_json_fn
{
template<typename BasicJsonType, typename T>
auto operator()(const BasicJsonType& j, T&& val) const
noexcept(noexcept(from_json(j, std::forward<T>(val))))
-> decltype(from_json(j, std::forward<T>(val)))
{
return from_json(j, std::forward<T>(val));
}
};
} // namespace detail
/// namespace to hold default `from_json` function
/// to see why this is required:
/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html
namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces)
{
constexpr const auto& from_json = detail::static_const<detail::from_json_fn>::value; // NOLINT(misc-definitions-in-headers)
} // namespace
} // namespace nlohmann

1103
lib/json/include/nlohmann/detail/conversions/to_chars.hpp

File diff suppressed because it is too large Load Diff

382
lib/json/include/nlohmann/detail/conversions/to_json.hpp

@ -0,0 +1,382 @@
#pragma once
#include <algorithm> // copy
#include <iterator> // begin, end
#include <string> // string
#include <tuple> // tuple, get
#include <type_traits> // is_same, is_constructible, is_floating_point, is_enum, underlying_type
#include <utility> // move, forward, declval, pair
#include <valarray> // valarray
#include <vector> // vector
#include <nlohmann/detail/iterators/iteration_proxy.hpp>
#include <nlohmann/detail/meta/cpp_future.hpp>
#include <nlohmann/detail/meta/type_traits.hpp>
#include <nlohmann/detail/value_t.hpp>
namespace nlohmann
{
namespace detail
{
//////////////////
// constructors //
//////////////////
template<value_t> struct external_constructor;
template<>
struct external_constructor<value_t::boolean>
{
template<typename BasicJsonType>
static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept
{
j.m_type = value_t::boolean;
j.m_value = b;
j.assert_invariant();
}
};
template<>
struct external_constructor<value_t::string>
{
template<typename BasicJsonType>
static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)
{
j.m_type = value_t::string;
j.m_value = s;
j.assert_invariant();
}
template<typename BasicJsonType>
static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)
{
j.m_type = value_t::string;
j.m_value = std::move(s);
j.assert_invariant();
}
template < typename BasicJsonType, typename CompatibleStringType,
enable_if_t < !std::is_same<CompatibleStringType, typename BasicJsonType::string_t>::value,
int > = 0 >
static void construct(BasicJsonType& j, const CompatibleStringType& str)
{
j.m_type = value_t::string;
j.m_value.string = j.template create<typename BasicJsonType::string_t>(str);
j.assert_invariant();
}
};
template<>
struct external_constructor<value_t::binary>
{
template<typename BasicJsonType>
static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b)
{
j.m_type = value_t::binary;
j.m_value = typename BasicJsonType::binary_t(b);
j.assert_invariant();
}
template<typename BasicJsonType>
static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b)
{
j.m_type = value_t::binary;
j.m_value = typename BasicJsonType::binary_t(std::move(b));;
j.assert_invariant();
}
};
template<>
struct external_constructor<value_t::number_float>
{
template<typename BasicJsonType>
static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept
{
j.m_type = value_t::number_float;
j.m_value = val;
j.assert_invariant();
}
};
template<>
struct external_constructor<value_t::number_unsigned>
{
template<typename BasicJsonType>
static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept
{
j.m_type = value_t::number_unsigned;
j.m_value = val;
j.assert_invariant();
}
};
template<>
struct external_constructor<value_t::number_integer>
{
template<typename BasicJsonType>
static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept
{
j.m_type = value_t::number_integer;
j.m_value = val;
j.assert_invariant();
}
};
template<>
struct external_constructor<value_t::array>
{
template<typename BasicJsonType>
static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
{
j.m_type = value_t::array;
j.m_value = arr;
j.set_parents();
j.assert_invariant();
}
template<typename BasicJsonType>
static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
{
j.m_type = value_t::array;
j.m_value = std::move(arr);
j.set_parents();
j.assert_invariant();
}
template < typename BasicJsonType, typename CompatibleArrayType,
enable_if_t < !std::is_same<CompatibleArrayType, typename BasicJsonType::array_t>::value,
int > = 0 >
static void construct(BasicJsonType& j, const CompatibleArrayType& arr)
{
using std::begin;
using std::end;
j.m_type = value_t::array;
j.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
j.set_parents();
j.assert_invariant();
}
template<typename BasicJsonType>
static void construct(BasicJsonType& j, const std::vector<bool>& arr)
{
j.m_type = value_t::array;
j.m_value = value_t::array;
j.m_value.array->reserve(arr.size());
for (const bool x : arr)
{
j.m_value.array->push_back(x);
j.set_parent(j.m_value.array->back());
}
j.assert_invariant();
}
template<typename BasicJsonType, typename T,
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
static void construct(BasicJsonType& j, const std::valarray<T>& arr)
{
j.m_type = value_t::array;
j.m_value = value_t::array;
j.m_value.array->resize(arr.size());
if (arr.size() > 0)
{
std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin());
}
j.set_parents();
j.assert_invariant();
}
};
template<>
struct external_constructor<value_t::object>
{
template<typename BasicJsonType>
static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
{
j.m_type = value_t::object;
j.m_value = obj;
j.set_parents();
j.assert_invariant();
}
template<typename BasicJsonType>
static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
{
j.m_type = value_t::object;
j.m_value = std::move(obj);
j.set_parents();
j.assert_invariant();
}
template < typename BasicJsonType, typename CompatibleObjectType,
enable_if_t < !std::is_same<CompatibleObjectType, typename BasicJsonType::object_t>::value, int > = 0 >
static void construct(BasicJsonType& j, const CompatibleObjectType& obj)
{
using std::begin;
using std::end;
j.m_type = value_t::object;
j.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
j.set_parents();
j.assert_invariant();
}
};
/////////////
// to_json //
/////////////
template<typename BasicJsonType, typename T,
enable_if_t<std::is_same<T, typename BasicJsonType::boolean_t>::value, int> = 0>
void to_json(BasicJsonType& j, T b) noexcept
{
external_constructor<value_t::boolean>::construct(j, b);
}
template<typename BasicJsonType, typename CompatibleString,
enable_if_t<std::is_constructible<typename BasicJsonType::string_t, CompatibleString>::value, int> = 0>
void to_json(BasicJsonType& j, const CompatibleString& s)
{
external_constructor<value_t::string>::construct(j, s);
}
template<typename BasicJsonType>
void to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s)
{
external_constructor<value_t::string>::construct(j, std::move(s));
}
template<typename BasicJsonType, typename FloatType,
enable_if_t<std::is_floating_point<FloatType>::value, int> = 0>
void to_json(BasicJsonType& j, FloatType val) noexcept
{
external_constructor<value_t::number_float>::construct(j, static_cast<typename BasicJsonType::number_float_t>(val));
}
template<typename BasicJsonType, typename CompatibleNumberUnsignedType,
enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_unsigned_t, CompatibleNumberUnsignedType>::value, int> = 0>
void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept
{
external_constructor<value_t::number_unsigned>::construct(j, static_cast<typename BasicJsonType::number_unsigned_t>(val));
}
template<typename BasicJsonType, typename CompatibleNumberIntegerType,
enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_integer_t, CompatibleNumberIntegerType>::value, int> = 0>
void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept
{
external_constructor<value_t::number_integer>::construct(j, static_cast<typename BasicJsonType::number_integer_t>(val));
}
template<typename BasicJsonType, typename EnumType,
enable_if_t<std::is_enum<EnumType>::value, int> = 0>
void to_json(BasicJsonType& j, EnumType e) noexcept
{
using underlying_type = typename std::underlying_type<EnumType>::type;
external_constructor<value_t::number_integer>::construct(j, static_cast<underlying_type>(e));
}
template<typename BasicJsonType>
void to_json(BasicJsonType& j, const std::vector<bool>& e)
{
external_constructor<value_t::array>::construct(j, e);
}
template < typename BasicJsonType, typename CompatibleArrayType,
enable_if_t < is_compatible_array_type<BasicJsonType,
CompatibleArrayType>::value&&
!is_compatible_object_type<BasicJsonType, CompatibleArrayType>::value&&
!is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value&&
!std::is_same<typename BasicJsonType::binary_t, CompatibleArrayType>::value&&
!is_basic_json<CompatibleArrayType>::value,
int > = 0 >
void to_json(BasicJsonType& j, const CompatibleArrayType& arr)
{
external_constructor<value_t::array>::construct(j, arr);
}
template<typename BasicJsonType>
void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin)
{
external_constructor<value_t::binary>::construct(j, bin);
}
template<typename BasicJsonType, typename T,
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
void to_json(BasicJsonType& j, const std::valarray<T>& arr)
{
external_constructor<value_t::array>::construct(j, std::move(arr));
}
template<typename BasicJsonType>
void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
{
external_constructor<value_t::array>::construct(j, std::move(arr));
}
template < typename BasicJsonType, typename CompatibleObjectType,
enable_if_t < is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value&& !is_basic_json<CompatibleObjectType>::value, int > = 0 >
void to_json(BasicJsonType& j, const CompatibleObjectType& obj)
{
external_constructor<value_t::object>::construct(j, obj);
}
template<typename BasicJsonType>
void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
{
external_constructor<value_t::object>::construct(j, std::move(obj));
}
template <
typename BasicJsonType, typename T, std::size_t N,
enable_if_t < !std::is_constructible<typename BasicJsonType::string_t,
const T(&)[N]>::value, // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
int > = 0 >
void to_json(BasicJsonType& j, const T(&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
{
external_constructor<value_t::array>::construct(j, arr);
}
template < typename BasicJsonType, typename T1, typename T2, enable_if_t < std::is_constructible<BasicJsonType, T1>::value&& std::is_constructible<BasicJsonType, T2>::value, int > = 0 >
void to_json(BasicJsonType& j, const std::pair<T1, T2>& p)
{
j = { p.first, p.second };
}
// for https://github.com/nlohmann/json/pull/1134
template<typename BasicJsonType, typename T,
enable_if_t<std::is_same<T, iteration_proxy_value<typename BasicJsonType::iterator>>::value, int> = 0>
void to_json(BasicJsonType& j, const T& b)
{
j = { {b.key(), b.value()} };
}
template<typename BasicJsonType, typename Tuple, std::size_t... Idx>
void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<Idx...> /*unused*/)
{
j = { std::get<Idx>(t)... };
}
template<typename BasicJsonType, typename T, enable_if_t<is_constructible_tuple<BasicJsonType, T>::value, int > = 0>
void to_json(BasicJsonType& j, const T& t)
{
to_json_tuple_impl(j, t, make_index_sequence<std::tuple_size<T>::value> {});
}
struct to_json_fn
{
template<typename BasicJsonType, typename T>
auto operator()(BasicJsonType& j, T&& val) const noexcept(noexcept(to_json(j, std::forward<T>(val))))
-> decltype(to_json(j, std::forward<T>(val)), void())
{
return to_json(j, std::forward<T>(val));
}
};
} // namespace detail
/// namespace to hold default `to_json` function
/// to see why this is required:
/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html
namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces)
{
constexpr const auto& to_json = detail::static_const<detail::to_json_fn>::value; // NOLINT(misc-definitions-in-headers)
} // namespace
} // namespace nlohmann

421
lib/json/include/nlohmann/detail/exceptions.hpp

@ -0,0 +1,421 @@
#pragma once
#include <exception> // exception
#include <stdexcept> // runtime_error
#include <string> // to_string
#include <vector> // vector
#include <nlohmann/detail/value_t.hpp>
#include <nlohmann/detail/string_escape.hpp>
#include <nlohmann/detail/input/position_t.hpp>
#include <nlohmann/detail/macro_scope.hpp>
namespace nlohmann
{
namespace detail
{
////////////////
// exceptions //
////////////////
/*!
@brief general exception of the @ref basic_json class
This class is an extension of `std::exception` objects with a member @a id for
exception ids. It is used as the base class for all exceptions thrown by the
@ref basic_json class. This class can hence be used as "wildcard" to catch
exceptions.
Subclasses:
- @ref parse_error for exceptions indicating a parse error
- @ref invalid_iterator for exceptions indicating errors with iterators
- @ref type_error for exceptions indicating executing a member function with
a wrong type
- @ref out_of_range for exceptions indicating access out of the defined range
- @ref other_error for exceptions indicating other library errors
@internal
@note To have nothrow-copy-constructible exceptions, we internally use
`std::runtime_error` which can cope with arbitrary-length error messages.
Intermediate strings are built with static functions and then passed to
the actual constructor.
@endinternal
@liveexample{The following code shows how arbitrary library exceptions can be
caught.,exception}
@since version 3.0.0
*/
class exception : public std::exception
{
public:
/// returns the explanatory string
const char* what() const noexcept override
{
return m.what();
}
/// the id of the exception
const int id; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes)
protected:
JSON_HEDLEY_NON_NULL(3)
exception(int id_, const char* what_arg) : id(id_), m(what_arg) {}
static std::string name(const std::string& ename, int id_)
{
return "[json.exception." + ename + "." + std::to_string(id_) + "] ";
}
template<typename BasicJsonType>
static std::string diagnostics(const BasicJsonType& leaf_element)
{
#if JSON_DIAGNOSTICS
std::vector<std::string> tokens;
for (const auto* current = &leaf_element; current->m_parent != nullptr; current = current->m_parent)
{
switch (current->m_parent->type())
{
case value_t::array:
{
for (std::size_t i = 0; i < current->m_parent->m_value.array->size(); ++i)
{
if (&current->m_parent->m_value.array->operator[](i) == current)
{
tokens.emplace_back(std::to_string(i));
break;
}
}
break;
}
case value_t::object:
{
for (const auto& element : *current->m_parent->m_value.object)
{
if (&element.second == current)
{
tokens.emplace_back(element.first.c_str());
break;
}
}
break;
}
default: // LCOV_EXCL_LINE
break; // LCOV_EXCL_LINE
}
}
if (tokens.empty())
{
return "";
}
return "(" + std::accumulate(tokens.rbegin(), tokens.rend(), std::string{},
[](const std::string & a, const std::string & b)
{
return a + "/" + detail::escape(b);
}) + ") ";
#else
static_cast<void>(leaf_element);
return "";
#endif
}
private:
/// an exception object as storage for error messages
std::runtime_error m;
};
/*!
@brief exception indicating a parse error
This exception is thrown by the library when a parse error occurs. Parse errors
can occur during the deserialization of JSON text, CBOR, MessagePack, as well
as when using JSON Patch.
Member @a byte holds the byte index of the last read character in the input
file.
Exceptions have ids 1xx.
name / id | example message | description
------------------------------ | --------------- | -------------------------
json.exception.parse_error.101 | parse error at 2: unexpected end of input; expected string literal | This error indicates a syntax error while deserializing a JSON text. The error message describes that an unexpected token (character) was encountered, and the member @a byte indicates the error position.
json.exception.parse_error.102 | parse error at 14: missing or wrong low surrogate | JSON uses the `\uxxxx` format to describe Unicode characters. Code points above above 0xFFFF are split into two `\uxxxx` entries ("surrogate pairs"). This error indicates that the surrogate pair is incomplete or contains an invalid code point.
json.exception.parse_error.103 | parse error: code points above 0x10FFFF are invalid | Unicode supports code points up to 0x10FFFF. Code points above 0x10FFFF are invalid.
json.exception.parse_error.104 | parse error: JSON patch must be an array of objects | [RFC 6902](https://tools.ietf.org/html/rfc6902) requires a JSON Patch document to be a JSON document that represents an array of objects.
json.exception.parse_error.105 | parse error: operation must have string member 'op' | An operation of a JSON Patch document must contain exactly one "op" member, whose value indicates the operation to perform. Its value must be one of "add", "remove", "replace", "move", "copy", or "test"; other values are errors.
json.exception.parse_error.106 | parse error: array index '01' must not begin with '0' | An array index in a JSON Pointer ([RFC 6901](https://tools.ietf.org/html/rfc6901)) may be `0` or any number without a leading `0`.
json.exception.parse_error.107 | parse error: JSON pointer must be empty or begin with '/' - was: 'foo' | A JSON Pointer must be a Unicode string containing a sequence of zero or more reference tokens, each prefixed by a `/` character.
json.exception.parse_error.108 | parse error: escape character '~' must be followed with '0' or '1' | In a JSON Pointer, only `~0` and `~1` are valid escape sequences.
json.exception.parse_error.109 | parse error: array index 'one' is not a number | A JSON Pointer array index must be a number.
json.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from vector | When parsing CBOR or MessagePack, the byte vector ends before the complete value has been read.
json.exception.parse_error.112 | parse error at 1: error reading CBOR; last byte: 0xF8 | Not all types of CBOR or MessagePack are supported. This exception occurs if an unsupported byte was read.
json.exception.parse_error.113 | parse error at 2: expected a CBOR string; last byte: 0x98 | While parsing a map key, a value that is not a string has been read.
json.exception.parse_error.114 | parse error: Unsupported BSON record type 0x0F | The parsing of the corresponding BSON record type is not implemented (yet).
json.exception.parse_error.115 | parse error at byte 5: syntax error while parsing UBJSON high-precision number: invalid number text: 1A | A UBJSON high-precision number could not be parsed.
@note For an input with n bytes, 1 is the index of the first character and n+1
is the index of the terminating null byte or the end of file. This also
holds true when reading a byte vector (CBOR or MessagePack).
@liveexample{The following code shows how a `parse_error` exception can be
caught.,parse_error}
@sa - @ref exception for the base class of the library exceptions
@sa - @ref invalid_iterator for exceptions indicating errors with iterators
@sa - @ref type_error for exceptions indicating executing a member function with
a wrong type
@sa - @ref out_of_range for exceptions indicating access out of the defined range
@sa - @ref other_error for exceptions indicating other library errors
@since version 3.0.0
*/
class parse_error : public exception
{
public:
/*!
@brief create a parse error exception
@param[in] id_ the id of the exception
@param[in] pos the position where the error occurred (or with
chars_read_total=0 if the position cannot be
determined)
@param[in] what_arg the explanatory string
@return parse_error object
*/
template<typename BasicJsonType>
static parse_error create(int id_, const position_t& pos, const std::string& what_arg, const BasicJsonType& context)
{
std::string w = exception::name("parse_error", id_) + "parse error" +
position_string(pos) + ": " + exception::diagnostics(context) + what_arg;
return parse_error(id_, pos.chars_read_total, w.c_str());
}
template<typename BasicJsonType>
static parse_error create(int id_, std::size_t byte_, const std::string& what_arg, const BasicJsonType& context)
{
std::string w = exception::name("parse_error", id_) + "parse error" +
(byte_ != 0 ? (" at byte " + std::to_string(byte_)) : "") +
": " + exception::diagnostics(context) + what_arg;
return parse_error(id_, byte_, w.c_str());
}
/*!
@brief byte index of the parse error
The byte index of the last read character in the input file.
@note For an input with n bytes, 1 is the index of the first character and
n+1 is the index of the terminating null byte or the end of file.
This also holds true when reading a byte vector (CBOR or MessagePack).
*/
const std::size_t byte;
private:
parse_error(int id_, std::size_t byte_, const char* what_arg)
: exception(id_, what_arg), byte(byte_) {}
static std::string position_string(const position_t& pos)
{
return " at line " + std::to_string(pos.lines_read + 1) +
", column " + std::to_string(pos.chars_read_current_line);
}
};
/*!
@brief exception indicating errors with iterators
This exception is thrown if iterators passed to a library function do not match
the expected semantics.
Exceptions have ids 2xx.
name / id | example message | description
----------------------------------- | --------------- | -------------------------
json.exception.invalid_iterator.201 | iterators are not compatible | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid.
json.exception.invalid_iterator.202 | iterator does not fit current value | In an erase or insert function, the passed iterator @a pos does not belong to the JSON value for which the function was called. It hence does not define a valid position for the deletion/insertion.
json.exception.invalid_iterator.203 | iterators do not fit current value | Either iterator passed to function @ref erase(IteratorType first, IteratorType last) does not belong to the JSON value from which values shall be erased. It hence does not define a valid range to delete values from.
json.exception.invalid_iterator.204 | iterators out of range | When an iterator range for a primitive type (number, boolean, or string) is passed to a constructor or an erase function, this range has to be exactly (@ref begin(), @ref end()), because this is the only way the single stored value is expressed. All other ranges are invalid.
json.exception.invalid_iterator.205 | iterator out of range | When an iterator for a primitive type (number, boolean, or string) is passed to an erase function, the iterator has to be the @ref begin() iterator, because it is the only way to address the stored value. All other iterators are invalid.
json.exception.invalid_iterator.206 | cannot construct with iterators from null | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) belong to a JSON null value and hence to not define a valid range.
json.exception.invalid_iterator.207 | cannot use key() for non-object iterators | The key() member function can only be used on iterators belonging to a JSON object, because other types do not have a concept of a key.
json.exception.invalid_iterator.208 | cannot use operator[] for object iterators | The operator[] to specify a concrete offset cannot be used on iterators belonging to a JSON object, because JSON objects are unordered.
json.exception.invalid_iterator.209 | cannot use offsets with object iterators | The offset operators (+, -, +=, -=) cannot be used on iterators belonging to a JSON object, because JSON objects are unordered.
json.exception.invalid_iterator.210 | iterators do not fit | The iterator range passed to the insert function are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid.
json.exception.invalid_iterator.211 | passed iterators may not belong to container | The iterator range passed to the insert function must not be a subrange of the container to insert to.
json.exception.invalid_iterator.212 | cannot compare iterators of different containers | When two iterators are compared, they must belong to the same container.
json.exception.invalid_iterator.213 | cannot compare order of object iterators | The order of object iterators cannot be compared, because JSON objects are unordered.
json.exception.invalid_iterator.214 | cannot get value | Cannot get value for iterator: Either the iterator belongs to a null value or it is an iterator to a primitive type (number, boolean, or string), but the iterator is different to @ref begin().
@liveexample{The following code shows how an `invalid_iterator` exception can be
caught.,invalid_iterator}
@sa - @ref exception for the base class of the library exceptions
@sa - @ref parse_error for exceptions indicating a parse error
@sa - @ref type_error for exceptions indicating executing a member function with
a wrong type
@sa - @ref out_of_range for exceptions indicating access out of the defined range
@sa - @ref other_error for exceptions indicating other library errors
@since version 3.0.0
*/
class invalid_iterator : public exception
{
public:
template<typename BasicJsonType>
static invalid_iterator create(int id_, const std::string& what_arg, const BasicJsonType& context)
{
std::string w = exception::name("invalid_iterator", id_) + exception::diagnostics(context) + what_arg;
return invalid_iterator(id_, w.c_str());
}
private:
JSON_HEDLEY_NON_NULL(3)
invalid_iterator(int id_, const char* what_arg)
: exception(id_, what_arg) {}
};
/*!
@brief exception indicating executing a member function with a wrong type
This exception is thrown in case of a type error; that is, a library function is
executed on a JSON value whose type does not match the expected semantics.
Exceptions have ids 3xx.
name / id | example message | description
----------------------------- | --------------- | -------------------------
json.exception.type_error.301 | cannot create object from initializer list | To create an object from an initializer list, the initializer list must consist only of a list of pairs whose first element is a string. When this constraint is violated, an array is created instead.
json.exception.type_error.302 | type must be object, but is array | During implicit or explicit value conversion, the JSON type must be compatible to the target type. For instance, a JSON string can only be converted into string types, but not into numbers or boolean types.
json.exception.type_error.303 | incompatible ReferenceType for get_ref, actual type is object | To retrieve a reference to a value stored in a @ref basic_json object with @ref get_ref, the type of the reference must match the value type. For instance, for a JSON array, the @a ReferenceType must be @ref array_t &.
json.exception.type_error.304 | cannot use at() with string | The @ref at() member functions can only be executed for certain JSON types.
json.exception.type_error.305 | cannot use operator[] with string | The @ref operator[] member functions can only be executed for certain JSON types.
json.exception.type_error.306 | cannot use value() with string | The @ref value() member functions can only be executed for certain JSON types.
json.exception.type_error.307 | cannot use erase() with string | The @ref erase() member functions can only be executed for certain JSON types.
json.exception.type_error.308 | cannot use push_back() with string | The @ref push_back() and @ref operator+= member functions can only be executed for certain JSON types.
json.exception.type_error.309 | cannot use insert() with | The @ref insert() member functions can only be executed for certain JSON types.
json.exception.type_error.310 | cannot use swap() with number | The @ref swap() member functions can only be executed for certain JSON types.
json.exception.type_error.311 | cannot use emplace_back() with string | The @ref emplace_back() member function can only be executed for certain JSON types.
json.exception.type_error.312 | cannot use update() with string | The @ref update() member functions can only be executed for certain JSON types.
json.exception.type_error.313 | invalid value to unflatten | The @ref unflatten function converts an object whose keys are JSON Pointers back into an arbitrary nested JSON value. The JSON Pointers must not overlap, because then the resulting value would not be well defined.
json.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers.
json.exception.type_error.315 | values in object must be primitive | The @ref unflatten function only works for an object whose keys are JSON Pointers and whose values are primitive.
json.exception.type_error.316 | invalid UTF-8 byte at index 10: 0x7E | The @ref dump function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded. |
json.exception.type_error.317 | JSON value cannot be serialized to requested format | The dynamic type of the object cannot be represented in the requested serialization format (e.g. a raw `true` or `null` JSON object cannot be serialized to BSON) |
@liveexample{The following code shows how a `type_error` exception can be
caught.,type_error}
@sa - @ref exception for the base class of the library exceptions
@sa - @ref parse_error for exceptions indicating a parse error
@sa - @ref invalid_iterator for exceptions indicating errors with iterators
@sa - @ref out_of_range for exceptions indicating access out of the defined range
@sa - @ref other_error for exceptions indicating other library errors
@since version 3.0.0
*/
class type_error : public exception
{
public:
template<typename BasicJsonType>
static type_error create(int id_, const std::string& what_arg, const BasicJsonType& context)
{
std::string w = exception::name("type_error", id_) + exception::diagnostics(context) + what_arg;
return type_error(id_, w.c_str());
}
private:
JSON_HEDLEY_NON_NULL(3)
type_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
};
/*!
@brief exception indicating access out of the defined range
This exception is thrown in case a library function is called on an input
parameter that exceeds the expected range, for instance in case of array
indices or nonexisting object keys.
Exceptions have ids 4xx.
name / id | example message | description
------------------------------- | --------------- | -------------------------
json.exception.out_of_range.401 | array index 3 is out of range | The provided array index @a i is larger than @a size-1.
json.exception.out_of_range.402 | array index '-' (3) is out of range | The special array index `-` in a JSON Pointer never describes a valid element of the array, but the index past the end. That is, it can only be used to add elements at this position, but not to read it.
json.exception.out_of_range.403 | key 'foo' not found | The provided key was not found in the JSON object.
json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved.
json.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value.
json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF.
json.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON and BSON only support integer numbers up to 9223372036854775807. (until version 3.8.0) |
json.exception.out_of_range.408 | excessive array size: 8658170730974374167 | The size (following `#`) of an UBJSON array or object exceeds the maximal capacity. |
json.exception.out_of_range.409 | BSON key cannot contain code point U+0000 (at byte 2) | Key identifiers to be serialized to BSON cannot contain code point U+0000, since the key is stored as zero-terminated c-string |
@liveexample{The following code shows how an `out_of_range` exception can be
caught.,out_of_range}
@sa - @ref exception for the base class of the library exceptions
@sa - @ref parse_error for exceptions indicating a parse error
@sa - @ref invalid_iterator for exceptions indicating errors with iterators
@sa - @ref type_error for exceptions indicating executing a member function with
a wrong type
@sa - @ref other_error for exceptions indicating other library errors
@since version 3.0.0
*/
class out_of_range : public exception
{
public:
template<typename BasicJsonType>
static out_of_range create(int id_, const std::string& what_arg, const BasicJsonType& context)
{
std::string w = exception::name("out_of_range", id_) + exception::diagnostics(context) + what_arg;
return out_of_range(id_, w.c_str());
}
private:
JSON_HEDLEY_NON_NULL(3)
out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {}
};
/*!
@brief exception indicating other library errors
This exception is thrown in case of errors that cannot be classified with the
other exception types.
Exceptions have ids 5xx.
name / id | example message | description
------------------------------ | --------------- | -------------------------
json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "value":"bar"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed.
@sa - @ref exception for the base class of the library exceptions
@sa - @ref parse_error for exceptions indicating a parse error
@sa - @ref invalid_iterator for exceptions indicating errors with iterators
@sa - @ref type_error for exceptions indicating executing a member function with
a wrong type
@sa - @ref out_of_range for exceptions indicating access out of the defined range
@liveexample{The following code shows how an `other_error` exception can be
caught.,other_error}
@since version 3.0.0
*/
class other_error : public exception
{
public:
template<typename BasicJsonType>
static other_error create(int id_, const std::string& what_arg, const BasicJsonType& context)
{
std::string w = exception::name("other_error", id_) + exception::diagnostics(context) + what_arg;
return other_error(id_, w.c_str());
}
private:
JSON_HEDLEY_NON_NULL(3)
other_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
};
} // namespace detail
} // namespace nlohmann

121
lib/json/include/nlohmann/detail/hash.hpp

@ -0,0 +1,121 @@
#pragma once
#include <cstdint> // uint8_t
#include <cstddef> // size_t
#include <functional> // hash
#include <nlohmann/detail/macro_scope.hpp>
namespace nlohmann
{
namespace detail
{
// boost::hash_combine
inline std::size_t combine(std::size_t seed, std::size_t h) noexcept
{
seed ^= h + 0x9e3779b9 + (seed << 6U) + (seed >> 2U);
return seed;
}
/*!
@brief hash a JSON value
The hash function tries to rely on std::hash where possible. Furthermore, the
type of the JSON value is taken into account to have different hash values for
null, 0, 0U, and false, etc.
@tparam BasicJsonType basic_json specialization
@param j JSON value to hash
@return hash value of j
*/
template<typename BasicJsonType>
std::size_t hash(const BasicJsonType& j)
{
using string_t = typename BasicJsonType::string_t;
using number_integer_t = typename BasicJsonType::number_integer_t;
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
using number_float_t = typename BasicJsonType::number_float_t;
const auto type = static_cast<std::size_t>(j.type());
switch (j.type())
{
case BasicJsonType::value_t::null:
case BasicJsonType::value_t::discarded:
{
return combine(type, 0);
}
case BasicJsonType::value_t::object:
{
auto seed = combine(type, j.size());
for (const auto& element : j.items())
{
const auto h = std::hash<string_t> {}(element.key());
seed = combine(seed, h);
seed = combine(seed, hash(element.value()));
}
return seed;
}
case BasicJsonType::value_t::array:
{
auto seed = combine(type, j.size());
for (const auto& element : j)
{
seed = combine(seed, hash(element));
}
return seed;
}
case BasicJsonType::value_t::string:
{
const auto h = std::hash<string_t> {}(j.template get_ref<const string_t&>());
return combine(type, h);
}
case BasicJsonType::value_t::boolean:
{
const auto h = std::hash<bool> {}(j.template get<bool>());
return combine(type, h);
}
case BasicJsonType::value_t::number_integer:
{
const auto h = std::hash<number_integer_t> {}(j.template get<number_integer_t>());
return combine(type, h);
}
case BasicJsonType::value_t::number_unsigned:
{
const auto h = std::hash<number_unsigned_t> {}(j.template get<number_unsigned_t>());
return combine(type, h);
}
case BasicJsonType::value_t::number_float:
{
const auto h = std::hash<number_float_t> {}(j.template get<number_float_t>());
return combine(type, h);
}
case BasicJsonType::value_t::binary:
{
auto seed = combine(type, j.get_binary().size());
const auto h = std::hash<bool> {}(j.get_binary().has_subtype());
seed = combine(seed, h);
seed = combine(seed, j.get_binary().subtype());
for (const auto byte : j.get_binary())
{
seed = combine(seed, std::hash<std::uint8_t> {}(byte));
}
return seed;
}
default: // LCOV_EXCL_LINE
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
return 0; // LCOV_EXCL_LINE
}
}
} // namespace detail
} // namespace nlohmann

2461
lib/json/include/nlohmann/detail/input/binary_reader.hpp

File diff suppressed because it is too large Load Diff

476
lib/json/include/nlohmann/detail/input/input_adapters.hpp

@ -0,0 +1,476 @@
#pragma once
#include <array> // array
#include <cstddef> // size_t
#include <cstdio> //FILE *
#include <cstring> // strlen
#include <istream> // istream
#include <iterator> // begin, end, iterator_traits, random_access_iterator_tag, distance, next
#include <memory> // shared_ptr, make_shared, addressof
#include <numeric> // accumulate
#include <string> // string, char_traits
#include <type_traits> // enable_if, is_base_of, is_pointer, is_integral, remove_pointer
#include <utility> // pair, declval
#include <nlohmann/detail/iterators/iterator_traits.hpp>
#include <nlohmann/detail/macro_scope.hpp>
namespace nlohmann
{
namespace detail
{
/// the supported input formats
enum class input_format_t { json, cbor, msgpack, ubjson, bson };
////////////////////
// input adapters //
////////////////////
/*!
Input adapter for stdio file access. This adapter read only 1 byte and do not use any
buffer. This adapter is a very low level adapter.
*/
class file_input_adapter
{
public:
using char_type = char;
JSON_HEDLEY_NON_NULL(2)
explicit file_input_adapter(std::FILE* f) noexcept
: m_file(f)
{}
// make class move-only
file_input_adapter(const file_input_adapter&) = delete;
file_input_adapter(file_input_adapter&&) noexcept = default;
file_input_adapter& operator=(const file_input_adapter&) = delete;
file_input_adapter& operator=(file_input_adapter&&) = delete;
~file_input_adapter() = default;
std::char_traits<char>::int_type get_character() noexcept
{
return std::fgetc(m_file);
}
private:
/// the file pointer to read from
std::FILE* m_file;
};
/*!
Input adapter for a (caching) istream. Ignores a UFT Byte Order Mark at
beginning of input. Does not support changing the underlying std::streambuf
in mid-input. Maintains underlying std::istream and std::streambuf to support
subsequent use of standard std::istream operations to process any input
characters following those used in parsing the JSON input. Clears the
std::istream flags; any input errors (e.g., EOF) will be detected by the first
subsequent call for input from the std::istream.
*/
class input_stream_adapter
{
public:
using char_type = char;
~input_stream_adapter()
{
// clear stream flags; we use underlying streambuf I/O, do not
// maintain ifstream flags, except eof
if (is != nullptr)
{
is->clear(is->rdstate() & std::ios::eofbit);
}
}
explicit input_stream_adapter(std::istream& i)
: is(&i), sb(i.rdbuf())
{}
// delete because of pointer members
input_stream_adapter(const input_stream_adapter&) = delete;
input_stream_adapter& operator=(input_stream_adapter&) = delete;
input_stream_adapter& operator=(input_stream_adapter&&) = delete;
input_stream_adapter(input_stream_adapter&& rhs) noexcept
: is(rhs.is), sb(rhs.sb)
{
rhs.is = nullptr;
rhs.sb = nullptr;
}
// std::istream/std::streambuf use std::char_traits<char>::to_int_type, to
// ensure that std::char_traits<char>::eof() and the character 0xFF do not
// end up as the same value, eg. 0xFFFFFFFF.
std::char_traits<char>::int_type get_character()
{
auto res = sb->sbumpc();
// set eof manually, as we don't use the istream interface.
if (JSON_HEDLEY_UNLIKELY(res == std::char_traits<char>::eof()))
{
is->clear(is->rdstate() | std::ios::eofbit);
}
return res;
}
private:
/// the associated input stream
std::istream* is = nullptr;
std::streambuf* sb = nullptr;
};
// General-purpose iterator-based adapter. It might not be as fast as
// theoretically possible for some containers, but it is extremely versatile.
template<typename IteratorType>
class iterator_input_adapter
{
public:
using char_type = typename std::iterator_traits<IteratorType>::value_type;
iterator_input_adapter(IteratorType first, IteratorType last)
: current(std::move(first)), end(std::move(last))
{}
typename std::char_traits<char_type>::int_type get_character()
{
if (JSON_HEDLEY_LIKELY(current != end))
{
auto result = std::char_traits<char_type>::to_int_type(*current);
std::advance(current, 1);
return result;
}
return std::char_traits<char_type>::eof();
}
private:
IteratorType current;
IteratorType end;
template<typename BaseInputAdapter, size_t T>
friend struct wide_string_input_helper;
bool empty() const
{
return current == end;
}
};
template<typename BaseInputAdapter, size_t T>
struct wide_string_input_helper;
template<typename BaseInputAdapter>
struct wide_string_input_helper<BaseInputAdapter, 4>
{
// UTF-32
static void fill_buffer(BaseInputAdapter& input,
std::array<std::char_traits<char>::int_type, 4>& utf8_bytes,
size_t& utf8_bytes_index,
size_t& utf8_bytes_filled)
{
utf8_bytes_index = 0;
if (JSON_HEDLEY_UNLIKELY(input.empty()))
{
utf8_bytes[0] = std::char_traits<char>::eof();
utf8_bytes_filled = 1;
}
else
{
// get the current character
const auto wc = input.get_character();
// UTF-32 to UTF-8 encoding
if (wc < 0x80)
{
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
utf8_bytes_filled = 1;
}
else if (wc <= 0x7FF)
{
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xC0u | ((static_cast<unsigned int>(wc) >> 6u) & 0x1Fu));
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
utf8_bytes_filled = 2;
}
else if (wc <= 0xFFFF)
{
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xE0u | ((static_cast<unsigned int>(wc) >> 12u) & 0x0Fu));
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));
utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
utf8_bytes_filled = 3;
}
else if (wc <= 0x10FFFF)
{
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | ((static_cast<unsigned int>(wc) >> 18u) & 0x07u));
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 12u) & 0x3Fu));
utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));
utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
utf8_bytes_filled = 4;
}
else
{
// unknown character
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
utf8_bytes_filled = 1;
}
}
}
};
template<typename BaseInputAdapter>
struct wide_string_input_helper<BaseInputAdapter, 2>
{
// UTF-16
static void fill_buffer(BaseInputAdapter& input,
std::array<std::char_traits<char>::int_type, 4>& utf8_bytes,
size_t& utf8_bytes_index,
size_t& utf8_bytes_filled)
{
utf8_bytes_index = 0;
if (JSON_HEDLEY_UNLIKELY(input.empty()))
{
utf8_bytes[0] = std::char_traits<char>::eof();
utf8_bytes_filled = 1;
}
else
{
// get the current character
const auto wc = input.get_character();
// UTF-16 to UTF-8 encoding
if (wc < 0x80)
{
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
utf8_bytes_filled = 1;
}
else if (wc <= 0x7FF)
{
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xC0u | ((static_cast<unsigned int>(wc) >> 6u)));
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
utf8_bytes_filled = 2;
}
else if (0xD800 > wc || wc >= 0xE000)
{
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xE0u | ((static_cast<unsigned int>(wc) >> 12u)));
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));
utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
utf8_bytes_filled = 3;
}
else
{
if (JSON_HEDLEY_UNLIKELY(!input.empty()))
{
const auto wc2 = static_cast<unsigned int>(input.get_character());
const auto charcode = 0x10000u + (((static_cast<unsigned int>(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu));
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | (charcode >> 18u));
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu));
utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu));
utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (charcode & 0x3Fu));
utf8_bytes_filled = 4;
}
else
{
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
utf8_bytes_filled = 1;
}
}
}
}
};
// Wraps another input apdater to convert wide character types into individual bytes.
template<typename BaseInputAdapter, typename WideCharType>
class wide_string_input_adapter
{
public:
using char_type = char;
wide_string_input_adapter(BaseInputAdapter base)
: base_adapter(base) {}
typename std::char_traits<char>::int_type get_character() noexcept
{
// check if buffer needs to be filled
if (utf8_bytes_index == utf8_bytes_filled)
{
fill_buffer<sizeof(WideCharType)>();
JSON_ASSERT(utf8_bytes_filled > 0);
JSON_ASSERT(utf8_bytes_index == 0);
}
// use buffer
JSON_ASSERT(utf8_bytes_filled > 0);
JSON_ASSERT(utf8_bytes_index < utf8_bytes_filled);
return utf8_bytes[utf8_bytes_index++];
}
private:
BaseInputAdapter base_adapter;
template<size_t T>
void fill_buffer()
{
wide_string_input_helper<BaseInputAdapter, T>::fill_buffer(base_adapter, utf8_bytes, utf8_bytes_index, utf8_bytes_filled);
}
/// a buffer for UTF-8 bytes
std::array<std::char_traits<char>::int_type, 4> utf8_bytes = {{0, 0, 0, 0}};
/// index to the utf8_codes array for the next valid byte
std::size_t utf8_bytes_index = 0;
/// number of valid bytes in the utf8_codes array
std::size_t utf8_bytes_filled = 0;
};
template<typename IteratorType, typename Enable = void>
struct iterator_input_adapter_factory
{
using iterator_type = IteratorType;
using char_type = typename std::iterator_traits<iterator_type>::value_type;
using adapter_type = iterator_input_adapter<iterator_type>;
static adapter_type create(IteratorType first, IteratorType last)
{
return adapter_type(std::move(first), std::move(last));
}
};
template<typename T>
struct is_iterator_of_multibyte
{
using value_type = typename std::iterator_traits<T>::value_type;
enum
{
value = sizeof(value_type) > 1
};
};
template<typename IteratorType>
struct iterator_input_adapter_factory<IteratorType, enable_if_t<is_iterator_of_multibyte<IteratorType>::value>>
{
using iterator_type = IteratorType;
using char_type = typename std::iterator_traits<iterator_type>::value_type;
using base_adapter_type = iterator_input_adapter<iterator_type>;
using adapter_type = wide_string_input_adapter<base_adapter_type, char_type>;
static adapter_type create(IteratorType first, IteratorType last)
{
return adapter_type(base_adapter_type(std::move(first), std::move(last)));
}
};
// General purpose iterator-based input
template<typename IteratorType>
typename iterator_input_adapter_factory<IteratorType>::adapter_type input_adapter(IteratorType first, IteratorType last)
{
using factory_type = iterator_input_adapter_factory<IteratorType>;
return factory_type::create(first, last);
}
// Convenience shorthand from container to iterator
// Enables ADL on begin(container) and end(container)
// Encloses the using declarations in namespace for not to leak them to outside scope
namespace container_input_adapter_factory_impl
{
using std::begin;
using std::end;
template<typename ContainerType, typename Enable = void>
struct container_input_adapter_factory {};
template<typename ContainerType>
struct container_input_adapter_factory< ContainerType,
void_t<decltype(begin(std::declval<ContainerType>()), end(std::declval<ContainerType>()))>>
{
using adapter_type = decltype(input_adapter(begin(std::declval<ContainerType>()), end(std::declval<ContainerType>())));
static adapter_type create(const ContainerType& container)
{
return input_adapter(begin(container), end(container));
}
};
} // namespace container_input_adapter_factory_impl
template<typename ContainerType>
typename container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::adapter_type input_adapter(const ContainerType& container)
{
return container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::create(container);
}
// Special cases with fast paths
inline file_input_adapter input_adapter(std::FILE* file)
{
return file_input_adapter(file);
}
inline input_stream_adapter input_adapter(std::istream& stream)
{
return input_stream_adapter(stream);
}
inline input_stream_adapter input_adapter(std::istream&& stream)
{
return input_stream_adapter(stream);
}
using contiguous_bytes_input_adapter = decltype(input_adapter(std::declval<const char*>(), std::declval<const char*>()));
// Null-delimited strings, and the like.
template < typename CharT,
typename std::enable_if <
std::is_pointer<CharT>::value&&
!std::is_array<CharT>::value&&
std::is_integral<typename std::remove_pointer<CharT>::type>::value&&
sizeof(typename std::remove_pointer<CharT>::type) == 1,
int >::type = 0 >
contiguous_bytes_input_adapter input_adapter(CharT b)
{
auto length = std::strlen(reinterpret_cast<const char*>(b));
const auto* ptr = reinterpret_cast<const char*>(b);
return input_adapter(ptr, ptr + length);
}
template<typename T, std::size_t N>
auto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N)) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
{
return input_adapter(array, array + N);
}
// This class only handles inputs of input_buffer_adapter type.
// It's required so that expressions like {ptr, len} can be implicitely casted
// to the correct adapter.
class span_input_adapter
{
public:
template < typename CharT,
typename std::enable_if <
std::is_pointer<CharT>::value&&
std::is_integral<typename std::remove_pointer<CharT>::type>::value&&
sizeof(typename std::remove_pointer<CharT>::type) == 1,
int >::type = 0 >
span_input_adapter(CharT b, std::size_t l)
: ia(reinterpret_cast<const char*>(b), reinterpret_cast<const char*>(b) + l) {}
template<class IteratorType,
typename std::enable_if<
std::is_same<typename iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value,
int>::type = 0>
span_input_adapter(IteratorType first, IteratorType last)
: ia(input_adapter(first, last)) {}
contiguous_bytes_input_adapter&& get()
{
return std::move(ia); // NOLINT(hicpp-move-const-arg,performance-move-const-arg)
}
private:
contiguous_bytes_input_adapter ia;
};
} // namespace detail
} // namespace nlohmann

711
lib/json/include/nlohmann/detail/input/json_sax.hpp

@ -0,0 +1,711 @@
#pragma once
#include <cstddef>
#include <string> // string
#include <utility> // move
#include <vector> // vector
#include <nlohmann/detail/exceptions.hpp>
#include <nlohmann/detail/macro_scope.hpp>
namespace nlohmann
{
/*!
@brief SAX interface
This class describes the SAX interface used by @ref nlohmann::json::sax_parse.
Each function is called in different situations while the input is parsed. The
boolean return value informs the parser whether to continue processing the
input.
*/
template<typename BasicJsonType>
struct json_sax
{
using number_integer_t = typename BasicJsonType::number_integer_t;
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
using number_float_t = typename BasicJsonType::number_float_t;
using string_t = typename BasicJsonType::string_t;
using binary_t = typename BasicJsonType::binary_t;
/*!
@brief a null value was read
@return whether parsing should proceed
*/
virtual bool null() = 0;
/*!
@brief a boolean value was read
@param[in] val boolean value
@return whether parsing should proceed
*/
virtual bool boolean(bool val) = 0;
/*!
@brief an integer number was read
@param[in] val integer value
@return whether parsing should proceed
*/
virtual bool number_integer(number_integer_t val) = 0;
/*!
@brief an unsigned integer number was read
@param[in] val unsigned integer value
@return whether parsing should proceed
*/
virtual bool number_unsigned(number_unsigned_t val) = 0;
/*!
@brief an floating-point number was read
@param[in] val floating-point value
@param[in] s raw token value
@return whether parsing should proceed
*/
virtual bool number_float(number_float_t val, const string_t& s) = 0;
/*!
@brief a string was read
@param[in] val string value
@return whether parsing should proceed
@note It is safe to move the passed string.
*/
virtual bool string(string_t& val) = 0;
/*!
@brief a binary string was read
@param[in] val binary value
@return whether parsing should proceed
@note It is safe to move the passed binary.
*/
virtual bool binary(binary_t& val) = 0;
/*!
@brief the beginning of an object was read
@param[in] elements number of object elements or -1 if unknown
@return whether parsing should proceed
@note binary formats may report the number of elements
*/
virtual bool start_object(std::size_t elements) = 0;
/*!
@brief an object key was read
@param[in] val object key
@return whether parsing should proceed
@note It is safe to move the passed string.
*/
virtual bool key(string_t& val) = 0;
/*!
@brief the end of an object was read
@return whether parsing should proceed
*/
virtual bool end_object() = 0;
/*!
@brief the beginning of an array was read
@param[in] elements number of array elements or -1 if unknown
@return whether parsing should proceed
@note binary formats may report the number of elements
*/
virtual bool start_array(std::size_t elements) = 0;
/*!
@brief the end of an array was read
@return whether parsing should proceed
*/
virtual bool end_array() = 0;
/*!
@brief a parse error occurred
@param[in] position the position in the input where the error occurs
@param[in] last_token the last read token
@param[in] ex an exception object describing the error
@return whether parsing should proceed (must return false)
*/
virtual bool parse_error(std::size_t position,
const std::string& last_token,
const detail::exception& ex) = 0;
json_sax() = default;
json_sax(const json_sax&) = default;
json_sax(json_sax&&) noexcept = default;
json_sax& operator=(const json_sax&) = default;
json_sax& operator=(json_sax&&) noexcept = default;
virtual ~json_sax() = default;
};
namespace detail
{
/*!
@brief SAX implementation to create a JSON value from SAX events
This class implements the @ref json_sax interface and processes the SAX events
to create a JSON value which makes it basically a DOM parser. The structure or
hierarchy of the JSON value is managed by the stack `ref_stack` which contains
a pointer to the respective array or object for each recursion depth.
After successful parsing, the value that is passed by reference to the
constructor contains the parsed value.
@tparam BasicJsonType the JSON type
*/
template<typename BasicJsonType>
class json_sax_dom_parser
{
public:
using number_integer_t = typename BasicJsonType::number_integer_t;
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
using number_float_t = typename BasicJsonType::number_float_t;
using string_t = typename BasicJsonType::string_t;
using binary_t = typename BasicJsonType::binary_t;
/*!
@param[in,out] r reference to a JSON value that is manipulated while
parsing
@param[in] allow_exceptions_ whether parse errors yield exceptions
*/
explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true)
: root(r), allow_exceptions(allow_exceptions_)
{}
// make class move-only
json_sax_dom_parser(const json_sax_dom_parser&) = delete;
json_sax_dom_parser(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
json_sax_dom_parser& operator=(const json_sax_dom_parser&) = delete;
json_sax_dom_parser& operator=(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
~json_sax_dom_parser() = default;
bool null()
{
handle_value(nullptr);
return true;
}
bool boolean(bool val)
{
handle_value(val);
return true;
}
bool number_integer(number_integer_t val)
{
handle_value(val);
return true;
}
bool number_unsigned(number_unsigned_t val)
{
handle_value(val);
return true;
}
bool number_float(number_float_t val, const string_t& /*unused*/)
{
handle_value(val);
return true;
}
bool string(string_t& val)
{
handle_value(val);
return true;
}
bool binary(binary_t& val)
{
handle_value(std::move(val));
return true;
}
bool start_object(std::size_t len)
{
ref_stack.push_back(handle_value(BasicJsonType::value_t::object));
if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size()))
{
JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len), *ref_stack.back()));
}
return true;
}
bool key(string_t& val)
{
// add null at given key and store the reference for later
object_element = &(ref_stack.back()->m_value.object->operator[](val));
return true;
}
bool end_object()
{
ref_stack.back()->set_parents();
ref_stack.pop_back();
return true;
}
bool start_array(std::size_t len)
{
ref_stack.push_back(handle_value(BasicJsonType::value_t::array));
if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size()))
{
JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len), *ref_stack.back()));
}
return true;
}
bool end_array()
{
ref_stack.back()->set_parents();
ref_stack.pop_back();
return true;
}
template<class Exception>
bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,
const Exception& ex)
{
errored = true;
static_cast<void>(ex);
if (allow_exceptions)
{
JSON_THROW(ex);
}
return false;
}
constexpr bool is_errored() const
{
return errored;
}
private:
/*!
@invariant If the ref stack is empty, then the passed value will be the new
root.
@invariant If the ref stack contains a value, then it is an array or an
object to which we can add elements
*/
template<typename Value>
JSON_HEDLEY_RETURNS_NON_NULL
BasicJsonType* handle_value(Value&& v)
{
if (ref_stack.empty())
{
root = BasicJsonType(std::forward<Value>(v));
return &root;
}
JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object());
if (ref_stack.back()->is_array())
{
ref_stack.back()->m_value.array->emplace_back(std::forward<Value>(v));
return &(ref_stack.back()->m_value.array->back());
}
JSON_ASSERT(ref_stack.back()->is_object());
JSON_ASSERT(object_element);
*object_element = BasicJsonType(std::forward<Value>(v));
return object_element;
}
/// the parsed JSON value
BasicJsonType& root;
/// stack to model hierarchy of values
std::vector<BasicJsonType*> ref_stack {};
/// helper to hold the reference for the next object element
BasicJsonType* object_element = nullptr;
/// whether a syntax error occurred
bool errored = false;
/// whether to throw exceptions in case of errors
const bool allow_exceptions = true;
};
template<typename BasicJsonType>
class json_sax_dom_callback_parser
{
public:
using number_integer_t = typename BasicJsonType::number_integer_t;
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
using number_float_t = typename BasicJsonType::number_float_t;
using string_t = typename BasicJsonType::string_t;
using binary_t = typename BasicJsonType::binary_t;
using parser_callback_t = typename BasicJsonType::parser_callback_t;
using parse_event_t = typename BasicJsonType::parse_event_t;
json_sax_dom_callback_parser(BasicJsonType& r,
const parser_callback_t cb,
const bool allow_exceptions_ = true)
: root(r), callback(cb), allow_exceptions(allow_exceptions_)
{
keep_stack.push_back(true);
}
// make class move-only
json_sax_dom_callback_parser(const json_sax_dom_callback_parser&) = delete;
json_sax_dom_callback_parser(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
json_sax_dom_callback_parser& operator=(const json_sax_dom_callback_parser&) = delete;
json_sax_dom_callback_parser& operator=(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
~json_sax_dom_callback_parser() = default;
bool null()
{
handle_value(nullptr);
return true;
}
bool boolean(bool val)
{
handle_value(val);
return true;
}
bool number_integer(number_integer_t val)
{
handle_value(val);
return true;
}
bool number_unsigned(number_unsigned_t val)
{
handle_value(val);
return true;
}
bool number_float(number_float_t val, const string_t& /*unused*/)
{
handle_value(val);
return true;
}
bool string(string_t& val)
{
handle_value(val);
return true;
}
bool binary(binary_t& val)
{
handle_value(std::move(val));
return true;
}
bool start_object(std::size_t len)
{
// check callback for object start
const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::object_start, discarded);
keep_stack.push_back(keep);
auto val = handle_value(BasicJsonType::value_t::object, true);
ref_stack.push_back(val.second);
// check object limit
if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size()))
{
JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len), *ref_stack.back()));
}
return true;
}
bool key(string_t& val)
{
BasicJsonType k = BasicJsonType(val);
// check callback for key
const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::key, k);
key_keep_stack.push_back(keep);
// add discarded value at given key and store the reference for later
if (keep && ref_stack.back())
{
object_element = &(ref_stack.back()->m_value.object->operator[](val) = discarded);
}
return true;
}
bool end_object()
{
if (ref_stack.back())
{
if (!callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back()))
{
// discard object
*ref_stack.back() = discarded;
}
else
{
ref_stack.back()->set_parents();
}
}
JSON_ASSERT(!ref_stack.empty());
JSON_ASSERT(!keep_stack.empty());
ref_stack.pop_back();
keep_stack.pop_back();
if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_structured())
{
// remove discarded value
for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it)
{
if (it->is_discarded())
{
ref_stack.back()->erase(it);
break;
}
}
}
return true;
}
bool start_array(std::size_t len)
{
const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::array_start, discarded);
keep_stack.push_back(keep);
auto val = handle_value(BasicJsonType::value_t::array, true);
ref_stack.push_back(val.second);
// check array limit
if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size()))
{
JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len), *ref_stack.back()));
}
return true;
}
bool end_array()
{
bool keep = true;
if (ref_stack.back())
{
keep = callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back());
if (keep)
{
ref_stack.back()->set_parents();
}
else
{
// discard array
*ref_stack.back() = discarded;
}
}
JSON_ASSERT(!ref_stack.empty());
JSON_ASSERT(!keep_stack.empty());
ref_stack.pop_back();
keep_stack.pop_back();
// remove discarded value
if (!keep && !ref_stack.empty() && ref_stack.back()->is_array())
{
ref_stack.back()->m_value.array->pop_back();
}
return true;
}
template<class Exception>
bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,
const Exception& ex)
{
errored = true;
static_cast<void>(ex);
if (allow_exceptions)
{
JSON_THROW(ex);
}
return false;
}
constexpr bool is_errored() const
{
return errored;
}
private:
/*!
@param[in] v value to add to the JSON value we build during parsing
@param[in] skip_callback whether we should skip calling the callback
function; this is required after start_array() and
start_object() SAX events, because otherwise we would call the
callback function with an empty array or object, respectively.
@invariant If the ref stack is empty, then the passed value will be the new
root.
@invariant If the ref stack contains a value, then it is an array or an
object to which we can add elements
@return pair of boolean (whether value should be kept) and pointer (to the
passed value in the ref_stack hierarchy; nullptr if not kept)
*/
template<typename Value>
std::pair<bool, BasicJsonType*> handle_value(Value&& v, const bool skip_callback = false)
{
JSON_ASSERT(!keep_stack.empty());
// do not handle this value if we know it would be added to a discarded
// container
if (!keep_stack.back())
{
return {false, nullptr};
}
// create value
auto value = BasicJsonType(std::forward<Value>(v));
// check callback
const bool keep = skip_callback || callback(static_cast<int>(ref_stack.size()), parse_event_t::value, value);
// do not handle this value if we just learnt it shall be discarded
if (!keep)
{
return {false, nullptr};
}
if (ref_stack.empty())
{
root = std::move(value);
return {true, &root};
}
// skip this value if we already decided to skip the parent
// (https://github.com/nlohmann/json/issues/971#issuecomment-413678360)
if (!ref_stack.back())
{
return {false, nullptr};
}
// we now only expect arrays and objects
JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object());
// array
if (ref_stack.back()->is_array())
{
ref_stack.back()->m_value.array->emplace_back(std::move(value));
return {true, &(ref_stack.back()->m_value.array->back())};
}
// object
JSON_ASSERT(ref_stack.back()->is_object());
// check if we should store an element for the current key
JSON_ASSERT(!key_keep_stack.empty());
const bool store_element = key_keep_stack.back();
key_keep_stack.pop_back();
if (!store_element)
{
return {false, nullptr};
}
JSON_ASSERT(object_element);
*object_element = std::move(value);
return {true, object_element};
}
/// the parsed JSON value
BasicJsonType& root;
/// stack to model hierarchy of values
std::vector<BasicJsonType*> ref_stack {};
/// stack to manage which values to keep
std::vector<bool> keep_stack {};
/// stack to manage which object keys to keep
std::vector<bool> key_keep_stack {};
/// helper to hold the reference for the next object element
BasicJsonType* object_element = nullptr;
/// whether a syntax error occurred
bool errored = false;
/// callback function
const parser_callback_t callback = nullptr;
/// whether to throw exceptions in case of errors
const bool allow_exceptions = true;
/// a discarded value for the callback
BasicJsonType discarded = BasicJsonType::value_t::discarded;
};
template<typename BasicJsonType>
class json_sax_acceptor
{
public:
using number_integer_t = typename BasicJsonType::number_integer_t;
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
using number_float_t = typename BasicJsonType::number_float_t;
using string_t = typename BasicJsonType::string_t;
using binary_t = typename BasicJsonType::binary_t;
bool null()
{
return true;
}
bool boolean(bool /*unused*/)
{
return true;
}
bool number_integer(number_integer_t /*unused*/)
{
return true;
}
bool number_unsigned(number_unsigned_t /*unused*/)
{
return true;
}
bool number_float(number_float_t /*unused*/, const string_t& /*unused*/)
{
return true;
}
bool string(string_t& /*unused*/)
{
return true;
}
bool binary(binary_t& /*unused*/)
{
return true;
}
bool start_object(std::size_t /*unused*/ = std::size_t(-1))
{
return true;
}
bool key(string_t& /*unused*/)
{
return true;
}
bool end_object()
{
return true;
}
bool start_array(std::size_t /*unused*/ = std::size_t(-1))
{
return true;
}
bool end_array()
{
return true;
}
bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const detail::exception& /*unused*/)
{
return false;
}
};
} // namespace detail
} // namespace nlohmann

1623
lib/json/include/nlohmann/detail/input/lexer.hpp

File diff suppressed because it is too large Load Diff

492
lib/json/include/nlohmann/detail/input/parser.hpp

@ -0,0 +1,492 @@
#pragma once
#include <cmath> // isfinite
#include <cstdint> // uint8_t
#include <functional> // function
#include <string> // string
#include <utility> // move
#include <vector> // vector
#include <nlohmann/detail/exceptions.hpp>
#include <nlohmann/detail/input/input_adapters.hpp>
#include <nlohmann/detail/input/json_sax.hpp>
#include <nlohmann/detail/input/lexer.hpp>
#include <nlohmann/detail/macro_scope.hpp>
#include <nlohmann/detail/meta/is_sax.hpp>
#include <nlohmann/detail/value_t.hpp>
namespace nlohmann
{
namespace detail
{
////////////
// parser //
////////////
enum class parse_event_t : uint8_t
{
/// the parser read `{` and started to process a JSON object
object_start,
/// the parser read `}` and finished processing a JSON object
object_end,
/// the parser read `[` and started to process a JSON array
array_start,
/// the parser read `]` and finished processing a JSON array
array_end,
/// the parser read a key of a value in an object
key,
/// the parser finished reading a JSON value
value
};
template<typename BasicJsonType>
using parser_callback_t =
std::function<bool(int /*depth*/, parse_event_t /*event*/, BasicJsonType& /*parsed*/)>;
/*!
@brief syntax analysis
This class implements a recursive descent parser.
*/
template<typename BasicJsonType, typename InputAdapterType>
class parser
{
using number_integer_t = typename BasicJsonType::number_integer_t;
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
using number_float_t = typename BasicJsonType::number_float_t;
using string_t = typename BasicJsonType::string_t;
using lexer_t = lexer<BasicJsonType, InputAdapterType>;
using token_type = typename lexer_t::token_type;
public:
/// a parser reading from an input adapter
explicit parser(InputAdapterType&& adapter,
const parser_callback_t<BasicJsonType> cb = nullptr,
const bool allow_exceptions_ = true,
const bool skip_comments = false)
: callback(cb)
, m_lexer(std::move(adapter), skip_comments)
, allow_exceptions(allow_exceptions_)
{
// read first token
get_token();
}
/*!
@brief public parser interface
@param[in] strict whether to expect the last token to be EOF
@param[in,out] result parsed JSON value
@throw parse_error.101 in case of an unexpected token
@throw parse_error.102 if to_unicode fails or surrogate error
@throw parse_error.103 if to_unicode fails
*/
void parse(const bool strict, BasicJsonType& result)
{
if (callback)
{
json_sax_dom_callback_parser<BasicJsonType> sdp(result, callback, allow_exceptions);
sax_parse_internal(&sdp);
// in strict mode, input must be completely read
if (strict && (get_token() != token_type::end_of_input))
{
sdp.parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(),
exception_message(token_type::end_of_input, "value"), BasicJsonType()));
}
// in case of an error, return discarded value
if (sdp.is_errored())
{
result = value_t::discarded;
return;
}
// set top-level value to null if it was discarded by the callback
// function
if (result.is_discarded())
{
result = nullptr;
}
}
else
{
json_sax_dom_parser<BasicJsonType> sdp(result, allow_exceptions);
sax_parse_internal(&sdp);
// in strict mode, input must be completely read
if (strict && (get_token() != token_type::end_of_input))
{
sdp.parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), BasicJsonType()));
}
// in case of an error, return discarded value
if (sdp.is_errored())
{
result = value_t::discarded;
return;
}
}
result.assert_invariant();
}
/*!
@brief public accept interface
@param[in] strict whether to expect the last token to be EOF
@return whether the input is a proper JSON text
*/
bool accept(const bool strict = true)
{
json_sax_acceptor<BasicJsonType> sax_acceptor;
return sax_parse(&sax_acceptor, strict);
}
template<typename SAX>
JSON_HEDLEY_NON_NULL(2)
bool sax_parse(SAX* sax, const bool strict = true)
{
(void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};
const bool result = sax_parse_internal(sax);
// strict mode: next byte must be EOF
if (result && strict && (get_token() != token_type::end_of_input))
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), BasicJsonType()));
}
return result;
}
private:
template<typename SAX>
JSON_HEDLEY_NON_NULL(2)
bool sax_parse_internal(SAX* sax)
{
// stack to remember the hierarchy of structured values we are parsing
// true = array; false = object
std::vector<bool> states;
// value to avoid a goto (see comment where set to true)
bool skip_to_state_evaluation = false;
while (true)
{
if (!skip_to_state_evaluation)
{
// invariant: get_token() was called before each iteration
switch (last_token)
{
case token_type::begin_object:
{
if (JSON_HEDLEY_UNLIKELY(!sax->start_object(std::size_t(-1))))
{
return false;
}
// closing } -> we are done
if (get_token() == token_type::end_object)
{
if (JSON_HEDLEY_UNLIKELY(!sax->end_object()))
{
return false;
}
break;
}
// parse key
if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string))
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), BasicJsonType()));
}
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
{
return false;
}
// parse separator (:)
if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), BasicJsonType()));
}
// remember we are now inside an object
states.push_back(false);
// parse values
get_token();
continue;
}
case token_type::begin_array:
{
if (JSON_HEDLEY_UNLIKELY(!sax->start_array(std::size_t(-1))))
{
return false;
}
// closing ] -> we are done
if (get_token() == token_type::end_array)
{
if (JSON_HEDLEY_UNLIKELY(!sax->end_array()))
{
return false;
}
break;
}
// remember we are now inside an array
states.push_back(true);
// parse values (no need to call get_token)
continue;
}
case token_type::value_float:
{
const auto res = m_lexer.get_number_float();
if (JSON_HEDLEY_UNLIKELY(!std::isfinite(res)))
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
out_of_range::create(406, "number overflow parsing '" + m_lexer.get_token_string() + "'", BasicJsonType()));
}
if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string())))
{
return false;
}
break;
}
case token_type::literal_false:
{
if (JSON_HEDLEY_UNLIKELY(!sax->boolean(false)))
{
return false;
}
break;
}
case token_type::literal_null:
{
if (JSON_HEDLEY_UNLIKELY(!sax->null()))
{
return false;
}
break;
}
case token_type::literal_true:
{
if (JSON_HEDLEY_UNLIKELY(!sax->boolean(true)))
{
return false;
}
break;
}
case token_type::value_integer:
{
if (JSON_HEDLEY_UNLIKELY(!sax->number_integer(m_lexer.get_number_integer())))
{
return false;
}
break;
}
case token_type::value_string:
{
if (JSON_HEDLEY_UNLIKELY(!sax->string(m_lexer.get_string())))
{
return false;
}
break;
}
case token_type::value_unsigned:
{
if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(m_lexer.get_number_unsigned())))
{
return false;
}
break;
}
case token_type::parse_error:
{
// using "uninitialized" to avoid "expected" message
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), BasicJsonType()));
}
default: // the last token was unexpected
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), BasicJsonType()));
}
}
}
else
{
skip_to_state_evaluation = false;
}
// we reached this line after we successfully parsed a value
if (states.empty())
{
// empty stack: we reached the end of the hierarchy: done
return true;
}
if (states.back()) // array
{
// comma -> next value
if (get_token() == token_type::value_separator)
{
// parse a new value
get_token();
continue;
}
// closing ]
if (JSON_HEDLEY_LIKELY(last_token == token_type::end_array))
{
if (JSON_HEDLEY_UNLIKELY(!sax->end_array()))
{
return false;
}
// We are done with this array. Before we can parse a
// new value, we need to evaluate the new state first.
// By setting skip_to_state_evaluation to false, we
// are effectively jumping to the beginning of this if.
JSON_ASSERT(!states.empty());
states.pop_back();
skip_to_state_evaluation = true;
continue;
}
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array, "array"), BasicJsonType()));
}
// states.back() is false -> object
// comma -> next value
if (get_token() == token_type::value_separator)
{
// parse key
if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::value_string))
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), BasicJsonType()));
}
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
{
return false;
}
// parse separator (:)
if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), BasicJsonType()));
}
// parse values
get_token();
continue;
}
// closing }
if (JSON_HEDLEY_LIKELY(last_token == token_type::end_object))
{
if (JSON_HEDLEY_UNLIKELY(!sax->end_object()))
{
return false;
}
// We are done with this object. Before we can parse a
// new value, we need to evaluate the new state first.
// By setting skip_to_state_evaluation to false, we
// are effectively jumping to the beginning of this if.
JSON_ASSERT(!states.empty());
states.pop_back();
skip_to_state_evaluation = true;
continue;
}
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object, "object"), BasicJsonType()));
}
}
/// get next token from lexer
token_type get_token()
{
return last_token = m_lexer.scan();
}
std::string exception_message(const token_type expected, const std::string& context)
{
std::string error_msg = "syntax error ";
if (!context.empty())
{
error_msg += "while parsing " + context + " ";
}
error_msg += "- ";
if (last_token == token_type::parse_error)
{
error_msg += std::string(m_lexer.get_error_message()) + "; last read: '" +
m_lexer.get_token_string() + "'";
}
else
{
error_msg += "unexpected " + std::string(lexer_t::token_type_name(last_token));
}
if (expected != token_type::uninitialized)
{
error_msg += "; expected " + std::string(lexer_t::token_type_name(expected));
}
return error_msg;
}
private:
/// callback function
const parser_callback_t<BasicJsonType> callback = nullptr;
/// the type of the last read token
token_type last_token = token_type::uninitialized;
/// the lexer
lexer_t m_lexer;
/// whether to throw exceptions in case of errors
const bool allow_exceptions = true;
};
} // namespace detail
} // namespace nlohmann

27
lib/json/include/nlohmann/detail/input/position_t.hpp

@ -0,0 +1,27 @@
#pragma once
#include <cstddef> // size_t
namespace nlohmann
{
namespace detail
{
/// struct to capture the start position of the current token
struct position_t
{
/// the total number of characters read
std::size_t chars_read_total = 0;
/// the number of characters read in the current line
std::size_t chars_read_current_line = 0;
/// the number of lines read
std::size_t lines_read = 0;
/// conversion to size_t to preserve SAX interface
constexpr operator size_t() const
{
return chars_read_total;
}
};
} // namespace detail
} // namespace nlohmann

25
lib/json/include/nlohmann/detail/iterators/internal_iterator.hpp

@ -0,0 +1,25 @@
#pragma once
#include <nlohmann/detail/iterators/primitive_iterator.hpp>
namespace nlohmann
{
namespace detail
{
/*!
@brief an iterator value
@note This structure could easily be a union, but MSVC currently does not allow
unions members with complex constructors, see https://github.com/nlohmann/json/pull/105.
*/
template<typename BasicJsonType> struct internal_iterator
{
/// iterator for JSON objects
typename BasicJsonType::object_t::iterator object_iterator {};
/// iterator for JSON arrays
typename BasicJsonType::array_t::iterator array_iterator {};
/// generic iterator for all other types
primitive_iterator_t primitive_iterator {};
};
} // namespace detail
} // namespace nlohmann

646
lib/json/include/nlohmann/detail/iterators/iter_impl.hpp

@ -0,0 +1,646 @@
#pragma once
#include <iterator> // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next
#include <type_traits> // conditional, is_const, remove_const
#include <nlohmann/detail/exceptions.hpp>
#include <nlohmann/detail/iterators/internal_iterator.hpp>
#include <nlohmann/detail/iterators/primitive_iterator.hpp>
#include <nlohmann/detail/macro_scope.hpp>
#include <nlohmann/detail/meta/cpp_future.hpp>
#include <nlohmann/detail/meta/type_traits.hpp>
#include <nlohmann/detail/value_t.hpp>
namespace nlohmann
{
namespace detail
{
// forward declare, to be able to friend it later on
template<typename IteratorType> class iteration_proxy;
template<typename IteratorType> class iteration_proxy_value;
/*!
@brief a template for a bidirectional iterator for the @ref basic_json class
This class implements a both iterators (iterator and const_iterator) for the
@ref basic_json class.
@note An iterator is called *initialized* when a pointer to a JSON value has
been set (e.g., by a constructor or a copy assignment). If the iterator is
default-constructed, it is *uninitialized* and most methods are undefined.
**The library uses assertions to detect calls on uninitialized iterators.**
@requirement The class satisfies the following concept requirements:
-
[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):
The iterator that can be moved can be moved in both directions (i.e.
incremented and decremented).
@since version 1.0.0, simplified in version 2.0.9, change to bidirectional
iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593)
*/
template<typename BasicJsonType>
class iter_impl
{
/// the iterator with BasicJsonType of different const-ness
using other_iter_impl = iter_impl<typename std::conditional<std::is_const<BasicJsonType>::value, typename std::remove_const<BasicJsonType>::type, const BasicJsonType>::type>;
/// allow basic_json to access private members
friend other_iter_impl;
friend BasicJsonType;
friend iteration_proxy<iter_impl>;
friend iteration_proxy_value<iter_impl>;
using object_t = typename BasicJsonType::object_t;
using array_t = typename BasicJsonType::array_t;
// make sure BasicJsonType is basic_json or const basic_json
static_assert(is_basic_json<typename std::remove_const<BasicJsonType>::type>::value,
"iter_impl only accepts (const) basic_json");
public:
/// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17.
/// The C++ Standard has never required user-defined iterators to derive from std::iterator.
/// A user-defined iterator should provide publicly accessible typedefs named
/// iterator_category, value_type, difference_type, pointer, and reference.
/// Note that value_type is required to be non-const, even for constant iterators.
using iterator_category = std::bidirectional_iterator_tag;
/// the type of the values when the iterator is dereferenced
using value_type = typename BasicJsonType::value_type;
/// a type to represent differences between iterators
using difference_type = typename BasicJsonType::difference_type;
/// defines a pointer to the type iterated over (value_type)
using pointer = typename std::conditional<std::is_const<BasicJsonType>::value,
typename BasicJsonType::const_pointer,
typename BasicJsonType::pointer>::type;
/// defines a reference to the type iterated over (value_type)
using reference =
typename std::conditional<std::is_const<BasicJsonType>::value,
typename BasicJsonType::const_reference,
typename BasicJsonType::reference>::type;
iter_impl() = default;
~iter_impl() = default;
iter_impl(iter_impl&&) noexcept = default;
iter_impl& operator=(iter_impl&&) noexcept = default;
/*!
@brief constructor for a given JSON instance
@param[in] object pointer to a JSON object for this iterator
@pre object != nullptr
@post The iterator is initialized; i.e. `m_object != nullptr`.
*/
explicit iter_impl(pointer object) noexcept : m_object(object)
{
JSON_ASSERT(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
{
m_it.object_iterator = typename object_t::iterator();
break;
}
case value_t::array:
{
m_it.array_iterator = typename array_t::iterator();
break;
}
default:
{
m_it.primitive_iterator = primitive_iterator_t();
break;
}
}
}
/*!
@note The conventional copy constructor and copy assignment are implicitly
defined. Combined with the following converting constructor and
assignment, they support: (1) copy from iterator to iterator, (2)
copy from const iterator to const iterator, and (3) conversion from
iterator to const iterator. However conversion from const iterator
to iterator is not defined.
*/
/*!
@brief const copy constructor
@param[in] other const iterator to copy from
@note This copy constructor had to be defined explicitly to circumvent a bug
occurring on msvc v19.0 compiler (VS 2015) debug build. For more
information refer to: https://github.com/nlohmann/json/issues/1608
*/
iter_impl(const iter_impl<const BasicJsonType>& other) noexcept
: m_object(other.m_object), m_it(other.m_it)
{}
/*!
@brief converting assignment
@param[in] other const iterator to copy from
@return const/non-const iterator
@note It is not checked whether @a other is initialized.
*/
iter_impl& operator=(const iter_impl<const BasicJsonType>& other) noexcept
{
if (&other != this)
{
m_object = other.m_object;
m_it = other.m_it;
}
return *this;
}
/*!
@brief converting constructor
@param[in] other non-const iterator to copy from
@note It is not checked whether @a other is initialized.
*/
iter_impl(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept
: m_object(other.m_object), m_it(other.m_it)
{}
/*!
@brief converting assignment
@param[in] other non-const iterator to copy from
@return const/non-const iterator
@note It is not checked whether @a other is initialized.
*/
iter_impl& operator=(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept // NOLINT(cert-oop54-cpp)
{
m_object = other.m_object;
m_it = other.m_it;
return *this;
}
JSON_PRIVATE_UNLESS_TESTED:
/*!
@brief set the iterator to the first value
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
void set_begin() noexcept
{
JSON_ASSERT(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
{
m_it.object_iterator = m_object->m_value.object->begin();
break;
}
case value_t::array:
{
m_it.array_iterator = m_object->m_value.array->begin();
break;
}
case value_t::null:
{
// set to end so begin()==end() is true: null is empty
m_it.primitive_iterator.set_end();
break;
}
default:
{
m_it.primitive_iterator.set_begin();
break;
}
}
}
/*!
@brief set the iterator past the last value
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
void set_end() noexcept
{
JSON_ASSERT(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
{
m_it.object_iterator = m_object->m_value.object->end();
break;
}
case value_t::array:
{
m_it.array_iterator = m_object->m_value.array->end();
break;
}
default:
{
m_it.primitive_iterator.set_end();
break;
}
}
}
public:
/*!
@brief return a reference to the value pointed to by the iterator
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
reference operator*() const
{
JSON_ASSERT(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
{
JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end());
return m_it.object_iterator->second;
}
case value_t::array:
{
JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end());
return *m_it.array_iterator;
}
case value_t::null:
JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object));
default:
{
if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin()))
{
return *m_object;
}
JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object));
}
}
}
/*!
@brief dereference the iterator
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
pointer operator->() const
{
JSON_ASSERT(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
{
JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end());
return &(m_it.object_iterator->second);
}
case value_t::array:
{
JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end());
return &*m_it.array_iterator;
}
default:
{
if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin()))
{
return m_object;
}
JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object));
}
}
}
/*!
@brief post-increment (it++)
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
iter_impl const operator++(int) // NOLINT(readability-const-return-type)
{
auto result = *this;
++(*this);
return result;
}
/*!
@brief pre-increment (++it)
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
iter_impl& operator++()
{
JSON_ASSERT(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
{
std::advance(m_it.object_iterator, 1);
break;
}
case value_t::array:
{
std::advance(m_it.array_iterator, 1);
break;
}
default:
{
++m_it.primitive_iterator;
break;
}
}
return *this;
}
/*!
@brief post-decrement (it--)
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
iter_impl const operator--(int) // NOLINT(readability-const-return-type)
{
auto result = *this;
--(*this);
return result;
}
/*!
@brief pre-decrement (--it)
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
iter_impl& operator--()
{
JSON_ASSERT(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
{
std::advance(m_it.object_iterator, -1);
break;
}
case value_t::array:
{
std::advance(m_it.array_iterator, -1);
break;
}
default:
{
--m_it.primitive_iterator;
break;
}
}
return *this;
}
/*!
@brief comparison: equal
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
template < typename IterImpl, detail::enable_if_t < (std::is_same<IterImpl, iter_impl>::value || std::is_same<IterImpl, other_iter_impl>::value), std::nullptr_t > = nullptr >
bool operator==(const IterImpl& other) const
{
// if objects are not the same, the comparison is undefined
if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
{
JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", *m_object));
}
JSON_ASSERT(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
return (m_it.object_iterator == other.m_it.object_iterator);
case value_t::array:
return (m_it.array_iterator == other.m_it.array_iterator);
default:
return (m_it.primitive_iterator == other.m_it.primitive_iterator);
}
}
/*!
@brief comparison: not equal
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
template < typename IterImpl, detail::enable_if_t < (std::is_same<IterImpl, iter_impl>::value || std::is_same<IterImpl, other_iter_impl>::value), std::nullptr_t > = nullptr >
bool operator!=(const IterImpl& other) const
{
return !operator==(other);
}
/*!
@brief comparison: smaller
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
bool operator<(const iter_impl& other) const
{
// if objects are not the same, the comparison is undefined
if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
{
JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", *m_object));
}
JSON_ASSERT(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators", *m_object));
case value_t::array:
return (m_it.array_iterator < other.m_it.array_iterator);
default:
return (m_it.primitive_iterator < other.m_it.primitive_iterator);
}
}
/*!
@brief comparison: less than or equal
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
bool operator<=(const iter_impl& other) const
{
return !other.operator < (*this);
}
/*!
@brief comparison: greater than
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
bool operator>(const iter_impl& other) const
{
return !operator<=(other);
}
/*!
@brief comparison: greater than or equal
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
bool operator>=(const iter_impl& other) const
{
return !operator<(other);
}
/*!
@brief add to iterator
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
iter_impl& operator+=(difference_type i)
{
JSON_ASSERT(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", *m_object));
case value_t::array:
{
std::advance(m_it.array_iterator, i);
break;
}
default:
{
m_it.primitive_iterator += i;
break;
}
}
return *this;
}
/*!
@brief subtract from iterator
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
iter_impl& operator-=(difference_type i)
{
return operator+=(-i);
}
/*!
@brief add to iterator
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
iter_impl operator+(difference_type i) const
{
auto result = *this;
result += i;
return result;
}
/*!
@brief addition of distance and iterator
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
friend iter_impl operator+(difference_type i, const iter_impl& it)
{
auto result = it;
result += i;
return result;
}
/*!
@brief subtract from iterator
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
iter_impl operator-(difference_type i) const
{
auto result = *this;
result -= i;
return result;
}
/*!
@brief return difference
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
difference_type operator-(const iter_impl& other) const
{
JSON_ASSERT(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", *m_object));
case value_t::array:
return m_it.array_iterator - other.m_it.array_iterator;
default:
return m_it.primitive_iterator - other.m_it.primitive_iterator;
}
}
/*!
@brief access to successor
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
reference operator[](difference_type n) const
{
JSON_ASSERT(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators", *m_object));
case value_t::array:
return *std::next(m_it.array_iterator, n);
case value_t::null:
JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object));
default:
{
if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.get_value() == -n))
{
return *m_object;
}
JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object));
}
}
}
/*!
@brief return the key of an object iterator
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
const typename object_t::key_type& key() const
{
JSON_ASSERT(m_object != nullptr);
if (JSON_HEDLEY_LIKELY(m_object->is_object()))
{
return m_it.object_iterator->first;
}
JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators", *m_object));
}
/*!
@brief return the value of an iterator
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
reference value() const
{
return operator*();
}
JSON_PRIVATE_UNLESS_TESTED:
/// associated JSON instance
pointer m_object = nullptr;
/// the actual iterator of the associated instance
internal_iterator<typename std::remove_const<BasicJsonType>::type> m_it {};
};
} // namespace detail
} // namespace nlohmann

181
lib/json/include/nlohmann/detail/iterators/iteration_proxy.hpp

@ -0,0 +1,181 @@
#pragma once
#include <cstddef> // size_t
#include <iterator> // input_iterator_tag
#include <string> // string, to_string
#include <tuple> // tuple_size, get, tuple_element
#include <utility> // move
#include <nlohmann/detail/meta/type_traits.hpp>
#include <nlohmann/detail/value_t.hpp>
namespace nlohmann
{
namespace detail
{
template<typename string_type>
void int_to_string( string_type& target, std::size_t value )
{
// For ADL
using std::to_string;
target = to_string(value);
}
template<typename IteratorType> class iteration_proxy_value
{
public:
using difference_type = std::ptrdiff_t;
using value_type = iteration_proxy_value;
using pointer = value_type * ;
using reference = value_type & ;
using iterator_category = std::input_iterator_tag;
using string_type = typename std::remove_cv< typename std::remove_reference<decltype( std::declval<IteratorType>().key() ) >::type >::type;
private:
/// the iterator
IteratorType anchor;
/// an index for arrays (used to create key names)
std::size_t array_index = 0;
/// last stringified array index
mutable std::size_t array_index_last = 0;
/// a string representation of the array index
mutable string_type array_index_str = "0";
/// an empty string (to return a reference for primitive values)
const string_type empty_str{};
public:
explicit iteration_proxy_value(IteratorType it) noexcept
: anchor(std::move(it))
{}
/// dereference operator (needed for range-based for)
iteration_proxy_value& operator*()
{
return *this;
}
/// increment operator (needed for range-based for)
iteration_proxy_value& operator++()
{
++anchor;
++array_index;
return *this;
}
/// equality operator (needed for InputIterator)
bool operator==(const iteration_proxy_value& o) const
{
return anchor == o.anchor;
}
/// inequality operator (needed for range-based for)
bool operator!=(const iteration_proxy_value& o) const
{
return anchor != o.anchor;
}
/// return key of the iterator
const string_type& key() const
{
JSON_ASSERT(anchor.m_object != nullptr);
switch (anchor.m_object->type())
{
// use integer array index as key
case value_t::array:
{
if (array_index != array_index_last)
{
int_to_string( array_index_str, array_index );
array_index_last = array_index;
}
return array_index_str;
}
// use key from the object
case value_t::object:
return anchor.key();
// use an empty key for all primitive types
default:
return empty_str;
}
}
/// return value of the iterator
typename IteratorType::reference value() const
{
return anchor.value();
}
};
/// proxy class for the items() function
template<typename IteratorType> class iteration_proxy
{
private:
/// the container to iterate
typename IteratorType::reference container;
public:
/// construct iteration proxy from a container
explicit iteration_proxy(typename IteratorType::reference cont) noexcept
: container(cont) {}
/// return iterator begin (needed for range-based for)
iteration_proxy_value<IteratorType> begin() noexcept
{
return iteration_proxy_value<IteratorType>(container.begin());
}
/// return iterator end (needed for range-based for)
iteration_proxy_value<IteratorType> end() noexcept
{
return iteration_proxy_value<IteratorType>(container.end());
}
};
// Structured Bindings Support
// For further reference see https://blog.tartanllama.xyz/structured-bindings/
// And see https://github.com/nlohmann/json/pull/1391
template<std::size_t N, typename IteratorType, enable_if_t<N == 0, int> = 0>
auto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.key())
{
return i.key();
}
// Structured Bindings Support
// For further reference see https://blog.tartanllama.xyz/structured-bindings/
// And see https://github.com/nlohmann/json/pull/1391
template<std::size_t N, typename IteratorType, enable_if_t<N == 1, int> = 0>
auto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.value())
{
return i.value();
}
} // namespace detail
} // namespace nlohmann
// The Addition to the STD Namespace is required to add
// Structured Bindings Support to the iteration_proxy_value class
// For further reference see https://blog.tartanllama.xyz/structured-bindings/
// And see https://github.com/nlohmann/json/pull/1391
namespace std
{
#if defined(__clang__)
// Fix: https://github.com/nlohmann/json/issues/1401
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wmismatched-tags"
#endif
template<typename IteratorType>
class tuple_size<::nlohmann::detail::iteration_proxy_value<IteratorType>>
: public std::integral_constant<std::size_t, 2> {};
template<std::size_t N, typename IteratorType>
class tuple_element<N, ::nlohmann::detail::iteration_proxy_value<IteratorType >>
{
public:
using type = decltype(
get<N>(std::declval <
::nlohmann::detail::iteration_proxy_value<IteratorType >> ()));
};
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
} // namespace std

51
lib/json/include/nlohmann/detail/iterators/iterator_traits.hpp

@ -0,0 +1,51 @@
#pragma once
#include <iterator> // random_access_iterator_tag
#include <nlohmann/detail/meta/void_t.hpp>
#include <nlohmann/detail/meta/cpp_future.hpp>
namespace nlohmann
{
namespace detail
{
template<typename It, typename = void>
struct iterator_types {};
template<typename It>
struct iterator_types <
It,
void_t<typename It::difference_type, typename It::value_type, typename It::pointer,
typename It::reference, typename It::iterator_category >>
{
using difference_type = typename It::difference_type;
using value_type = typename It::value_type;
using pointer = typename It::pointer;
using reference = typename It::reference;
using iterator_category = typename It::iterator_category;
};
// This is required as some compilers implement std::iterator_traits in a way that
// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341.
template<typename T, typename = void>
struct iterator_traits
{
};
template<typename T>
struct iterator_traits < T, enable_if_t < !std::is_pointer<T>::value >>
: iterator_types<T>
{
};
template<typename T>
struct iterator_traits<T*, enable_if_t<std::is_object<T>::value>>
{
using iterator_category = std::random_access_iterator_tag;
using value_type = T;
using difference_type = ptrdiff_t;
using pointer = T*;
using reference = T&;
};
} // namespace detail
} // namespace nlohmann

119
lib/json/include/nlohmann/detail/iterators/json_reverse_iterator.hpp

@ -0,0 +1,119 @@
#pragma once
#include <cstddef> // ptrdiff_t
#include <iterator> // reverse_iterator
#include <utility> // declval
namespace nlohmann
{
namespace detail
{
//////////////////////
// reverse_iterator //
//////////////////////
/*!
@brief a template for a reverse iterator class
@tparam Base the base iterator type to reverse. Valid types are @ref
iterator (to create @ref reverse_iterator) and @ref const_iterator (to
create @ref const_reverse_iterator).
@requirement The class satisfies the following concept requirements:
-
[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):
The iterator that can be moved can be moved in both directions (i.e.
incremented and decremented).
- [OutputIterator](https://en.cppreference.com/w/cpp/named_req/OutputIterator):
It is possible to write to the pointed-to element (only if @a Base is
@ref iterator).
@since version 1.0.0
*/
template<typename Base>
class json_reverse_iterator : public std::reverse_iterator<Base>
{
public:
using difference_type = std::ptrdiff_t;
/// shortcut to the reverse iterator adapter
using base_iterator = std::reverse_iterator<Base>;
/// the reference type for the pointed-to element
using reference = typename Base::reference;
/// create reverse iterator from iterator
explicit json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept
: base_iterator(it) {}
/// create reverse iterator from base class
explicit json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {}
/// post-increment (it++)
json_reverse_iterator const operator++(int) // NOLINT(readability-const-return-type)
{
return static_cast<json_reverse_iterator>(base_iterator::operator++(1));
}
/// pre-increment (++it)
json_reverse_iterator& operator++()
{
return static_cast<json_reverse_iterator&>(base_iterator::operator++());
}
/// post-decrement (it--)
json_reverse_iterator const operator--(int) // NOLINT(readability-const-return-type)
{
return static_cast<json_reverse_iterator>(base_iterator::operator--(1));
}
/// pre-decrement (--it)
json_reverse_iterator& operator--()
{
return static_cast<json_reverse_iterator&>(base_iterator::operator--());
}
/// add to iterator
json_reverse_iterator& operator+=(difference_type i)
{
return static_cast<json_reverse_iterator&>(base_iterator::operator+=(i));
}
/// add to iterator
json_reverse_iterator operator+(difference_type i) const
{
return static_cast<json_reverse_iterator>(base_iterator::operator+(i));
}
/// subtract from iterator
json_reverse_iterator operator-(difference_type i) const
{
return static_cast<json_reverse_iterator>(base_iterator::operator-(i));
}
/// return difference
difference_type operator-(const json_reverse_iterator& other) const
{
return base_iterator(*this) - base_iterator(other);
}
/// access to successor
reference operator[](difference_type n) const
{
return *(this->operator+(n));
}
/// return the key of an object iterator
auto key() const -> decltype(std::declval<Base>().key())
{
auto it = --this->base();
return it.key();
}
/// return the value of an iterator
reference value() const
{
auto it = --this->base();
return it.operator * ();
}
};
} // namespace detail
} // namespace nlohmann

123
lib/json/include/nlohmann/detail/iterators/primitive_iterator.hpp

@ -0,0 +1,123 @@
#pragma once
#include <cstddef> // ptrdiff_t
#include <limits> // numeric_limits
#include <nlohmann/detail/macro_scope.hpp>
namespace nlohmann
{
namespace detail
{
/*
@brief an iterator for primitive JSON types
This class models an iterator for primitive JSON types (boolean, number,
string). It's only purpose is to allow the iterator/const_iterator classes
to "iterate" over primitive values. Internally, the iterator is modeled by
a `difference_type` variable. Value begin_value (`0`) models the begin,
end_value (`1`) models past the end.
*/
class primitive_iterator_t
{
private:
using difference_type = std::ptrdiff_t;
static constexpr difference_type begin_value = 0;
static constexpr difference_type end_value = begin_value + 1;
JSON_PRIVATE_UNLESS_TESTED:
/// iterator as signed integer type
difference_type m_it = (std::numeric_limits<std::ptrdiff_t>::min)();
public:
constexpr difference_type get_value() const noexcept
{
return m_it;
}
/// set iterator to a defined beginning
void set_begin() noexcept
{
m_it = begin_value;
}
/// set iterator to a defined past the end
void set_end() noexcept
{
m_it = end_value;
}
/// return whether the iterator can be dereferenced
constexpr bool is_begin() const noexcept
{
return m_it == begin_value;
}
/// return whether the iterator is at end
constexpr bool is_end() const noexcept
{
return m_it == end_value;
}
friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
{
return lhs.m_it == rhs.m_it;
}
friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
{
return lhs.m_it < rhs.m_it;
}
primitive_iterator_t operator+(difference_type n) noexcept
{
auto result = *this;
result += n;
return result;
}
friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
{
return lhs.m_it - rhs.m_it;
}
primitive_iterator_t& operator++() noexcept
{
++m_it;
return *this;
}
primitive_iterator_t const operator++(int) noexcept // NOLINT(readability-const-return-type)
{
auto result = *this;
++m_it;
return result;
}
primitive_iterator_t& operator--() noexcept
{
--m_it;
return *this;
}
primitive_iterator_t const operator--(int) noexcept // NOLINT(readability-const-return-type)
{
auto result = *this;
--m_it;
return result;
}
primitive_iterator_t& operator+=(difference_type n) noexcept
{
m_it += n;
return *this;
}
primitive_iterator_t& operator-=(difference_type n) noexcept
{
m_it -= n;
return *this;
}
};
} // namespace detail
} // namespace nlohmann

934
lib/json/include/nlohmann/detail/json_pointer.hpp

@ -0,0 +1,934 @@
#pragma once
#include <algorithm> // all_of
#include <cctype> // isdigit
#include <limits> // max
#include <numeric> // accumulate
#include <string> // string
#include <utility> // move
#include <vector> // vector
#include <nlohmann/detail/exceptions.hpp>
#include <nlohmann/detail/macro_scope.hpp>
#include <nlohmann/detail/string_escape.hpp>
#include <nlohmann/detail/value_t.hpp>
namespace nlohmann
{
template<typename BasicJsonType>
class json_pointer
{
// allow basic_json to access private members
NLOHMANN_BASIC_JSON_TPL_DECLARATION
friend class basic_json;
public:
/*!
@brief create JSON pointer
Create a JSON pointer according to the syntax described in
[Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3).
@param[in] s string representing the JSON pointer; if omitted, the empty
string is assumed which references the whole JSON value
@throw parse_error.107 if the given JSON pointer @a s is nonempty and does
not begin with a slash (`/`); see example below
@throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s is
not followed by `0` (representing `~`) or `1` (representing `/`); see
example below
@liveexample{The example shows the construction several valid JSON pointers
as well as the exceptional behavior.,json_pointer}
@since version 2.0.0
*/
explicit json_pointer(const std::string& s = "")
: reference_tokens(split(s))
{}
/*!
@brief return a string representation of the JSON pointer
@invariant For each JSON pointer `ptr`, it holds:
@code {.cpp}
ptr == json_pointer(ptr.to_string());
@endcode
@return a string representation of the JSON pointer
@liveexample{The example shows the result of `to_string`.,json_pointer__to_string}
@since version 2.0.0
*/
std::string to_string() const
{
return std::accumulate(reference_tokens.begin(), reference_tokens.end(),
std::string{},
[](const std::string & a, const std::string & b)
{
return a + "/" + detail::escape(b);
});
}
/// @copydoc to_string()
operator std::string() const
{
return to_string();
}
/*!
@brief append another JSON pointer at the end of this JSON pointer
@param[in] ptr JSON pointer to append
@return JSON pointer with @a ptr appended
@liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add}
@complexity Linear in the length of @a ptr.
@sa see @ref operator/=(std::string) to append a reference token
@sa see @ref operator/=(std::size_t) to append an array index
@sa see @ref operator/(const json_pointer&, const json_pointer&) for a binary operator
@since version 3.6.0
*/
json_pointer& operator/=(const json_pointer& ptr)
{
reference_tokens.insert(reference_tokens.end(),
ptr.reference_tokens.begin(),
ptr.reference_tokens.end());
return *this;
}
/*!
@brief append an unescaped reference token at the end of this JSON pointer
@param[in] token reference token to append
@return JSON pointer with @a token appended without escaping @a token
@liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add}
@complexity Amortized constant.
@sa see @ref operator/=(const json_pointer&) to append a JSON pointer
@sa see @ref operator/=(std::size_t) to append an array index
@sa see @ref operator/(const json_pointer&, std::size_t) for a binary operator
@since version 3.6.0
*/
json_pointer& operator/=(std::string token)
{
push_back(std::move(token));
return *this;
}
/*!
@brief append an array index at the end of this JSON pointer
@param[in] array_idx array index to append
@return JSON pointer with @a array_idx appended
@liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add}
@complexity Amortized constant.
@sa see @ref operator/=(const json_pointer&) to append a JSON pointer
@sa see @ref operator/=(std::string) to append a reference token
@sa see @ref operator/(const json_pointer&, std::string) for a binary operator
@since version 3.6.0
*/
json_pointer& operator/=(std::size_t array_idx)
{
return *this /= std::to_string(array_idx);
}
/*!
@brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer
@param[in] lhs JSON pointer
@param[in] rhs JSON pointer
@return a new JSON pointer with @a rhs appended to @a lhs
@liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary}
@complexity Linear in the length of @a lhs and @a rhs.
@sa see @ref operator/=(const json_pointer&) to append a JSON pointer
@since version 3.6.0
*/
friend json_pointer operator/(const json_pointer& lhs,
const json_pointer& rhs)
{
return json_pointer(lhs) /= rhs;
}
/*!
@brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer
@param[in] ptr JSON pointer
@param[in] token reference token
@return a new JSON pointer with unescaped @a token appended to @a ptr
@liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary}
@complexity Linear in the length of @a ptr.
@sa see @ref operator/=(std::string) to append a reference token
@since version 3.6.0
*/
friend json_pointer operator/(const json_pointer& ptr, std::string token) // NOLINT(performance-unnecessary-value-param)
{
return json_pointer(ptr) /= std::move(token);
}
/*!
@brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer
@param[in] ptr JSON pointer
@param[in] array_idx array index
@return a new JSON pointer with @a array_idx appended to @a ptr
@liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary}
@complexity Linear in the length of @a ptr.
@sa see @ref operator/=(std::size_t) to append an array index
@since version 3.6.0
*/
friend json_pointer operator/(const json_pointer& ptr, std::size_t array_idx)
{
return json_pointer(ptr) /= array_idx;
}
/*!
@brief returns the parent of this JSON pointer
@return parent of this JSON pointer; in case this JSON pointer is the root,
the root itself is returned
@complexity Linear in the length of the JSON pointer.
@liveexample{The example shows the result of `parent_pointer` for different
JSON Pointers.,json_pointer__parent_pointer}
@since version 3.6.0
*/
json_pointer parent_pointer() const
{
if (empty())
{
return *this;
}
json_pointer res = *this;
res.pop_back();
return res;
}
/*!
@brief remove last reference token
@pre not `empty()`
@liveexample{The example shows the usage of `pop_back`.,json_pointer__pop_back}
@complexity Constant.
@throw out_of_range.405 if JSON pointer has no parent
@since version 3.6.0
*/
void pop_back()
{
if (JSON_HEDLEY_UNLIKELY(empty()))
{
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType()));
}
reference_tokens.pop_back();
}
/*!
@brief return last reference token
@pre not `empty()`
@return last reference token
@liveexample{The example shows the usage of `back`.,json_pointer__back}
@complexity Constant.
@throw out_of_range.405 if JSON pointer has no parent
@since version 3.6.0
*/
const std::string& back() const
{
if (JSON_HEDLEY_UNLIKELY(empty()))
{
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType()));
}
return reference_tokens.back();
}
/*!
@brief append an unescaped token at the end of the reference pointer
@param[in] token token to add
@complexity Amortized constant.
@liveexample{The example shows the result of `push_back` for different
JSON Pointers.,json_pointer__push_back}
@since version 3.6.0
*/
void push_back(const std::string& token)
{
reference_tokens.push_back(token);
}
/// @copydoc push_back(const std::string&)
void push_back(std::string&& token)
{
reference_tokens.push_back(std::move(token));
}
/*!
@brief return whether pointer points to the root document
@return true iff the JSON pointer points to the root document
@complexity Constant.
@exceptionsafety No-throw guarantee: this function never throws exceptions.
@liveexample{The example shows the result of `empty` for different JSON
Pointers.,json_pointer__empty}
@since version 3.6.0
*/
bool empty() const noexcept
{
return reference_tokens.empty();
}
private:
/*!
@param[in] s reference token to be converted into an array index
@return integer representation of @a s
@throw parse_error.106 if an array index begins with '0'
@throw parse_error.109 if an array index begins not with a digit
@throw out_of_range.404 if string @a s could not be converted to an integer
@throw out_of_range.410 if an array index exceeds size_type
*/
static typename BasicJsonType::size_type array_index(const std::string& s)
{
using size_type = typename BasicJsonType::size_type;
// error condition (cf. RFC 6901, Sect. 4)
if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0'))
{
JSON_THROW(detail::parse_error::create(106, 0, "array index '" + s + "' must not begin with '0'", BasicJsonType()));
}
// error condition (cf. RFC 6901, Sect. 4)
if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9')))
{
JSON_THROW(detail::parse_error::create(109, 0, "array index '" + s + "' is not a number", BasicJsonType()));
}
std::size_t processed_chars = 0;
unsigned long long res = 0; // NOLINT(runtime/int)
JSON_TRY
{
res = std::stoull(s, &processed_chars);
}
JSON_CATCH(std::out_of_range&)
{
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'", BasicJsonType()));
}
// check if the string was completely read
if (JSON_HEDLEY_UNLIKELY(processed_chars != s.size()))
{
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'", BasicJsonType()));
}
// only triggered on special platforms (like 32bit), see also
// https://github.com/nlohmann/json/pull/2203
if (res >= static_cast<unsigned long long>((std::numeric_limits<size_type>::max)())) // NOLINT(runtime/int)
{
JSON_THROW(detail::out_of_range::create(410, "array index " + s + " exceeds size_type", BasicJsonType())); // LCOV_EXCL_LINE
}
return static_cast<size_type>(res);
}
JSON_PRIVATE_UNLESS_TESTED:
json_pointer top() const
{
if (JSON_HEDLEY_UNLIKELY(empty()))
{
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType()));
}
json_pointer result = *this;
result.reference_tokens = {reference_tokens[0]};
return result;
}
private:
/*!
@brief create and return a reference to the pointed to value
@complexity Linear in the number of reference tokens.
@throw parse_error.109 if array index is not a number
@throw type_error.313 if value cannot be unflattened
*/
BasicJsonType& get_and_create(BasicJsonType& j) const
{
auto* result = &j;
// in case no reference tokens exist, return a reference to the JSON value
// j which will be overwritten by a primitive value
for (const auto& reference_token : reference_tokens)
{
switch (result->type())
{
case detail::value_t::null:
{
if (reference_token == "0")
{
// start a new array if reference token is 0
result = &result->operator[](0);
}
else
{
// start a new object otherwise
result = &result->operator[](reference_token);
}
break;
}
case detail::value_t::object:
{
// create an entry in the object
result = &result->operator[](reference_token);
break;
}
case detail::value_t::array:
{
// create an entry in the array
result = &result->operator[](array_index(reference_token));
break;
}
/*
The following code is only reached if there exists a reference
token _and_ the current value is primitive. In this case, we have
an error situation, because primitive values may only occur as
single value; that is, with an empty list of reference tokens.
*/
default:
JSON_THROW(detail::type_error::create(313, "invalid value to unflatten", j));
}
}
return *result;
}
/*!
@brief return a reference to the pointed to value
@note This version does not throw if a value is not present, but tries to
create nested values instead. For instance, calling this function
with pointer `"/this/that"` on a null value is equivalent to calling
`operator[]("this").operator[]("that")` on that value, effectively
changing the null value to an object.
@param[in] ptr a JSON value
@return reference to the JSON value pointed to by the JSON pointer
@complexity Linear in the length of the JSON pointer.
@throw parse_error.106 if an array index begins with '0'
@throw parse_error.109 if an array index was not a number
@throw out_of_range.404 if the JSON pointer can not be resolved
*/
BasicJsonType& get_unchecked(BasicJsonType* ptr) const
{
for (const auto& reference_token : reference_tokens)
{
// convert null values to arrays or objects before continuing
if (ptr->is_null())
{
// check if reference token is a number
const bool nums =
std::all_of(reference_token.begin(), reference_token.end(),
[](const unsigned char x)
{
return std::isdigit(x);
});
// change value to array for numbers or "-" or to object otherwise
*ptr = (nums || reference_token == "-")
? detail::value_t::array
: detail::value_t::object;
}
switch (ptr->type())
{
case detail::value_t::object:
{
// use unchecked object access
ptr = &ptr->operator[](reference_token);
break;
}
case detail::value_t::array:
{
if (reference_token == "-")
{
// explicitly treat "-" as index beyond the end
ptr = &ptr->operator[](ptr->m_value.array->size());
}
else
{
// convert array index to number; unchecked access
ptr = &ptr->operator[](array_index(reference_token));
}
break;
}
default:
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr));
}
}
return *ptr;
}
/*!
@throw parse_error.106 if an array index begins with '0'
@throw parse_error.109 if an array index was not a number
@throw out_of_range.402 if the array index '-' is used
@throw out_of_range.404 if the JSON pointer can not be resolved
*/
BasicJsonType& get_checked(BasicJsonType* ptr) const
{
for (const auto& reference_token : reference_tokens)
{
switch (ptr->type())
{
case detail::value_t::object:
{
// note: at performs range check
ptr = &ptr->at(reference_token);
break;
}
case detail::value_t::array:
{
if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
{
// "-" always fails the range check
JSON_THROW(detail::out_of_range::create(402,
"array index '-' (" + std::to_string(ptr->m_value.array->size()) +
") is out of range", *ptr));
}
// note: at performs range check
ptr = &ptr->at(array_index(reference_token));
break;
}
default:
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr));
}
}
return *ptr;
}
/*!
@brief return a const reference to the pointed to value
@param[in] ptr a JSON value
@return const reference to the JSON value pointed to by the JSON
pointer
@throw parse_error.106 if an array index begins with '0'
@throw parse_error.109 if an array index was not a number
@throw out_of_range.402 if the array index '-' is used
@throw out_of_range.404 if the JSON pointer can not be resolved
*/
const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const
{
for (const auto& reference_token : reference_tokens)
{
switch (ptr->type())
{
case detail::value_t::object:
{
// use unchecked object access
ptr = &ptr->operator[](reference_token);
break;
}
case detail::value_t::array:
{
if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
{
// "-" cannot be used for const access
JSON_THROW(detail::out_of_range::create(402, "array index '-' (" + std::to_string(ptr->m_value.array->size()) + ") is out of range", *ptr));
}
// use unchecked array access
ptr = &ptr->operator[](array_index(reference_token));
break;
}
default:
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr));
}
}
return *ptr;
}
/*!
@throw parse_error.106 if an array index begins with '0'
@throw parse_error.109 if an array index was not a number
@throw out_of_range.402 if the array index '-' is used
@throw out_of_range.404 if the JSON pointer can not be resolved
*/
const BasicJsonType& get_checked(const BasicJsonType* ptr) const
{
for (const auto& reference_token : reference_tokens)
{
switch (ptr->type())
{
case detail::value_t::object:
{
// note: at performs range check
ptr = &ptr->at(reference_token);
break;
}
case detail::value_t::array:
{
if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
{
// "-" always fails the range check
JSON_THROW(detail::out_of_range::create(402,
"array index '-' (" + std::to_string(ptr->m_value.array->size()) +
") is out of range", *ptr));
}
// note: at performs range check
ptr = &ptr->at(array_index(reference_token));
break;
}
default:
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr));
}
}
return *ptr;
}
/*!
@throw parse_error.106 if an array index begins with '0'
@throw parse_error.109 if an array index was not a number
*/
bool contains(const BasicJsonType* ptr) const
{
for (const auto& reference_token : reference_tokens)
{
switch (ptr->type())
{
case detail::value_t::object:
{
if (!ptr->contains(reference_token))
{
// we did not find the key in the object
return false;
}
ptr = &ptr->operator[](reference_token);
break;
}
case detail::value_t::array:
{
if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
{
// "-" always fails the range check
return false;
}
if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !("0" <= reference_token && reference_token <= "9")))
{
// invalid char
return false;
}
if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1))
{
if (JSON_HEDLEY_UNLIKELY(!('1' <= reference_token[0] && reference_token[0] <= '9')))
{
// first char should be between '1' and '9'
return false;
}
for (std::size_t i = 1; i < reference_token.size(); i++)
{
if (JSON_HEDLEY_UNLIKELY(!('0' <= reference_token[i] && reference_token[i] <= '9')))
{
// other char should be between '0' and '9'
return false;
}
}
}
const auto idx = array_index(reference_token);
if (idx >= ptr->size())
{
// index out of range
return false;
}
ptr = &ptr->operator[](idx);
break;
}
default:
{
// we do not expect primitive values if there is still a
// reference token to process
return false;
}
}
}
// no reference token left means we found a primitive value
return true;
}
/*!
@brief split the string input to reference tokens
@note This function is only called by the json_pointer constructor.
All exceptions below are documented there.
@throw parse_error.107 if the pointer is not empty or begins with '/'
@throw parse_error.108 if character '~' is not followed by '0' or '1'
*/
static std::vector<std::string> split(const std::string& reference_string)
{
std::vector<std::string> result;
// special case: empty reference string -> no reference tokens
if (reference_string.empty())
{
return result;
}
// check if nonempty reference string begins with slash
if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/'))
{
JSON_THROW(detail::parse_error::create(107, 1, "JSON pointer must be empty or begin with '/' - was: '" + reference_string + "'", BasicJsonType()));
}
// extract the reference tokens:
// - slash: position of the last read slash (or end of string)
// - start: position after the previous slash
for (
// search for the first slash after the first character
std::size_t slash = reference_string.find_first_of('/', 1),
// set the beginning of the first reference token
start = 1;
// we can stop if start == 0 (if slash == std::string::npos)
start != 0;
// set the beginning of the next reference token
// (will eventually be 0 if slash == std::string::npos)
start = (slash == std::string::npos) ? 0 : slash + 1,
// find next slash
slash = reference_string.find_first_of('/', start))
{
// use the text between the beginning of the reference token
// (start) and the last slash (slash).
auto reference_token = reference_string.substr(start, slash - start);
// check reference tokens are properly escaped
for (std::size_t pos = reference_token.find_first_of('~');
pos != std::string::npos;
pos = reference_token.find_first_of('~', pos + 1))
{
JSON_ASSERT(reference_token[pos] == '~');
// ~ must be followed by 0 or 1
if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 ||
(reference_token[pos + 1] != '0' &&
reference_token[pos + 1] != '1')))
{
JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'", BasicJsonType()));
}
}
// finally, store the reference token
detail::unescape(reference_token);
result.push_back(reference_token);
}
return result;
}
private:
/*!
@param[in] reference_string the reference string to the current value
@param[in] value the value to consider
@param[in,out] result the result object to insert values to
@note Empty objects or arrays are flattened to `null`.
*/
static void flatten(const std::string& reference_string,
const BasicJsonType& value,
BasicJsonType& result)
{
switch (value.type())
{
case detail::value_t::array:
{
if (value.m_value.array->empty())
{
// flatten empty array as null
result[reference_string] = nullptr;
}
else
{
// iterate array and use index as reference string
for (std::size_t i = 0; i < value.m_value.array->size(); ++i)
{
flatten(reference_string + "/" + std::to_string(i),
value.m_value.array->operator[](i), result);
}
}
break;
}
case detail::value_t::object:
{
if (value.m_value.object->empty())
{
// flatten empty object as null
result[reference_string] = nullptr;
}
else
{
// iterate object and use keys as reference string
for (const auto& element : *value.m_value.object)
{
flatten(reference_string + "/" + detail::escape(element.first), element.second, result);
}
}
break;
}
default:
{
// add primitive value with its reference string
result[reference_string] = value;
break;
}
}
}
/*!
@param[in] value flattened JSON
@return unflattened JSON
@throw parse_error.109 if array index is not a number
@throw type_error.314 if value is not an object
@throw type_error.315 if object values are not primitive
@throw type_error.313 if value cannot be unflattened
*/
static BasicJsonType
unflatten(const BasicJsonType& value)
{
if (JSON_HEDLEY_UNLIKELY(!value.is_object()))
{
JSON_THROW(detail::type_error::create(314, "only objects can be unflattened", value));
}
BasicJsonType result;
// iterate the JSON object values
for (const auto& element : *value.m_value.object)
{
if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive()))
{
JSON_THROW(detail::type_error::create(315, "values in object must be primitive", element.second));
}
// assign value to reference pointed to by JSON pointer; Note that if
// the JSON pointer is "" (i.e., points to the whole value), function
// get_and_create returns a reference to result itself. An assignment
// will then create a primitive value.
json_pointer(element.first).get_and_create(result) = element.second;
}
return result;
}
/*!
@brief compares two JSON pointers for equality
@param[in] lhs JSON pointer to compare
@param[in] rhs JSON pointer to compare
@return whether @a lhs is equal to @a rhs
@complexity Linear in the length of the JSON pointer
@exceptionsafety No-throw guarantee: this function never throws exceptions.
*/
friend bool operator==(json_pointer const& lhs,
json_pointer const& rhs) noexcept
{
return lhs.reference_tokens == rhs.reference_tokens;
}
/*!
@brief compares two JSON pointers for inequality
@param[in] lhs JSON pointer to compare
@param[in] rhs JSON pointer to compare
@return whether @a lhs is not equal @a rhs
@complexity Linear in the length of the JSON pointer
@exceptionsafety No-throw guarantee: this function never throws exceptions.
*/
friend bool operator!=(json_pointer const& lhs,
json_pointer const& rhs) noexcept
{
return !(lhs == rhs);
}
/// the reference tokens
std::vector<std::string> reference_tokens;
};
} // namespace nlohmann

68
lib/json/include/nlohmann/detail/json_ref.hpp

@ -0,0 +1,68 @@
#pragma once
#include <initializer_list>
#include <utility>
#include <nlohmann/detail/meta/type_traits.hpp>
namespace nlohmann
{
namespace detail
{
template<typename BasicJsonType>
class json_ref
{
public:
using value_type = BasicJsonType;
json_ref(value_type&& value)
: owned_value(std::move(value))
{}
json_ref(const value_type& value)
: value_ref(&value)
{}
json_ref(std::initializer_list<json_ref> init)
: owned_value(init)
{}
template <
class... Args,
enable_if_t<std::is_constructible<value_type, Args...>::value, int> = 0 >
json_ref(Args && ... args)
: owned_value(std::forward<Args>(args)...)
{}
// class should be movable only
json_ref(json_ref&&) noexcept = default;
json_ref(const json_ref&) = delete;
json_ref& operator=(const json_ref&) = delete;
json_ref& operator=(json_ref&&) = delete;
~json_ref() = default;
value_type moved_or_copied() const
{
if (value_ref == nullptr)
{
return std::move(owned_value);
}
return *value_ref;
}
value_type const& operator*() const
{
return value_ref ? *value_ref : owned_value;
}
value_type const* operator->() const
{
return &** this;
}
private:
mutable value_type owned_value = nullptr;
value_type const* value_ref = nullptr;
};
} // namespace detail
} // namespace nlohmann

302
lib/json/include/nlohmann/detail/macro_scope.hpp

@ -0,0 +1,302 @@
#pragma once
#include <utility> // pair
#include <nlohmann/thirdparty/hedley/hedley.hpp>
// This file contains all internal macro definitions
// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them
// exclude unsupported compilers
#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK)
#if defined(__clang__)
#if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400
#error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers"
#endif
#elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER))
#if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800
#error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers"
#endif
#endif
#endif
// C++ language standard detection
// if the user manually specified the used c++ version this is skipped
#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11)
#if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L)
#define JSON_HAS_CPP_20
#define JSON_HAS_CPP_17
#define JSON_HAS_CPP_14
#elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464
#define JSON_HAS_CPP_17
#define JSON_HAS_CPP_14
#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1)
#define JSON_HAS_CPP_14
#endif
// the cpp 11 flag is always specified because it is the minimal required version
#define JSON_HAS_CPP_11
#endif
// disable documentation warnings on clang
#if defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdocumentation"
#endif
// allow to disable exceptions
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION)
#define JSON_THROW(exception) throw exception
#define JSON_TRY try
#define JSON_CATCH(exception) catch(exception)
#define JSON_INTERNAL_CATCH(exception) catch(exception)
#else
#include <cstdlib>
#define JSON_THROW(exception) std::abort()
#define JSON_TRY if(true)
#define JSON_CATCH(exception) if(false)
#define JSON_INTERNAL_CATCH(exception) if(false)
#endif
// override exception macros
#if defined(JSON_THROW_USER)
#undef JSON_THROW
#define JSON_THROW JSON_THROW_USER
#endif
#if defined(JSON_TRY_USER)
#undef JSON_TRY
#define JSON_TRY JSON_TRY_USER
#endif
#if defined(JSON_CATCH_USER)
#undef JSON_CATCH
#define JSON_CATCH JSON_CATCH_USER
#undef JSON_INTERNAL_CATCH
#define JSON_INTERNAL_CATCH JSON_CATCH_USER
#endif
#if defined(JSON_INTERNAL_CATCH_USER)
#undef JSON_INTERNAL_CATCH
#define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER
#endif
// allow to override assert
#if !defined(JSON_ASSERT)
#include <cassert> // assert
#define JSON_ASSERT(x) assert(x)
#endif
// allow to access some private functions (needed by the test suite)
#if defined(JSON_TESTS_PRIVATE)
#define JSON_PRIVATE_UNLESS_TESTED public
#else
#define JSON_PRIVATE_UNLESS_TESTED private
#endif
/*!
@brief macro to briefly define a mapping between an enum and JSON
@def NLOHMANN_JSON_SERIALIZE_ENUM
@since version 3.4.0
*/
#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \
template<typename BasicJsonType> \
inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \
{ \
static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE " must be an enum!"); \
static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__; \
auto it = std::find_if(std::begin(m), std::end(m), \
[e](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool \
{ \
return ej_pair.first == e; \
}); \
j = ((it != std::end(m)) ? it : std::begin(m))->second; \
} \
template<typename BasicJsonType> \
inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \
{ \
static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE " must be an enum!"); \
static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__; \
auto it = std::find_if(std::begin(m), std::end(m), \
[&j](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool \
{ \
return ej_pair.second == j; \
}); \
e = ((it != std::end(m)) ? it : std::begin(m))->first; \
}
// Ugly macros to avoid uglier copy-paste when specializing basic_json. They
// may be removed in the future once the class is split.
#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \
template<template<typename, typename, typename...> class ObjectType, \
template<typename, typename...> class ArrayType, \
class StringType, class BooleanType, class NumberIntegerType, \
class NumberUnsignedType, class NumberFloatType, \
template<typename> class AllocatorType, \
template<typename, typename = void> class JSONSerializer, \
class BinaryType>
#define NLOHMANN_BASIC_JSON_TPL \
basic_json<ObjectType, ArrayType, StringType, BooleanType, \
NumberIntegerType, NumberUnsignedType, NumberFloatType, \
AllocatorType, JSONSerializer, BinaryType>
// Macros to simplify conversion from/to types
#define NLOHMANN_JSON_EXPAND( x ) x
#define NLOHMANN_JSON_GET_MACRO(_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, NAME,...) NAME
#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \
NLOHMANN_JSON_PASTE64, \
NLOHMANN_JSON_PASTE63, \
NLOHMANN_JSON_PASTE62, \
NLOHMANN_JSON_PASTE61, \
NLOHMANN_JSON_PASTE60, \
NLOHMANN_JSON_PASTE59, \
NLOHMANN_JSON_PASTE58, \
NLOHMANN_JSON_PASTE57, \
NLOHMANN_JSON_PASTE56, \
NLOHMANN_JSON_PASTE55, \
NLOHMANN_JSON_PASTE54, \
NLOHMANN_JSON_PASTE53, \
NLOHMANN_JSON_PASTE52, \
NLOHMANN_JSON_PASTE51, \
NLOHMANN_JSON_PASTE50, \
NLOHMANN_JSON_PASTE49, \
NLOHMANN_JSON_PASTE48, \
NLOHMANN_JSON_PASTE47, \
NLOHMANN_JSON_PASTE46, \
NLOHMANN_JSON_PASTE45, \
NLOHMANN_JSON_PASTE44, \
NLOHMANN_JSON_PASTE43, \
NLOHMANN_JSON_PASTE42, \
NLOHMANN_JSON_PASTE41, \
NLOHMANN_JSON_PASTE40, \
NLOHMANN_JSON_PASTE39, \
NLOHMANN_JSON_PASTE38, \
NLOHMANN_JSON_PASTE37, \
NLOHMANN_JSON_PASTE36, \
NLOHMANN_JSON_PASTE35, \
NLOHMANN_JSON_PASTE34, \
NLOHMANN_JSON_PASTE33, \
NLOHMANN_JSON_PASTE32, \
NLOHMANN_JSON_PASTE31, \
NLOHMANN_JSON_PASTE30, \
NLOHMANN_JSON_PASTE29, \
NLOHMANN_JSON_PASTE28, \
NLOHMANN_JSON_PASTE27, \
NLOHMANN_JSON_PASTE26, \
NLOHMANN_JSON_PASTE25, \
NLOHMANN_JSON_PASTE24, \
NLOHMANN_JSON_PASTE23, \
NLOHMANN_JSON_PASTE22, \
NLOHMANN_JSON_PASTE21, \
NLOHMANN_JSON_PASTE20, \
NLOHMANN_JSON_PASTE19, \
NLOHMANN_JSON_PASTE18, \
NLOHMANN_JSON_PASTE17, \
NLOHMANN_JSON_PASTE16, \
NLOHMANN_JSON_PASTE15, \
NLOHMANN_JSON_PASTE14, \
NLOHMANN_JSON_PASTE13, \
NLOHMANN_JSON_PASTE12, \
NLOHMANN_JSON_PASTE11, \
NLOHMANN_JSON_PASTE10, \
NLOHMANN_JSON_PASTE9, \
NLOHMANN_JSON_PASTE8, \
NLOHMANN_JSON_PASTE7, \
NLOHMANN_JSON_PASTE6, \
NLOHMANN_JSON_PASTE5, \
NLOHMANN_JSON_PASTE4, \
NLOHMANN_JSON_PASTE3, \
NLOHMANN_JSON_PASTE2, \
NLOHMANN_JSON_PASTE1)(__VA_ARGS__))
#define NLOHMANN_JSON_PASTE2(func, v1) func(v1)
#define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2)
#define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3)
#define NLOHMANN_JSON_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4)
#define NLOHMANN_JSON_PASTE6(func, v1, v2, v3, v4, v5) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5)
#define NLOHMANN_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6)
#define NLOHMANN_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7)
#define NLOHMANN_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8)
#define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE9(func, v2, v3, v4, v5, v6, v7, v8, v9)
#define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE10(func, v2, v3, v4, v5, v6, v7, v8, v9, v10)
#define NLOHMANN_JSON_PASTE12(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE11(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11)
#define NLOHMANN_JSON_PASTE13(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE12(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12)
#define NLOHMANN_JSON_PASTE14(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE13(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13)
#define NLOHMANN_JSON_PASTE15(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE14(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14)
#define NLOHMANN_JSON_PASTE16(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE15(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15)
#define NLOHMANN_JSON_PASTE17(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE16(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16)
#define NLOHMANN_JSON_PASTE18(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE17(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17)
#define NLOHMANN_JSON_PASTE19(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE18(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18)
#define NLOHMANN_JSON_PASTE20(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE19(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19)
#define NLOHMANN_JSON_PASTE21(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE20(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20)
#define NLOHMANN_JSON_PASTE22(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE21(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21)
#define NLOHMANN_JSON_PASTE23(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE22(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22)
#define NLOHMANN_JSON_PASTE24(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE23(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23)
#define NLOHMANN_JSON_PASTE25(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE24(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24)
#define NLOHMANN_JSON_PASTE26(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE25(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25)
#define NLOHMANN_JSON_PASTE27(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE26(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26)
#define NLOHMANN_JSON_PASTE28(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE27(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27)
#define NLOHMANN_JSON_PASTE29(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE28(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28)
#define NLOHMANN_JSON_PASTE30(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE29(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29)
#define NLOHMANN_JSON_PASTE31(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE30(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30)
#define NLOHMANN_JSON_PASTE32(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE31(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31)
#define NLOHMANN_JSON_PASTE33(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE32(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32)
#define NLOHMANN_JSON_PASTE34(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE33(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33)
#define NLOHMANN_JSON_PASTE35(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE34(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34)
#define NLOHMANN_JSON_PASTE36(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE35(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35)
#define NLOHMANN_JSON_PASTE37(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE36(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36)
#define NLOHMANN_JSON_PASTE38(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE37(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37)
#define NLOHMANN_JSON_PASTE39(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE38(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38)
#define NLOHMANN_JSON_PASTE40(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE39(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39)
#define NLOHMANN_JSON_PASTE41(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE40(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40)
#define NLOHMANN_JSON_PASTE42(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE41(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41)
#define NLOHMANN_JSON_PASTE43(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE42(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42)
#define NLOHMANN_JSON_PASTE44(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE43(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43)
#define NLOHMANN_JSON_PASTE45(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE44(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44)
#define NLOHMANN_JSON_PASTE46(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE45(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45)
#define NLOHMANN_JSON_PASTE47(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE46(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46)
#define NLOHMANN_JSON_PASTE48(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE47(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47)
#define NLOHMANN_JSON_PASTE49(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE48(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48)
#define NLOHMANN_JSON_PASTE50(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE49(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49)
#define NLOHMANN_JSON_PASTE51(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE50(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50)
#define NLOHMANN_JSON_PASTE52(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE51(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51)
#define NLOHMANN_JSON_PASTE53(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE52(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52)
#define NLOHMANN_JSON_PASTE54(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE53(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53)
#define NLOHMANN_JSON_PASTE55(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE54(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54)
#define NLOHMANN_JSON_PASTE56(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE55(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55)
#define NLOHMANN_JSON_PASTE57(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE56(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56)
#define NLOHMANN_JSON_PASTE58(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE57(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57)
#define NLOHMANN_JSON_PASTE59(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE58(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58)
#define NLOHMANN_JSON_PASTE60(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE59(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59)
#define NLOHMANN_JSON_PASTE61(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE60(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60)
#define NLOHMANN_JSON_PASTE62(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE61(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61)
#define NLOHMANN_JSON_PASTE63(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE62(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62)
#define NLOHMANN_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63)
#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1;
#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1);
/*!
@brief macro
@def NLOHMANN_DEFINE_TYPE_INTRUSIVE
@since version 3.9.0
*/
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \
friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
/*!
@brief macro
@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE
@since version 3.9.0
*/
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \
inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
#ifndef JSON_USE_IMPLICIT_CONVERSIONS
#define JSON_USE_IMPLICIT_CONVERSIONS 1
#endif
#if JSON_USE_IMPLICIT_CONVERSIONS
#define JSON_EXPLICIT
#else
#define JSON_EXPLICIT explicit
#endif

23
lib/json/include/nlohmann/detail/macro_unscope.hpp

@ -0,0 +1,23 @@
#pragma once
// restore GCC/clang diagnostic settings
#if defined(__clang__)
#pragma GCC diagnostic pop
#endif
// clean up
#undef JSON_ASSERT
#undef JSON_INTERNAL_CATCH
#undef JSON_CATCH
#undef JSON_THROW
#undef JSON_TRY
#undef JSON_PRIVATE_UNLESS_TESTED
#undef JSON_HAS_CPP_11
#undef JSON_HAS_CPP_14
#undef JSON_HAS_CPP_17
#undef JSON_HAS_CPP_20
#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION
#undef NLOHMANN_BASIC_JSON_TPL
#undef JSON_EXPLICIT
#include <nlohmann/thirdparty/hedley/hedley_undef.hpp>

154
lib/json/include/nlohmann/detail/meta/cpp_future.hpp

@ -0,0 +1,154 @@
#pragma once
#include <cstddef> // size_t
#include <type_traits> // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type
#include <utility> // index_sequence, make_index_sequence, index_sequence_for
#include <nlohmann/detail/macro_scope.hpp>
namespace nlohmann
{
namespace detail
{
template<typename T>
using uncvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
#ifdef JSON_HAS_CPP_14
// the following utilities are natively available in C++14
using std::enable_if_t;
using std::index_sequence;
using std::make_index_sequence;
using std::index_sequence_for;
#else
// alias templates to reduce boilerplate
template<bool B, typename T = void>
using enable_if_t = typename std::enable_if<B, T>::type;
// The following code is taken from https://github.com/abseil/abseil-cpp/blob/10cb35e459f5ecca5b2ff107635da0bfa41011b4/absl/utility/utility.h
// which is part of Google Abseil (https://github.com/abseil/abseil-cpp), licensed under the Apache License 2.0.
//// START OF CODE FROM GOOGLE ABSEIL
// integer_sequence
//
// Class template representing a compile-time integer sequence. An instantiation
// of `integer_sequence<T, Ints...>` has a sequence of integers encoded in its
// type through its template arguments (which is a common need when
// working with C++11 variadic templates). `absl::integer_sequence` is designed
// to be a drop-in replacement for C++14's `std::integer_sequence`.
//
// Example:
//
// template< class T, T... Ints >
// void user_function(integer_sequence<T, Ints...>);
//
// int main()
// {
// // user_function's `T` will be deduced to `int` and `Ints...`
// // will be deduced to `0, 1, 2, 3, 4`.
// user_function(make_integer_sequence<int, 5>());
// }
template <typename T, T... Ints>
struct integer_sequence
{
using value_type = T;
static constexpr std::size_t size() noexcept
{
return sizeof...(Ints);
}
};
// index_sequence
//
// A helper template for an `integer_sequence` of `size_t`,
// `absl::index_sequence` is designed to be a drop-in replacement for C++14's
// `std::index_sequence`.
template <size_t... Ints>
using index_sequence = integer_sequence<size_t, Ints...>;
namespace utility_internal
{
template <typename Seq, size_t SeqSize, size_t Rem>
struct Extend;
// Note that SeqSize == sizeof...(Ints). It's passed explicitly for efficiency.
template <typename T, T... Ints, size_t SeqSize>
struct Extend<integer_sequence<T, Ints...>, SeqSize, 0>
{
using type = integer_sequence < T, Ints..., (Ints + SeqSize)... >;
};
template <typename T, T... Ints, size_t SeqSize>
struct Extend<integer_sequence<T, Ints...>, SeqSize, 1>
{
using type = integer_sequence < T, Ints..., (Ints + SeqSize)..., 2 * SeqSize >;
};
// Recursion helper for 'make_integer_sequence<T, N>'.
// 'Gen<T, N>::type' is an alias for 'integer_sequence<T, 0, 1, ... N-1>'.
template <typename T, size_t N>
struct Gen
{
using type =
typename Extend < typename Gen < T, N / 2 >::type, N / 2, N % 2 >::type;
};
template <typename T>
struct Gen<T, 0>
{
using type = integer_sequence<T>;
};
} // namespace utility_internal
// Compile-time sequences of integers
// make_integer_sequence
//
// This template alias is equivalent to
// `integer_sequence<int, 0, 1, ..., N-1>`, and is designed to be a drop-in
// replacement for C++14's `std::make_integer_sequence`.
template <typename T, T N>
using make_integer_sequence = typename utility_internal::Gen<T, N>::type;
// make_index_sequence
//
// This template alias is equivalent to `index_sequence<0, 1, ..., N-1>`,
// and is designed to be a drop-in replacement for C++14's
// `std::make_index_sequence`.
template <size_t N>
using make_index_sequence = make_integer_sequence<size_t, N>;
// index_sequence_for
//
// Converts a typename pack into an index sequence of the same length, and
// is designed to be a drop-in replacement for C++14's
// `std::index_sequence_for()`
template <typename... Ts>
using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
//// END OF CODE FROM GOOGLE ABSEIL
#endif
// dispatch utility (taken from ranges-v3)
template<unsigned N> struct priority_tag : priority_tag < N - 1 > {};
template<> struct priority_tag<0> {};
// taken from ranges-v3
template<typename T>
struct static_const
{
static constexpr T value{};
};
template<typename T>
constexpr T static_const<T>::value;
} // namespace detail
} // namespace nlohmann

58
lib/json/include/nlohmann/detail/meta/detected.hpp

@ -0,0 +1,58 @@
#pragma once
#include <type_traits>
#include <nlohmann/detail/meta/void_t.hpp>
// https://en.cppreference.com/w/cpp/experimental/is_detected
namespace nlohmann
{
namespace detail
{
struct nonesuch
{
nonesuch() = delete;
~nonesuch() = delete;
nonesuch(nonesuch const&) = delete;
nonesuch(nonesuch const&&) = delete;
void operator=(nonesuch const&) = delete;
void operator=(nonesuch&&) = delete;
};
template<class Default,
class AlwaysVoid,
template<class...> class Op,
class... Args>
struct detector
{
using value_t = std::false_type;
using type = Default;
};
template<class Default, template<class...> class Op, class... Args>
struct detector<Default, void_t<Op<Args...>>, Op, Args...>
{
using value_t = std::true_type;
using type = Op<Args...>;
};
template<template<class...> class Op, class... Args>
using is_detected = typename detector<nonesuch, void, Op, Args...>::value_t;
template<template<class...> class Op, class... Args>
using detected_t = typename detector<nonesuch, void, Op, Args...>::type;
template<class Default, template<class...> class Op, class... Args>
using detected_or = detector<Default, void, Op, Args...>;
template<class Default, template<class...> class Op, class... Args>
using detected_or_t = typename detected_or<Default, Op, Args...>::type;
template<class Expected, template<class...> class Op, class... Args>
using is_detected_exact = std::is_same<Expected, detected_t<Op, Args...>>;
template<class To, template<class...> class Op, class... Args>
using is_detected_convertible =
std::is_convertible<detected_t<Op, Args...>, To>;
} // namespace detail
} // namespace nlohmann

10
lib/json/include/nlohmann/detail/meta/identity_tag.hpp

@ -0,0 +1,10 @@
#pragma once
namespace nlohmann
{
namespace detail
{
// dispatching helper struct
template <class T> struct identity_tag {};
} // namespace detail
} // namespace nlohmann

149
lib/json/include/nlohmann/detail/meta/is_sax.hpp

@ -0,0 +1,149 @@
#pragma once
#include <cstdint> // size_t
#include <utility> // declval
#include <string> // string
#include <nlohmann/detail/meta/detected.hpp>
#include <nlohmann/detail/meta/type_traits.hpp>
namespace nlohmann
{
namespace detail
{
template<typename T>
using null_function_t = decltype(std::declval<T&>().null());
template<typename T>
using boolean_function_t =
decltype(std::declval<T&>().boolean(std::declval<bool>()));
template<typename T, typename Integer>
using number_integer_function_t =
decltype(std::declval<T&>().number_integer(std::declval<Integer>()));
template<typename T, typename Unsigned>
using number_unsigned_function_t =
decltype(std::declval<T&>().number_unsigned(std::declval<Unsigned>()));
template<typename T, typename Float, typename String>
using number_float_function_t = decltype(std::declval<T&>().number_float(
std::declval<Float>(), std::declval<const String&>()));
template<typename T, typename String>
using string_function_t =
decltype(std::declval<T&>().string(std::declval<String&>()));
template<typename T, typename Binary>
using binary_function_t =
decltype(std::declval<T&>().binary(std::declval<Binary&>()));
template<typename T>
using start_object_function_t =
decltype(std::declval<T&>().start_object(std::declval<std::size_t>()));
template<typename T, typename String>
using key_function_t =
decltype(std::declval<T&>().key(std::declval<String&>()));
template<typename T>
using end_object_function_t = decltype(std::declval<T&>().end_object());
template<typename T>
using start_array_function_t =
decltype(std::declval<T&>().start_array(std::declval<std::size_t>()));
template<typename T>
using end_array_function_t = decltype(std::declval<T&>().end_array());
template<typename T, typename Exception>
using parse_error_function_t = decltype(std::declval<T&>().parse_error(
std::declval<std::size_t>(), std::declval<const std::string&>(),
std::declval<const Exception&>()));
template<typename SAX, typename BasicJsonType>
struct is_sax
{
private:
static_assert(is_basic_json<BasicJsonType>::value,
"BasicJsonType must be of type basic_json<...>");
using number_integer_t = typename BasicJsonType::number_integer_t;
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
using number_float_t = typename BasicJsonType::number_float_t;
using string_t = typename BasicJsonType::string_t;
using binary_t = typename BasicJsonType::binary_t;
using exception_t = typename BasicJsonType::exception;
public:
static constexpr bool value =
is_detected_exact<bool, null_function_t, SAX>::value &&
is_detected_exact<bool, boolean_function_t, SAX>::value &&
is_detected_exact<bool, number_integer_function_t, SAX, number_integer_t>::value &&
is_detected_exact<bool, number_unsigned_function_t, SAX, number_unsigned_t>::value &&
is_detected_exact<bool, number_float_function_t, SAX, number_float_t, string_t>::value &&
is_detected_exact<bool, string_function_t, SAX, string_t>::value &&
is_detected_exact<bool, binary_function_t, SAX, binary_t>::value &&
is_detected_exact<bool, start_object_function_t, SAX>::value &&
is_detected_exact<bool, key_function_t, SAX, string_t>::value &&
is_detected_exact<bool, end_object_function_t, SAX>::value &&
is_detected_exact<bool, start_array_function_t, SAX>::value &&
is_detected_exact<bool, end_array_function_t, SAX>::value &&
is_detected_exact<bool, parse_error_function_t, SAX, exception_t>::value;
};
template<typename SAX, typename BasicJsonType>
struct is_sax_static_asserts
{
private:
static_assert(is_basic_json<BasicJsonType>::value,
"BasicJsonType must be of type basic_json<...>");
using number_integer_t = typename BasicJsonType::number_integer_t;
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
using number_float_t = typename BasicJsonType::number_float_t;
using string_t = typename BasicJsonType::string_t;
using binary_t = typename BasicJsonType::binary_t;
using exception_t = typename BasicJsonType::exception;
public:
static_assert(is_detected_exact<bool, null_function_t, SAX>::value,
"Missing/invalid function: bool null()");
static_assert(is_detected_exact<bool, boolean_function_t, SAX>::value,
"Missing/invalid function: bool boolean(bool)");
static_assert(is_detected_exact<bool, boolean_function_t, SAX>::value,
"Missing/invalid function: bool boolean(bool)");
static_assert(
is_detected_exact<bool, number_integer_function_t, SAX,
number_integer_t>::value,
"Missing/invalid function: bool number_integer(number_integer_t)");
static_assert(
is_detected_exact<bool, number_unsigned_function_t, SAX,
number_unsigned_t>::value,
"Missing/invalid function: bool number_unsigned(number_unsigned_t)");
static_assert(is_detected_exact<bool, number_float_function_t, SAX,
number_float_t, string_t>::value,
"Missing/invalid function: bool number_float(number_float_t, const string_t&)");
static_assert(
is_detected_exact<bool, string_function_t, SAX, string_t>::value,
"Missing/invalid function: bool string(string_t&)");
static_assert(
is_detected_exact<bool, binary_function_t, SAX, binary_t>::value,
"Missing/invalid function: bool binary(binary_t&)");
static_assert(is_detected_exact<bool, start_object_function_t, SAX>::value,
"Missing/invalid function: bool start_object(std::size_t)");
static_assert(is_detected_exact<bool, key_function_t, SAX, string_t>::value,
"Missing/invalid function: bool key(string_t&)");
static_assert(is_detected_exact<bool, end_object_function_t, SAX>::value,
"Missing/invalid function: bool end_object()");
static_assert(is_detected_exact<bool, start_array_function_t, SAX>::value,
"Missing/invalid function: bool start_array(std::size_t)");
static_assert(is_detected_exact<bool, end_array_function_t, SAX>::value,
"Missing/invalid function: bool end_array()");
static_assert(
is_detected_exact<bool, parse_error_function_t, SAX, exception_t>::value,
"Missing/invalid function: bool parse_error(std::size_t, const "
"std::string&, const exception&)");
};
} // namespace detail
} // namespace nlohmann

436
lib/json/include/nlohmann/detail/meta/type_traits.hpp

@ -0,0 +1,436 @@
#pragma once
#include <limits> // numeric_limits
#include <type_traits> // false_type, is_constructible, is_integral, is_same, true_type
#include <utility> // declval
#include <tuple> // tuple
#include <nlohmann/detail/iterators/iterator_traits.hpp>
#include <nlohmann/detail/macro_scope.hpp>
#include <nlohmann/detail/meta/cpp_future.hpp>
#include <nlohmann/detail/meta/detected.hpp>
#include <nlohmann/json_fwd.hpp>
namespace nlohmann
{
/*!
@brief detail namespace with internal helper functions
This namespace collects functions that should not be exposed,
implementations of some @ref basic_json methods, and meta-programming helpers.
@since version 2.1.0
*/
namespace detail
{
/////////////
// helpers //
/////////////
// Note to maintainers:
//
// Every trait in this file expects a non CV-qualified type.
// The only exceptions are in the 'aliases for detected' section
// (i.e. those of the form: decltype(T::member_function(std::declval<T>())))
//
// In this case, T has to be properly CV-qualified to constraint the function arguments
// (e.g. to_json(BasicJsonType&, const T&))
template<typename> struct is_basic_json : std::false_type {};
NLOHMANN_BASIC_JSON_TPL_DECLARATION
struct is_basic_json<NLOHMANN_BASIC_JSON_TPL> : std::true_type {};
//////////////////////
// json_ref helpers //
//////////////////////
template<typename>
class json_ref;
template<typename>
struct is_json_ref : std::false_type {};
template<typename T>
struct is_json_ref<json_ref<T>> : std::true_type {};
//////////////////////////
// aliases for detected //
//////////////////////////
template<typename T>
using mapped_type_t = typename T::mapped_type;
template<typename T>
using key_type_t = typename T::key_type;
template<typename T>
using value_type_t = typename T::value_type;
template<typename T>
using difference_type_t = typename T::difference_type;
template<typename T>
using pointer_t = typename T::pointer;
template<typename T>
using reference_t = typename T::reference;
template<typename T>
using iterator_category_t = typename T::iterator_category;
template<typename T>
using iterator_t = typename T::iterator;
template<typename T, typename... Args>
using to_json_function = decltype(T::to_json(std::declval<Args>()...));
template<typename T, typename... Args>
using from_json_function = decltype(T::from_json(std::declval<Args>()...));
template<typename T, typename U>
using get_template_function = decltype(std::declval<T>().template get<U>());
// trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists
template<typename BasicJsonType, typename T, typename = void>
struct has_from_json : std::false_type {};
// trait checking if j.get<T> is valid
// use this trait instead of std::is_constructible or std::is_convertible,
// both rely on, or make use of implicit conversions, and thus fail when T
// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958)
template <typename BasicJsonType, typename T>
struct is_getable
{
static constexpr bool value = is_detected<get_template_function, const BasicJsonType&, T>::value;
};
template<typename BasicJsonType, typename T>
struct has_from_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>
{
using serializer = typename BasicJsonType::template json_serializer<T, void>;
static constexpr bool value =
is_detected_exact<void, from_json_function, serializer,
const BasicJsonType&, T&>::value;
};
// This trait checks if JSONSerializer<T>::from_json(json const&) exists
// this overload is used for non-default-constructible user-defined-types
template<typename BasicJsonType, typename T, typename = void>
struct has_non_default_from_json : std::false_type {};
template<typename BasicJsonType, typename T>
struct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>
{
using serializer = typename BasicJsonType::template json_serializer<T, void>;
static constexpr bool value =
is_detected_exact<T, from_json_function, serializer,
const BasicJsonType&>::value;
};
// This trait checks if BasicJsonType::json_serializer<T>::to_json exists
// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion.
template<typename BasicJsonType, typename T, typename = void>
struct has_to_json : std::false_type {};
template<typename BasicJsonType, typename T>
struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>
{
using serializer = typename BasicJsonType::template json_serializer<T, void>;
static constexpr bool value =
is_detected_exact<void, to_json_function, serializer, BasicJsonType&,
T>::value;
};
///////////////////
// is_ functions //
///////////////////
// https://en.cppreference.com/w/cpp/types/conjunction
template<class...> struct conjunction : std::true_type { };
template<class B1> struct conjunction<B1> : B1 { };
template<class B1, class... Bn>
struct conjunction<B1, Bn...>
: std::conditional<bool(B1::value), conjunction<Bn...>, B1>::type {};
// Reimplementation of is_constructible and is_default_constructible, due to them being broken for
// std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367).
// This causes compile errors in e.g. clang 3.5 or gcc 4.9.
template <typename T>
struct is_default_constructible : std::is_default_constructible<T> {};
template <typename T1, typename T2>
struct is_default_constructible<std::pair<T1, T2>>
: conjunction<is_default_constructible<T1>, is_default_constructible<T2>> {};
template <typename T1, typename T2>
struct is_default_constructible<const std::pair<T1, T2>>
: conjunction<is_default_constructible<T1>, is_default_constructible<T2>> {};
template <typename... Ts>
struct is_default_constructible<std::tuple<Ts...>>
: conjunction<is_default_constructible<Ts>...> {};
template <typename... Ts>
struct is_default_constructible<const std::tuple<Ts...>>
: conjunction<is_default_constructible<Ts>...> {};
template <typename T, typename... Args>
struct is_constructible : std::is_constructible<T, Args...> {};
template <typename T1, typename T2>
struct is_constructible<std::pair<T1, T2>> : is_default_constructible<std::pair<T1, T2>> {};
template <typename T1, typename T2>
struct is_constructible<const std::pair<T1, T2>> : is_default_constructible<const std::pair<T1, T2>> {};
template <typename... Ts>
struct is_constructible<std::tuple<Ts...>> : is_default_constructible<std::tuple<Ts...>> {};
template <typename... Ts>
struct is_constructible<const std::tuple<Ts...>> : is_default_constructible<const std::tuple<Ts...>> {};
template<typename T, typename = void>
struct is_iterator_traits : std::false_type {};
template<typename T>
struct is_iterator_traits<iterator_traits<T>>
{
private:
using traits = iterator_traits<T>;
public:
static constexpr auto value =
is_detected<value_type_t, traits>::value &&
is_detected<difference_type_t, traits>::value &&
is_detected<pointer_t, traits>::value &&
is_detected<iterator_category_t, traits>::value &&
is_detected<reference_t, traits>::value;
};
// The following implementation of is_complete_type is taken from
// https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/
// and is written by Xiang Fan who agreed to using it in this library.
template<typename T, typename = void>
struct is_complete_type : std::false_type {};
template<typename T>
struct is_complete_type<T, decltype(void(sizeof(T)))> : std::true_type {};
template<typename BasicJsonType, typename CompatibleObjectType,
typename = void>
struct is_compatible_object_type_impl : std::false_type {};
template<typename BasicJsonType, typename CompatibleObjectType>
struct is_compatible_object_type_impl <
BasicJsonType, CompatibleObjectType,
enable_if_t < is_detected<mapped_type_t, CompatibleObjectType>::value&&
is_detected<key_type_t, CompatibleObjectType>::value >>
{
using object_t = typename BasicJsonType::object_t;
// macOS's is_constructible does not play well with nonesuch...
static constexpr bool value =
is_constructible<typename object_t::key_type,
typename CompatibleObjectType::key_type>::value &&
is_constructible<typename object_t::mapped_type,
typename CompatibleObjectType::mapped_type>::value;
};
template<typename BasicJsonType, typename CompatibleObjectType>
struct is_compatible_object_type
: is_compatible_object_type_impl<BasicJsonType, CompatibleObjectType> {};
template<typename BasicJsonType, typename ConstructibleObjectType,
typename = void>
struct is_constructible_object_type_impl : std::false_type {};
template<typename BasicJsonType, typename ConstructibleObjectType>
struct is_constructible_object_type_impl <
BasicJsonType, ConstructibleObjectType,
enable_if_t < is_detected<mapped_type_t, ConstructibleObjectType>::value&&
is_detected<key_type_t, ConstructibleObjectType>::value >>
{
using object_t = typename BasicJsonType::object_t;
static constexpr bool value =
(is_default_constructible<ConstructibleObjectType>::value &&
(std::is_move_assignable<ConstructibleObjectType>::value ||
std::is_copy_assignable<ConstructibleObjectType>::value) &&
(is_constructible<typename ConstructibleObjectType::key_type,
typename object_t::key_type>::value &&
std::is_same <
typename object_t::mapped_type,
typename ConstructibleObjectType::mapped_type >::value)) ||
(has_from_json<BasicJsonType,
typename ConstructibleObjectType::mapped_type>::value ||
has_non_default_from_json <
BasicJsonType,
typename ConstructibleObjectType::mapped_type >::value);
};
template<typename BasicJsonType, typename ConstructibleObjectType>
struct is_constructible_object_type
: is_constructible_object_type_impl<BasicJsonType,
ConstructibleObjectType> {};
template<typename BasicJsonType, typename CompatibleStringType,
typename = void>
struct is_compatible_string_type_impl : std::false_type {};
template<typename BasicJsonType, typename CompatibleStringType>
struct is_compatible_string_type_impl <
BasicJsonType, CompatibleStringType,
enable_if_t<is_detected_exact<typename BasicJsonType::string_t::value_type,
value_type_t, CompatibleStringType>::value >>
{
static constexpr auto value =
is_constructible<typename BasicJsonType::string_t, CompatibleStringType>::value;
};
template<typename BasicJsonType, typename ConstructibleStringType>
struct is_compatible_string_type
: is_compatible_string_type_impl<BasicJsonType, ConstructibleStringType> {};
template<typename BasicJsonType, typename ConstructibleStringType,
typename = void>
struct is_constructible_string_type_impl : std::false_type {};
template<typename BasicJsonType, typename ConstructibleStringType>
struct is_constructible_string_type_impl <
BasicJsonType, ConstructibleStringType,
enable_if_t<is_detected_exact<typename BasicJsonType::string_t::value_type,
value_type_t, ConstructibleStringType>::value >>
{
static constexpr auto value =
is_constructible<ConstructibleStringType,
typename BasicJsonType::string_t>::value;
};
template<typename BasicJsonType, typename ConstructibleStringType>
struct is_constructible_string_type
: is_constructible_string_type_impl<BasicJsonType, ConstructibleStringType> {};
template<typename BasicJsonType, typename CompatibleArrayType, typename = void>
struct is_compatible_array_type_impl : std::false_type {};
template<typename BasicJsonType, typename CompatibleArrayType>
struct is_compatible_array_type_impl <
BasicJsonType, CompatibleArrayType,
enable_if_t < is_detected<value_type_t, CompatibleArrayType>::value&&
is_detected<iterator_t, CompatibleArrayType>::value&&
// This is needed because json_reverse_iterator has a ::iterator type...
// Therefore it is detected as a CompatibleArrayType.
// The real fix would be to have an Iterable concept.
!is_iterator_traits <
iterator_traits<CompatibleArrayType >>::value >>
{
static constexpr bool value =
is_constructible<BasicJsonType,
typename CompatibleArrayType::value_type>::value;
};
template<typename BasicJsonType, typename CompatibleArrayType>
struct is_compatible_array_type
: is_compatible_array_type_impl<BasicJsonType, CompatibleArrayType> {};
template<typename BasicJsonType, typename ConstructibleArrayType, typename = void>
struct is_constructible_array_type_impl : std::false_type {};
template<typename BasicJsonType, typename ConstructibleArrayType>
struct is_constructible_array_type_impl <
BasicJsonType, ConstructibleArrayType,
enable_if_t<std::is_same<ConstructibleArrayType,
typename BasicJsonType::value_type>::value >>
: std::true_type {};
template<typename BasicJsonType, typename ConstructibleArrayType>
struct is_constructible_array_type_impl <
BasicJsonType, ConstructibleArrayType,
enable_if_t < !std::is_same<ConstructibleArrayType,
typename BasicJsonType::value_type>::value&&
is_default_constructible<ConstructibleArrayType>::value&&
(std::is_move_assignable<ConstructibleArrayType>::value ||
std::is_copy_assignable<ConstructibleArrayType>::value)&&
is_detected<value_type_t, ConstructibleArrayType>::value&&
is_detected<iterator_t, ConstructibleArrayType>::value&&
is_complete_type <
detected_t<value_type_t, ConstructibleArrayType >>::value >>
{
static constexpr bool value =
// This is needed because json_reverse_iterator has a ::iterator type,
// furthermore, std::back_insert_iterator (and other iterators) have a
// base class `iterator`... Therefore it is detected as a
// ConstructibleArrayType. The real fix would be to have an Iterable
// concept.
!is_iterator_traits<iterator_traits<ConstructibleArrayType>>::value &&
(std::is_same<typename ConstructibleArrayType::value_type,
typename BasicJsonType::array_t::value_type>::value ||
has_from_json<BasicJsonType,
typename ConstructibleArrayType::value_type>::value ||
has_non_default_from_json <
BasicJsonType, typename ConstructibleArrayType::value_type >::value);
};
template<typename BasicJsonType, typename ConstructibleArrayType>
struct is_constructible_array_type
: is_constructible_array_type_impl<BasicJsonType, ConstructibleArrayType> {};
template<typename RealIntegerType, typename CompatibleNumberIntegerType,
typename = void>
struct is_compatible_integer_type_impl : std::false_type {};
template<typename RealIntegerType, typename CompatibleNumberIntegerType>
struct is_compatible_integer_type_impl <
RealIntegerType, CompatibleNumberIntegerType,
enable_if_t < std::is_integral<RealIntegerType>::value&&
std::is_integral<CompatibleNumberIntegerType>::value&&
!std::is_same<bool, CompatibleNumberIntegerType>::value >>
{
// is there an assert somewhere on overflows?
using RealLimits = std::numeric_limits<RealIntegerType>;
using CompatibleLimits = std::numeric_limits<CompatibleNumberIntegerType>;
static constexpr auto value =
is_constructible<RealIntegerType,
CompatibleNumberIntegerType>::value &&
CompatibleLimits::is_integer &&
RealLimits::is_signed == CompatibleLimits::is_signed;
};
template<typename RealIntegerType, typename CompatibleNumberIntegerType>
struct is_compatible_integer_type
: is_compatible_integer_type_impl<RealIntegerType,
CompatibleNumberIntegerType> {};
template<typename BasicJsonType, typename CompatibleType, typename = void>
struct is_compatible_type_impl: std::false_type {};
template<typename BasicJsonType, typename CompatibleType>
struct is_compatible_type_impl <
BasicJsonType, CompatibleType,
enable_if_t<is_complete_type<CompatibleType>::value >>
{
static constexpr bool value =
has_to_json<BasicJsonType, CompatibleType>::value;
};
template<typename BasicJsonType, typename CompatibleType>
struct is_compatible_type
: is_compatible_type_impl<BasicJsonType, CompatibleType> {};
template<typename T1, typename T2>
struct is_constructible_tuple : std::false_type {};
template<typename T1, typename... Args>
struct is_constructible_tuple<T1, std::tuple<Args...>> : conjunction<is_constructible<T1, Args>...> {};
} // namespace detail
} // namespace nlohmann

13
lib/json/include/nlohmann/detail/meta/void_t.hpp

@ -0,0 +1,13 @@
#pragma once
namespace nlohmann
{
namespace detail
{
template<typename ...Ts> struct make_void
{
using type = void;
};
template<typename ...Ts> using void_t = typename make_void<Ts...>::type;
} // namespace detail
} // namespace nlohmann

1594
lib/json/include/nlohmann/detail/output/binary_writer.hpp

File diff suppressed because it is too large Load Diff

129
lib/json/include/nlohmann/detail/output/output_adapters.hpp

@ -0,0 +1,129 @@
#pragma once
#include <algorithm> // copy
#include <cstddef> // size_t
#include <ios> // streamsize
#include <iterator> // back_inserter
#include <memory> // shared_ptr, make_shared
#include <ostream> // basic_ostream
#include <string> // basic_string
#include <vector> // vector
#include <nlohmann/detail/macro_scope.hpp>
namespace nlohmann
{
namespace detail
{
/// abstract output adapter interface
template<typename CharType> struct output_adapter_protocol
{
virtual void write_character(CharType c) = 0;
virtual void write_characters(const CharType* s, std::size_t length) = 0;
virtual ~output_adapter_protocol() = default;
output_adapter_protocol() = default;
output_adapter_protocol(const output_adapter_protocol&) = default;
output_adapter_protocol(output_adapter_protocol&&) noexcept = default;
output_adapter_protocol& operator=(const output_adapter_protocol&) = default;
output_adapter_protocol& operator=(output_adapter_protocol&&) noexcept = default;
};
/// a type to simplify interfaces
template<typename CharType>
using output_adapter_t = std::shared_ptr<output_adapter_protocol<CharType>>;
/// output adapter for byte vectors
template<typename CharType>
class output_vector_adapter : public output_adapter_protocol<CharType>
{
public:
explicit output_vector_adapter(std::vector<CharType>& vec) noexcept
: v(vec)
{}
void write_character(CharType c) override
{
v.push_back(c);
}
JSON_HEDLEY_NON_NULL(2)
void write_characters(const CharType* s, std::size_t length) override
{
std::copy(s, s + length, std::back_inserter(v));
}
private:
std::vector<CharType>& v;
};
/// output adapter for output streams
template<typename CharType>
class output_stream_adapter : public output_adapter_protocol<CharType>
{
public:
explicit output_stream_adapter(std::basic_ostream<CharType>& s) noexcept
: stream(s)
{}
void write_character(CharType c) override
{
stream.put(c);
}
JSON_HEDLEY_NON_NULL(2)
void write_characters(const CharType* s, std::size_t length) override
{
stream.write(s, static_cast<std::streamsize>(length));
}
private:
std::basic_ostream<CharType>& stream;
};
/// output adapter for basic_string
template<typename CharType, typename StringType = std::basic_string<CharType>>
class output_string_adapter : public output_adapter_protocol<CharType>
{
public:
explicit output_string_adapter(StringType& s) noexcept
: str(s)
{}
void write_character(CharType c) override
{
str.push_back(c);
}
JSON_HEDLEY_NON_NULL(2)
void write_characters(const CharType* s, std::size_t length) override
{
str.append(s, length);
}
private:
StringType& str;
};
template<typename CharType, typename StringType = std::basic_string<CharType>>
class output_adapter
{
public:
output_adapter(std::vector<CharType>& vec)
: oa(std::make_shared<output_vector_adapter<CharType>>(vec)) {}
output_adapter(std::basic_ostream<CharType>& s)
: oa(std::make_shared<output_stream_adapter<CharType>>(s)) {}
output_adapter(StringType& s)
: oa(std::make_shared<output_string_adapter<CharType, StringType>>(s)) {}
operator output_adapter_t<CharType>()
{
return oa;
}
private:
output_adapter_t<CharType> oa = nullptr;
};
} // namespace detail
} // namespace nlohmann

954
lib/json/include/nlohmann/detail/output/serializer.hpp

@ -0,0 +1,954 @@
#pragma once
#include <algorithm> // reverse, remove, fill, find, none_of
#include <array> // array
#include <clocale> // localeconv, lconv
#include <cmath> // labs, isfinite, isnan, signbit
#include <cstddef> // size_t, ptrdiff_t
#include <cstdint> // uint8_t
#include <cstdio> // snprintf
#include <limits> // numeric_limits
#include <string> // string, char_traits
#include <type_traits> // is_same
#include <utility> // move
#include <nlohmann/detail/conversions/to_chars.hpp>
#include <nlohmann/detail/exceptions.hpp>
#include <nlohmann/detail/macro_scope.hpp>
#include <nlohmann/detail/meta/cpp_future.hpp>
#include <nlohmann/detail/output/binary_writer.hpp>
#include <nlohmann/detail/output/output_adapters.hpp>
#include <nlohmann/detail/value_t.hpp>
namespace nlohmann
{
namespace detail
{
///////////////////
// serialization //
///////////////////
/// how to treat decoding errors
enum class error_handler_t
{
strict, ///< throw a type_error exception in case of invalid UTF-8
replace, ///< replace invalid UTF-8 sequences with U+FFFD
ignore ///< ignore invalid UTF-8 sequences
};
template<typename BasicJsonType>
class serializer
{
using string_t = typename BasicJsonType::string_t;
using number_float_t = typename BasicJsonType::number_float_t;
using number_integer_t = typename BasicJsonType::number_integer_t;
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
using binary_char_t = typename BasicJsonType::binary_t::value_type;
static constexpr std::uint8_t UTF8_ACCEPT = 0;
static constexpr std::uint8_t UTF8_REJECT = 1;
public:
/*!
@param[in] s output stream to serialize to
@param[in] ichar indentation character to use
@param[in] error_handler_ how to react on decoding errors
*/
serializer(output_adapter_t<char> s, const char ichar,
error_handler_t error_handler_ = error_handler_t::strict)
: o(std::move(s))
, loc(std::localeconv())
, thousands_sep(loc->thousands_sep == nullptr ? '\0' : std::char_traits<char>::to_char_type(* (loc->thousands_sep)))
, decimal_point(loc->decimal_point == nullptr ? '\0' : std::char_traits<char>::to_char_type(* (loc->decimal_point)))
, indent_char(ichar)
, indent_string(512, indent_char)
, error_handler(error_handler_)
{}
// delete because of pointer members
serializer(const serializer&) = delete;
serializer& operator=(const serializer&) = delete;
serializer(serializer&&) = delete;
serializer& operator=(serializer&&) = delete;
~serializer() = default;
/*!
@brief internal implementation of the serialization function
This function is called by the public member function dump and organizes
the serialization internally. The indentation level is propagated as
additional parameter. In case of arrays and objects, the function is
called recursively.
- strings and object keys are escaped using `escape_string()`
- integer numbers are converted implicitly via `operator<<`
- floating-point numbers are converted to a string using `"%g"` format
- binary values are serialized as objects containing the subtype and the
byte array
@param[in] val value to serialize
@param[in] pretty_print whether the output shall be pretty-printed
@param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters
in the output are escaped with `\uXXXX` sequences, and the result consists
of ASCII characters only.
@param[in] indent_step the indent level
@param[in] current_indent the current indent level (only used internally)
*/
void dump(const BasicJsonType& val,
const bool pretty_print,
const bool ensure_ascii,
const unsigned int indent_step,
const unsigned int current_indent = 0)
{
switch (val.m_type)
{
case value_t::object:
{
if (val.m_value.object->empty())
{
o->write_characters("{}", 2);
return;
}
if (pretty_print)
{
o->write_characters("{\n", 2);
// variable to hold indentation for recursive calls
const auto new_indent = current_indent + indent_step;
if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))
{
indent_string.resize(indent_string.size() * 2, ' ');
}
// first n-1 elements
auto i = val.m_value.object->cbegin();
for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
{
o->write_characters(indent_string.c_str(), new_indent);
o->write_character('\"');
dump_escaped(i->first, ensure_ascii);
o->write_characters("\": ", 3);
dump(i->second, true, ensure_ascii, indent_step, new_indent);
o->write_characters(",\n", 2);
}
// last element
JSON_ASSERT(i != val.m_value.object->cend());
JSON_ASSERT(std::next(i) == val.m_value.object->cend());
o->write_characters(indent_string.c_str(), new_indent);
o->write_character('\"');
dump_escaped(i->first, ensure_ascii);
o->write_characters("\": ", 3);
dump(i->second, true, ensure_ascii, indent_step, new_indent);
o->write_character('\n');
o->write_characters(indent_string.c_str(), current_indent);
o->write_character('}');
}
else
{
o->write_character('{');
// first n-1 elements
auto i = val.m_value.object->cbegin();
for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
{
o->write_character('\"');
dump_escaped(i->first, ensure_ascii);
o->write_characters("\":", 2);
dump(i->second, false, ensure_ascii, indent_step, current_indent);
o->write_character(',');
}
// last element
JSON_ASSERT(i != val.m_value.object->cend());
JSON_ASSERT(std::next(i) == val.m_value.object->cend());
o->write_character('\"');
dump_escaped(i->first, ensure_ascii);
o->write_characters("\":", 2);
dump(i->second, false, ensure_ascii, indent_step, current_indent);
o->write_character('}');
}
return;
}
case value_t::array:
{
if (val.m_value.array->empty())
{
o->write_characters("[]", 2);
return;
}
if (pretty_print)
{
o->write_characters("[\n", 2);
// variable to hold indentation for recursive calls
const auto new_indent = current_indent + indent_step;
if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))
{
indent_string.resize(indent_string.size() * 2, ' ');
}
// first n-1 elements
for (auto i = val.m_value.array->cbegin();
i != val.m_value.array->cend() - 1; ++i)
{
o->write_characters(indent_string.c_str(), new_indent);
dump(*i, true, ensure_ascii, indent_step, new_indent);
o->write_characters(",\n", 2);
}
// last element
JSON_ASSERT(!val.m_value.array->empty());
o->write_characters(indent_string.c_str(), new_indent);
dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent);
o->write_character('\n');
o->write_characters(indent_string.c_str(), current_indent);
o->write_character(']');
}
else
{
o->write_character('[');
// first n-1 elements
for (auto i = val.m_value.array->cbegin();
i != val.m_value.array->cend() - 1; ++i)
{
dump(*i, false, ensure_ascii, indent_step, current_indent);
o->write_character(',');
}
// last element
JSON_ASSERT(!val.m_value.array->empty());
dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent);
o->write_character(']');
}
return;
}
case value_t::string:
{
o->write_character('\"');
dump_escaped(*val.m_value.string, ensure_ascii);
o->write_character('\"');
return;
}
case value_t::binary:
{
if (pretty_print)
{
o->write_characters("{\n", 2);
// variable to hold indentation for recursive calls
const auto new_indent = current_indent + indent_step;
if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))
{
indent_string.resize(indent_string.size() * 2, ' ');
}
o->write_characters(indent_string.c_str(), new_indent);
o->write_characters("\"bytes\": [", 10);
if (!val.m_value.binary->empty())
{
for (auto i = val.m_value.binary->cbegin();
i != val.m_value.binary->cend() - 1; ++i)
{
dump_integer(*i);
o->write_characters(", ", 2);
}
dump_integer(val.m_value.binary->back());
}
o->write_characters("],\n", 3);
o->write_characters(indent_string.c_str(), new_indent);
o->write_characters("\"subtype\": ", 11);
if (val.m_value.binary->has_subtype())
{
dump_integer(val.m_value.binary->subtype());
}
else
{
o->write_characters("null", 4);
}
o->write_character('\n');
o->write_characters(indent_string.c_str(), current_indent);
o->write_character('}');
}
else
{
o->write_characters("{\"bytes\":[", 10);
if (!val.m_value.binary->empty())
{
for (auto i = val.m_value.binary->cbegin();
i != val.m_value.binary->cend() - 1; ++i)
{
dump_integer(*i);
o->write_character(',');
}
dump_integer(val.m_value.binary->back());
}
o->write_characters("],\"subtype\":", 12);
if (val.m_value.binary->has_subtype())
{
dump_integer(val.m_value.binary->subtype());
o->write_character('}');
}
else
{
o->write_characters("null}", 5);
}
}
return;
}
case value_t::boolean:
{
if (val.m_value.boolean)
{
o->write_characters("true", 4);
}
else
{
o->write_characters("false", 5);
}
return;
}
case value_t::number_integer:
{
dump_integer(val.m_value.number_integer);
return;
}
case value_t::number_unsigned:
{
dump_integer(val.m_value.number_unsigned);
return;
}
case value_t::number_float:
{
dump_float(val.m_value.number_float);
return;
}
case value_t::discarded:
{
o->write_characters("<discarded>", 11);
return;
}
case value_t::null:
{
o->write_characters("null", 4);
return;
}
default: // LCOV_EXCL_LINE
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
}
}
JSON_PRIVATE_UNLESS_TESTED:
/*!
@brief dump escaped string
Escape a string by replacing certain special characters by a sequence of an
escape character (backslash) and another character and other control
characters by a sequence of "\u" followed by a four-digit hex
representation. The escaped string is written to output stream @a o.
@param[in] s the string to escape
@param[in] ensure_ascii whether to escape non-ASCII characters with
\uXXXX sequences
@complexity Linear in the length of string @a s.
*/
void dump_escaped(const string_t& s, const bool ensure_ascii)
{
std::uint32_t codepoint{};
std::uint8_t state = UTF8_ACCEPT;
std::size_t bytes = 0; // number of bytes written to string_buffer
// number of bytes written at the point of the last valid byte
std::size_t bytes_after_last_accept = 0;
std::size_t undumped_chars = 0;
for (std::size_t i = 0; i < s.size(); ++i)
{
const auto byte = static_cast<uint8_t>(s[i]);
switch (decode(state, codepoint, byte))
{
case UTF8_ACCEPT: // decode found a new code point
{
switch (codepoint)
{
case 0x08: // backspace
{
string_buffer[bytes++] = '\\';
string_buffer[bytes++] = 'b';
break;
}
case 0x09: // horizontal tab
{
string_buffer[bytes++] = '\\';
string_buffer[bytes++] = 't';
break;
}
case 0x0A: // newline
{
string_buffer[bytes++] = '\\';
string_buffer[bytes++] = 'n';
break;
}
case 0x0C: // formfeed
{
string_buffer[bytes++] = '\\';
string_buffer[bytes++] = 'f';
break;
}
case 0x0D: // carriage return
{
string_buffer[bytes++] = '\\';
string_buffer[bytes++] = 'r';
break;
}
case 0x22: // quotation mark
{
string_buffer[bytes++] = '\\';
string_buffer[bytes++] = '\"';
break;
}
case 0x5C: // reverse solidus
{
string_buffer[bytes++] = '\\';
string_buffer[bytes++] = '\\';
break;
}
default:
{
// escape control characters (0x00..0x1F) or, if
// ensure_ascii parameter is used, non-ASCII characters
if ((codepoint <= 0x1F) || (ensure_ascii && (codepoint >= 0x7F)))
{
if (codepoint <= 0xFFFF)
{
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
(std::snprintf)(string_buffer.data() + bytes, 7, "\\u%04x",
static_cast<std::uint16_t>(codepoint));
bytes += 6;
}
else
{
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
(std::snprintf)(string_buffer.data() + bytes, 13, "\\u%04x\\u%04x",
static_cast<std::uint16_t>(0xD7C0u + (codepoint >> 10u)),
static_cast<std::uint16_t>(0xDC00u + (codepoint & 0x3FFu)));
bytes += 12;
}
}
else
{
// copy byte to buffer (all previous bytes
// been copied have in default case above)
string_buffer[bytes++] = s[i];
}
break;
}
}
// write buffer and reset index; there must be 13 bytes
// left, as this is the maximal number of bytes to be
// written ("\uxxxx\uxxxx\0") for one code point
if (string_buffer.size() - bytes < 13)
{
o->write_characters(string_buffer.data(), bytes);
bytes = 0;
}
// remember the byte position of this accept
bytes_after_last_accept = bytes;
undumped_chars = 0;
break;
}
case UTF8_REJECT: // decode found invalid UTF-8 byte
{
switch (error_handler)
{
case error_handler_t::strict:
{
std::string sn(3, '\0');
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
(std::snprintf)(&sn[0], sn.size(), "%.2X", byte);
JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + sn, BasicJsonType()));
}
case error_handler_t::ignore:
case error_handler_t::replace:
{
// in case we saw this character the first time, we
// would like to read it again, because the byte
// may be OK for itself, but just not OK for the
// previous sequence
if (undumped_chars > 0)
{
--i;
}
// reset length buffer to the last accepted index;
// thus removing/ignoring the invalid characters
bytes = bytes_after_last_accept;
if (error_handler == error_handler_t::replace)
{
// add a replacement character
if (ensure_ascii)
{
string_buffer[bytes++] = '\\';
string_buffer[bytes++] = 'u';
string_buffer[bytes++] = 'f';
string_buffer[bytes++] = 'f';
string_buffer[bytes++] = 'f';
string_buffer[bytes++] = 'd';
}
else
{
string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xEF');
string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xBF');
string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xBD');
}
// write buffer and reset index; there must be 13 bytes
// left, as this is the maximal number of bytes to be
// written ("\uxxxx\uxxxx\0") for one code point
if (string_buffer.size() - bytes < 13)
{
o->write_characters(string_buffer.data(), bytes);
bytes = 0;
}
bytes_after_last_accept = bytes;
}
undumped_chars = 0;
// continue processing the string
state = UTF8_ACCEPT;
break;
}
default: // LCOV_EXCL_LINE
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
}
break;
}
default: // decode found yet incomplete multi-byte code point
{
if (!ensure_ascii)
{
// code point will not be escaped - copy byte to buffer
string_buffer[bytes++] = s[i];
}
++undumped_chars;
break;
}
}
}
// we finished processing the string
if (JSON_HEDLEY_LIKELY(state == UTF8_ACCEPT))
{
// write buffer
if (bytes > 0)
{
o->write_characters(string_buffer.data(), bytes);
}
}
else
{
// we finish reading, but do not accept: string was incomplete
switch (error_handler)
{
case error_handler_t::strict:
{
std::string sn(3, '\0');
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
(std::snprintf)(&sn[0], sn.size(), "%.2X", static_cast<std::uint8_t>(s.back()));
JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + sn, BasicJsonType()));
}
case error_handler_t::ignore:
{
// write all accepted bytes
o->write_characters(string_buffer.data(), bytes_after_last_accept);
break;
}
case error_handler_t::replace:
{
// write all accepted bytes
o->write_characters(string_buffer.data(), bytes_after_last_accept);
// add a replacement character
if (ensure_ascii)
{
o->write_characters("\\ufffd", 6);
}
else
{
o->write_characters("\xEF\xBF\xBD", 3);
}
break;
}
default: // LCOV_EXCL_LINE
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
}
}
}
private:
/*!
@brief count digits
Count the number of decimal (base 10) digits for an input unsigned integer.
@param[in] x unsigned integer number to count its digits
@return number of decimal digits
*/
inline unsigned int count_digits(number_unsigned_t x) noexcept
{
unsigned int n_digits = 1;
for (;;)
{
if (x < 10)
{
return n_digits;
}
if (x < 100)
{
return n_digits + 1;
}
if (x < 1000)
{
return n_digits + 2;
}
if (x < 10000)
{
return n_digits + 3;
}
x = x / 10000u;
n_digits += 4;
}
}
/*!
@brief dump an integer
Dump a given integer to output stream @a o. Works internally with
@a number_buffer.
@param[in] x integer number (signed or unsigned) to dump
@tparam NumberType either @a number_integer_t or @a number_unsigned_t
*/
template < typename NumberType, detail::enable_if_t <
std::is_same<NumberType, number_unsigned_t>::value ||
std::is_same<NumberType, number_integer_t>::value ||
std::is_same<NumberType, binary_char_t>::value,
int > = 0 >
void dump_integer(NumberType x)
{
static constexpr std::array<std::array<char, 2>, 100> digits_to_99
{
{
{{'0', '0'}}, {{'0', '1'}}, {{'0', '2'}}, {{'0', '3'}}, {{'0', '4'}}, {{'0', '5'}}, {{'0', '6'}}, {{'0', '7'}}, {{'0', '8'}}, {{'0', '9'}},
{{'1', '0'}}, {{'1', '1'}}, {{'1', '2'}}, {{'1', '3'}}, {{'1', '4'}}, {{'1', '5'}}, {{'1', '6'}}, {{'1', '7'}}, {{'1', '8'}}, {{'1', '9'}},
{{'2', '0'}}, {{'2', '1'}}, {{'2', '2'}}, {{'2', '3'}}, {{'2', '4'}}, {{'2', '5'}}, {{'2', '6'}}, {{'2', '7'}}, {{'2', '8'}}, {{'2', '9'}},
{{'3', '0'}}, {{'3', '1'}}, {{'3', '2'}}, {{'3', '3'}}, {{'3', '4'}}, {{'3', '5'}}, {{'3', '6'}}, {{'3', '7'}}, {{'3', '8'}}, {{'3', '9'}},
{{'4', '0'}}, {{'4', '1'}}, {{'4', '2'}}, {{'4', '3'}}, {{'4', '4'}}, {{'4', '5'}}, {{'4', '6'}}, {{'4', '7'}}, {{'4', '8'}}, {{'4', '9'}},
{{'5', '0'}}, {{'5', '1'}}, {{'5', '2'}}, {{'5', '3'}}, {{'5', '4'}}, {{'5', '5'}}, {{'5', '6'}}, {{'5', '7'}}, {{'5', '8'}}, {{'5', '9'}},
{{'6', '0'}}, {{'6', '1'}}, {{'6', '2'}}, {{'6', '3'}}, {{'6', '4'}}, {{'6', '5'}}, {{'6', '6'}}, {{'6', '7'}}, {{'6', '8'}}, {{'6', '9'}},
{{'7', '0'}}, {{'7', '1'}}, {{'7', '2'}}, {{'7', '3'}}, {{'7', '4'}}, {{'7', '5'}}, {{'7', '6'}}, {{'7', '7'}}, {{'7', '8'}}, {{'7', '9'}},
{{'8', '0'}}, {{'8', '1'}}, {{'8', '2'}}, {{'8', '3'}}, {{'8', '4'}}, {{'8', '5'}}, {{'8', '6'}}, {{'8', '7'}}, {{'8', '8'}}, {{'8', '9'}},
{{'9', '0'}}, {{'9', '1'}}, {{'9', '2'}}, {{'9', '3'}}, {{'9', '4'}}, {{'9', '5'}}, {{'9', '6'}}, {{'9', '7'}}, {{'9', '8'}}, {{'9', '9'}},
}
};
// special case for "0"
if (x == 0)
{
o->write_character('0');
return;
}
// use a pointer to fill the buffer
auto buffer_ptr = number_buffer.begin(); // NOLINT(llvm-qualified-auto,readability-qualified-auto,cppcoreguidelines-pro-type-vararg,hicpp-vararg)
const bool is_negative = std::is_same<NumberType, number_integer_t>::value && !(x >= 0); // see issue #755
number_unsigned_t abs_value;
unsigned int n_chars{};
if (is_negative)
{
*buffer_ptr = '-';
abs_value = remove_sign(static_cast<number_integer_t>(x));
// account one more byte for the minus sign
n_chars = 1 + count_digits(abs_value);
}
else
{
abs_value = static_cast<number_unsigned_t>(x);
n_chars = count_digits(abs_value);
}
// spare 1 byte for '\0'
JSON_ASSERT(n_chars < number_buffer.size() - 1);
// jump to the end to generate the string from backward
// so we later avoid reversing the result
buffer_ptr += n_chars;
// Fast int2ascii implementation inspired by "Fastware" talk by Andrei Alexandrescu
// See: https://www.youtube.com/watch?v=o4-CwDo2zpg
while (abs_value >= 100)
{
const auto digits_index = static_cast<unsigned>((abs_value % 100));
abs_value /= 100;
*(--buffer_ptr) = digits_to_99[digits_index][1];
*(--buffer_ptr) = digits_to_99[digits_index][0];
}
if (abs_value >= 10)
{
const auto digits_index = static_cast<unsigned>(abs_value);
*(--buffer_ptr) = digits_to_99[digits_index][1];
*(--buffer_ptr) = digits_to_99[digits_index][0];
}
else
{
*(--buffer_ptr) = static_cast<char>('0' + abs_value);
}
o->write_characters(number_buffer.data(), n_chars);
}
/*!
@brief dump a floating-point number
Dump a given floating-point number to output stream @a o. Works internally
with @a number_buffer.
@param[in] x floating-point number to dump
*/
void dump_float(number_float_t x)
{
// NaN / inf
if (!std::isfinite(x))
{
o->write_characters("null", 4);
return;
}
// If number_float_t is an IEEE-754 single or double precision number,
// use the Grisu2 algorithm to produce short numbers which are
// guaranteed to round-trip, using strtof and strtod, resp.
//
// NB: The test below works if <long double> == <double>.
static constexpr bool is_ieee_single_or_double
= (std::numeric_limits<number_float_t>::is_iec559 && std::numeric_limits<number_float_t>::digits == 24 && std::numeric_limits<number_float_t>::max_exponent == 128) ||
(std::numeric_limits<number_float_t>::is_iec559 && std::numeric_limits<number_float_t>::digits == 53 && std::numeric_limits<number_float_t>::max_exponent == 1024);
dump_float(x, std::integral_constant<bool, is_ieee_single_or_double>());
}
void dump_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/)
{
auto* begin = number_buffer.data();
auto* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x);
o->write_characters(begin, static_cast<size_t>(end - begin));
}
void dump_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/)
{
// get number of digits for a float -> text -> float round-trip
static constexpr auto d = std::numeric_limits<number_float_t>::max_digits10;
// the actual conversion
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
std::ptrdiff_t len = (std::snprintf)(number_buffer.data(), number_buffer.size(), "%.*g", d, x);
// negative value indicates an error
JSON_ASSERT(len > 0);
// check if buffer was large enough
JSON_ASSERT(static_cast<std::size_t>(len) < number_buffer.size());
// erase thousands separator
if (thousands_sep != '\0')
{
auto* const end = std::remove(number_buffer.begin(),
number_buffer.begin() + len, thousands_sep);
std::fill(end, number_buffer.end(), '\0');
JSON_ASSERT((end - number_buffer.begin()) <= len);
len = (end - number_buffer.begin());
}
// convert decimal point to '.'
if (decimal_point != '\0' && decimal_point != '.')
{
auto* const dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point);
if (dec_pos != number_buffer.end())
{
*dec_pos = '.';
}
}
o->write_characters(number_buffer.data(), static_cast<std::size_t>(len));
// determine if need to append ".0"
const bool value_is_int_like =
std::none_of(number_buffer.begin(), number_buffer.begin() + len + 1,
[](char c)
{
return c == '.' || c == 'e';
});
if (value_is_int_like)
{
o->write_characters(".0", 2);
}
}
/*!
@brief check whether a string is UTF-8 encoded
The function checks each byte of a string whether it is UTF-8 encoded. The
result of the check is stored in the @a state parameter. The function must
be called initially with state 0 (accept). State 1 means the string must
be rejected, because the current byte is not allowed. If the string is
completely processed, but the state is non-zero, the string ended
prematurely; that is, the last byte indicated more bytes should have
followed.
@param[in,out] state the state of the decoding
@param[in,out] codep codepoint (valid only if resulting state is UTF8_ACCEPT)
@param[in] byte next byte to decode
@return new state
@note The function has been edited: a std::array is used.
@copyright Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
@sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
*/
static std::uint8_t decode(std::uint8_t& state, std::uint32_t& codep, const std::uint8_t byte) noexcept
{
static const std::array<std::uint8_t, 400> utf8d =
{
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7F
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF
8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0..DF
0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF
0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF
0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2
1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4
1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6
1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8
}
};
JSON_ASSERT(byte < utf8d.size());
const std::uint8_t type = utf8d[byte];
codep = (state != UTF8_ACCEPT)
? (byte & 0x3fu) | (codep << 6u)
: (0xFFu >> type) & (byte);
std::size_t index = 256u + static_cast<size_t>(state) * 16u + static_cast<size_t>(type);
JSON_ASSERT(index < 400);
state = utf8d[index];
return state;
}
/*
* Overload to make the compiler happy while it is instantiating
* dump_integer for number_unsigned_t.
* Must never be called.
*/
number_unsigned_t remove_sign(number_unsigned_t x)
{
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
return x; // LCOV_EXCL_LINE
}
/*
* Helper function for dump_integer
*
* This function takes a negative signed integer and returns its absolute
* value as unsigned integer. The plus/minus shuffling is necessary as we can
* not directly remove the sign of an arbitrary signed integer as the
* absolute values of INT_MIN and INT_MAX are usually not the same. See
* #1708 for details.
*/
inline number_unsigned_t remove_sign(number_integer_t x) noexcept
{
JSON_ASSERT(x < 0 && x < (std::numeric_limits<number_integer_t>::max)()); // NOLINT(misc-redundant-expression)
return static_cast<number_unsigned_t>(-(x + 1)) + 1;
}
private:
/// the output of the serializer
output_adapter_t<char> o = nullptr;
/// a (hopefully) large enough character buffer
std::array<char, 64> number_buffer{{}};
/// the locale
const std::lconv* loc = nullptr;
/// the locale's thousand separator character
const char thousands_sep = '\0';
/// the locale's decimal point character
const char decimal_point = '\0';
/// string buffer
std::array<char, 512> string_buffer{{}};
/// the indentation character
const char indent_char;
/// the indentation string
string_t indent_string;
/// error_handler how to react on decoding errors
const error_handler_t error_handler;
};
} // namespace detail
} // namespace nlohmann

63
lib/json/include/nlohmann/detail/string_escape.hpp

@ -0,0 +1,63 @@
#pragma once
#include <string>
#include <nlohmann/detail/macro_scope.hpp>
namespace nlohmann
{
namespace detail
{
/*!
@brief replace all occurrences of a substring by another string
@param[in,out] s the string to manipulate; changed so that all
occurrences of @a f are replaced with @a t
@param[in] f the substring to replace with @a t
@param[in] t the string to replace @a f
@pre The search string @a f must not be empty. **This precondition is
enforced with an assertion.**
@since version 2.0.0
*/
inline void replace_substring(std::string& s, const std::string& f,
const std::string& t)
{
JSON_ASSERT(!f.empty());
for (auto pos = s.find(f); // find first occurrence of f
pos != std::string::npos; // make sure f was found
s.replace(pos, f.size(), t), // replace with t, and
pos = s.find(f, pos + t.size())) // find next occurrence of f
{}
}
/*!
* @brief string escaping as described in RFC 6901 (Sect. 4)
* @param[in] s string to escape
* @return escaped string
*
* Note the order of escaping "~" to "~0" and "/" to "~1" is important.
*/
inline std::string escape(std::string s)
{
replace_substring(s, "~", "~0");
replace_substring(s, "/", "~1");
return s;
}
/*!
* @brief string unescaping as described in RFC 6901 (Sect. 4)
* @param[in] s string to unescape
* @return unescaped string
*
* Note the order of escaping "~1" to "/" and "~0" to "~" is important.
*/
static void unescape(std::string& s)
{
replace_substring(s, "~1", "/");
replace_substring(s, "~0", "~");
}
} // namespace detail
} // namespace nlohmann

81
lib/json/include/nlohmann/detail/value_t.hpp

@ -0,0 +1,81 @@
#pragma once
#include <array> // array
#include <cstddef> // size_t
#include <cstdint> // uint8_t
#include <string> // string
namespace nlohmann
{
namespace detail
{
///////////////////////////
// JSON type enumeration //
///////////////////////////
/*!
@brief the JSON type enumeration
This enumeration collects the different JSON types. It is internally used to
distinguish the stored values, and the functions @ref basic_json::is_null(),
@ref basic_json::is_object(), @ref basic_json::is_array(),
@ref basic_json::is_string(), @ref basic_json::is_boolean(),
@ref basic_json::is_number() (with @ref basic_json::is_number_integer(),
@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()),
@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and
@ref basic_json::is_structured() rely on it.
@note There are three enumeration entries (number_integer, number_unsigned, and
number_float), because the library distinguishes these three types for numbers:
@ref basic_json::number_unsigned_t is used for unsigned integers,
@ref basic_json::number_integer_t is used for signed integers, and
@ref basic_json::number_float_t is used for floating-point numbers or to
approximate integers which do not fit in the limits of their respective type.
@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON
value with the default value for a given type
@since version 1.0.0
*/
enum class value_t : std::uint8_t
{
null, ///< null value
object, ///< object (unordered set of name/value pairs)
array, ///< array (ordered collection of values)
string, ///< string value
boolean, ///< boolean value
number_integer, ///< number value (signed integer)
number_unsigned, ///< number value (unsigned integer)
number_float, ///< number value (floating-point)
binary, ///< binary array (ordered collection of bytes)
discarded ///< discarded by the parser callback function
};
/*!
@brief comparison operator for JSON types
Returns an ordering that is similar to Python:
- order: null < boolean < number < object < array < string < binary
- furthermore, each type is not smaller than itself
- discarded values are not comparable
- binary is represented as a b"" string in python and directly comparable to a
string; however, making a binary array directly comparable with a string would
be surprising behavior in a JSON file.
@since version 1.0.0
*/
inline bool operator<(const value_t lhs, const value_t rhs) noexcept
{
static constexpr std::array<std::uint8_t, 9> order = {{
0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */,
1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */,
6 /* binary */
}
};
const auto l_index = static_cast<std::size_t>(lhs);
const auto r_index = static_cast<std::size_t>(rhs);
return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index];
}
} // namespace detail
} // namespace nlohmann

8942
lib/json/include/nlohmann/json.hpp

File diff suppressed because it is too large Load Diff

78
lib/json/include/nlohmann/json_fwd.hpp

@ -0,0 +1,78 @@
#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_
#define INCLUDE_NLOHMANN_JSON_FWD_HPP_
#include <cstdint> // int64_t, uint64_t
#include <map> // map
#include <memory> // allocator
#include <string> // string
#include <vector> // vector
/*!
@brief namespace for Niels Lohmann
@see https://github.com/nlohmann
@since version 1.0.0
*/
namespace nlohmann
{
/*!
@brief default JSONSerializer template argument
This serializer ignores the template arguments and uses ADL
([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl))
for serialization.
*/
template<typename T = void, typename SFINAE = void>
struct adl_serializer;
template<template<typename U, typename V, typename... Args> class ObjectType =
std::map,
template<typename U, typename... Args> class ArrayType = std::vector,
class StringType = std::string, class BooleanType = bool,
class NumberIntegerType = std::int64_t,
class NumberUnsignedType = std::uint64_t,
class NumberFloatType = double,
template<typename U> class AllocatorType = std::allocator,
template<typename T, typename SFINAE = void> class JSONSerializer =
adl_serializer,
class BinaryType = std::vector<std::uint8_t>>
class basic_json;
/*!
@brief JSON Pointer
A JSON pointer defines a string syntax for identifying a specific value
within a JSON document. It can be used with functions `at` and
`operator[]`. Furthermore, JSON pointers are the base for JSON patches.
@sa [RFC 6901](https://tools.ietf.org/html/rfc6901)
@since version 2.0.0
*/
template<typename BasicJsonType>
class json_pointer;
/*!
@brief default JSON class
This type is the default specialization of the @ref basic_json class which
uses the standard template types.
@since version 1.0.0
*/
using json = basic_json<>;
template<class Key, class T, class IgnoredLess, class Allocator>
struct ordered_map;
/*!
@brief ordered JSON class
This type preserves the insertion order of object keys.
@since version 3.9.0
*/
using ordered_json = basic_json<nlohmann::ordered_map>;
} // namespace nlohmann
#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_

190
lib/json/include/nlohmann/ordered_map.hpp

@ -0,0 +1,190 @@
#pragma once
#include <functional> // less
#include <initializer_list> // initializer_list
#include <iterator> // input_iterator_tag, iterator_traits
#include <memory> // allocator
#include <stdexcept> // for out_of_range
#include <type_traits> // enable_if, is_convertible
#include <utility> // pair
#include <vector> // vector
#include <nlohmann/detail/macro_scope.hpp>
namespace nlohmann
{
/// ordered_map: a minimal map-like container that preserves insertion order
/// for use within nlohmann::basic_json<ordered_map>
template <class Key, class T, class IgnoredLess = std::less<Key>,
class Allocator = std::allocator<std::pair<const Key, T>>>
struct ordered_map : std::vector<std::pair<const Key, T>, Allocator>
{
using key_type = Key;
using mapped_type = T;
using Container = std::vector<std::pair<const Key, T>, Allocator>;
using typename Container::iterator;
using typename Container::const_iterator;
using typename Container::size_type;
using typename Container::value_type;
// Explicit constructors instead of `using Container::Container`
// otherwise older compilers choke on it (GCC <= 5.5, xcode <= 9.4)
ordered_map(const Allocator& alloc = Allocator()) : Container{alloc} {}
template <class It>
ordered_map(It first, It last, const Allocator& alloc = Allocator())
: Container{first, last, alloc} {}
ordered_map(std::initializer_list<T> init, const Allocator& alloc = Allocator() )
: Container{init, alloc} {}
std::pair<iterator, bool> emplace(const key_type& key, T&& t)
{
for (auto it = this->begin(); it != this->end(); ++it)
{
if (it->first == key)
{
return {it, false};
}
}
Container::emplace_back(key, t);
return {--this->end(), true};
}
T& operator[](const Key& key)
{
return emplace(key, T{}).first->second;
}
const T& operator[](const Key& key) const
{
return at(key);
}
T& at(const Key& key)
{
for (auto it = this->begin(); it != this->end(); ++it)
{
if (it->first == key)
{
return it->second;
}
}
JSON_THROW(std::out_of_range("key not found"));
}
const T& at(const Key& key) const
{
for (auto it = this->begin(); it != this->end(); ++it)
{
if (it->first == key)
{
return it->second;
}
}
JSON_THROW(std::out_of_range("key not found"));
}
size_type erase(const Key& key)
{
for (auto it = this->begin(); it != this->end(); ++it)
{
if (it->first == key)
{
// Since we cannot move const Keys, re-construct them in place
for (auto next = it; ++next != this->end(); ++it)
{
it->~value_type(); // Destroy but keep allocation
new (&*it) value_type{std::move(*next)};
}
Container::pop_back();
return 1;
}
}
return 0;
}
iterator erase(iterator pos)
{
auto it = pos;
// Since we cannot move const Keys, re-construct them in place
for (auto next = it; ++next != this->end(); ++it)
{
it->~value_type(); // Destroy but keep allocation
new (&*it) value_type{std::move(*next)};
}
Container::pop_back();
return pos;
}
size_type count(const Key& key) const
{
for (auto it = this->begin(); it != this->end(); ++it)
{
if (it->first == key)
{
return 1;
}
}
return 0;
}
iterator find(const Key& key)
{
for (auto it = this->begin(); it != this->end(); ++it)
{
if (it->first == key)
{
return it;
}
}
return Container::end();
}
const_iterator find(const Key& key) const
{
for (auto it = this->begin(); it != this->end(); ++it)
{
if (it->first == key)
{
return it;
}
}
return Container::end();
}
std::pair<iterator, bool> insert( value_type&& value )
{
return emplace(value.first, std::move(value.second));
}
std::pair<iterator, bool> insert( const value_type& value )
{
for (auto it = this->begin(); it != this->end(); ++it)
{
if (it->first == value.first)
{
return {it, false};
}
}
Container::push_back(value);
return {--this->end(), true};
}
template<typename InputIt>
using require_input_iter = typename std::enable_if<std::is_convertible<typename std::iterator_traits<InputIt>::iterator_category,
std::input_iterator_tag>::value>::type;
template<typename InputIt, typename = require_input_iter<InputIt>>
void insert(InputIt first, InputIt last)
{
for (auto it = first; it != last; ++it)
{
insert(*it);
}
}
};
} // namespace nlohmann

2044
lib/json/include/nlohmann/thirdparty/hedley/hedley.hpp vendored

File diff suppressed because it is too large Load Diff

150
lib/json/include/nlohmann/thirdparty/hedley/hedley_undef.hpp vendored

@ -0,0 +1,150 @@
#pragma once
#undef JSON_HEDLEY_ALWAYS_INLINE
#undef JSON_HEDLEY_ARM_VERSION
#undef JSON_HEDLEY_ARM_VERSION_CHECK
#undef JSON_HEDLEY_ARRAY_PARAM
#undef JSON_HEDLEY_ASSUME
#undef JSON_HEDLEY_BEGIN_C_DECLS
#undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE
#undef JSON_HEDLEY_CLANG_HAS_BUILTIN
#undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE
#undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE
#undef JSON_HEDLEY_CLANG_HAS_EXTENSION
#undef JSON_HEDLEY_CLANG_HAS_FEATURE
#undef JSON_HEDLEY_CLANG_HAS_WARNING
#undef JSON_HEDLEY_COMPCERT_VERSION
#undef JSON_HEDLEY_COMPCERT_VERSION_CHECK
#undef JSON_HEDLEY_CONCAT
#undef JSON_HEDLEY_CONCAT3
#undef JSON_HEDLEY_CONCAT3_EX
#undef JSON_HEDLEY_CONCAT_EX
#undef JSON_HEDLEY_CONST
#undef JSON_HEDLEY_CONSTEXPR
#undef JSON_HEDLEY_CONST_CAST
#undef JSON_HEDLEY_CPP_CAST
#undef JSON_HEDLEY_CRAY_VERSION
#undef JSON_HEDLEY_CRAY_VERSION_CHECK
#undef JSON_HEDLEY_C_DECL
#undef JSON_HEDLEY_DEPRECATED
#undef JSON_HEDLEY_DEPRECATED_FOR
#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL
#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_
#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED
#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES
#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS
#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION
#undef JSON_HEDLEY_DIAGNOSTIC_POP
#undef JSON_HEDLEY_DIAGNOSTIC_PUSH
#undef JSON_HEDLEY_DMC_VERSION
#undef JSON_HEDLEY_DMC_VERSION_CHECK
#undef JSON_HEDLEY_EMPTY_BASES
#undef JSON_HEDLEY_EMSCRIPTEN_VERSION
#undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK
#undef JSON_HEDLEY_END_C_DECLS
#undef JSON_HEDLEY_FLAGS
#undef JSON_HEDLEY_FLAGS_CAST
#undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE
#undef JSON_HEDLEY_GCC_HAS_BUILTIN
#undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE
#undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE
#undef JSON_HEDLEY_GCC_HAS_EXTENSION
#undef JSON_HEDLEY_GCC_HAS_FEATURE
#undef JSON_HEDLEY_GCC_HAS_WARNING
#undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK
#undef JSON_HEDLEY_GCC_VERSION
#undef JSON_HEDLEY_GCC_VERSION_CHECK
#undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE
#undef JSON_HEDLEY_GNUC_HAS_BUILTIN
#undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE
#undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE
#undef JSON_HEDLEY_GNUC_HAS_EXTENSION
#undef JSON_HEDLEY_GNUC_HAS_FEATURE
#undef JSON_HEDLEY_GNUC_HAS_WARNING
#undef JSON_HEDLEY_GNUC_VERSION
#undef JSON_HEDLEY_GNUC_VERSION_CHECK
#undef JSON_HEDLEY_HAS_ATTRIBUTE
#undef JSON_HEDLEY_HAS_BUILTIN
#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE
#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS
#undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE
#undef JSON_HEDLEY_HAS_EXTENSION
#undef JSON_HEDLEY_HAS_FEATURE
#undef JSON_HEDLEY_HAS_WARNING
#undef JSON_HEDLEY_IAR_VERSION
#undef JSON_HEDLEY_IAR_VERSION_CHECK
#undef JSON_HEDLEY_IBM_VERSION
#undef JSON_HEDLEY_IBM_VERSION_CHECK
#undef JSON_HEDLEY_IMPORT
#undef JSON_HEDLEY_INLINE
#undef JSON_HEDLEY_INTEL_CL_VERSION
#undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK
#undef JSON_HEDLEY_INTEL_VERSION
#undef JSON_HEDLEY_INTEL_VERSION_CHECK
#undef JSON_HEDLEY_IS_CONSTANT
#undef JSON_HEDLEY_IS_CONSTEXPR_
#undef JSON_HEDLEY_LIKELY
#undef JSON_HEDLEY_MALLOC
#undef JSON_HEDLEY_MCST_LCC_VERSION
#undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK
#undef JSON_HEDLEY_MESSAGE
#undef JSON_HEDLEY_MSVC_VERSION
#undef JSON_HEDLEY_MSVC_VERSION_CHECK
#undef JSON_HEDLEY_NEVER_INLINE
#undef JSON_HEDLEY_NON_NULL
#undef JSON_HEDLEY_NO_ESCAPE
#undef JSON_HEDLEY_NO_RETURN
#undef JSON_HEDLEY_NO_THROW
#undef JSON_HEDLEY_NULL
#undef JSON_HEDLEY_PELLES_VERSION
#undef JSON_HEDLEY_PELLES_VERSION_CHECK
#undef JSON_HEDLEY_PGI_VERSION
#undef JSON_HEDLEY_PGI_VERSION_CHECK
#undef JSON_HEDLEY_PREDICT
#undef JSON_HEDLEY_PRINTF_FORMAT
#undef JSON_HEDLEY_PRIVATE
#undef JSON_HEDLEY_PUBLIC
#undef JSON_HEDLEY_PURE
#undef JSON_HEDLEY_REINTERPRET_CAST
#undef JSON_HEDLEY_REQUIRE
#undef JSON_HEDLEY_REQUIRE_CONSTEXPR
#undef JSON_HEDLEY_REQUIRE_MSG
#undef JSON_HEDLEY_RESTRICT
#undef JSON_HEDLEY_RETURNS_NON_NULL
#undef JSON_HEDLEY_SENTINEL
#undef JSON_HEDLEY_STATIC_ASSERT
#undef JSON_HEDLEY_STATIC_CAST
#undef JSON_HEDLEY_STRINGIFY
#undef JSON_HEDLEY_STRINGIFY_EX
#undef JSON_HEDLEY_SUNPRO_VERSION
#undef JSON_HEDLEY_SUNPRO_VERSION_CHECK
#undef JSON_HEDLEY_TINYC_VERSION
#undef JSON_HEDLEY_TINYC_VERSION_CHECK
#undef JSON_HEDLEY_TI_ARMCL_VERSION
#undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK
#undef JSON_HEDLEY_TI_CL2000_VERSION
#undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK
#undef JSON_HEDLEY_TI_CL430_VERSION
#undef JSON_HEDLEY_TI_CL430_VERSION_CHECK
#undef JSON_HEDLEY_TI_CL6X_VERSION
#undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK
#undef JSON_HEDLEY_TI_CL7X_VERSION
#undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK
#undef JSON_HEDLEY_TI_CLPRU_VERSION
#undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK
#undef JSON_HEDLEY_TI_VERSION
#undef JSON_HEDLEY_TI_VERSION_CHECK
#undef JSON_HEDLEY_UNAVAILABLE
#undef JSON_HEDLEY_UNLIKELY
#undef JSON_HEDLEY_UNPREDICTABLE
#undef JSON_HEDLEY_UNREACHABLE
#undef JSON_HEDLEY_UNREACHABLE_RETURN
#undef JSON_HEDLEY_VERSION
#undef JSON_HEDLEY_VERSION_DECODE_MAJOR
#undef JSON_HEDLEY_VERSION_DECODE_MINOR
#undef JSON_HEDLEY_VERSION_DECODE_REVISION
#undef JSON_HEDLEY_VERSION_ENCODE
#undef JSON_HEDLEY_WARNING
#undef JSON_HEDLEY_WARN_UNUSED_RESULT
#undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG
#undef JSON_HEDLEY_FALL_THROUGH

2
lib/libclangmm

@ -1 +1 @@
Subproject commit 663e54847f091b9e143db48361538a1e9ea540e9
Subproject commit 8dca5d81af05d5184da92a2ca2ce175b5577de25

1
lib/pybind11

@ -0,0 +1 @@
Subproject commit 9a19306fbf30642ca331d0ec88e7da54a96860f9

2
lib/tiny-process-library

@ -1 +1 @@
Subproject commit 15e4f77f8254e4b093f6be128db50fe4b6bee120
Subproject commit 8bbb5a211c5c9df8ee69301da9d22fb977b27dc1

26
share/set_icon.sh

@ -0,0 +1,26 @@
#!/bin/sh
# Based on https://stackoverflow.com/a/8375093
# TODO: Rez, DeRez and SetFile are deprecated, but I have not yet found replacement tools
# Sets an icon on file or directory
# Usage setIcon.sh iconimage.jpg /path/to/[file|folder]
iconSource=$1
iconDestination=$2
icon=/tmp/`basename $iconSource`
rsrc=/tmp/juci.rsrc
# Create icon from the iconSource
cp $iconSource $icon
# Add icon to image file, meaning use itself as the icon
sips -i $icon > /dev/null
# Take that icon and put it into a rsrc file
DeRez -only icns $icon > $rsrc
# Apply the rsrc file to
SetFile -a C $iconDestination
# Append resource to the file you want to icon-ize.
Rez -append $rsrc -o $iconDestination
rm $rsrc $icon

4
share/set_icon_macos.py

@ -1,4 +0,0 @@
import Cocoa
import sys
Cocoa.NSWorkspace.sharedWorkspace().setIcon_forFile_options_(Cocoa.NSImage.alloc().initWithContentsOfFile_(sys.argv[1].decode('utf-8')), sys.argv[2].decode('utf-8'), 0) or sys.exit("Unable to set file icon")

27
src/CMakeLists.txt

@ -3,6 +3,7 @@ set(JUCI_SHARED_FILES
autocomplete.cpp
cmake.cpp
commands.cpp
config.cpp
compile_commands.cpp
ctags.cpp
dispatcher.cpp
@ -10,9 +11,11 @@ set(JUCI_SHARED_FILES
filesystem.cpp
git.cpp
grep.cpp
json.cpp
menu.cpp
meson.cpp
project_build.cpp
plugins.cc
snippets.cpp
source.cpp
source_base.cpp
@ -22,9 +25,13 @@ set(JUCI_SHARED_FILES
source_language_protocol.cpp
source_spellcheck.cpp
terminal.cpp
tiny_process_module.cpp
tooltips.cpp
usages_clang.cpp
utility.cpp
python_module.cc
config_module.cc
workers.cpp
)
if(LIBLLDB_FOUND)
list(APPEND JUCI_SHARED_FILES debug_lldb.cpp)
@ -43,7 +50,6 @@ target_link_libraries(juci_shared
)
set(JUCI_FILES
config.cpp
dialog.cpp
directories.cpp
entrybox.cpp
@ -52,14 +58,25 @@ set(JUCI_FILES
notebook.cpp
project.cpp
selection_dialog.cpp
tooltips.cpp
window.cpp
plugins.cc
)
if(APPLE)
list(APPEND JUCI_FILES window_macos.m)
list(APPEND JUCI_SOURCES window_macos.m)
endif()
set(JUCI_TARGET_LIBRARIES
juci_shared
)
if(${PYTHONLIBS_FOUND})
list(APPEND JUCI_TARGET_LIBRARIES pybind11 ${PYTHON_LIBRARIES} ${PYGOBJECT_LIBRARIES})
endif()
add_executable(juci ${JUCI_FILES})
target_link_libraries(juci juci_shared)
add_executable(juci ${JUCI_SOURCES})
target_link_libraries(juci ${JUCI_TARGET_LIBRARIES})
if(APPLE)
target_link_libraries(juci "-framework Foundation -framework AppKit")
@ -72,7 +89,7 @@ if(${CMAKE_SYSTEM_NAME} MATCHES Linux|.*BSD|DragonFly)
install(FILES "${CMAKE_SOURCE_DIR}/share/juci.svg"
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/scalable/apps")
elseif(APPLE)
install(CODE "execute_process(COMMAND /usr/bin/python ${CMAKE_SOURCE_DIR}/share/set_icon_macos.py ${CMAKE_SOURCE_DIR}/share/juci.png ${CMAKE_INSTALL_PREFIX}/bin/juci)")
install(CODE "execute_process(COMMAND ${CMAKE_SOURCE_DIR}/share/set_icon.sh ${CMAKE_SOURCE_DIR}/share/juci.png ${CMAKE_INSTALL_PREFIX}/bin/juci)")
endif()
# add a target to generate API documentation with Doxygen

54
src/autocomplete.cpp

@ -1,9 +1,9 @@
#include "autocomplete.hpp"
#include "selection_dialog.hpp"
Autocomplete::Autocomplete(Gsv::View *view, bool &interactive_completion, guint &last_keyval, bool pass_buffer_and_strip_word)
: view(view), interactive_completion(interactive_completion), pass_buffer_and_strip_word(pass_buffer_and_strip_word) {
view->get_buffer()->signal_changed().connect([this, &last_keyval] {
Autocomplete::Autocomplete(Source::BaseView *view, bool &interactive_completion, guint &last_keyval, bool pass_buffer_and_strip_word, bool use_thread)
: view(view), interactive_completion(interactive_completion), pass_buffer_and_strip_word(pass_buffer_and_strip_word), use_thread(use_thread) {
view->get_buffer()->signal_insert().connect([this, &last_keyval](const Gtk::TextIter & /*iter*/, const Glib::ustring & /*text*/, int /*bytes*/) {
if(CompletionDialog::get() && CompletionDialog::get()->is_visible()) {
cancel_reparse();
return;
@ -19,6 +19,15 @@ Autocomplete::Autocomplete(Gsv::View *view, bool &interactive_completion, guint
run();
}
});
view->get_buffer()->signal_erase().connect([this](const Gtk::TextIter & /*start*/, const Gtk::TextIter & /*end*/) {
if(CompletionDialog::get() && CompletionDialog::get()->is_visible()) {
cancel_reparse();
return;
}
if(!this->view->has_focus())
return;
stop();
});
view->get_buffer()->signal_mark_set().connect([this](const Gtk::TextIter &iterator, const Glib::RefPtr<Gtk::TextBuffer::Mark> &mark) {
if(mark->get_name() == "insert")
@ -56,22 +65,23 @@ void Autocomplete::run() {
before_add_rows();
if(thread.joinable())
if(use_thread && thread.joinable())
thread.join();
auto iter = view->get_buffer()->get_insert()->get_iter();
auto line_nr = iter.get_line() + 1;
auto column_nr = iter.get_line_index() + 1;
auto line = iter.get_line();
auto line_index = iter.get_line_index();
Glib::ustring buffer;
if(pass_buffer_and_strip_word) {
auto pos = iter.get_offset() - 1;
buffer = view->get_buffer()->get_text();
while(pos >= 0 && Source::BaseView::is_token_char(buffer[pos])) {
while(pos >= 0 && view->is_token_char(buffer[pos])) {
buffer.replace(pos, 1, " ");
column_nr--;
line_index--;
pos--;
}
}
thread = std::thread([this, line_nr, column_nr, buffer = std::move(buffer)] {
auto func = [this, line, line_index, buffer = std::move(buffer)] {
auto lock = get_parse_lock();
if(!is_processing())
return;
@ -79,12 +89,12 @@ void Autocomplete::run() {
rows.clear();
auto &buffer_raw = const_cast<std::string &>(buffer.raw());
bool success = add_rows(buffer_raw, line_nr, column_nr);
bool success = add_rows(buffer_raw, line, line_index);
if(!is_processing())
return;
if(success) {
dispatcher.post([this]() {
auto func = [this]() {
after_add_rows();
if(state == State::restarting) {
state = State::idle;
@ -118,15 +128,27 @@ void Autocomplete::run() {
view->get_buffer()->begin_user_action();
CompletionDialog::get()->show();
}
});
};
if(use_thread)
dispatcher.post(std::move(func));
else
func();
}
else {
dispatcher.post([this] {
auto func = [this] {
state = State::canceled;
on_add_rows_error();
});
};
if(use_thread)
dispatcher.post(std::move(func));
else
func();
}
});
};
if(use_thread)
thread = std::thread(std::move(func));
else
func();
}
if(state != State::idle)
@ -156,7 +178,7 @@ void Autocomplete::setup_dialog() {
on_change(index, text);
if(!index) {
tooltips.hide();
clear_tooltips();
return;
}

12
src/autocomplete.hpp

@ -7,7 +7,7 @@
#include <thread>
class Autocomplete {
Gsv::View *view;
Source::BaseView *view;
bool &interactive_completion;
/// If view buffer should be passed to add_rows. Empty buffer is passed if not.
/// Also, some utilities, like libclang, require that autocomplete is started at the beginning of a word.
@ -39,7 +39,7 @@ public:
std::function<std::unique_ptr<LockGuard>()> get_parse_lock = [] { return nullptr; };
std::function<void()> stop_parse = [] {};
std::function<bool(guint last_keyval)> is_continue_key = [](guint keyval) { return Source::BaseView::is_token_char(gdk_keyval_to_unicode(keyval)); };
std::function<bool(guint last_keyval)> is_continue_key = [this](guint keyval) { return view->is_token_char(gdk_keyval_to_unicode(keyval)); };
std::function<bool(guint last_keyval)> is_restart_key = [](guint) { return false; };
std::function<bool()> run_check = [] { return false; };
@ -47,8 +47,8 @@ public:
std::function<void()> after_add_rows = [] {};
std::function<void()> on_add_rows_error = [] {};
/// The handler is not run in the main loop. Should return false on error.
std::function<bool(std::string &buffer, int line_number, int column)> add_rows = [](std::string &, int, int) { return true; };
/// The handler is not run in the main loop if use_thread is true. Should return false on error.
std::function<bool(std::string &buffer, int line, int line_index)> add_rows = [](std::string &, int, int) { return true; };
std::function<void()> on_show = [] {};
std::function<void()> on_hide = [] {};
@ -56,12 +56,14 @@ public:
std::function<void(unsigned int, const std::string &, bool)> on_select;
std::function<std::function<void(Tooltip &tooltip)>(unsigned int)> set_tooltip_buffer = [](unsigned int index) { return nullptr; };
std::function<void()> clear_tooltips = [this] { tooltips.hide(); };
Autocomplete(Gsv::View *view, bool &interactive_completion, guint &last_keyval, bool pass_buffer_and_strip_word);
Autocomplete(Source::BaseView *view, bool &interactive_completion, guint &last_keyval, bool pass_buffer_and_strip_word, bool use_thread);
void run();
void stop();
private:
void setup_dialog();
bool use_thread;
};

116
src/cmake.cpp

@ -3,20 +3,22 @@
#include "config.hpp"
#include "dialog.hpp"
#include "filesystem.hpp"
#include "json.hpp"
#include "terminal.hpp"
#include "utility.hpp"
#include <boost/algorithm/string.hpp>
#include <boost/optional.hpp>
#include <fstream>
#include <future>
#include <regex>
CMake::CMake(const boost::filesystem::path &path) {
const auto find_cmake_project = [](const boost::filesystem::path &file_path) {
std::ifstream input(file_path.string(), std::ofstream::binary);
std::ifstream input(file_path.string(), std::ios::binary);
if(input) {
std::string line;
while(std::getline(input, line)) {
const static std::regex project_regex("^ *project *\\(.*$", std::regex::icase | std::regex::optimize);
const static std::regex project_regex("^ *project *\\(.*\r?$", std::regex::icase | std::regex::optimize);
std::smatch sm;
if(std::regex_match(line, sm, project_regex))
return true;
@ -56,7 +58,7 @@ bool CMake::update_default_build(const boost::filesystem::path &default_build_pa
}
}
if(!force && boost::filesystem::exists(default_build_path / "compile_commands.json", ec))
if(!create_file_api_query(default_build_path) && !force && boost::filesystem::exists(default_build_path / "compile_commands.json", ec))
return true;
auto compile_commands_path = default_build_path / "compile_commands.json";
@ -82,7 +84,7 @@ bool CMake::update_default_build(const boost::filesystem::path &default_build_pa
}
message.hide();
if(exit_status == 0) {
#ifdef _WIN32 //Temporary fix to MSYS2's libclang
#ifdef _WIN32 // Temporary fix to MSYS2's libclang
auto compile_commands_file = filesystem::read(compile_commands_path);
auto replace_drive = [&compile_commands_file](const std::string &param) {
size_t pos = 0;
@ -145,6 +147,11 @@ bool CMake::update_debug_build(const boost::filesystem::path &debug_build_path,
}
boost::filesystem::path CMake::get_executable(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path) {
// Prefer the CMake file API (if available) which gives exact information about the targets a file belongs to
if(auto executable = get_executable_from_file_api(build_path, file_path))
return *executable;
// CMake does not store in compile_commands.json if an object is part of an executable or not.
// Therefore, executables are first attempted found in the cmake files. These executables
// are then used to identify if a file in compile_commands.json is part of an executable or not
@ -153,7 +160,7 @@ boost::filesystem::path CMake::get_executable(const boost::filesystem::path &bui
std::vector<std::pair<boost::filesystem::path, boost::filesystem::path>> source_files_and_maybe_executables;
for(auto &command : compile_commands.commands) {
auto source_file = filesystem::get_normal_path(command.file);
auto values = command.parameter_values("-o");
auto values = command.get_argument_values("-o");
if(!values.empty()) {
size_t pos;
if((pos = values[0].find("CMakeFiles/")) != std::string::npos)
@ -349,3 +356,102 @@ void CMake::parse_file(const std::string &src, std::map<std::string, std::list<s
}
}
}
void CMake::init_module(pybind11::module &api) {
py::class_<CMake>(api, "CMake")
.def(py::init<const boost::filesystem::path &>())
.def_readwrite("project_path", &CMake::project_path)
.def("update_default_build", &CMake::update_default_build,
py::arg("default_build_path"),
py::arg("force") = false)
.def("update_debug_build", &CMake::update_debug_build,
py::arg("debug_build_path"),
py::arg("force") = false)
.def("get_executable", &CMake::get_executable,
py::arg("build_path"),
py::arg("file_path"))
;
}
bool CMake::create_file_api_query(const boost::filesystem::path &build_path) {
auto query_directory = build_path / ".cmake" / "api" / "v1" / "query" / "client-jucipp";
auto query_file = query_directory / "query.json";
boost::system::error_code ec;
if(boost::filesystem::exists(query_file, ec))
return false;
boost::filesystem::create_directories(query_directory, ec);
if(ec) {
Terminal::get().print("\e[31mError\e[m: could not create cmake file api query directory " + filesystem::get_short_path(query_directory).string() + ": " + ec.message() + "\n", true);
return false;
}
if(!filesystem::write(query_file, R"({
"requests": [
{ "kind": "codemodel" , "version": 2 }
]
})")) {
Terminal::get().print("\e[31mError\e[m: could not create cmake file api query file " + filesystem::get_short_path(query_file).string() + ": " + ec.message() + "\n", true);
return false;
}
return true;
}
boost::optional<boost::filesystem::path> CMake::get_executable_from_file_api(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path) {
auto reply_directory = build_path / ".cmake" / "api" / "v1" / "reply";
boost::system::error_code ec;
if(!boost::filesystem::is_directory(reply_directory, ec)) {
// If the reply directory does not exist, either CMake was not run or the file API is not yet supported
return {};
}
// Check all target-*.json files and filter for the given file path
std::vector<boost::filesystem::path> target_files;
for(const auto &reply_file : boost::filesystem::directory_iterator(reply_directory, ec)) {
auto reply_file_path = reply_file.path();
if(starts_with(reply_file_path.stem().string(), "target-"))
target_files.emplace_back(reply_file_path);
}
std::sort(target_files.begin(), target_files.end(), [](const boost::filesystem::path &path1, const boost::filesystem::path &path2) {
return Natural::compare(path1.string(), path2.string()) < 0;
});
boost::optional<boost::filesystem::path> executable;
ssize_t best_match_size = -1;
for(const auto &target_file : target_files) {
try {
JSON json(target_file);
if(json.string("type") != "EXECUTABLE")
continue;
auto artifacts = json.array("artifacts");
if(artifacts.empty())
continue;
auto relative_path = artifacts.front().string("path");
for(auto &source : json.array("sources")) {
auto source_file = project_path / source.string("path");
if(source_file == file_path)
return {build_path / relative_path};
auto source_directory = source_file.parent_path();
if(filesystem::file_in_path(file_path, source_directory)) {
auto size = std::distance(source_directory.begin(), source_directory.end());
if(size > best_match_size) {
best_match_size = size;
executable = build_path / relative_path;
}
}
}
}
catch(...) {
}
}
return executable;
}

10
src/cmake.hpp

@ -1,18 +1,19 @@
#pragma once
#include <boost/filesystem.hpp>
#include <boost/optional.hpp>
#include <list>
#include <map>
#include <vector>
#include "python_bind.h"
class CMake {
public:
CMake(const boost::filesystem::path &path);
boost::filesystem::path project_path;
bool update_default_build(const boost::filesystem::path &default_build_path, bool force = false);
bool update_debug_build(const boost::filesystem::path &debug_build_path, bool force = false);
boost::filesystem::path get_executable(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path);
static void init_module(py::module &api);
private:
std::vector<boost::filesystem::path> paths;
@ -22,4 +23,9 @@ private:
std::list<std::string> parameters;
};
static void parse_file(const std::string &src, std::map<std::string, std::list<std::string>> &variables, std::function<void(Function &&)> &&on_function);
// Cmake file API functions
/// Returns true if file api query file was created, false otherwise including if the file already exists
bool create_file_api_query(const boost::filesystem::path &build_path);
boost::optional<boost::filesystem::path> get_executable_from_file_api(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path);
};

20
src/commands.cpp

@ -1,8 +1,8 @@
#include "commands.hpp"
#include "config.hpp"
#include "filesystem.hpp"
#include "json.hpp"
#include "terminal.hpp"
#include <boost/property_tree/json_parser.hpp>
void Commands::load() {
auto commands_file = Config::get().home_juci_path / "commands.json";
@ -20,17 +20,18 @@ void Commands::load() {
"run": "echo <path_match> && echo <working_directory>",
"debug_comment": "Whether or not this command should run through debugger",
"debug": false,
"debug_remote_host": ""
"debug_remote_host": "",
"label_comment": "Output to the terminal instead of the run command",
"label": ""
}
]
)");
commands.clear();
try {
boost::property_tree::ptree pt;
boost::property_tree::json_parser::read_json(commands_file.string(), pt);
for(auto command_it = pt.begin(); command_it != pt.end(); ++command_it) {
auto key_string = command_it->second.get<std::string>("key");
JSON commands_json(commands_file);
for(auto &command : commands_json.array()) {
auto key_string = command.string("key");
guint key = 0;
GdkModifierType modifier = static_cast<GdkModifierType>(0);
if(!key_string.empty()) {
@ -38,13 +39,14 @@ void Commands::load() {
if(key == 0 && modifier == 0)
Terminal::get().async_print("\e[31mError\e[m: could not parse key string: " + key_string + "\n", true);
}
auto path = command_it->second.get<std::string>("path", "");
auto path = command.string_or("path", "");
boost::optional<std::regex> regex;
if(!path.empty())
regex = std::regex(path, std::regex::optimize);
commands.emplace_back(Command{key, modifier, std::move(regex),
command_it->second.get<std::string>("compile", ""), command_it->second.get<std::string>("run"),
command_it->second.get<bool>("debug", false), command_it->second.get<std::string>("debug_remote_host", "")});
command.string_or("compile", ""), command.string("run"),
command.boolean_or("debug", false), command.string_or("debug_remote_host", ""),
command.string_or("label", "")});
}
}
catch(const std::exception &e) {

1
src/commands.hpp

@ -17,6 +17,7 @@ public:
std::string run;
bool debug;
std::string debug_remote_host;
std::string label;
};
static Commands &get() {

295
src/compile_commands.cpp

@ -1,213 +1,146 @@
#include "compile_commands.hpp"
#include "clangmm.hpp"
#include "config.hpp"
#include "filesystem.hpp"
#include "python_type_casters.h"
#include <pybind11/stl.h>
#include "terminal.hpp"
#include "filesystem.hpp"
#include "json.hpp"
#include "utility.hpp"
#include <algorithm>
#include <boost/property_tree/json_parser.hpp>
#include <regex>
CompileCommands::FindSystemIncludePaths::FindSystemIncludePaths() {
std::stringstream stdin_stream, stdout_stream;
stdin_stream << "int main() {}";
exit_status = Terminal::get().process(stdin_stream, stdout_stream, "clang++ -v -x c++ -E 2>&1 -");
if(exit_status != 0)
return;
std::string line;
while(std::getline(stdout_stream, line)) {
if(starts_with(line, "#include <...> search starts here:")) {
while(std::getline(stdout_stream, line)) {
if(!line.empty() && line[0] == ' ') {
#ifdef _WIN32
if(line.back() == '\r')
line.pop_back();
#endif
auto end = line.find(" (framework directory)", 1);
if(end == std::string::npos)
include_paths.emplace_back(line.substr(1, end));
else
framework_paths.emplace_back(line.substr(1, end));
}
else
return;
}
return;
}
}
}
std::vector<std::string> CompileCommands::Command::parameter_values(const std::string &parameter_name) const {
std::vector<std::string> parameter_values;
std::vector<std::string> CompileCommands::Command::get_argument_values(const std::string &argument_name) const {
std::vector<std::string> argument_values;
bool found_argument = false;
for(auto &parameter : parameters) {
for(auto &argument : arguments) {
if(found_argument) {
parameter_values.emplace_back(parameter);
argument_values.emplace_back(argument);
found_argument = false;
}
else if(parameter == parameter_name)
else if(argument == argument_name)
found_argument = true;
}
return parameter_values;
return argument_values;
}
CompileCommands::CompileCommands(const boost::filesystem::path &build_path) {
try {
boost::property_tree::ptree root_pt;
boost::property_tree::json_parser::read_json((build_path / "compile_commands.json").string(), root_pt);
auto commands_pt = root_pt.get_child("");
for(auto &command : commands_pt) {
boost::filesystem::path directory = command.second.get<std::string>("directory");
auto parameters_str = command.second.get<std::string>("command");
boost::filesystem::path file = command.second.get<std::string>("file");
std::vector<std::string> parameters;
bool backslash = false;
bool single_quote = false;
bool double_quote = false;
size_t parameter_start_pos = std::string::npos;
size_t parameter_size = 0;
auto add_parameter = [&parameters, &parameters_str, &parameter_start_pos, &parameter_size] {
auto parameter = parameters_str.substr(parameter_start_pos, parameter_size);
// Remove escaping
for(size_t c = 0; c < parameter.size() - 1; ++c) {
if(parameter[c] == '\\')
parameter.replace(c, 2, std::string() + parameter[c + 1]);
}
parameters.emplace_back(parameter);
};
for(size_t c = 0; c < parameters_str.size(); ++c) {
if(backslash)
backslash = false;
else if(parameters_str[c] == '\\')
backslash = true;
else if((parameters_str[c] == ' ' || parameters_str[c] == '\t') && !backslash && !single_quote && !double_quote) {
if(parameter_start_pos != std::string::npos) {
add_parameter();
parameter_start_pos = std::string::npos;
parameter_size = 0;
}
continue;
}
else if(parameters_str[c] == '\'' && !backslash && !double_quote) {
single_quote = !single_quote;
continue;
}
else if(parameters_str[c] == '\"' && !backslash && !single_quote) {
double_quote = !double_quote;
continue;
}
if(parameter_start_pos == std::string::npos)
parameter_start_pos = c;
++parameter_size;
}
if(parameter_start_pos != std::string::npos)
add_parameter();
commands.emplace_back(Command{directory, parameters, filesystem::get_absolute_path(file, build_path)});
}
}
catch(...) {
clangmm::CompilationDatabase db(build_path.string());
if(db) {
clangmm::CompileCommands compile_commands({}, db);
for(auto &command : compile_commands.get_commands())
commands.emplace_back(Command{clangmm::to_string(clang_CompileCommand_getDirectory(command.cx_command)),
command.get_arguments(),
filesystem::get_absolute_path(clangmm::to_string(clang_CompileCommand_getFilename(command.cx_command)), build_path)});
}
}
std::vector<std::string> CompileCommands::get_arguments(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path) {
std::string default_std_argument = "-std=c++1y";
auto extension = file_path.extension().string();
bool is_header = CompileCommands::is_header(file_path) || extension.empty(); // Include std C++ headers that are without extensions
// If header file, use source file flags if they are in the same folder
std::vector<boost::filesystem::path> file_paths;
if(is_header && !extension.empty()) {
auto parent_path = file_path.parent_path();
CompileCommands compile_commands(build_path);
for(auto &command : compile_commands.commands) {
if(command.file.parent_path() == parent_path)
file_paths.emplace_back(command.file);
}
}
if(file_paths.empty())
file_paths.emplace_back(file_path);
std::vector<std::string> arguments;
if(!build_path.empty()) {
clangmm::CompilationDatabase db(build_path.string());
if(db) {
for(auto &file_path : file_paths) {
std::vector<std::vector<std::string>> compile_commands_arguments;
// If header file, use source file flags if they are in the same folder
if(is_header && !extension.empty()) {
auto parent_path = file_path.parent_path();
clangmm::CompileCommands compile_commands({}, db);
for(auto &command : compile_commands.get_commands()) {
boost::filesystem::path file = filesystem::get_absolute_path(clangmm::to_string(clang_CompileCommand_getFilename(command.cx_command)), build_path);
if(file.parent_path() == parent_path)
compile_commands_arguments.emplace_back(command.get_arguments());
}
}
if(compile_commands_arguments.empty()) {
clangmm::CompileCommands compile_commands(file_path.string(), db);
auto commands = compile_commands.get_commands();
for(auto &command : commands) {
auto cmd_arguments = command.get_arguments();
bool ignore_next = false;
for(size_t c = 1; c + 1 < cmd_arguments.size(); c++) { // Exclude first and last argument
if(ignore_next)
ignore_next = false;
else if(cmd_arguments[c] == "-o" ||
cmd_arguments[c] == "-x" || // Remove language arguments since some tools add languages not understood by clang
(is_header && cmd_arguments[c] == "-include-pch") || // Header files should not use precompiled headers
cmd_arguments[c] == "-MF") { // Exclude dependency file generation
ignore_next = true;
}
else if(cmd_arguments[c] == "-c") {
}
else
arguments.emplace_back(cmd_arguments[c]);
for(auto &command : compile_commands.get_commands())
compile_commands_arguments.emplace_back(command.get_arguments());
}
for(auto &command_arguments : compile_commands_arguments) {
bool ignore_next = false;
for(size_t i = 1; i + 1 < command_arguments.size(); i++) { // Exclude first and last argument
if(ignore_next)
ignore_next = false;
else if(command_arguments[i] == "-o" ||
command_arguments[i] == "-x" || // Remove language arguments since some tools add languages not understood by clang
(is_header && command_arguments[i] == "-include-pch") || // Header files should not use precompiled headers
command_arguments[i] == "-MF") { // Exclude dependency file generation
ignore_next = true;
}
else if(command_arguments[i] == "-c") {
}
else if(command_arguments[i] == "--") // End of command options
break;
else
arguments.emplace_back(command_arguments[i]);
}
}
}
else
arguments.emplace_back(default_std_argument);
}
else
arguments.emplace_back(default_std_argument);
static FindSystemIncludePaths system_include_paths;
if(system_include_paths) {
for(auto &path : system_include_paths.include_paths)
arguments.emplace_back("-I" + path);
for(auto &path : system_include_paths.framework_paths)
arguments.emplace_back("-F" + path);
#ifdef _WIN32
auto clang_version_string = clangmm::to_string(clang_getClangVersion());
const static std::regex clang_version_regex(R"(^[A-Za-z ]+([0-9.]+).*$)", std::regex::optimize);
static std::string resource_path = []() -> std::string {
auto find_resource_path = [](const boost::filesystem::path &base_path, std::string version) -> std::string {
while(!version.empty() && version.front() != '.' && version.back() != '.') {
auto path = base_path / "clang" / version;
boost::system::error_code ec;
if(boost::filesystem::is_directory(path / "include", ec))
return path.string();
auto pos = version.rfind('.');
if(pos == std::string::npos)
break;
version.erase(pos);
}
return "";
};
// Try based on clang_getClangVersion first, even though its format is not guaranteed to be stable.
auto clang_version = clangmm::to_string(clang_getClangVersion());
std::regex clang_version_regex("([0-9]+\\.[0-9]+\\.[0-9]+)");
std::smatch sm;
if(std::regex_match(clang_version_string, sm, clang_version_regex)) {
auto clang_version = sm[1].str();
auto env_msystem_prefix = std::getenv("MSYSTEM_PREFIX");
if(env_msystem_prefix)
arguments.emplace_back("-I" + (boost::filesystem::path(env_msystem_prefix) / "lib/clang" / clang_version / "include").string());
if(std::regex_search(clang_version, sm, clang_version_regex)) {
auto version = sm[1].str();
auto path = find_resource_path(boost::filesystem::path(LIBCLANG_LIBRARY_DIR), version);
if(path.empty()) // For e.g. Fedora the LibClang is located in /usr/lib64/libclang.so while the resource dir is in /usr/lib/clang/<version>
path = find_resource_path(boost::filesystem::path(LIBCLANG_LIBRARY_DIR) / ".." / "lib", version);
if(!path.empty())
return path;
}
#endif
boost::system::error_code ec;
for(boost::filesystem::directory_iterator it(boost::filesystem::path(LIBCLANG_LIBRARY_DIR) / "clang", ec), end; it != end; ++it) {
if(boost::filesystem::is_directory(it->path() / "include", ec))
return it->path().string();
}
return {};
}();
if(!resource_path.empty()) {
arguments.emplace_back("-resource-dir");
arguments.emplace_back(resource_path);
}
else {
auto clang_version_string = clangmm::to_string(clang_getClangVersion());
const static std::regex clang_version_regex(R"(^[A-Za-z ]+([0-9.]+).*$)", std::regex::optimize);
std::smatch sm;
if(std::regex_match(clang_version_string, sm, clang_version_regex)) {
auto clang_version = sm[1].str();
arguments.emplace_back("-I/usr/lib/clang/" + clang_version + "/include");
arguments.emplace_back("-I/usr/lib64/clang/" + clang_version + "/include"); // For Fedora
#if defined(__APPLE__)
// Missing include and framework folders for MacOS:
arguments.emplace_back("-I/usr/local/opt/llvm/include/c++/v1");
arguments.emplace_back("-I/usr/local/opt/llvm/lib/clang/" + clang_version + "/include");
arguments.emplace_back("-I/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include");
arguments.emplace_back("-F/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks");
#endif
#ifdef _WIN32
auto env_msystem_prefix = std::getenv("MSYSTEM_PREFIX");
if(env_msystem_prefix)
arguments.emplace_back("-I" + (boost::filesystem::path(env_msystem_prefix) / "lib/clang" / clang_version / "include").string());
#endif
#ifdef __APPLE__
// Add -isysroot argument if it is missing on MacOS, which is needed by newer libclang
if(std::none_of(arguments.begin(), arguments.end(), [](const std::string &argument) { return argument == "-isysroot"; })) {
auto sysroots = {"/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk",
"/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk"};
boost::system::error_code ec;
for(auto &sysroot : sysroots) {
if(boost::filesystem::exists(sysroot, ec)) {
arguments.emplace_back("-isysroot");
arguments.emplace_back(sysroot);
break;
}
}
}
#endif
// Do not add -fretain-comments-from-system-headers if pch is used, since the pch was most likely made without this flag
if(std::none_of(arguments.begin(), arguments.end(), [](const std::string &argument) { return argument == "-include-pch"; }))
@ -233,7 +166,7 @@ std::vector<std::string> CompileCommands::get_arguments(const boost::filesystem:
arguments.emplace_back("-finclude-default-header");
arguments.emplace_back("-Wno-gcc-compat");
}
else if(is_header)
else if(is_header && extension != ".h") // libclang crashes if it tries to parse .h files as C++ file in a C project
arguments.emplace_back("-xc++");
if(!build_path.empty()) {
@ -277,3 +210,27 @@ bool CompileCommands::is_source(const boost::filesystem::path &path) {
else
return false;
}
void CompileCommands::init_module(py::module &api) {
py::class_<CompileCommands> compile_commands(api, "CompileCommands");
py::class_<CompileCommands::Command>(compile_commands, "Command")
.def_readwrite("directory", &CompileCommands::Command::directory)
.def_readwrite("parameters", &CompileCommands::Command::parameters)
.def_readwrite("file", &CompileCommands::Command::file)
.def("parameter_values", &CompileCommands::Command::parameter_values,
py::arg("parameter_name"))
;
compile_commands
.def(py::init<const boost::filesystem::path &>())
.def_readwrite("commands", &CompileCommands::commands)
.def_static("get_arguments", &CompileCommands::get_arguments,
py::arg("build_path"),
py::arg("file_path"))
.def_static("is_header", &CompileCommands::is_header,
py::arg("path"))
.def_static("is_source", &CompileCommands::is_source,
py::arg("path"))
;
}

20
src/compile_commands.hpp

@ -1,36 +1,24 @@
#pragma once
#include "python_bind.h"
#include <boost/filesystem.hpp>
#include <string>
#include <vector>
class CompileCommands {
public:
class FindSystemIncludePaths {
int exit_status;
public:
std::vector<std::string> include_paths;
std::vector<std::string> framework_paths;
operator bool() const { return exit_status == 0; }
FindSystemIncludePaths();
};
class Command {
public:
boost::filesystem::path directory;
std::vector<std::string> parameters;
std::vector<std::string> arguments;
boost::filesystem::path file;
std::vector<std::string> parameter_values(const std::string &parameter_name) const;
std::vector<std::string> get_argument_values(const std::string &argument_name) const;
};
CompileCommands(const boost::filesystem::path &build_path);
std::vector<Command> commands;
/// Return arguments for the given file using libclangmm
static std::vector<std::string> get_arguments(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path);
static bool is_header(const boost::filesystem::path &path);
static bool is_source(const boost::filesystem::path &path);
static void init_module(py::module &api);
};

758
src/config.cpp

@ -1,10 +1,13 @@
#include "config.hpp"
#include "files.hpp"
#include "filesystem.hpp"
#include "process.hpp"
#include "terminal.hpp"
#include "utility.hpp"
#include <algorithm>
#include <boost/algorithm/string.hpp>
#include <exception>
#include <iostream>
#include <thread>
Config::Config() {
home_path = filesystem::get_home_path();
@ -20,25 +23,24 @@ void Config::load() {
boost::filesystem::create_directories(config_dir);
if(!boost::filesystem::exists(config_json))
filesystem::write(config_json, default_config_file);
filesystem::write(config_json, default_config());
auto juci_style_path = home_juci_path / "styles";
boost::filesystem::create_directories(juci_style_path);
juci_style_path /= "juci-light.xml";
if(!boost::filesystem::exists(juci_style_path))
filesystem::write(juci_style_path, juci_light_style);
filesystem::write(juci_style_path, juci_light_style());
juci_style_path = juci_style_path.parent_path();
juci_style_path /= "juci-dark.xml";
if(!boost::filesystem::exists(juci_style_path))
filesystem::write(juci_style_path, juci_dark_style);
filesystem::write(juci_style_path, juci_dark_style());
juci_style_path = juci_style_path.parent_path();
juci_style_path /= "juci-dark-blue.xml";
if(!boost::filesystem::exists(juci_style_path))
filesystem::write(juci_style_path, juci_dark_blue_style);
filesystem::write(juci_style_path, juci_dark_blue_style());
boost::property_tree::ptree cfg;
boost::property_tree::json_parser::read_json(config_json.string(), cfg);
JSON cfg(config_json);
update(cfg);
read(cfg);
}
@ -46,51 +48,40 @@ void Config::load() {
dispatcher.post([config_json = std::move(config_json), e_what = std::string(e.what())] {
::Terminal::get().print("\e[31mError\e[m: could not parse " + filesystem::get_short_path(config_json).string() + ": " + e_what + "\n", true);
});
std::stringstream ss;
ss << default_config_file;
boost::property_tree::ptree cfg;
boost::property_tree::read_json(ss, cfg);
read(cfg);
JSON default_cfg(default_config());
read(default_cfg);
}
}
void Config::update(boost::property_tree::ptree &cfg) {
boost::property_tree::ptree default_cfg;
bool cfg_ok = true;
if(cfg.get<std::string>("version") != JUCI_VERSION) {
std::stringstream ss;
ss << default_config_file;
boost::property_tree::read_json(ss, default_cfg);
cfg_ok = false;
auto it_version = cfg.find("version");
if(it_version != cfg.not_found()) {
make_version_dependent_corrections(cfg, default_cfg, it_version->second.data());
it_version->second.data() = JUCI_VERSION;
}
auto style_path = home_juci_path / "styles";
filesystem::write(style_path / "juci-light.xml", juci_light_style);
filesystem::write(style_path / "juci-dark.xml", juci_dark_style);
filesystem::write(style_path / "juci-dark-blue.xml", juci_dark_blue_style);
}
else
void Config::update(JSON &cfg) {
auto version = cfg.string("version");
if(version == JUCI_VERSION)
return;
cfg_ok &= add_missing_nodes(cfg, default_cfg);
cfg_ok &= remove_deprecated_nodes(cfg, default_cfg);
if(!cfg_ok)
boost::property_tree::write_json((home_juci_path / "config" / "config.json").string(), cfg);
JSON default_cfg(default_config());
make_version_dependent_corrections(cfg, default_cfg, version);
cfg.set("version", JUCI_VERSION);
add_missing_nodes(cfg, default_cfg);
remove_deprecated_nodes(cfg, default_cfg);
cfg.to_file(home_juci_path / "config" / "config.json", 2);
auto style_path = home_juci_path / "styles";
filesystem::write(style_path / "juci-light.xml", juci_light_style());
filesystem::write(style_path / "juci-dark.xml", juci_dark_style());
filesystem::write(style_path / "juci-dark-blue.xml", juci_dark_blue_style());
}
void Config::make_version_dependent_corrections(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, const std::string &version) {
auto &keybindings_cfg = cfg.get_child("keybindings");
void Config::make_version_dependent_corrections(JSON &cfg, const JSON &default_cfg, const std::string &version) {
try {
if(version <= "1.2.4") {
auto it_file_print = keybindings_cfg.find("print");
if(it_file_print != keybindings_cfg.not_found() && it_file_print->second.data() == "<primary>p") {
if(version_compare(version, "1.2.4") <= 0) {
auto keybindings = cfg.object("keybindings");
auto print = keybindings.string_optional("print");
if(print && *print == "<primary>p") {
keybindings.set("print", "");
dispatcher.post([] {
::Terminal::get().print("Preference change: keybindings.print set to \"\"\n");
});
it_file_print->second.data() = "";
}
}
}
@ -99,118 +90,609 @@ void Config::make_version_dependent_corrections(boost::property_tree::ptree &cfg
}
}
bool Config::add_missing_nodes(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, std::string parent_path) {
if(parent_path.size() > 0)
parent_path += ".";
bool unchanged = true;
for(auto &node : default_cfg) {
auto path = parent_path + node.first;
void Config::add_missing_nodes(JSON &cfg, const JSON &default_cfg) {
for(auto &default_cfg_child : default_cfg.children_or_empty()) {
try {
cfg.get<std::string>(path);
auto cfg_child = cfg.child(default_cfg_child.first);
add_missing_nodes(cfg_child, default_cfg_child.second);
}
catch(const std::exception &e) {
cfg.add(path, node.second.data());
unchanged = false;
catch(...) {
cfg.set(default_cfg_child.first, default_cfg_child.second);
}
unchanged &= add_missing_nodes(cfg, node.second, path);
}
return unchanged;
}
bool Config::remove_deprecated_nodes(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, std::string parent_path) {
if(parent_path.size() > 0)
parent_path += ".";
bool unchanged = true;
for(auto it = cfg.begin(); it != cfg.end();) {
auto path = parent_path + it->first;
void Config::remove_deprecated_nodes(JSON &cfg, const JSON &default_cfg) {
auto children = cfg.children_or_empty();
for(size_t i = 0; i < children.size();) {
try {
default_cfg.get<std::string>(path);
unchanged &= remove_deprecated_nodes(it->second, default_cfg, path);
++it;
auto default_cfg_child = default_cfg.child(children[i].first);
remove_deprecated_nodes(children[i].second, default_cfg_child);
++i;
}
catch(const std::exception &e) {
it = cfg.erase(it);
unchanged = false;
catch(...) {
cfg.remove(children[i].first);
children = cfg.children_or_empty();
}
}
return unchanged;
}
void Config::read(const boost::property_tree::ptree &cfg) {
auto keybindings_pt = cfg.get_child("keybindings");
for(auto &i : keybindings_pt) {
menu.keys[i.first] = i.second.get_value<std::string>();
}
void Config::read(const JSON &cfg) {
for(auto &keybinding : cfg.children("keybindings"))
menu.keys[keybinding.first] = keybinding.second.string();
auto source_json = cfg.get_child("source");
source.style = source_json.get<std::string>("style");
source.font = source_json.get<std::string>("font");
source.cleanup_whitespace_characters = source_json.get<bool>("cleanup_whitespace_characters");
source.show_whitespace_characters = source_json.get<std::string>("show_whitespace_characters");
source.format_style_on_save = source_json.get<bool>("format_style_on_save");
source.format_style_on_save_if_style_file_found = source_json.get<bool>("format_style_on_save_if_style_file_found");
source.smart_brackets = source_json.get<bool>("smart_brackets");
source.smart_inserts = source_json.get<bool>("smart_inserts");
auto source_json = cfg.object("source");
source.style = source_json.string("style");
source.font = source_json.string("font");
source.add_missing_newline_at_end_of_file_on_save = source_json.boolean("add_missing_newline_at_end_of_file_on_save", JSON::ParseOptions::accept_string);
source.remove_trailing_whitespace_characters_on_save = source_json.boolean("remove_trailing_whitespace_characters_on_save", JSON::ParseOptions::accept_string);
source.show_whitespace_characters = source_json.string("show_whitespace_characters");
source.format_style_on_save = source_json.boolean("format_style_on_save", JSON::ParseOptions::accept_string);
source.format_style_on_save_if_style_file_found = source_json.boolean("format_style_on_save_if_style_file_found", JSON::ParseOptions::accept_string);
source.smart_brackets = source_json.boolean("smart_brackets", JSON::ParseOptions::accept_string);
source.smart_inserts = source_json.boolean("smart_inserts", JSON::ParseOptions::accept_string);
if(source.smart_inserts)
source.smart_brackets = true;
source.show_map = source_json.get<bool>("show_map");
source.map_font_size = source_json.get<unsigned>("map_font_size");
source.show_git_diff = source_json.get<bool>("show_git_diff");
source.show_background_pattern = source_json.get<bool>("show_background_pattern");
source.show_right_margin = source_json.get<bool>("show_right_margin");
source.right_margin_position = source_json.get<unsigned>("right_margin_position");
source.spellcheck_language = source_json.get<std::string>("spellcheck_language");
source.default_tab_char = source_json.get<char>("default_tab_char");
source.default_tab_size = source_json.get<unsigned>("default_tab_size");
source.auto_tab_char_and_size = source_json.get<bool>("auto_tab_char_and_size");
source.tab_indents_line = source_json.get<bool>("tab_indents_line");
source.word_wrap = source_json.get<std::string>("word_wrap");
source.highlight_current_line = source_json.get<bool>("highlight_current_line");
source.show_line_numbers = source_json.get<bool>("show_line_numbers");
source.enable_multiple_cursors = source_json.get<bool>("enable_multiple_cursors");
source.auto_reload_changed_files = source_json.get<bool>("auto_reload_changed_files");
source.search_for_selection = source_json.get<bool>("search_for_selection");
source.clang_format_style = source_json.get<std::string>("clang_format_style");
source.clang_usages_threads = static_cast<unsigned>(source_json.get<int>("clang_usages_threads"));
source.clang_tidy_enable = source_json.get<bool>("clang_tidy_enable");
source.clang_tidy_checks = source_json.get<std::string>("clang_tidy_checks");
source.clang_detailed_preprocessing_record = source_json.get<bool>("clang_detailed_preprocessing_record");
source.debug_place_cursor_at_stop = source_json.get<bool>("debug_place_cursor_at_stop");
auto pt_doc_search = cfg.get_child("documentation_searches");
for(auto &pt_doc_search_lang : pt_doc_search) {
source.documentation_searches[pt_doc_search_lang.first].separator = pt_doc_search_lang.second.get<std::string>("separator");
auto &queries = source.documentation_searches.find(pt_doc_search_lang.first)->second.queries;
for(auto &i : pt_doc_search_lang.second.get_child("queries")) {
queries[i.first] = i.second.get_value<std::string>();
source.show_map = source_json.boolean("show_map", JSON::ParseOptions::accept_string);
source.map_font_size = source_json.integer("map_font_size", JSON::ParseOptions::accept_string);
source.show_git_diff = source_json.boolean("show_git_diff", JSON::ParseOptions::accept_string);
source.show_background_pattern = source_json.boolean("show_background_pattern", JSON::ParseOptions::accept_string);
source.show_right_margin = source_json.boolean("show_right_margin", JSON::ParseOptions::accept_string);
source.right_margin_position = source_json.integer("right_margin_position", JSON::ParseOptions::accept_string);
source.pixels_between_lines = source_json.integer("pixels_between_lines", JSON::ParseOptions::accept_string);
source.spellcheck_language = source_json.string("spellcheck_language");
auto default_tab_char_str = source_json.string("default_tab_char");
if(default_tab_char_str.size() == 1)
source.default_tab_char = default_tab_char_str[0];
else
source.default_tab_char = ' ';
source.default_tab_size = source_json.integer("default_tab_size", JSON::ParseOptions::accept_string);
source.auto_tab_char_and_size = source_json.boolean("auto_tab_char_and_size", JSON::ParseOptions::accept_string);
source.tab_indents_line = source_json.boolean("tab_indents_line", JSON::ParseOptions::accept_string);
source.word_wrap = source_json.string("word_wrap");
source.highlight_current_line = source_json.boolean("highlight_current_line", JSON::ParseOptions::accept_string);
source.show_line_numbers = source_json.boolean("show_line_numbers", JSON::ParseOptions::accept_string);
source.enable_multiple_cursors = source_json.boolean("enable_multiple_cursors", JSON::ParseOptions::accept_string);
source.auto_reload_changed_files = source_json.boolean("auto_reload_changed_files", JSON::ParseOptions::accept_string);
source.search_for_selection = source_json.boolean("search_for_selection", JSON::ParseOptions::accept_string);
source.tooltip_top_offset = source_json.integer("tooltip_top_offset", JSON::ParseOptions::accept_string);
source.clang_format_style = source_json.string("clang_format_style");
source.clang_usages_threads = static_cast<unsigned>(source_json.integer("clang_usages_threads", JSON::ParseOptions::accept_string));
source.clang_tidy_enable = source_json.boolean("clang_tidy_enable", JSON::ParseOptions::accept_string);
source.clang_tidy_checks = source_json.string("clang_tidy_checks");
source.clang_detailed_preprocessing_record = source_json.boolean("clang_detailed_preprocessing_record", JSON::ParseOptions::accept_string);
source.debug_place_cursor_at_stop = source_json.boolean("debug_place_cursor_at_stop", JSON::ParseOptions::accept_string);
for(auto &documentation_searches : cfg.children("documentation_searches")) {
auto &documentation_search = source.documentation_searches[documentation_searches.first];
documentation_search.separator = documentation_searches.second.string("separator");
for(auto &i : documentation_searches.second.children("queries"))
documentation_search.queries[i.first] = i.second.string();
}
auto theme_json = cfg.object("gtk_theme");
theme.name = theme_json.string("name");
theme.variant = theme_json.string("variant");
theme.font = theme_json.string("font");
auto project_json = cfg.object("project");
project.default_build_path = project_json.string("default_build_path");
project.debug_build_path = project_json.string("debug_build_path");
auto cmake_json = project_json.object("cmake");
project.cmake.command = cmake_json.string("command");
project.cmake.compile_command = cmake_json.string("compile_command");
auto meson_json = project_json.object("meson");
project.meson.command = meson_json.string("command");
project.meson.compile_command = meson_json.string("compile_command");
project.default_build_management_system = project_json.string("default_build_management_system");
project.save_on_compile_or_run = project_json.boolean("save_on_compile_or_run", JSON::ParseOptions::accept_string);
project.ctags_command = project_json.string("ctags_command");
project.grep_command = project_json.string("grep_command");
project.cargo_command = project_json.string("cargo_command");
project.python_command = project_json.string("python_command");
project.markdown_command = project_json.string("markdown_command");
std::stringstream ss(project_json.string("open_with_default_application"));
std::string str;
project.open_with_default_application.clear();
while(getline(ss, str, ',')) {
while(!str.empty() && str.front() == ' ')
str.erase(str.begin());
project.open_with_default_application.emplace_back(std::move(str));
}
auto terminal_json = cfg.object("terminal");
terminal.history_size = terminal_json.integer("history_size", JSON::ParseOptions::accept_string);
terminal.font = terminal_json.string("font");
terminal.clear_on_compile = terminal_json.boolean("clear_on_compile", JSON::ParseOptions::accept_string);
terminal.clear_on_run_command = terminal_json.boolean("clear_on_run_command", JSON::ParseOptions::accept_string);
terminal.hide_entry_on_run_command = terminal_json.boolean("hide_entry_on_run_command", JSON::ParseOptions::accept_string);
auto plugins_path = cfg.get<std::string>("plugins.path");
boost::replace_all(plugins_path, "<juci_home_directory>", home_juci_path.string());
plugins.path = plugins_path;
plugins.enabled = cfg.get<bool>("plugins.enabled");
auto log_json = cfg.object("log");
log.libclang = log_json.boolean("libclang", JSON::ParseOptions::accept_string);
log.language_server = log_json.boolean("language_server", JSON::ParseOptions::accept_string);
}
std::string Config::default_config() {
static auto get_config = [] {
std::string cmake_version;
unsigned thread_count = 0;
std::stringstream ss;
TinyProcessLib::Config processConfig{};
#ifdef JUCI_FLATPAK_SANDBOX
processConfig.flatpak_spawn_host = true;
#endif
TinyProcessLib::Process process(
"cmake --version", "",
[&ss](const char *buffer, size_t n) {
ss.write(buffer, n);
},
[](const char *buffer, size_t n) {}, false, processConfig);
if(process.get_exit_status() == 0) {
std::string line;
if(std::getline(ss, line)) {
auto pos = line.rfind(" ");
if(pos != std::string::npos) {
cmake_version = line.substr(pos + 1);
thread_count = std::thread::hardware_concurrency();
}
}
}
return std::string(R"RAW({
"version": ")RAW" +
std::string(JUCI_VERSION) +
R"RAW(",
"gtk_theme": {
"name_comment": "Use \"\" for default theme, At least these two exist on all systems: Adwaita, Raleigh",
"name": "",
"variant_comment": "Use \"\" for default variant, and \"dark\" for dark theme variant. Note that not all themes support dark variant, but for instance Adwaita does",
"variant": "",
"font_comment": "Set to override theme font, for instance: \"Arial 12\"",
"font": ""
},
"source": {
"style_comment": "Use \"\" for default style, and for instance juci-dark or juci-dark-blue together with dark gtk_theme variant. Styles from normal gtksourceview install: classic, cobalt, kate, oblivion, solarized-dark, solarized-light, tango",
"style": "juci-light",
"font_comment": "Use \"\" for default font, and for instance \"Monospace 12\" to also set size",)RAW"
#ifdef __APPLE__
R"RAW(
"font": "Menlo",)RAW"
#else
#ifdef _WIN32
R"RAW(
"font": "Consolas",)RAW"
#else
R"RAW(
"font": "Monospace",)RAW"
#endif
#endif
R"RAW(
"add_missing_newline_at_end_of_file_on_save": true,
"remove_trailing_whitespace_characters_on_save": false,
"show_whitespace_characters_comment": "Determines what kind of whitespaces should be drawn. Use comma-separated list of: space, tab, newline, nbsp, leading, text, trailing or all",
"show_whitespace_characters": "",
"format_style_on_save_comment": "Performs style format on save if supported on language in buffer",
"format_style_on_save": false,
"format_style_on_save_if_style_file_found_comment": "Format style if format file is found, even if format_style_on_save is false",
"format_style_on_save_if_style_file_found": true,
"smart_brackets_comment": "If smart_inserts is enabled, this option is automatically enabled. When inserting an already closed bracket, the cursor might instead be moved, avoiding the need of arrow keys after autocomplete",
"smart_brackets": true,
"smart_inserts_comment": "When for instance inserting (, () gets inserted. Applies to: (), [], \", '. Also enables pressing ; inside an expression before a final ) to insert ; at the end of line, and deletions of empty insertions",
"smart_inserts": true,
"show_map": true,
"map_font_size": 1,
"show_git_diff": true,
"show_background_pattern": true,
"show_right_margin": false,
"right_margin_position": 80,
"pixels_between_lines": 0,
"spellcheck_language_comment": "Use \"\" to set language from your locale settings",
"spellcheck_language": "en_US",
"auto_tab_char_and_size_comment": "Use false to always use default tab char and size",
"auto_tab_char_and_size": true,
"default_tab_char_comment": "Use \"\t\" for regular tab",
"default_tab_char": " ",
"default_tab_size": 2,
"tab_indents_line": true,
"word_wrap_comment": "Specify language ids that should enable word wrap, for instance: chdr, c, cpphdr, cpp, js, python, or all to enable word wrap for all languages",
"word_wrap": "markdown, latex",
"highlight_current_line": true,
"show_line_numbers": true,
"enable_multiple_cursors": false,
"auto_reload_changed_files": true,
"search_for_selection": true,
"tooltip_top_offset": 0,
"clang_format_style_comment": "IndentWidth, AccessModifierOffset and UseTab are set automatically. See http://clang.llvm.org/docs/ClangFormatStyleOptions.html",
"clang_format_style": "ColumnLimit: 0, NamespaceIndentation: All",
"clang_tidy_enable_comment": "Enable clang-tidy in new C/C++ buffers",
"clang_tidy_enable": false,
"clang_tidy_checks_comment": "In new C/C++ buffers, these checks are appended to the value of 'Checks' in the .clang-tidy file, if any",
"clang_tidy_checks": "",
"clang_usages_threads_comment": "The number of threads used in finding usages in unparsed files. -1 corresponds to the number of cores available, and 0 disables the search",
"clang_usages_threads": -1,
"clang_detailed_preprocessing_record_comment": "Set to true to, at the cost of increased resource use, include all macro definitions and instantiations when parsing new C/C++ buffers. You should reopen buffers and delete build/.usages_clang after changing this option.",
"clang_detailed_preprocessing_record": false,
"debug_place_cursor_at_stop": false
},
"terminal": {
"history_size": 10000,
"font_comment": "Use \"\" to use source.font with slightly smaller size",
"font": "",
"clear_on_compile": true,
"clear_on_run_command": false,
"hide_entry_on_run_command": true
},
"project": {
"default_build_path_comment": "Use <project_directory_name> to insert the project top level directory name",
"default_build_path": "./build",
"debug_build_path_comment": "Use <project_directory_name> to insert the project top level directory name, and <default_build_path> to insert your default_build_path setting.",
"debug_build_path": "<default_build_path>/debug",
"cmake": {)RAW"
#ifdef _WIN32
R"RAW(
"command": "cmake -G\"MSYS Makefiles\"",)RAW"
#else
R"RAW(
"command": "cmake",)RAW"
#endif
R"RAW(
"compile_command": "cmake --build .)RAW" +
(thread_count > 1 && !cmake_version.empty() && version_compare(cmake_version, "3.12") >= 0 ? " --parallel " + std::to_string(thread_count) : "") +
R"RAW("
},
"meson": {
"command": "meson",
"compile_command": "ninja"
},
"default_build_management_system_comment": "Select which build management system to use when creating a new C or C++ project, for instance \"cmake\" or \"meson\"",
"default_build_management_system": "cmake",
"save_on_compile_or_run": true,)RAW"
#ifdef JUCI_USE_UCTAGS
R"RAW(
"ctags_command": "uctags",)RAW"
#else
R"RAW(
"ctags_command": "ctags",)RAW"
#endif
R"RAW(
"grep_command": "grep",
"cargo_command": "cargo",
"python_command": "python -u",
"markdown_command": "grip -b",
"open_with_default_application_comment": "Comma-separated list of file extensions that should be opened with system default applications",
"open_with_default_application": ".pdf,.png"
},
"keybindings": {
"preferences": "<primary>comma",
"snippets": "",
"commands": "",
"quit": "<primary>q",
"file_new_file": "<primary>n",
"file_new_folder": "<primary><shift>n",
"file_open_file": "<primary>o",
"file_open_folder": "<primary><shift>o",
"file_find_file": "<primary>p",
"file_switch_file_type": "<alt>o",
"file_reload_file": "",
"file_save": "<primary>s",
"file_save_as": "<primary><shift>s",
"file_close_file": "<primary>w",
"file_close_folder": "",
"file_close_project": "",
"file_close_other_files": "",
"file_print": "",
"edit_undo": "<primary>z",
"edit_redo": "<primary><shift>z",
"edit_cut": "<primary>x",
"edit_cut_lines": "<primary><shift>x",
"edit_copy": "<primary>c",
"edit_copy_lines": "<primary><shift>c",
"edit_paste": "<primary>v",
"edit_extend_selection": "<primary><shift>a",
"edit_shrink_selection": "<primary><shift><alt>a",
"edit_show_or_hide": "",
"edit_find": "<primary>f",
"source_spellcheck": "",
"source_spellcheck_clear": "",
"source_spellcheck_next_error": "<primary><shift>e",
"source_git_next_diff": "<primary>k",
"source_git_show_diff": "<alt>k",
"source_indentation_set_buffer_tab": "",
"source_indentation_auto_indent_buffer": "<primary><shift>i",
"source_goto_line": "<primary>g",
"source_center_cursor": "<primary>l",
"source_cursor_history_back": "<alt>Left",
"source_cursor_history_forward": "<alt>Right",
"source_show_completion_comment": "Add completion keybinding to disable interactive autocompletion",
"source_show_completion": "",
"source_find_symbol": "<primary><shift>f",
"source_find_pattern": "<alt><shift>f",
"source_comments_toggle": "<primary>slash",
"source_comments_add_documentation": "<primary><alt>slash",
"source_find_documentation": "<primary><shift>d",
"source_goto_declaration": "<primary>d",
"source_goto_type_declaration": "<alt><shift>d",
"source_goto_implementation": "<primary>i",
"source_goto_usage": "<primary>u",
"source_goto_method": "<primary>m",
"source_rename": "<primary>r",
"source_implement_method": "<primary><shift>m",
"source_goto_next_diagnostic": "<primary>e",
"source_apply_fix_its": "<control>space",
"project_set_run_arguments": "",
"project_compile_and_run": "<primary>Return",
"project_compile": "<primary><shift>Return",
"project_run_command": "<alt>Return",
"project_kill_last_running": "<primary>Escape",
"project_force_kill_last_running": "<primary><shift>Escape",
"debug_set_run_arguments": "",
"debug_start_continue": "<primary>y",
"debug_stop": "<primary><shift>y",
"debug_kill": "<primary><shift>k",
"debug_step_over": "<primary>j",
"debug_step_into": "<primary>t",
"debug_step_out": "<primary><shift>t",
"debug_backtrace": "<primary><shift>j",
"debug_show_variables": "<primary><shift>b",
"debug_run_command": "<alt><shift>Return",
"debug_toggle_breakpoint": "<primary>b",
"debug_show_breakpoints": "<primary><shift><alt>b",
"debug_goto_stop": "<primary><shift>l",)RAW"
#ifdef __linux
R"RAW(
"window_next_tab": "<primary>Tab",
"window_previous_tab": "<primary><shift>Tab",)RAW"
#else
R"RAW(
"window_next_tab": "<primary><alt>Right",
"window_previous_tab": "<primary><alt>Left",)RAW"
#endif
R"RAW(
"window_goto_tab": "",
"window_toggle_split": "",
"window_split_source_buffer": "",)RAW"
#ifdef __APPLE__
R"RAW(
"window_toggle_full_screen": "<primary><control>f",)RAW"
#else
R"RAW(
"window_toggle_full_screen": "F11",)RAW"
#endif
R"RAW(
"window_toggle_directories": "",
"window_toggle_terminal": "",
"window_toggle_status": "",
"window_toggle_menu": "",
"window_toggle_tabs": "",
"window_toggle_zen_mode": "",
"window_clear_terminal": ""
},
"documentation_searches": {
"clang": {
"separator": "::",
"queries": {
"@empty": "https://www.google.com/search?q=c%2B%2B+",
"std": "https://www.google.com/search?q=site:http://www.cplusplus.com/reference/+",
"boost": "https://www.google.com/search?q=site:http://www.boost.org/doc/libs/1_59_0/+",
"Gtk": "https://www.google.com/search?q=site:https://developer.gnome.org/gtkmm/stable/+",
"@any": "https://www.google.com/search?q="
}
}
},
"log": {
"libclang_comment": "Outputs diagnostics for new C/C++ buffers",
"libclang": false,
"language_server": false
}
}
)RAW");
};
static auto config = get_config();
return config;
}
const char *Config::juci_light_style() {
return R"RAW(<?xml version="1.0" encoding="UTF-8"?>
<style-scheme id="juci-light" _name="juci" version="1.0">
<author>juCi++ team</author>
<_description>Default juCi++ style</_description>
<!-- Palette -->
<color name="white" value="#FFFFFF"/>
<color name="black" value="#000000"/>
<color name="gray" value="#888888"/>
<color name="red" value="#CC0000"/>
<color name="green" value="#008800"/>
<color name="blue" value="#0000FF"/>
<color name="dark-blue" value="#002299"/>
<color name="yellow" value="#FFFF00"/>
<color name="light-yellow" value="#FFFF88"/>
<color name="orange" value="#FF8800"/>
<color name="purple" value="#990099"/>
<style name="text" foreground="#000000" background="#FFFFFF"/>
<style name="background-pattern" background="#rgba(0,0,0,.03)"/>
<style name="selection" background="#4A90D9"/>
<!-- Current Line Highlighting -->
<style name="current-line" background="#rgba(0,0,0,.07)"/>
<!-- Bracket Matching -->
<style name="bracket-match" foreground="white" background="gray" bold="true"/>
<style name="bracket-mismatch" foreground="white" background="#FF0000" bold="true"/>
<!-- Search Matching -->
<style name="search-match" foreground="#000000" background="#FFFF00"/>
<!-- Language specifics -->
<style name="def:builtin" foreground="blue"/>
<style name="def:constant" foreground="blue"/>
<style name="def:boolean" foreground="red"/>
<style name="def:decimal" foreground="red"/>
<style name="def:base-n-integer" foreground="red"/>
<style name="def:floating-point" foreground="red"/>
<style name="def:complex" foreground="red"/>
<style name="def:character" foreground="red"/>
<style name="def:special-char" foreground="red"/>
<!-- Language specifics used by clang-parser in default config -->
<style name="def:string" foreground="red"/>
<style name="def:comment" foreground="gray"/>
<style name="def:statement" foreground="blue"/>
<style name="def:type" foreground="blue"/>
<style name="def:function" foreground="dark-blue"/>
<style name="def:identifier" foreground="purple"/>
<style name="def:preprocessor" foreground="green"/>
<style name="def:error" foreground="red"/>
<style name="def:warning" foreground="orange"/>
<style name="def:note" foreground="black" background="light-yellow"/>
<style name="diff:added-line" foreground="green"/>
<style name="diff:removed-line" foreground="red"/>
<style name="diff:changed-line" foreground="orange"/>
<style name="diff:diff-file" use-style="def:type"/>
<style name="diff:location" use-style="def:statement"/>
<style name="diff:special-case" use-style="def:constant"/>
</style-scheme>
)RAW";
}
const char *Config::juci_dark_style() {
return R"RAW(<?xml version="1.0" encoding="UTF-8"?>
<style-scheme id="juci-dark" _name="juci" version="1.0">
<author>juCi++ team</author>
<_description>Dark juCi++ style</_description>
<!-- Palette -->
<color name="white" value="#D6D6D6"/>
<color name="black" value="#202428"/>
<color name="gray" value="#919191"/>
<color name="red" value="#FF9999"/>
<color name="yellow" value="#EEEE66"/>
<color name="green" value="#AACC99"/>
<color name="blue" value="#88AAFF"/>
<color name="light-blue" value="#AABBEE"/>
<color name="purple" value="#DD99DD"/>
<style name="text" foreground="white" background="black"/>
<style name="background-pattern" background="#rgba(255,255,255,.04)"/>
<style name="selection" background="#215D9C"/>
<!-- Current Line Highlighting -->
<style name="current-line" background="#rgba(255,255,255,.05)"/>
<!-- Bracket Matching -->
<style name="bracket-match" foreground="black" background="gray" bold="true"/>
<style name="bracket-mismatch" foreground="black" background="#FF0000" bold="true"/>
<!-- Search Matching -->
<style name="search-match" foreground="#000000" background="#FFFF00"/>
<!-- Language specifics -->
<style name="def:builtin" foreground="blue"/>
<style name="def:constant" foreground="blue"/>
<style name="def:boolean" foreground="red"/>
<style name="def:decimal" foreground="red"/>
<style name="def:base-n-integer" foreground="red"/>
<style name="def:floating-point" foreground="red"/>
<style name="def:complex" foreground="red"/>
<style name="def:character" foreground="red"/>
<style name="def:special-char" foreground="red"/>
<!-- Language specifics used by clang-parser in default config -->
<style name="def:string" foreground="red"/>
<style name="def:comment" foreground="gray"/>
<style name="def:statement" foreground="blue"/>
<style name="def:type" foreground="blue"/>
<style name="def:function" foreground="light-blue"/>
<style name="def:identifier" foreground="purple"/>
<style name="def:preprocessor" foreground="green"/>
<style name="def:error" foreground="red"/>
<style name="def:warning" foreground="yellow"/>
<style name="def:note" foreground="#E6E6E6" background="#383F46"/>
<style name="diff:added-line" foreground="green"/>
<style name="diff:removed-line" foreground="red"/>
<style name="diff:changed-line" foreground="yellow"/>
<style name="diff:diff-file" use-style="def:type"/>
<style name="diff:location" use-style="def:statement"/>
<style name="diff:special-case" use-style="def:constant"/>
</style-scheme>
)RAW";
}
const char *Config::juci_dark_blue_style() {
return R"RAW(<?xml version="1.0" encoding="UTF-8"?>
<style-scheme id="juci-dark-blue" _name="juci" version="1.0">
<author>juCi++ team</author>
<_description>Dark blue juCi++ style based on the Emacs deeper blue theme</_description>
<!-- Palette -->
<color name="white" value="#D6D6D6"/>
<color name="dark-blue" value="#202233"/>
<color name="gray" value="#919191"/>
<color name="red" value="#FF7777"/>
<color name="yellow" value="#FFE100"/>
<color name="light-yellow" value="#EAC595"/>
<color name="blue" value="#00CCFF"/>
<color name="green" value="#14ECA8"/>
<color name="light-blue" value="#8BFAFF"/>
<color name="light-green" value="#A0DB6B"/>
<style name="text" foreground="white" background="dark-blue"/>
<style name="background-pattern" background="#rgba(255,255,255,.04)"/>
<style name="selection" background="#215D9C"/>
<!-- Current Line Highlighting -->
<style name="current-line" background="#rgba(255,255,255,.05)"/>
<!-- Bracket Matching -->
<style name="bracket-match" foreground="dark-blue" background="gray" bold="true"/>
<style name="bracket-mismatch" foreground="dark-blue" background="#FF0000" bold="true"/>
<!-- Search Matching -->
<style name="search-match" foreground="#000000" background="#FFFF00"/>
<!-- Language specifics -->
<style name="def:builtin" foreground="blue"/>
<style name="def:constant" foreground="blue"/>
<style name="def:boolean" foreground="light-yellow"/>
<style name="def:decimal" foreground="light-yellow"/>
<style name="def:base-n-integer" foreground="light-yellow"/>
<style name="def:floating-point" foreground="light-yellow"/>
<style name="def:complex" foreground="light-yellow"/>
<style name="def:character" foreground="light-yellow"/>
<style name="def:special-char" foreground="light-yellow"/>
<!-- Language specifics used by clang-parser in default config -->
<style name="def:string" foreground="light-yellow"/>
<style name="def:comment" foreground="gray"/>
<style name="def:statement" foreground="blue"/>
<style name="def:type" foreground="blue"/>
<style name="def:function" foreground="light-blue"/>
<style name="def:identifier" foreground="light-green"/>
<style name="def:preprocessor" foreground="yellow"/>
<style name="def:error" foreground="red"/>
<style name="def:warning" foreground="yellow"/>
<style name="def:note" foreground="#E6E6E6" background="#383C59"/>
<style name="diff:added-line" foreground="green"/>
<style name="diff:removed-line" foreground="red"/>
<style name="diff:changed-line" foreground="yellow"/>
<style name="diff:diff-file" use-style="def:type"/>
<style name="diff:location" use-style="def:statement"/>
<style name="diff:special-case" use-style="def:constant"/>
version = cfg.get<std::string>("version");
theme.name = cfg.get<std::string>("gtk_theme.name");
theme.variant = cfg.get<std::string>("gtk_theme.variant");
theme.font = cfg.get<std::string>("gtk_theme.font");
project.default_build_path = cfg.get<std::string>("project.default_build_path");
project.debug_build_path = cfg.get<std::string>("project.debug_build_path");
project.cmake.command = cfg.get<std::string>("project.cmake.command");
project.cmake.compile_command = cfg.get<std::string>("project.cmake.compile_command");
project.meson.command = cfg.get<std::string>("project.meson.command");
project.meson.compile_command = cfg.get<std::string>("project.meson.compile_command");
project.default_build_management_system = cfg.get<std::string>("project.default_build_management_system");
project.save_on_compile_or_run = cfg.get<bool>("project.save_on_compile_or_run");
project.ctags_command = cfg.get<std::string>("project.ctags_command");
project.grep_command = cfg.get<std::string>("project.grep_command");
project.cargo_command = cfg.get<std::string>("project.cargo_command");
project.python_command = cfg.get<std::string>("project.python_command");
project.markdown_command = cfg.get<std::string>("project.markdown_command");
terminal.history_size = cfg.get<int>("terminal.history_size");
terminal.font = cfg.get<std::string>("terminal.font");
terminal.clear_on_compile = cfg.get<bool>("terminal.clear_on_compile");
terminal.clear_on_run_command = cfg.get<bool>("terminal.clear_on_run_command");
terminal.hide_entry_on_run_command = cfg.get<bool>("terminal.hide_entry_on_run_command");
log.libclang = cfg.get<bool>("log.libclang");
log.language_server = cfg.get<bool>("log.language_server");
</style-scheme>
)RAW";
}

36
src/config.hpp

@ -1,11 +1,12 @@
#pragma once
#include "dispatcher.hpp"
#include "json.hpp"
#include <boost/filesystem.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include "python_bind.h"
class Config {
public:
@ -54,6 +55,7 @@ public:
std::string cargo_command;
std::string python_command;
std::string markdown_command;
std::vector<boost::filesystem::path> open_with_default_application;
};
class Source {
@ -68,7 +70,8 @@ public:
std::string font;
std::string spellcheck_language;
bool cleanup_whitespace_characters;
bool add_missing_newline_at_end_of_file_on_save;
bool remove_trailing_whitespace_characters_on_save;
std::string show_whitespace_characters;
bool format_style_on_save;
@ -83,6 +86,7 @@ public:
bool show_background_pattern;
bool show_right_margin;
unsigned right_margin_position;
unsigned pixels_between_lines;
bool auto_tab_char_and_size;
char default_tab_char;
@ -94,6 +98,7 @@ public:
bool enable_multiple_cursors;
bool auto_reload_changed_files;
bool search_for_selection;
int tooltip_top_offset;
std::string clang_format_style;
unsigned clang_usages_threads;
@ -114,6 +119,12 @@ public:
bool language_server;
};
class Plugins {
public:
bool enabled;
std::string path;
};
private:
Config();
@ -125,24 +136,33 @@ public:
void load();
std::string version;
Menu menu;
Theme theme;
Terminal terminal;
Project project;
Source source;
Log log;
Plugins plugins;
boost::filesystem::path home_path;
boost::filesystem::path home_juci_path;
static void init_module(py::module &api);
private:
/// Used to dispatch Terminal outputs after juCi++ GUI setup and configuration
Dispatcher dispatcher;
void update(boost::property_tree::ptree &cfg);
void make_version_dependent_corrections(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, const std::string &version);
bool add_missing_nodes(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, std::string parent_path = "");
bool remove_deprecated_nodes(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, std::string parent_path = "");
void read(const boost::property_tree::ptree &cfg);
void update(JSON &cfg);
void make_version_dependent_corrections(JSON &cfg, const JSON &default_cfg, const std::string &version);
void add_missing_nodes(JSON &cfg, const JSON &default_cfg);
void remove_deprecated_nodes(JSON &cfg, const JSON &default_cfg);
void read(const JSON &cfg);
/// If you add or remove nodes from the default_config, increase the juci
/// version number (JUCI_VERSION) in ../CMakeLists.txt to automatically apply
/// the changes to user's ~/.juci/config/config.json files
std::string default_config();
const char *juci_light_style();
const char *juci_dark_style();
const char *juci_dark_blue_style();
};

116
src/config_module.cc

@ -0,0 +1,116 @@
#include "config.hpp"
#include "python_type_casters.h"
#include <pybind11/stl.h>
void Config::init_module(py::module &api) {
py::class_<Config, std::unique_ptr<Config, py::nodelete>> config(api, "Config");
py::class_<Config::Menu>(config, "Menu")
.def(py::init())
.def_readwrite("keys", &Config::Menu::keys)
;
py::class_<Config::Theme>(config, "Theme")
.def(py::init())
.def_readwrite("name", &Config::Theme::name)
.def_readwrite("variant", &Config::Theme::variant)
.def_readwrite("font", &Config::Theme::font)
;
py::class_<Config::Terminal>(config, "Terminal")
.def(py::init())
.def_readwrite("history_size", &Config::Terminal::history_size)
.def_readwrite("font", &Config::Terminal::font)
;
py::class_<Config::Project> project(config, "Project");
py::class_<Config::Project::CMake>(project, "CMake")
.def(py::init())
.def_readwrite("command", &Config::Project::CMake::command)
.def_readwrite("compile_command", &Config::Project::CMake::compile_command)
;
py::class_<Config::Project::Meson>(project, "Meson")
.def(py::init())
.def_readwrite("command", &Config::Project::Meson::command)
.def_readwrite("compile_command", &Config::Project::Meson::compile_command)
;
project
.def(py::init())
.def_readwrite("default_build_path", &Config::Project::default_build_path)
.def_readwrite("debug_build_path", &Config::Project::debug_build_path)
.def_readwrite("cmake", &Config::Project::cmake)
.def_readwrite("meson", &Config::Project::meson)
.def_readwrite("save_on_compile_or_run", &Config::Project::save_on_compile_or_run)
// .def_readwrite("clear_terminal_on_compile", &Config::Project::clear_terminal_on_compile)
.def_readwrite("ctags_command", &Config::Project::ctags_command)
.def_readwrite("python_command", &Config::Project::python_command)
;
py::class_<Config::Source> source(config, "Source");
py::class_<Config::Source::DocumentationSearch>(source, "DocumentationSearch")
.def(py::init())
.def_readwrite("separator", &Config::Source::DocumentationSearch::separator)
.def_readwrite("queries", &Config::Source::DocumentationSearch::queries)
;
source
.def(py::init())
.def_readwrite("style", &Config::Source::style)
.def_readwrite("font", &Config::Source::font)
.def_readwrite("spellcheck_language", &Config::Source::spellcheck_language)
.def_readwrite("cleanup_whitespace_characters", &Config::Source::cleanup_whitespace_characters)
.def_readwrite("show_whitespace_characters", &Config::Source::show_whitespace_characters)
.def_readwrite("format_style_on_save", &Config::Source::format_style_on_save)
.def_readwrite("format_style_on_save_if_style_file_found", &Config::Source::format_style_on_save_if_style_file_found)
.def_readwrite("smart_inserts", &Config::Source::smart_inserts)
.def_readwrite("show_map", &Config::Source::show_map)
.def_readwrite("map_font_size", &Config::Source::map_font_size)
.def_readwrite("show_git_diff", &Config::Source::show_git_diff)
.def_readwrite("show_background_pattern", &Config::Source::show_background_pattern)
.def_readwrite("show_right_margin", &Config::Source::show_right_margin)
.def_readwrite("right_margin_position", &Config::Source::right_margin_position)
.def_readwrite("auto_tab_char_and_size", &Config::Source::auto_tab_char_and_size)
.def_readwrite("default_tab_char", &Config::Source::default_tab_char)
.def_readwrite("default_tab_size", &Config::Source::default_tab_size)
.def_readwrite("tab_indents_line", &Config::Source::tab_indents_line)
// .def_readwrite("wrap_lines", &Config::Source::wrap_lines)
.def_readwrite("highlight_current_line", &Config::Source::highlight_current_line)
.def_readwrite("show_line_numbers", &Config::Source::show_line_numbers)
.def_readwrite("enable_multiple_cursors", &Config::Source::enable_multiple_cursors)
.def_readwrite("auto_reload_changed_files", &Config::Source::auto_reload_changed_files)
.def_readwrite("clang_format_style", &Config::Source::clang_format_style)
.def_readwrite("clang_usages_threads", &Config::Source::clang_usages_threads)
.def_readwrite("documentation_searches", &Config::Source::documentation_searches)
;
py::class_<Config::Log>(config, "Log")
.def(py::init())
.def_readwrite("libclang", &Config::Log::libclang)
.def_readwrite("language_server", &Config::Log::language_server)
;
py::class_<Config::Plugins>(config, "Plugins")
.def(py::init())
.def_readwrite("enabled", &Config::Plugins::enabled)
.def_readwrite("path", &Config::Plugins::path)
;
config
.def(py::init([]() { return &(Config::get()); }))
.def("load", &Config::load)
.def_readonly("version", &Config::version)
.def_readwrite("menu", &Config::menu)
.def_readwrite("theme", &Config::theme)
.def_readwrite("terminal", &Config::terminal)
.def_readwrite("project", &Config::project)
.def_readwrite("source", &Config::source)
.def_readwrite("log", &Config::log)
.def_readwrite("plugins", &Config::plugins)
.def_readwrite("home_path", &Config::home_path)
.def_readwrite("home_juci_path", &Config::home_juci_path)
;
}

68
src/ctags.cpp

@ -36,6 +36,11 @@ Ctags::Ctags(const boost::filesystem::path &path, bool enable_scope, bool enable
command = Config::get().project.ctags_command + options + fields + " --c-kinds=+p --c++-kinds=+p " + filesystem::escape_argument(path.string());
}
TinyProcessLib::Config processConfig{};
#ifdef JUCI_FLATPAK_SANDBOX
processConfig.flatpak_spawn_host = true;
#endif
TinyProcessLib::Process process(
command, project_path.string(),
[this](const char *output, size_t length) {
@ -43,7 +48,8 @@ Ctags::Ctags(const boost::filesystem::path &path, bool enable_scope, bool enable
},
[](const char *bytes, size_t n) {
Terminal::get().async_print(std::string(bytes, n), true);
});
},
false, processConfig);
int exit_status;
size_t count = 0;
@ -96,10 +102,24 @@ Ctags::Location Ctags::get_location(const std::string &line_, bool add_markup, b
auto symbol_end = line.find('\t');
if(symbol_end == std::string::npos)
return location;
location.symbol = line.substr(0, symbol_end);
// Unescape symbol
if(symbol_end > 0) {
location.symbol.reserve(symbol_end);
bool escaped = false;
for(size_t i = 0; i < symbol_end; ++i) {
if(!escaped && line[i] == '\\') {
escaped = true;
continue;
}
escaped = false;
location.symbol += line[i];
}
}
if(9 < location.symbol.size() && location.symbol[8] == ' ' && starts_with(location.symbol, "operator")) {
auto &chr = location.symbol[9];
if(!Source::BaseView::is_token_char(chr))
if(!((chr >= 'A' && chr <= 'Z') || (chr >= 'a' && chr <= 'z') || (chr >= '0' && chr <= '9') || chr == '_' || chr == '$' || static_cast<unsigned char>(chr) >= 128))
location.symbol.erase(8, 1);
}
@ -215,7 +235,7 @@ std::vector<std::string> Ctags::get_type_parts(const std::string &type) {
size_t text_start = std::string::npos;
for(size_t c = 0; c < type.size(); ++c) {
auto &chr = type[c];
if(Source::BaseView::is_token_char(chr) || chr == '~') {
if((chr >= 'A' && chr <= 'Z') || (chr >= 'a' && chr <= 'z') || (chr >= '0' && chr <= '9') || chr == '_' || chr == '$' || static_cast<unsigned char>(chr) >= 128 || chr == '~') {
if(text_start == std::string::npos)
text_start = c;
}
@ -238,7 +258,7 @@ std::vector<Ctags::Location> Ctags::get_locations(const boost::filesystem::path
if(!ctags)
return {};
//insert name into type
// insert name into type
size_t c = 0;
size_t bracket_count = 0;
for(; c < type.size(); ++c) {
@ -272,7 +292,7 @@ std::vector<Ctags::Location> Ctags::get_locations(const boost::filesystem::path
auto source_parts = get_type_parts(location.source);
//Find match score
// Find match score
long score = 0;
size_t source_index = 0;
for(auto &part : parts) {
@ -314,3 +334,39 @@ std::vector<Ctags::Location> Ctags::get_locations(const boost::filesystem::path
return best_locations;
}
void Ctags::init_module(py::module &api) {
py::class_<Ctags> ctags(api, "Ctags");
py::class_<Ctags::Location>(api, "Location")
.def(py::init<>())
.def_readwrite("file_path", &Ctags::Location::file_path)
.def_readwrite("line", &Ctags::Location::line)
.def_readwrite("index", &Ctags::Location::index)
.def_readwrite("symbol", &Ctags::Location::symbol)
.def_readwrite("scope", &Ctags::Location::scope)
.def_readwrite("source", &Ctags::Location::source)
.def_readwrite("kind", &Ctags::Location::kind)
.def("__bool__", &Ctags::Location::operator bool)
;
ctags
.def(py::init<const boost::filesystem::path &, bool, bool, std::string>(),
py::arg("path"),
py::arg("enable_scope"),
py::arg("enable_kind"),
py::arg("languages"))
.def("get_location", &Ctags::get_location,
py::arg("line"),
py::arg("add_markup"),
py::arg("symbol_ends_with_open_parenthesis"))
.def_static("get_locations", &Ctags::get_locations,
py::arg("path"),
py::arg("name"),
py::arg("type"),
py::arg("languages"))
.def_readwrite("project_path", &Ctags::project_path)
.def("__bool__", &Ctags::operator bool)
;
}

2
src/ctags.hpp

@ -1,4 +1,5 @@
#pragma once
#include "python_bind.h"
#include <boost/filesystem.hpp>
#include <sstream>
#include <string>
@ -28,6 +29,7 @@ public:
std::stringstream output;
static std::vector<Location> get_locations(const boost::filesystem::path &path, const std::string &name, const std::string &type, const std::string &languages = {});
static void init_module(py::module &api);
private:
bool enable_scope, enable_kind;

113
src/debug_lldb.cpp

@ -1,8 +1,4 @@
#include "debug_lldb.hpp"
#include <cstdio>
#ifdef __APPLE__
#include <cstdlib>
#endif
#include "config.hpp"
#include "filesystem.hpp"
#include "process.hpp"
@ -10,28 +6,24 @@
#include "utility.hpp"
#include <boost/filesystem.hpp>
#include <iostream>
#include <pybind11/functional.h>
#include <pybind11/stl.h>
extern char **environ;
bool Debug::LLDB::initialized = false;
const size_t Debug::LLDB::buffer_size = 131072;
void log(const char *msg, void *) {
std::cout << "debugger log: " << msg << std::endl;
}
Debug::LLDB::LLDB() : state(lldb::StateType::eStateInvalid), buffer_size(131072) {
if(!getenv("LLDB_DEBUGSERVER_PATH")) {
Debug::LLDB::LLDB() : listener("juCi++ lldb listener"), state(lldb::StateType::eStateInvalid) {
#ifndef __APPLE__
auto debug_server_path = filesystem::get_executable("lldb-server").string();
if(debug_server_path != "lldb-server") {
#ifdef _WIN32
Glib::setenv("LLDB_DEBUGSERVER_PATH", debug_server_path.c_str(), 0);
#else
setenv("LLDB_DEBUGSERVER_PATH", debug_server_path.c_str(), 0);
#endif
}
auto debug_server_path = filesystem::get_executable("lldb-server").string();
if(debug_server_path != "lldb-server")
Glib::setenv("LLDB_DEBUGSERVER_PATH", debug_server_path, false);
#endif
}
}
void Debug::LLDB::destroy_() {
@ -110,10 +102,9 @@ void Debug::LLDB::start(const std::string &command, const boost::filesystem::pat
initialized = true;
lldb::SBDebugger::Initialize();
debugger = std::make_unique<lldb::SBDebugger>(lldb::SBDebugger::Create(true, log, nullptr));
listener = std::make_unique<lldb::SBListener>("juCi++ lldb listener");
}
//Create executable string and argument array
// Create executable string and argument array
auto parsed_run_arguments = parse_run_arguments(command);
auto &environment_from_arguments = std::get<0>(parsed_run_arguments);
auto &executable = std::get<1>(parsed_run_arguments);
@ -138,7 +129,7 @@ void Debug::LLDB::start(const std::string &command, const boost::filesystem::pat
return;
}
//Set breakpoints
// Set breakpoints
for(auto &breakpoint : breakpoints) {
if(!(target.BreakpointCreateByLocation(breakpoint.first.string().c_str(), breakpoint.second)).IsValid()) {
Terminal::get().async_print("\e[31mError (debug)\e[m: Could not create breakpoint at: " + breakpoint.first.string() + ":" + std::to_string(breakpoint.second) + '\n', true);
@ -151,7 +142,7 @@ void Debug::LLDB::start(const std::string &command, const boost::filesystem::pat
lldb::SBError error;
if(!remote_host.empty()) {
auto connect_string = "connect://" + remote_host;
process = std::make_unique<lldb::SBProcess>(target.ConnectRemote(*listener, connect_string.c_str(), "gdb-remote", error));
process = std::make_unique<lldb::SBProcess>(target.ConnectRemote(listener, connect_string.c_str(), "gdb-remote", error));
if(error.Fail()) {
Terminal::get().async_print(std::string("\e[31mError (debug)\e[m: ") + error.GetCString() + '\n', true);
for(auto &handler : on_exit)
@ -160,7 +151,7 @@ void Debug::LLDB::start(const std::string &command, const boost::filesystem::pat
}
lldb::SBEvent event;
while(true) {
if(listener->GetNextEvent(event)) {
if(listener.WaitForEvent(std::numeric_limits<uint32_t>::max(), event)) {
if((event.GetType() & lldb::SBProcess::eBroadcastBitStateChanged) > 0) {
auto state = process->GetStateFromEvent(event);
this->state = state;
@ -194,7 +185,7 @@ void Debug::LLDB::start(const std::string &command, const boost::filesystem::pat
environment.emplace_back(environ[c]);
environment.emplace_back(nullptr);
process = std::make_unique<lldb::SBProcess>(target.Launch(*listener, argv.data(), environment.data(), nullptr, nullptr, nullptr, path.string().c_str(), lldb::eLaunchFlagNone, false, error));
process = std::make_unique<lldb::SBProcess>(target.Launch(listener, argv.data(), environment.data(), nullptr, nullptr, nullptr, path.string().c_str(), lldb::eLaunchFlagNone, false, error));
}
if(error.Fail()) {
@ -217,8 +208,8 @@ void Debug::LLDB::start(const std::string &command, const boost::filesystem::pat
debug_thread = std::thread([this]() {
lldb::SBEvent event;
while(true) {
LockGuard lock(mutex);
if(listener->GetNextEvent(event)) {
if(listener.WaitForEvent(std::numeric_limits<uint32_t>::max(), event)) {
LockGuard lock(mutex);
if((event.GetType() & lldb::SBProcess::eBroadcastBitStateChanged) > 0) {
auto state = process->GetStateFromEvent(event);
this->state = state;
@ -255,7 +246,7 @@ void Debug::LLDB::start(const std::string &command, const boost::filesystem::pat
while((n = process->GetSTDOUT(buffer, buffer_size)) != 0)
Terminal::get().async_print(std::string(buffer, n));
}
//TODO: for some reason stderr is redirected to stdout
// TODO: for some reason stderr is redirected to stdout
if((event.GetType() & lldb::SBProcess::eBroadcastBitSTDERR) > 0) {
char buffer[buffer_size];
size_t n;
@ -263,8 +254,6 @@ void Debug::LLDB::start(const std::string &command, const boost::filesystem::pat
Terminal::get().async_print(std::string(buffer, n), true);
}
}
lock.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
});
}
@ -438,7 +427,7 @@ std::string Debug::LLDB::get_value(const std::string &variable, const boost::fil
auto frame = process->GetSelectedThread().GetSelectedFrame();
auto values = frame.GetVariables(true, true, true, false);
//First try to find variable based on name, file and line number
// First try to find variable based on name, file and line number
if(!file_path.empty()) {
for(uint32_t value_index = 0; value_index < values.GetSize(); value_index++) {
lldb::SBStream stream;
@ -573,3 +562,73 @@ void Debug::LLDB::write(const std::string &buffer) {
process->PutSTDIN(buffer.c_str(), buffer.size());
}
}
void Debug::LLDB::init_module(pybind11::module &api) {
py::class_<Debug::LLDB, std::unique_ptr<Debug::LLDB, py::nodelete>> dbg(api, "LLDB");
py::class_<Debug::LLDB::Frame>(dbg, "Frame")
.def_readwrite("index", &Debug::LLDB::Frame::index)
.def_readwrite("module_filename", &Debug::LLDB::Frame::module_filename)
.def_readwrite("file_path", &Debug::LLDB::Frame::file_path)
.def_readwrite("function_name", &Debug::LLDB::Frame::function_name)
.def_readwrite("line_nr", &Debug::LLDB::Frame::line_nr)
.def_readwrite("line_index", &Debug::LLDB::Frame::line_index)
;
py::class_<Debug::LLDB::Variable>(dbg, "Variable")
.def_readwrite("thread_index_id", &Debug::LLDB::Variable::thread_index_id)
.def_readwrite("frame_index", &Debug::LLDB::Variable::frame_index)
.def_readwrite("name", &Debug::LLDB::Variable::name)
.def_readwrite("value", &Debug::LLDB::Variable::value)
.def_readwrite("declaration_found", &Debug::LLDB::Variable::declaration_found)
.def_readwrite("file_path", &Debug::LLDB::Variable::file_path)
.def_readwrite("line_nr", &Debug::LLDB::Variable::line_nr)
.def_readwrite("line_index", &Debug::LLDB::Variable::line_index)
;
// py::class_<lldb::SBProcess>(api, "SBProcess");
// .def_readwrite("on_start", &Debug::LLDB::on_start)
// .def_readwrite("on_event", &Debug::LLDB::on_event)
dbg
.def(py::init([]() { return &(Debug::LLDB::get()); }))
.def_static("destroy", &Debug::LLDB::destroy)
.def_readwrite("on_exit", &Debug::LLDB::on_exit)
.def("continue_debug", &Debug::LLDB::continue_debug)
.def("stop", &Debug::LLDB::stop)
.def("kill", &Debug::LLDB::kill)
.def("step_over", &Debug::LLDB::step_over)
.def("step_into", &Debug::LLDB::step_into)
.def("step_out", &Debug::LLDB::step_out)
.def("is_invalid", &Debug::LLDB::is_invalid)
.def("is_stopped", &Debug::LLDB::is_stopped)
.def("is_running", &Debug::LLDB::is_running)
.def("get_backtrace", &Debug::LLDB::get_backtrace)
.def("get_variables", &Debug::LLDB::get_variables)
.def("start", &Debug::LLDB::start,
py::arg("command"),
py::arg("path") = "",
py::arg("breakpoints") = std::vector<std::pair<boost::filesystem::path, int>>(),
py::arg("startup_commands") = std::vector<std::string>(),
py::arg("remote_host") = "")
.def("run_command", &Debug::LLDB::run_command,
py::arg("command"))
.def("select_frame", &Debug::LLDB::select_frame,
py::arg("frame_index"),
py::arg("thread_index_id") = 0)
.def("get_return_value", &Debug::LLDB::get_return_value,
py::arg("file_path"),
py::arg("line_nr"),
py::arg("line_index"))
.def("add_breakpoint", &Debug::LLDB::add_breakpoint,
py::arg("file_path"),
py::arg("line_nr"))
.def("remove_breakpoint", &Debug::LLDB::remove_breakpoint,
py::arg("file_path"),
py::arg("line_nr"),
py::arg("line_count"))
.def("write", &Debug::LLDB::write,
py::arg("buffer"))
;
}

9
src/debug_lldb.hpp

@ -5,6 +5,7 @@
#include <lldb/API/LLDB.h>
#include <thread>
#include <tuple>
#include "python_bind.h"
namespace Debug {
class LLDB {
@ -59,7 +60,7 @@ namespace Debug {
void start(const std::string &command, const boost::filesystem::path &path = "",
const std::vector<std::pair<boost::filesystem::path, int>> &breakpoints = {},
const std::vector<std::string> &startup_commands = {}, const std::string &remote_host = "") EXCLUDES(mutex);
void continue_debug() EXCLUDES(mutex); //can't use continue as function name
void continue_debug() EXCLUDES(mutex); // can't use continue as function name
void stop() EXCLUDES(mutex);
void kill() EXCLUDES(mutex);
void step_over() EXCLUDES(mutex);
@ -82,19 +83,19 @@ namespace Debug {
void add_breakpoint(const boost::filesystem::path &file_path, int line_nr) EXCLUDES(mutex);
void remove_breakpoint(const boost::filesystem::path &file_path, int line_nr, int line_count) EXCLUDES(mutex);
void write(const std::string &buffer) EXCLUDES(mutex);
static void init_module(py::module &api);
private:
std::tuple<std::vector<std::string>, std::string, std::vector<std::string>> parse_run_arguments(const std::string &command);
lldb::SBListener listener;
std::unique_ptr<lldb::SBDebugger> debugger GUARDED_BY(mutex);
std::unique_ptr<lldb::SBListener> listener GUARDED_BY(mutex);
std::unique_ptr<lldb::SBProcess> process GUARDED_BY(mutex);
std::thread debug_thread;
lldb::StateType state GUARDED_BY(mutex);
size_t buffer_size;
const static size_t buffer_size;
};
} // namespace Debug

52
src/dialog.cpp

@ -3,21 +3,28 @@
#include <cmath>
Dialog::Message::Message(const std::string &text, std::function<void()> &&on_cancel, bool show_progress_bar) : Gtk::Window(Gtk::WindowType::WINDOW_POPUP) {
auto g_application = g_application_get_default();
auto gio_application = Glib::wrap(g_application, true);
auto application = Glib::RefPtr<Gtk::Application>::cast_static(gio_application);
set_transient_for(*application->get_active_window());
set_transient_for(*Glib::RefPtr<Gtk::Application>::cast_dynamic(Gtk::Application::get_default())->get_active_window());
set_position(Gtk::WindowPosition::WIN_POS_CENTER_ON_PARENT);
set_modal(true);
set_type_hint(Gdk::WindowTypeHint::WINDOW_TYPE_HINT_NOTIFICATION);
property_decorated() = false;
set_skip_taskbar_hint(true);
auto box = Gtk::manage(new Gtk::Box(Gtk::Orientation::ORIENTATION_VERTICAL));
auto visual = get_screen()->get_rgba_visual();
if(visual)
gtk_widget_set_visual(reinterpret_cast<GtkWidget *>(gobj()), visual->gobj());
get_style_context()->add_class("juci_message_window");
auto vbox = Gtk::manage(new Gtk::Box(Gtk::Orientation::ORIENTATION_VERTICAL));
vbox->get_style_context()->add_class("juci_message_box");
auto hbox = Gtk::manage(new Gtk::Box(Gtk::Orientation::ORIENTATION_HORIZONTAL));
auto image = Gtk::manage(new Gtk::Image());
image->set_from_icon_name("dialog-information", Gtk::BuiltinIconSize::ICON_SIZE_BUTTON);
hbox->pack_start(*image, Gtk::PackOptions::PACK_SHRINK);
auto label = Gtk::manage(new Gtk::Label(text));
label->set_padding(10, 10);
box->pack_start(*label);
label->set_padding(7, 7);
hbox->pack_start(*label);
vbox->pack_start(*hbox);
if(on_cancel) {
auto cancel_button = Gtk::manage(new Gtk::Button("Cancel"));
cancel_button->signal_clicked().connect([label, on_cancel = std::move(on_cancel)] {
@ -25,12 +32,11 @@ Dialog::Message::Message(const std::string &text, std::function<void()> &&on_can
if(on_cancel)
on_cancel();
});
box->pack_start(*cancel_button);
vbox->pack_start(*cancel_button);
}
if(show_progress_bar)
box->pack_start(progress_bar);
add(*box);
vbox->pack_start(progress_bar);
add(*vbox);
show_all_children();
show_now();
@ -74,10 +80,7 @@ std::string Dialog::gtk_dialog(const boost::filesystem::path &path, const std::s
Gtk::FileChooserDialog dialog(title, action);
#endif
auto g_application = g_application_get_default();
auto gio_application = Glib::wrap(g_application, true);
auto application = Glib::RefPtr<Gtk::Application>::cast_static(gio_application);
dialog.set_transient_for(*application->get_active_window());
dialog.set_transient_for(*Glib::RefPtr<Gtk::Application>::cast_dynamic(Gtk::Application::get_default())->get_active_window());
dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ON_PARENT);
if(title == "Save File As")
@ -95,6 +98,23 @@ std::string Dialog::gtk_dialog(const boost::filesystem::path &path, const std::s
return dialog.run() == Gtk::RESPONSE_OK ? dialog.get_filename() : "";
}
void Dialog::init_module(py::module &api) {
py::class_<Dialog>(api, "Dialog")
.def_static("open_folder", Dialog::open_folder,
py::arg("path"))
.def_static("open_file", Dialog::open_file,
py::arg("path"))
.def_static("new_file", Dialog::new_file,
py::arg("path"))
.def_static("new_folder", Dialog::new_folder,
py::arg("path"))
.def_static("save_file_as", Dialog::save_file_as,
py::arg("path"))
;
}
std::string Dialog::open_folder(const boost::filesystem::path &path) {
return gtk_dialog(path, "Open Folder",
{std::make_pair("Cancel", Gtk::RESPONSE_CANCEL), std::make_pair("Open", Gtk::RESPONSE_OK)},

2
src/dialog.hpp

@ -1,4 +1,5 @@
#pragma once
#include "python_bind.h"
#include <boost/filesystem.hpp>
#include <gtkmm.h>
#include <string>
@ -29,4 +30,5 @@ public:
static std::string new_file(const boost::filesystem::path &path);
static std::string new_folder(const boost::filesystem::path &path);
static std::string save_file_as(const boost::filesystem::path &path);
static void init_module(py::module &api);
};

301
src/directories.cpp

@ -1,10 +1,14 @@
#include "directories.hpp"
#include "config.hpp"
#include "entrybox.hpp"
#include "filesystem.hpp"
#include "info.hpp"
#include "notebook.hpp"
#include "project.hpp"
#include "source.hpp"
#include "terminal.hpp"
#include "utility.hpp"
#include "window.hpp"
#include <algorithm>
bool Directories::TreeStore::row_drop_possible_vfunc(const Gtk::TreeModel::Path &path, const Gtk::SelectionData &selection_data) const {
@ -119,111 +123,6 @@ Directories::Directories() : Gtk::ListViewText(1) {
tree_store->set_sort_column(column_record.name, Gtk::SortType::SORT_ASCENDING);
tree_store->set_sort_func(column_record.name, [this](const Gtk::TreeModel::iterator &it1, const Gtk::TreeModel::iterator &it2) {
/// Natural comparison supporting UTF-8 and locale
struct Natural {
static bool is_digit(char chr) {
return chr >= '0' && chr <= '9';
}
static int compare_characters(size_t &i1, size_t &i2, const std::string &s1, const std::string &s2) {
ScopeGuard scope_guard{[&i1, &i2] {
++i1;
++i2;
}};
auto c1 = static_cast<unsigned char>(s1[i1]);
auto c2 = static_cast<unsigned char>(s2[i2]);
if(c1 < 0b10000000 && c2 < 0b10000000) { // Both characters are ascii
auto at = std::tolower(s1[i1]);
auto bt = std::tolower(s2[i2]);
if(at < bt)
return -1;
else if(at == bt)
return 0;
else
return 1;
}
Glib::ustring u1;
if(c1 >= 0b11110000)
u1 = s1.substr(i1, 4);
else if(c1 >= 0b11100000)
u1 = s1.substr(i1, 3);
else if(c1 >= 0b11000000)
u1 = s1.substr(i1, 2);
else
u1 = s1[i1];
Glib::ustring u2;
if(c2 >= 0b11110000)
u2 = s2.substr(i2, 4);
else if(c2 >= 0b11100000)
u2 = s2.substr(i2, 3);
else if(c2 >= 0b11000000)
u2 = s2.substr(i2, 2);
else
u2 = s2[i2];
i1 += u1.bytes() - 1;
i2 += u2.bytes() - 1;
u1 = u1.lowercase();
u2 = u2.lowercase();
if(u1 < u2)
return -1;
else if(u1 == u2)
return 0;
else
return 1;
}
static int compare_numbers(size_t &i1, size_t &i2, const std::string &s1, const std::string &s2) {
int result = 0;
while(true) {
if(i1 >= s1.size() || !is_digit(s1[i1])) {
if(i2 >= s2.size() || !is_digit(s2[i2])) // a and b has equal number of digits
return result;
return -1; // a has fewer digits
}
if(i2 >= s2.size() || !is_digit(s2[i2]))
return 1; // b has fewer digits
if(result == 0) {
if(s1[i1] < s2[i2])
result = -1;
if(s1[i1] > s2[i2])
result = 1;
}
++i1;
++i2;
}
}
static int compare(const std::string &s1, const std::string &s2) {
size_t i1 = 0;
size_t i2 = 0;
while(i1 < s1.size() && i2 < s2.size()) {
if(is_digit(s1[i1]) && !is_digit(s2[i2]))
return -1;
if(!is_digit(s1[i1]) && is_digit(s2[i2]))
return 1;
if(!is_digit(s1[i1]) && !is_digit(s2[i2])) {
auto result = compare_characters(i1, i2, s1, s2);
if(result != 0)
return result;
}
else {
auto result = compare_numbers(i1, i2, s1, s2);
if(result != 0)
return result;
}
}
if(i1 >= s1.size())
return -1;
return 1;
}
};
auto name1 = it1->get_value(column_record.name);
auto name2 = it2->get_value(column_record.name);
if(name1.empty())
@ -240,7 +139,7 @@ Directories::Directories() : Gtk::ListViewText(1) {
return Natural::compare(prefix1 + name1, prefix2 + name2);
});
set_enable_search(true); //TODO: why does this not work in OS X?
set_enable_search(true); // TODO: why does this not work in OS X?
set_search_column(column_record.name);
signal_row_activated().connect([this](const Gtk::TreeModel::Path &path, Gtk::TreeViewColumn *column) {
@ -248,9 +147,31 @@ Directories::Directories() : Gtk::ListViewText(1) {
if(iter) {
auto filesystem_path = iter->get_value(column_record.path);
if(filesystem_path != "") {
GdkModifierType state;
boost::system::error_code ec;
if(boost::filesystem::is_directory(boost::filesystem::path(filesystem_path), ec))
row_expanded(path) ? collapse_row(path) : expand_row(path, false);
else if(gtk_get_current_event_state(&state) && (state &
#ifdef __APPLE__
GDK_MOD2_MASK
#else
GDK_CONTROL_MASK
#endif
)) {
if(Project::compiling || Project::debugging) {
Info::get().print("Compile or debug in progress");
return;
}
Project::current = Project::create(filesystem_path);
if(Config::get().project.save_on_compile_or_run)
Project::save_files(!Project::current->build->project_path.empty() ? Project::current->build->project_path : filesystem_path.parent_path());
Project::current->compile_and_run(filesystem_path);
set_cursor(path);
}
else
Notebook::get().open(filesystem_path);
}
@ -431,9 +352,13 @@ Directories::Directories() : Gtk::ListViewText(1) {
menu_item_delete.signal_activate().connect([this] {
if(menu_popup_row_path.empty())
return;
Gtk::MessageDialog dialog(*static_cast<Gtk::Window *>(get_toplevel()), "Delete!", false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO);
Gtk::MessageDialog dialog(*static_cast<Gtk::Window *>(get_toplevel()), "Delete?", false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO);
Gtk::Image image;
image.set_from_icon_name("dialog-warning", Gtk::BuiltinIconSize::ICON_SIZE_DIALOG);
dialog.set_image(image);
dialog.set_default_response(Gtk::RESPONSE_NO);
dialog.set_secondary_text("Are you sure you want to delete " + filesystem::get_short_path(menu_popup_row_path).string() + "?");
dialog.show_all();
int result = dialog.run();
if(result == Gtk::RESPONSE_YES) {
boost::system::error_code ec;
@ -504,7 +429,7 @@ Directories::Directories() : Gtk::ListViewText(1) {
}
Directories::~Directories() {
thread_pool.shutdown(true);
worker.stop();
}
void Directories::open(const boost::filesystem::path &dir_path) {
@ -518,7 +443,7 @@ void Directories::open(const boost::filesystem::path &dir_path) {
path = filesystem::get_normal_path(dir_path);
//TODO: report that set_title does not handle '_' correctly?
// TODO: report that set_title does not handle '_' correctly?
auto title = path.filename().string();
size_t pos = 0;
while((pos = title.find('_', pos)) != std::string::npos) {
@ -526,6 +451,7 @@ void Directories::open(const boost::filesystem::path &dir_path) {
pos += 2;
}
get_column(0)->set_title(title);
Window::get().set_title(path);
for(auto &directory : directories) {
if(directory.second.repository)
@ -534,6 +460,9 @@ void Directories::open(const boost::filesystem::path &dir_path) {
directories.clear();
add_or_update_path(path, Gtk::TreeModel::Row(), true);
if(auto view = Notebook::get().get_current_view())
view->update_status_file_path(view);
}
void Directories::close(const boost::filesystem::path &dir_path) {
@ -543,9 +472,13 @@ void Directories::close(const boost::filesystem::path &dir_path) {
tree_store->clear();
path.clear();
get_column(0)->set_title("");
Window::get().set_title();
}
else
remove_path(dir_path);
if(auto view = Notebook::get().get_current_view())
view->update_status_file_path(view);
}
void Directories::update() {
@ -572,7 +505,7 @@ void Directories::select(const boost::filesystem::path &select_path) {
if(!filesystem::file_in_path(select_path, path))
return;
//return if the select_path is already selected
// return if the select_path is already selected
auto iter = get_selection()->get_selected();
if(iter) {
if(iter->get_value(column_record.path) == select_path)
@ -587,9 +520,9 @@ void Directories::select(const boost::filesystem::path &select_path) {
else
parent_path = select_path.parent_path();
//check if select_path is already expanded
// check if select_path is already expanded
if(directories.find(parent_path.string()) != directories.end()) {
//set cursor at select_path and return
// set cursor at select_path and return
tree_store->foreach_iter([this, &select_path](const Gtk::TreeModel::iterator &iter) {
if(iter->get_value(column_record.path) == select_path) {
auto tree_path = Gtk::TreePath(iter);
@ -608,7 +541,7 @@ void Directories::select(const boost::filesystem::path &select_path) {
paths.emplace_front(parent_path);
}
//expand to select_path
// expand to select_path
for(auto &a_path : paths) {
tree_store->foreach_iter([this, &a_path](const Gtk::TreeModel::iterator &iter) {
if(iter->get_value(column_record.path) == a_path) {
@ -619,7 +552,7 @@ void Directories::select(const boost::filesystem::path &select_path) {
});
}
//set cursor at select_path
// set cursor at select_path
tree_store->foreach_iter([this, &select_path](const Gtk::TreeModel::iterator &iter) {
if(iter->get_value(column_record.path) == select_path) {
auto tree_path = Gtk::TreePath(iter);
@ -810,21 +743,83 @@ void Directories::remove_path(const boost::filesystem::path &dir_path) {
}
}
void Directories::colorize_path(boost::filesystem::path dir_path_, bool include_parent_paths) {
auto dir_path = std::make_shared<boost::filesystem::path>(std::move(dir_path_));
auto it = directories.find(dir_path->string());
void Directories::colorize_path(boost::filesystem::path dir_path, bool include_parent_paths) {
auto it = directories.find(dir_path.string());
if(it == directories.end())
return;
boost::system::error_code ec;
if(!boost::filesystem::exists(*dir_path, ec)) {
if(!boost::filesystem::exists(dir_path, ec)) {
directories.erase(it);
return;
}
auto colorize = [this, dir_path = std::move(dir_path), include_parent_paths](const Git::Repository::Status &status = {}) {
auto it = directories.find(dir_path.string());
if(it == directories.end())
return;
auto normal_color = get_style_context()->get_color(Gtk::StateFlags::STATE_FLAG_NORMAL);
Gdk::RGBA yellow;
yellow.set_rgba(1.0, 1.0, 0.2);
double factor = 0.5;
yellow.set_red(normal_color.get_red() + factor * (yellow.get_red() - normal_color.get_red()));
yellow.set_green(normal_color.get_green() + factor * (yellow.get_green() - normal_color.get_green()));
yellow.set_blue(normal_color.get_blue() + factor * (yellow.get_blue() - normal_color.get_blue()));
Gdk::RGBA green;
green.set_rgba(0.0, 1.0, 0.0);
factor = 0.4;
green.set_red(normal_color.get_red() + factor * (green.get_red() - normal_color.get_red()));
green.set_green(normal_color.get_green() + factor * (green.get_green() - normal_color.get_green()));
green.set_blue(normal_color.get_blue() + factor * (green.get_blue() - normal_color.get_blue()));
boost::system::error_code ec;
do {
Gtk::TreeNodeChildren children(it->second.row ? it->second.row.children() : tree_store->children());
if(!children)
return;
for(auto &child : children) {
auto name = Glib::Markup::escape_text(child.get_value(column_record.name));
auto path = child.get_value(column_record.path);
// Use canonical path to follow symbolic links
auto canonical_path = filesystem::get_canonical_path(path);
Gdk::RGBA *color;
if(status.modified.find(canonical_path.generic_string()) != status.modified.end())
color = &yellow;
else if(status.added.find(canonical_path.generic_string()) != status.added.end())
color = &green;
else
color = &normal_color;
std::stringstream ss;
ss << '#' << std::setfill('0') << std::hex;
ss << std::setw(2) << std::hex << (color->get_red_u() >> 8);
ss << std::setw(2) << std::hex << (color->get_green_u() >> 8);
ss << std::setw(2) << std::hex << (color->get_blue_u() >> 8);
child.set_value(column_record.markup, "<span foreground=\"" + ss.str() + "\">" + name + "</span>");
auto type = child.get_value(column_record.type);
if(type == PathType::unknown)
child.set_value(column_record.markup, "<i>" + child.get_value(column_record.markup) + "</i>");
}
if(!include_parent_paths)
break;
auto path = boost::filesystem::path(it->first);
if(boost::filesystem::exists(path / ".git", ec))
break;
if(path == path.root_directory())
break;
auto parent_path = boost::filesystem::path(it->first).parent_path();
it = directories.find(parent_path.string());
} while(it != directories.end());
};
if(it->second.repository) {
auto repository = it->second.repository;
thread_pool.push([this, dir_path, repository, include_parent_paths] {
worker.post([this, repository = it->second.repository, colorize = std::move(colorize)] {
Git::Repository::Status status;
try {
status = repository->get_status();
@ -833,69 +828,11 @@ void Directories::colorize_path(boost::filesystem::path dir_path_, bool include_
Terminal::get().async_print(std::string("\e[31mError (git)\e[m: ") + e.what() + '\n', true);
}
dispatcher.post([this, dir_path, include_parent_paths, status = std::move(status)] {
auto it = directories.find(dir_path->string());
if(it == directories.end())
return;
auto normal_color = get_style_context()->get_color(Gtk::StateFlags::STATE_FLAG_NORMAL);
Gdk::RGBA yellow;
yellow.set_rgba(1.0, 1.0, 0.2);
double factor = 0.5;
yellow.set_red(normal_color.get_red() + factor * (yellow.get_red() - normal_color.get_red()));
yellow.set_green(normal_color.get_green() + factor * (yellow.get_green() - normal_color.get_green()));
yellow.set_blue(normal_color.get_blue() + factor * (yellow.get_blue() - normal_color.get_blue()));
Gdk::RGBA green;
green.set_rgba(0.0, 1.0, 0.0);
factor = 0.4;
green.set_red(normal_color.get_red() + factor * (green.get_red() - normal_color.get_red()));
green.set_green(normal_color.get_green() + factor * (green.get_green() - normal_color.get_green()));
green.set_blue(normal_color.get_blue() + factor * (green.get_blue() - normal_color.get_blue()));
boost::system::error_code ec;
do {
Gtk::TreeNodeChildren children(it->second.row ? it->second.row.children() : tree_store->children());
if(!children)
return;
for(auto &child : children) {
auto name = Glib::Markup::escape_text(child.get_value(column_record.name));
auto path = child.get_value(column_record.path);
// Use canonical path to follow symbolic links
auto canonical_path = filesystem::get_canonical_path(path);
Gdk::RGBA *color;
if(status.modified.find(canonical_path.generic_string()) != status.modified.end())
color = &yellow;
else if(status.added.find(canonical_path.generic_string()) != status.added.end())
color = &green;
else
color = &normal_color;
std::stringstream ss;
ss << '#' << std::setfill('0') << std::hex;
ss << std::setw(2) << std::hex << (color->get_red_u() >> 8);
ss << std::setw(2) << std::hex << (color->get_green_u() >> 8);
ss << std::setw(2) << std::hex << (color->get_blue_u() >> 8);
child.set_value(column_record.markup, "<span foreground=\"" + ss.str() + "\">" + name + "</span>");
auto type = child.get_value(column_record.type);
if(type == PathType::unknown)
child.set_value(column_record.markup, "<i>" + child.get_value(column_record.markup) + "</i>");
}
if(!include_parent_paths)
break;
auto path = boost::filesystem::path(it->first);
if(boost::filesystem::exists(path / ".git", ec))
break;
if(path == path.root_directory())
break;
auto parent_path = boost::filesystem::path(it->first).parent_path();
it = directories.find(parent_path.string());
} while(it != directories.end());
dispatcher.post([colorize = std::move(colorize), status = std::move(status)] {
colorize(status);
});
});
}
else
colorize();
}

3
src/directories.hpp

@ -2,6 +2,7 @@
#include "boost/filesystem.hpp"
#include "dispatcher.hpp"
#include "git.hpp"
#include "workers.hpp"
#include <atomic>
#include <gtkmm.h>
#include <string>
@ -81,7 +82,7 @@ private:
std::unordered_map<std::string, DirectoryData> directories;
Glib::ThreadPool thread_pool;
Workers worker;
Dispatcher dispatcher;
Gtk::Menu menu;

9
src/dispatcher.cpp

@ -40,3 +40,12 @@ void Dispatcher::reset() {
functions.clear();
connect();
}
void Dispatcher::init_module(py::module &api) {
py::class_<Dispatcher>(api, "Dispatcher")
.def(py::init())
.def("disconnect", &Dispatcher::disconnect)
.def("post", &Dispatcher::post<std::function<void()> &>)
;
}

3
src/dispatcher.hpp

@ -1,5 +1,6 @@
#pragma once
#include "mutex.hpp"
#include "python_bind.h"
#include <functional>
#include <gtkmm.h>
#include <list>
@ -32,4 +33,6 @@ public:
/// Must be called from main GUI thread
void reset();
static void init_module(py::module &api);
};

5
src/documentation.cpp

@ -12711,3 +12711,8 @@ std::experimental::filesystem::status_known cpp/experimental/fs/status_known
return std::string();
return "http://en.cppreference.com/w/" + it->second;
}
void Documentation::CppReference::init_module(py::module &api) {
py::class_<CppReference>(api, "CppReference")
.def("get_url", &CppReference::get_url);
}

2
src/documentation.hpp

@ -1,4 +1,5 @@
#pragma once
#include "python_bind.h"
#include <string>
#include <vector>
@ -7,5 +8,6 @@ namespace Documentation {
public:
static std::vector<std::string> get_headers(const std::string &symbol) noexcept;
static std::string get_url(const std::string &symbol) noexcept;
static void init_module(py::module &api);
};
} // namespace Documentation

21
src/entrybox.cpp

@ -2,20 +2,15 @@
std::unordered_map<std::string, std::vector<std::string>> EntryBox::entry_histories;
EntryBox::Entry::Entry(const std::string &content, std::function<void(const std::string &content)> on_activate_, unsigned width_chars) : Gtk::Entry(), on_activate(std::move(on_activate_)) {
EntryBox::Entry::Entry(const std::string &content, std::function<void(const std::string &content)> on_activate_) : Gtk::Entry(), on_activate(std::move(on_activate_)) {
set_max_length(0);
set_width_chars(width_chars);
set_text(content);
last_content = content;
selected_history = -1;
signal_activate().connect([this]() {
if(this->on_activate) {
auto &history = EntryBox::entry_histories[get_placeholder_text()];
auto text = get_text();
if(!text.empty() && (history.empty() || *history.begin() != text))
history.emplace(history.begin(), text);
selected_history = -1;
this->on_activate(text);
update_history();
this->on_activate(get_text());
}
});
signal_changed().connect([this] {
@ -58,6 +53,14 @@ EntryBox::Entry::Entry(const std::string &content, std::function<void(const std:
});
}
void EntryBox::Entry::update_history() {
auto &history = EntryBox::entry_histories[get_placeholder_text()];
auto text = get_text();
if(!text.empty() && (history.empty() || *history.begin() != text))
history.emplace(history.begin(), text);
selected_history = -1;
}
EntryBox::Button::Button(const std::string &label, std::function<void()> on_activate_) : Gtk::Button(label), on_activate(std::move(on_activate_)) {
set_focus_on_click(false);
signal_clicked().connect([this]() {
@ -97,7 +100,7 @@ void EntryBox::clear() {
void EntryBox::show() {
std::vector<Gtk::Widget *> focus_chain;
for(auto &entry : entries) {
lower_box.pack_start(entry, Gtk::PACK_SHRINK);
lower_box.pack_start(entry, Gtk::PACK_EXPAND_WIDGET);
focus_chain.emplace_back(&entry);
}
for(auto &button : buttons)

5
src/entrybox.hpp

@ -10,9 +10,12 @@ class EntryBox : public Gtk::Box {
public:
class Entry : public Gtk::Entry {
public:
Entry(const std::string &content = "", std::function<void(const std::string &content)> on_activate_ = nullptr, unsigned width_chars = -1);
Entry(const std::string &content = "", std::function<void(const std::string &content)> on_activate_ = nullptr);
std::function<void(const std::string &content)> on_activate;
/// Only the default activation on an entry will update its history.
void update_history();
private:
long selected_history;
std::string last_content;

55
src/files.hpp

@ -4,11 +4,8 @@
/// If you add or remove nodes from the default_config_file, increase the juci
/// version number (JUCI_VERSION) in ../CMakeLists.txt to automatically apply
/// the changes to user's ~/.juci/config/config.json files
const std::string default_config_file =
R"RAW({
"version": ")RAW" +
std::string(JUCI_VERSION) +
R"RAW(",
const std::string default_config_file = R"RAW({
"version": ")RAW" + std::string(JUCI_VERSION) + R"RAW(",
"gtk_theme": {
"name_comment": "Use \"\" for default theme, At least these two exist on all systems: Adwaita, Raleigh",
"name": "",
@ -22,18 +19,18 @@ const std::string default_config_file =
"style": "juci-light",
"font_comment": "Use \"\" for default font, and for instance \"Monospace 12\" to also set size",)RAW"
#ifdef __APPLE__
R"RAW(
R"RAW(
"font": "Menlo",)RAW"
#else
#ifdef _WIN32
R"RAW(
R"RAW(
"font": "Consolas",)RAW"
#else
R"RAW(
R"RAW(
"font": "Monospace",)RAW"
#endif
#endif
R"RAW(
R"RAW(
"cleanup_whitespace_characters_comment": "Remove trailing whitespace characters on save, and add trailing newline if missing",
"cleanup_whitespace_characters": false,
"show_whitespace_characters_comment": "Determines what kind of whitespaces should be drawn. Use comma-separated list of: space, tab, newline, nbsp, leading, text, trailing or all",
@ -84,8 +81,7 @@ const std::string default_config_file =
"font_comment": "Use \"\" to use source.font with slightly smaller size",
"font": "",
"clear_on_compile": true,
"clear_on_run_command": false,
"hide_entry_on_run_command": true
"clear_on_run_command": false
},
"project": {
"default_build_path_comment": "Use <project_directory_name> to insert the project top level directory name",
@ -94,13 +90,13 @@ const std::string default_config_file =
"debug_build_path": "<default_build_path>/debug",
"cmake": {)RAW"
#ifdef _WIN32
R"RAW(
R"RAW(
"command": "cmake -G\"MSYS Makefiles\"",)RAW"
#else
R"RAW(
R"RAW(
"command": "cmake",)RAW"
#endif
R"RAW(
R"RAW(
"compile_command": "cmake --build ."
},
"meson": {
@ -111,13 +107,13 @@ const std::string default_config_file =
"default_build_management_system": "cmake",
"save_on_compile_or_run": true,)RAW"
#ifdef JUCI_USE_UCTAGS
R"RAW(
R"RAW(
"ctags_command": "uctags",)RAW"
#else
R"RAW(
R"RAW(
"ctags_command": "ctags",)RAW"
#endif
R"RAW(
R"RAW(
"grep_command": "grep",
"cargo_command": "cargo",
"python_command": "python -u",
@ -126,7 +122,6 @@ const std::string default_config_file =
"keybindings": {
"preferences": "<primary>comma",
"snippets": "",
"commands": "",
"quit": "<primary>q",
"file_new_file": "<primary>n",
"file_new_folder": "<primary><shift>n",
@ -161,8 +156,8 @@ const std::string default_config_file =
"source_center_cursor": "<primary>l",
"source_cursor_history_back": "<alt>Left",
"source_cursor_history_forward": "<alt>Right",
"source_show_completion_comment": "Add completion keybinding to disable interactive autocompletion",
"source_show_completion": "",
"source_show_completion_comment" : "Add completion keybinding to disable interactive autocompletion",
"source_show_completion" : "",
"source_find_file": "<primary>p",
"source_find_symbol": "<primary><shift>f",
"source_find_pattern": "<alt><shift>f",
@ -198,29 +193,26 @@ const std::string default_config_file =
"debug_show_breakpoints": "<primary><shift><alt>b",
"debug_goto_stop": "<primary><shift>l",)RAW"
#ifdef __linux
R"RAW(
R"RAW(
"window_next_tab": "<primary>Tab",
"window_previous_tab": "<primary><shift>Tab",)RAW"
#else
R"RAW(
R"RAW(
"window_next_tab": "<primary><alt>Right",
"window_previous_tab": "<primary><alt>Left",)RAW"
#endif
R"RAW(
R"RAW(
"window_goto_tab": "",
"window_toggle_split": "",
"window_split_source_buffer": "",)RAW"
#ifdef __APPLE__
R"RAW(
R"RAW(
"window_toggle_full_screen": "<primary><control>f",)RAW"
#else
R"RAW(
R"RAW(
"window_toggle_full_screen": "F11",)RAW"
#endif
R"RAW(
"window_toggle_directories": "",
"window_toggle_terminal": "",
"window_toggle_menu": "",
R"RAW(
"window_toggle_tabs": "",
"window_toggle_zen_mode": "",
"window_clear_terminal": ""
@ -241,6 +233,11 @@ const std::string default_config_file =
"libclang_comment": "Outputs diagnostics for new C/C++ buffers",
"libclang": false,
"language_server": false
},
"plugins": {
"enabled": true,
"path_comment": "Directory where plugins are loaded from.",
"path": "<juci_home_directory>/plugins"
}
}
)RAW";

159
src/filesystem.cpp

@ -2,14 +2,16 @@
#include "process.hpp"
#include "utility.hpp"
#include <algorithm>
#include <fstream>
#include <iostream>
#include <sstream>
//Only use on small files
boost::optional<boost::filesystem::path> filesystem::rust_sysroot_path;
boost::optional<std::vector<boost::filesystem::path>> filesystem::executable_search_paths;
// Only use on small files
std::string filesystem::read(const std::string &path) {
std::string str;
std::ifstream input(path, std::ofstream::binary);
std::ifstream input(path, std::ios::binary);
if(input) {
input.seekg(0, std::ios::end);
auto size = input.tellg();
@ -21,9 +23,9 @@ std::string filesystem::read(const std::string &path) {
return str;
}
//Only use on small files
// Only use on small files
bool filesystem::write(const std::string &path, const std::string &new_content) {
std::ofstream output(path, std::ofstream::binary);
std::ofstream output(path, std::ios::binary);
if(output)
output << new_content;
else
@ -32,7 +34,7 @@ bool filesystem::write(const std::string &path, const std::string &new_content)
return true;
}
std::string filesystem::escape_argument(const std::string &argument) {
std::string filesystem::escape_argument(const std::string &argument) noexcept {
auto escaped = argument;
for(size_t pos = 0; pos < escaped.size(); ++pos) {
if(escaped[pos] == ' ' || escaped[pos] == '(' || escaped[pos] == ')' || escaped[pos] == '\'' || escaped[pos] == '"') {
@ -43,7 +45,7 @@ std::string filesystem::escape_argument(const std::string &argument) {
return escaped;
}
std::string filesystem::unescape_argument(const std::string &argument) {
std::string filesystem::unescape_argument(const std::string &argument) noexcept {
auto unescaped = argument;
if(unescaped.size() >= 2) {
@ -82,8 +84,8 @@ std::string filesystem::unescape_argument(const std::string &argument) {
return unescaped;
}
boost::filesystem::path filesystem::get_current_path() noexcept {
auto current_path = [] {
const boost::filesystem::path &filesystem::get_current_path() noexcept {
auto get_path = [] {
#ifdef _WIN32
boost::system::error_code ec;
auto path = boost::filesystem::current_path(ec);
@ -104,12 +106,12 @@ boost::filesystem::path filesystem::get_current_path() noexcept {
#endif
};
static boost::filesystem::path path = current_path();
static boost::filesystem::path path = get_path();
return path;
}
boost::filesystem::path filesystem::get_home_path() noexcept {
auto home_path = [] {
const boost::filesystem::path &filesystem::get_home_path() noexcept {
auto get_path = [] {
std::vector<std::string> environment_variables = {"HOME", "AppData"};
for(auto &variable : environment_variables) {
if(auto ptr = std::getenv(variable.c_str())) {
@ -122,16 +124,23 @@ boost::filesystem::path filesystem::get_home_path() noexcept {
return boost::filesystem::path();
};
static boost::filesystem::path path = home_path();
static boost::filesystem::path path = get_path();
return path;
}
boost::filesystem::path filesystem::get_rust_sysroot_path() noexcept {
auto rust_sysroot_path = [] {
const boost::filesystem::path &filesystem::get_rust_sysroot_path() noexcept {
auto get_path = [] {
std::string path;
TinyProcessLib::Process process("rustc --print sysroot", "", [&path](const char *buffer, size_t length) {
path += std::string(buffer, length);
});
TinyProcessLib::Config processConfig{};
#ifdef JUCI_FLATPAK_SANDBOX
processConfig.flatpak_spawn_host = true;
#endif
TinyProcessLib::Process process(
"rustc --print sysroot", "",
[&path](const char *buffer, size_t length) {
path += std::string(buffer, length);
},
[](const char *buffer, size_t n) {}, false, processConfig);
if(process.get_exit_status() == 0) {
while(!path.empty() && (path.back() == '\n' || path.back() == '\r'))
path.pop_back();
@ -140,8 +149,9 @@ boost::filesystem::path filesystem::get_rust_sysroot_path() noexcept {
return boost::filesystem::path();
};
static boost::filesystem::path path = rust_sysroot_path();
return path;
if(!rust_sysroot_path)
rust_sysroot_path = get_path();
return *rust_sysroot_path;
}
boost::filesystem::path filesystem::get_short_path(const boost::filesystem::path &path) noexcept {
@ -173,13 +183,13 @@ boost::filesystem::path filesystem::get_long_path(const boost::filesystem::path
#endif
}
bool filesystem::file_in_path(const boost::filesystem::path &file_path, const boost::filesystem::path &path) {
bool filesystem::file_in_path(const boost::filesystem::path &file_path, const boost::filesystem::path &path) noexcept {
if(std::distance(file_path.begin(), file_path.end()) < std::distance(path.begin(), path.end()))
return false;
return std::equal(path.begin(), path.end(), file_path.begin());
}
boost::filesystem::path filesystem::find_file_in_path_parents(const std::string &file_name, const boost::filesystem::path &path) {
boost::filesystem::path filesystem::find_file_in_path_parents(const std::string &file_name, const boost::filesystem::path &path) noexcept {
auto current_path = path;
boost::system::error_code ec;
while(true) {
@ -248,35 +258,38 @@ boost::filesystem::path filesystem::get_executable(const boost::filesystem::path
return executable_name;
#endif
static std::vector<boost::filesystem::path> bin_paths = {"/usr/bin", "/usr/local/bin"};
try {
for(auto &path : bin_paths) {
if(boost::filesystem::exists(path / executable_name))
for(auto &path : get_executable_search_paths()) {
if(is_executable(path / executable_name))
return executable_name;
}
auto &executable_name_str = executable_name.string();
for(auto &path : bin_paths) {
boost::filesystem::path executable;
for(boost::filesystem::directory_iterator it(path), end; it != end; ++it) {
auto it_path = it->path();
auto it_path_filename_str = it_path.filename().string();
if(starts_with(it_path_filename_str, executable_name_str)) {
if(it_path > executable &&
((it_path_filename_str.size() > executable_name_str.size() &&
it_path_filename_str[executable_name_str.size()] >= '0' &&
it_path_filename_str[executable_name_str.size()] <= '9') ||
(it_path_filename_str.size() > executable_name_str.size() + 1 &&
it_path_filename_str[executable_name_str.size()] == '-' &&
it_path_filename_str[executable_name_str.size() + 1] >= '0' &&
it_path_filename_str[executable_name_str.size() + 1] <= '9')) &&
!boost::filesystem::is_directory(it_path))
executable = it_path;
auto executable_name_str = executable_name.string();
for(auto &folder : get_executable_search_paths()) {
boost::filesystem::path latest_executable;
std::string latest_version;
for(boost::filesystem::directory_iterator it(folder), end; it != end; ++it) {
const auto &file = it->path();
auto filename = file.filename().string();
if(starts_with(filename, executable_name_str)) {
if(filename.size() > executable_name_str.size() && filename[executable_name_str.size()] >= '0' && filename[executable_name_str.size()] <= '9' && is_executable(file)) {
auto version = filename.substr(executable_name_str.size());
if(version_compare(version, latest_version) > 0) {
latest_executable = file;
latest_version = version;
}
}
else if(filename.size() > executable_name_str.size() + 1 && filename[executable_name_str.size()] == '-' && filename[executable_name_str.size() + 1] >= '0' && filename[executable_name_str.size() + 1] <= '9' && is_executable(file)) {
auto version = filename.substr(executable_name_str.size() + 1);
if(version_compare(version, latest_version) > 0) {
latest_executable = file;
latest_version = version;
}
}
}
}
if(!executable.empty())
return executable;
if(!latest_executable.empty())
return latest_executable;
}
}
catch(...) {
@ -286,12 +299,19 @@ boost::filesystem::path filesystem::get_executable(const boost::filesystem::path
}
// Based on https://stackoverflow.com/a/11295568
const std::vector<boost::filesystem::path> &filesystem::get_executable_search_paths() {
auto executable_search_paths = [] {
const std::vector<boost::filesystem::path> &filesystem::get_executable_search_paths() noexcept {
auto get_paths = [] {
std::vector<boost::filesystem::path> paths;
const std::string env = getenv("PATH");
auto c_env = std::getenv("PATH");
if(!c_env)
return paths;
const std::string env = c_env;
#ifdef _WIN32
const char delimiter = ';';
#else
const char delimiter = ':';
#endif
size_t previous = 0;
size_t pos;
@ -304,21 +324,21 @@ const std::vector<boost::filesystem::path> &filesystem::get_executable_search_pa
return paths;
};
static std::vector<boost::filesystem::path> paths = executable_search_paths();
return paths;
if(!executable_search_paths)
executable_search_paths = get_paths();
return *executable_search_paths;
}
boost::filesystem::path filesystem::find_executable(const std::string &executable_name) {
boost::filesystem::path filesystem::find_executable(const std::string &executable_name) noexcept {
for(auto &path : get_executable_search_paths()) {
boost::system::error_code ec;
auto executable_path = path / executable_name;
if(boost::filesystem::exists(executable_path, ec))
if(is_executable(executable_path))
return executable_path;
}
return boost::filesystem::path();
}
std::string filesystem::get_uri_from_path(const boost::filesystem::path &path) {
std::string filesystem::get_uri_from_path(const boost::filesystem::path &path) noexcept {
std::string uri{"file://"};
static auto hex_chars = "0123456789ABCDEF";
@ -332,10 +352,15 @@ std::string filesystem::get_uri_from_path(const boost::filesystem::path &path) {
uri += chr;
}
#ifdef _WIN32
if(uri.size() > 9 && ((uri[7] >= 'a' && uri[7] <= 'z') || (uri[7] >= 'A' && uri[7] <= 'Z')) && uri[8] == ':' && uri[9] == '/')
uri.insert(7, "/");
#endif
return uri;
}
boost::filesystem::path filesystem::get_path_from_uri(const std::string &uri) {
boost::filesystem::path filesystem::get_path_from_uri(const std::string &uri) noexcept {
std::string encoded;
if(starts_with(uri, "file://"))
@ -357,10 +382,17 @@ boost::filesystem::path filesystem::get_path_from_uri(const std::string &uri) {
unencoded += encoded[i];
}
#ifdef _WIN32
if(unencoded.size() > 3 && unencoded[0] == '/' && ((unencoded[1] >= 'a' && unencoded[1] <= 'z') || (unencoded[1] >= 'A' && unencoded[1] <= 'Z')) && unencoded[2] == ':' && unencoded[3] == '/') {
unencoded.erase(0, 1);
unencoded[0] = std::toupper(unencoded[0]);
}
#endif
return unencoded;
}
boost::filesystem::path filesystem::get_canonical_path(const boost::filesystem::path &path) {
boost::filesystem::path filesystem::get_canonical_path(const boost::filesystem::path &path) noexcept {
try {
return boost::filesystem::canonical(path);
}
@ -368,3 +400,18 @@ boost::filesystem::path filesystem::get_canonical_path(const boost::filesystem::
return path;
}
}
bool filesystem::is_executable(const boost::filesystem::path &path) noexcept {
if(path.empty())
return false;
boost::system::error_code ec;
#ifdef _WIN32
// Cannot for sure identify executable files in MSYS2
if(boost::filesystem::exists(path, ec))
return !boost::filesystem::is_directory(path, ec);
auto filename = path.filename().string() + ".exe";
return boost::filesystem::exists(path.has_parent_path() ? path.parent_path() / filename : filename, ec);
#else
return boost::filesystem::exists(path, ec) && !boost::filesystem::is_directory(path, ec) && boost::filesystem::status(path, ec).permissions() & (boost::filesystem::perms::owner_exe | boost::filesystem::perms::group_exe | boost::filesystem::perms::others_exe);
#endif
}

37
src/filesystem.hpp

@ -1,5 +1,7 @@
#pragma once
#include <boost/filesystem.hpp>
#include <boost/optional.hpp>
#include <fstream>
#include <string>
#include <vector>
@ -13,43 +15,50 @@ public:
static bool write(const std::string &path) { return write(path, ""); };
static bool write(const boost::filesystem::path &path) { return write(path, ""); };
static std::string escape_argument(const std::string &argument);
static std::string unescape_argument(const std::string &argument);
static std::string escape_argument(const std::string &argument) noexcept;
static std::string unescape_argument(const std::string &argument) noexcept;
/// Does not resolve symbolic links. Returns empty path on failure.
static boost::filesystem::path get_current_path() noexcept;
static const boost::filesystem::path &get_current_path() noexcept;
/// Returns empty path on failure
static boost::filesystem::path get_home_path() noexcept;
static const boost::filesystem::path &get_home_path() noexcept;
/// Returns empty path on failure
static boost::filesystem::path get_rust_sysroot_path() noexcept;
static const boost::filesystem::path &get_rust_sysroot_path() noexcept;
/// Set to {} to reset get_rust_sysroot_path
static boost::optional<boost::filesystem::path> rust_sysroot_path;
/// Replaces home path with ~
static boost::filesystem::path get_short_path(const boost::filesystem::path &path) noexcept;
/// Replaces ~ with home path (boost::filesystem does not recognize ~)
static boost::filesystem::path get_long_path(const boost::filesystem::path &path) noexcept;
static bool file_in_path(const boost::filesystem::path &file_path, const boost::filesystem::path &path);
static boost::filesystem::path find_file_in_path_parents(const std::string &file_name, const boost::filesystem::path &path);
static bool file_in_path(const boost::filesystem::path &file_path, const boost::filesystem::path &path) noexcept;
static boost::filesystem::path find_file_in_path_parents(const std::string &file_name, const boost::filesystem::path &path) noexcept;
/// Return path with dot, dot-dot and directory separator elements removed
/// Returns path with dot, dot-dot and directory separator elements removed
static boost::filesystem::path get_normal_path(const boost::filesystem::path &path) noexcept;
static boost::filesystem::path get_relative_path(const boost::filesystem::path &path, const boost::filesystem::path &base) noexcept;
static boost::filesystem::path get_absolute_path(const boost::filesystem::path &path, const boost::filesystem::path &base) noexcept;
/// Return executable with latest version in filename on systems that is lacking executable_name symbolic link
/// Returns executable name with latest version in filename on systems that is lacking executable_name symbolic link
static boost::filesystem::path get_executable(const boost::filesystem::path &executable_name) noexcept;
static const std::vector<boost::filesystem::path> &get_executable_search_paths();
static const std::vector<boost::filesystem::path> &get_executable_search_paths() noexcept;
/// Set to {} to reset get_executable_search_paths
static boost::optional<std::vector<boost::filesystem::path>> executable_search_paths;
/// Returns full executable path if found, or empty path otherwise.
static boost::filesystem::path find_executable(const std::string &executable_name);
static boost::filesystem::path find_executable(const std::string &executable_name) noexcept;
/// Get uri from path
static std::string get_uri_from_path(const boost::filesystem::path &path);
static std::string get_uri_from_path(const boost::filesystem::path &path) noexcept;
/// Get path from file uri
static boost::filesystem::path get_path_from_uri(const std::string &uri);
static boost::filesystem::path get_path_from_uri(const std::string &uri) noexcept;
/// Returns path on error. Do not use boost::filesystem::canonical_path since it is bugged when current_folder() fails.
static boost::filesystem::path get_canonical_path(const boost::filesystem::path &path);
static boost::filesystem::path get_canonical_path(const boost::filesystem::path &path) noexcept;
/// Platform independent check if path is executable
static bool is_executable(const boost::filesystem::path &path) noexcept;
};

65
src/git.cpp

@ -38,7 +38,7 @@ Git::Repository::Diff::Lines Git::Repository::Diff::get_lines(const std::string
LockGuard lock(mutex);
error.code = git_diff_blob_to_buffer(
blob.get(), nullptr, buffer.c_str(), buffer.size(), nullptr, &options, nullptr, nullptr, [](const git_diff_delta *delta, const git_diff_hunk *hunk, void *payload) {
//Based on https://github.com/atom/git-diff/blob/master/lib/git-diff-view.coffee
// Based on https://github.com/atom/git-diff/blob/master/lib/git-diff-view.coffee
auto lines = static_cast<Lines *>(payload);
auto start = hunk->new_start - 1;
auto end = hunk->new_start + hunk->new_lines - 1;
@ -268,3 +268,66 @@ boost::filesystem::path Git::path(const char *cpath, boost::optional<size_t> cpa
else
return std::string(cpath, cpath_length);
}
void Git::init_module(py::module &api) {
py::class_<Git> git(api, "Git");
py::class_<Git::Error>(git, "Error")
.def("__bool__", &Git::Error::operator bool)
.def_readwrite("code", &Git::Error::code)
;
py::class_<Git::Repository> repository(git, "Repository");
py::class_<Git::Repository::Diff> diff(repository, "Diff");
py::class_<Git::Repository::Diff::Lines>(diff, "Lines")
.def_readwrite("added", &Git::Repository::Diff::Lines::added)
.def_readwrite("modified", &Git::Repository::Diff::Lines::modified)
.def_readwrite("removed", &Git::Repository::Diff::Lines::removed)
;
py::class_<Git::Repository::Diff::Hunk>(repository, "Lines")
.def(py::init<int, int, int, int>(),
py::arg("old_start"),
py::arg("old_size"),
py::arg("new_start"),
py::arg("new_size"))
.def_readwrite("old_lines", &Git::Repository::Diff::Hunk::old_lines)
.def_readwrite("new_lines", &Git::Repository::Diff::Hunk::new_lines)
;
diff
.def("get_lines", &Git::Repository::Diff::get_lines,
py::arg("buffer"))
.def_static("get_hunks", &Git::Repository::Diff::get_hunks,
py::arg("old_buffer"),
py::arg("new_buffer"))
.def("get_details", &Git::Repository::Diff::get_details,
py::arg("buffer"),
py::arg("line_nr"))
;
py::class_<Git::Repository::Status>(repository, "Status")
.def_readwrite("added", &Git::Repository::Status::added)
.def_readwrite("modified", &Git::Repository::Status::modified)
;
repository
.def("get_status", &Git::Repository::get_status)
.def("clear_saved_status", &Git::Repository::clear_saved_status)
.def("get_work_path", &Git::Repository::get_work_path)
.def_static("get_root_path", &Git::Repository::get_root_path,
py::arg("path"))
.def("get_diff", &Git::Repository::get_diff,
py::arg("path"))
.def("get_branch", &Git::Repository::get_branch)
;
git
.def_static("get_repository", &Git::get_repository,
py::arg("path"))
;
}

6
src/git.hpp

@ -8,6 +8,7 @@
#include <memory>
#include <unordered_set>
#include <vector>
#include "python_bind.h"
class Git {
friend class Repository;
@ -91,16 +92,17 @@ public:
private:
static bool initialized GUARDED_BY(mutex);
///Mutex for thread safe operations
/// Mutex for thread safe operations
static Mutex mutex;
static Error error GUARDED_BY(mutex);
///Call initialize in public static methods
/// Call initialize in public static methods
static void initialize() noexcept REQUIRES(mutex);
static boost::filesystem::path path(const char *cpath, boost::optional<size_t> cpath_length = {}) noexcept REQUIRES(mutex);
public:
static std::shared_ptr<Repository> get_repository(const boost::filesystem::path &path);
static void init_module(py::module &api);
};

9
src/info.cpp

@ -11,9 +11,9 @@ Info::Info() {
get_style_context()->add_class("juci_info");
//Workaround from https://bugzilla.gnome.org/show_bug.cgi?id=710888
//Issue described at the same issue report
//TODO: remove later
// Workaround from https://bugzilla.gnome.org/show_bug.cgi?id=710888
// Issue described at the same issue report
// TODO: remove later
auto revealer = gtk_widget_get_template_child(GTK_WIDGET(gobj()), GTK_TYPE_INFO_BAR, "revealer");
if(revealer) {
gtk_revealer_set_transition_type(GTK_REVEALER(revealer), GTK_REVEALER_TRANSITION_TYPE_NONE);
@ -21,9 +21,10 @@ Info::Info() {
}
}
void Info::print(const std::string &text) {
timeout_connection.disconnect();
//Timeout based on https://en.wikipedia.org/wiki/Words_per_minute
// Timeout based on https://en.wikipedia.org/wiki/Words_per_minute
//(average_words_per_minute*average_letters_per_word)/60 => (228*4.5)/60 = 17.1
double timeout = 1000.0 * std::max(3.0, 1.0 + text.size() / 17.1);
timeout_connection = Glib::signal_timeout().connect(

497
src/json.cpp

@ -0,0 +1,497 @@
#include "nlohmann/json.hpp"
#include "json.hpp"
#include <algorithm>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <sstream>
std::string JSON::escape_string(std::string string) {
for(size_t c = 0; c < string.size(); ++c) {
if(string[c] == '\b') {
string.replace(c, 1, "\\b");
++c;
}
else if(string[c] == '\f') {
string.replace(c, 1, "\\f");
++c;
}
else if(string[c] == '\n') {
string.replace(c, 1, "\\n");
++c;
}
else if(string[c] == '\r') {
string.replace(c, 1, "\\r");
++c;
}
else if(string[c] == '\t') {
string.replace(c, 1, "\\t");
++c;
}
else if(string[c] == '"') {
string.replace(c, 1, "\\\"");
++c;
}
else if(string[c] == '\\') {
string.replace(c, 1, "\\\\");
++c;
}
}
return string;
}
JSON::JSON(StructureType type) noexcept : ptr(type == StructureType::object ? new nlohmann::ordered_json() : new nlohmann::ordered_json(nlohmann::ordered_json::array())), owner(true) {}
JSON::JSON(const std::string &string) : ptr(new nlohmann::ordered_json(nlohmann::ordered_json::parse(string))), owner(true) {}
JSON::JSON(const char *c_str) : ptr(new nlohmann::ordered_json(nlohmann::ordered_json::parse(c_str))), owner(true) {}
JSON::JSON(std::istream &istream) : ptr(new nlohmann::ordered_json(nlohmann::ordered_json::parse(istream))), owner(true) {}
JSON::JSON(const boost::filesystem::path &path) {
std::ifstream input(path.string(), std::ios::binary);
if(!input)
throw std::runtime_error("could not open file " + path.string());
ptr = new nlohmann::ordered_json(nlohmann::ordered_json::parse(input));
owner = true;
}
JSON::JSON(JSON &&other) noexcept : ptr(other.ptr), owner(other.owner) {
other.owner = false;
}
JSON &JSON::operator=(JSON &&other) noexcept {
if(owner)
delete ptr;
ptr = other.ptr;
owner = other.owner;
other.owner = false;
return *this;
}
JSON::~JSON() {
if(owner)
delete ptr;
}
JSON JSON::make_owner(JSON &&other) noexcept {
auto owner = JSON(new nlohmann::ordered_json(std::move(*other.ptr)));
owner.owner = true;
other.owner = false;
return owner;
}
std::ostream &operator<<(std::ostream &os, const JSON &json) {
return os << *json.ptr;
}
std::string JSON::to_string(int indent) const {
return ptr->dump(indent);
}
void JSON::to_file(const boost::filesystem::path &path, int indent) const {
std::ofstream file(path.string(), std::ios::binary);
if(!file)
throw std::runtime_error("could not open file " + path.string());
if(indent != -1)
file << std::setw(indent);
file << *ptr << '\n';
}
void JSON::set(const std::string &key, std::string value) noexcept {
(*ptr)[key] = std::move(value);
}
void JSON::set(const std::string &key, const char *value) noexcept {
(*ptr)[key] = value;
}
void JSON::set(const std::string &key, long long value) noexcept {
(*ptr)[key] = value;
}
void JSON::set(const std::string &key, double value) noexcept {
(*ptr)[key] = value;
}
void JSON::set(const std::string &key, bool value) noexcept {
(*ptr)[key] = value;
}
void JSON::set(const std::string &key, JSON &&value) noexcept {
(*ptr)[key] = std::move(*value.ptr);
value.owner = false;
}
void JSON::set(const std::string &key, const JSON &value) noexcept {
(*ptr)[key] = *value.ptr;
}
void JSON::remove(const std::string &key) noexcept {
ptr->erase(key);
}
void JSON::emplace_back(JSON &&value) {
ptr->emplace_back(std::move(*value.ptr));
value.owner = false;
}
boost::optional<JSON> JSON::child_optional(const std::string &key) const noexcept {
try {
return child(key);
}
catch(...) {
return {};
}
}
JSON JSON::child(const std::string &key) const {
return JSON(&ptr->at(key));
}
boost::optional<std::vector<std::pair<std::string, JSON>>> JSON::children_optional(const std::string &key) const noexcept {
try {
return children(key);
}
catch(...) {
return {};
}
}
std::vector<std::pair<std::string, JSON>> JSON::children_or_empty(const std::string &key) const noexcept {
try {
return children(key);
}
catch(...) {
return {};
}
}
std::vector<std::pair<std::string, JSON>> JSON::children(const std::string &key) const {
return JSON(&ptr->at(key)).children();
}
boost::optional<std::vector<std::pair<std::string, JSON>>> JSON::children_optional() const noexcept {
try {
return children();
}
catch(...) {
return {};
}
}
std::vector<std::pair<std::string, JSON>> JSON::children_or_empty() const noexcept {
try {
return children();
}
catch(...) {
return {};
}
}
std::vector<std::pair<std::string, JSON>> JSON::children() const {
if(!ptr->is_object())
throw std::runtime_error("value '" + to_string() + "' is not an object");
std::vector<std::pair<std::string, JSON>> result;
for(auto it = ptr->begin(); it != ptr->end(); ++it)
result.emplace_back(it.key(), JSON(&*it));
return result;
}
boost::optional<JSON> JSON::object_optional(const std::string &key) const noexcept {
try {
return object(key);
}
catch(...) {
return {};
}
}
JSON JSON::object(const std::string &key) const {
return JSON(&ptr->at(key)).object();
}
boost::optional<JSON> JSON::object_optional() const noexcept {
try {
return object();
}
catch(...) {
return {};
}
}
JSON JSON::object() const {
if(!ptr->is_object())
throw std::runtime_error("value '" + to_string() + "' is not an object");
return JSON(ptr);
}
boost::optional<std::vector<JSON>> JSON::array_optional(const std::string &key) const noexcept {
try {
return array(key);
}
catch(...) {
return {};
}
}
std::vector<JSON> JSON::array_or_empty(const std::string &key) const noexcept {
try {
return array(key);
}
catch(...) {
return {};
}
}
std::vector<JSON> JSON::array(const std::string &key) const {
return JSON(&ptr->at(key)).array();
}
boost::optional<std::vector<JSON>> JSON::array_optional() const noexcept {
try {
return array();
}
catch(...) {
return {};
}
}
std::vector<JSON> JSON::array_or_empty() const noexcept {
try {
return array();
}
catch(...) {
return {};
}
}
std::vector<JSON> JSON::array() const {
if(!ptr->is_array())
throw std::runtime_error("value '" + to_string() + "' is not an array");
std::vector<JSON> result;
result.reserve(ptr->size());
for(auto &e : *ptr)
result.emplace_back(&e);
return result;
}
boost::optional<std::string> JSON::string_optional(const std::string &key) const noexcept {
try {
return string(key);
}
catch(...) {
return {};
}
}
std::string JSON::string_or(const std::string &key, const std::string &default_value) const noexcept {
try {
return string(key);
}
catch(...) {
return default_value;
}
}
std::string JSON::string(const std::string &key) const {
return ptr->at(key).get<std::string>();
}
boost::optional<std::string> JSON::string_optional() const noexcept {
try {
return string();
}
catch(...) {
return {};
}
}
std::string JSON::string_or(const std::string &default_value) const noexcept {
try {
return string();
}
catch(...) {
return default_value;
}
}
std::string JSON::string() const {
return ptr->get<std::string>();
}
boost::optional<long long> JSON::integer_optional(const std::string &key, ParseOptions parse_options) const noexcept {
try {
return integer(key, parse_options);
}
catch(...) {
return {};
}
}
long long JSON::integer_or(const std::string &key, long long default_value, ParseOptions parse_options) const noexcept {
try {
return integer(key, parse_options);
}
catch(...) {
return default_value;
}
}
long long JSON::integer(const std::string &key, ParseOptions parse_options) const {
return JSON(&ptr->at(key)).integer(parse_options);
}
boost::optional<long long> JSON::integer_optional(ParseOptions parse_options) const noexcept {
try {
return integer(parse_options);
}
catch(...) {
return {};
}
}
long long JSON::integer_or(long long default_value, ParseOptions parse_options) const noexcept {
try {
return integer(parse_options);
}
catch(...) {
return default_value;
}
}
long long JSON::integer(ParseOptions parse_options) const {
if(auto integer = ptr->get<nlohmann::ordered_json::number_integer_t *>())
return *integer;
if(auto floating_point = ptr->get<nlohmann::ordered_json::number_float_t *>())
return *floating_point;
if(parse_options == ParseOptions::accept_string) {
if(auto string = ptr->get<nlohmann::ordered_json::string_t *>()) {
try {
return std::stoll(*string);
}
catch(...) {
}
}
}
throw std::runtime_error("value '" + to_string() + "' could not be converted to integer");
}
boost::optional<double> JSON::floating_point_optional(const std::string &key, ParseOptions parse_options) const noexcept {
try {
return floating_point(key, parse_options);
}
catch(...) {
return {};
}
}
double JSON::floating_point_or(const std::string &key, double default_value, ParseOptions parse_options) const noexcept {
try {
return floating_point(key, parse_options);
}
catch(...) {
return default_value;
}
}
double JSON::floating_point(const std::string &key, ParseOptions parse_options) const {
return JSON(&ptr->at(key)).floating_point(parse_options);
}
boost::optional<double> JSON::floating_point_optional(ParseOptions parse_options) const noexcept {
try {
return floating_point(parse_options);
}
catch(...) {
return {};
}
}
double JSON::floating_point_or(double default_value, ParseOptions parse_options) const noexcept {
try {
return floating_point(parse_options);
}
catch(...) {
return default_value;
}
}
double JSON::floating_point(ParseOptions parse_options) const {
if(auto floating_point = ptr->get<nlohmann::ordered_json::number_float_t *>())
return *floating_point;
if(auto integer = ptr->get<nlohmann::ordered_json::number_integer_t *>())
return *integer;
if(parse_options == ParseOptions::accept_string) {
if(auto string = ptr->get<nlohmann::ordered_json::string_t *>()) {
try {
return std::stof(*string);
}
catch(...) {
}
}
}
throw std::runtime_error("value '" + to_string() + "' could not be converted to floating point");
}
boost::optional<bool> JSON::boolean_optional(const std::string &key, ParseOptions parse_options) const noexcept {
try {
return boolean(key, parse_options);
}
catch(...) {
return {};
}
}
bool JSON::boolean_or(const std::string &key, bool default_value, ParseOptions parse_options) const noexcept {
try {
return boolean(key, parse_options);
}
catch(...) {
return default_value;
}
}
bool JSON::boolean(const std::string &key, ParseOptions parse_options) const {
return JSON(&ptr->at(key)).boolean(parse_options);
}
boost::optional<bool> JSON::boolean_optional(ParseOptions parse_options) const noexcept {
try {
return boolean(parse_options);
}
catch(...) {
return {};
}
}
bool JSON::boolean_or(bool default_value, ParseOptions parse_options) const noexcept {
try {
return boolean(parse_options);
}
catch(...) {
return default_value;
}
}
bool JSON::boolean(ParseOptions parse_options) const {
if(auto boolean = ptr->get<nlohmann::ordered_json::boolean_t *>())
return *boolean;
if(auto integer = ptr->get<nlohmann::ordered_json::number_integer_t *>()) {
if(*integer == 1)
return true;
if(*integer == 0)
return false;
}
if(parse_options == ParseOptions::accept_string) {
if(auto string = ptr->get<nlohmann::ordered_json::string_t *>()) {
if(*string == "true" || *string == "1")
return true;
if(*string == "false" || *string == "0")
return false;
}
}
throw std::runtime_error("value '" + to_string() + "' could not be converted to bool");
}

128
src/json.hpp

@ -0,0 +1,128 @@
#pragma once
#include "nlohmann/json_fwd.hpp"
#include <boost/filesystem.hpp>
#include <boost/optional.hpp>
#include <ostream>
class JSON {
nlohmann::ordered_json *ptr;
bool owner;
public:
static std::string escape_string(std::string string);
enum class ParseOptions { none = 0,
accept_string };
enum class StructureType { object = 0,
array };
/// Create an empty structure (null) or array
JSON(StructureType type = StructureType::object)
noexcept;
explicit JSON(nlohmann::ordered_json *json_ptr) noexcept : ptr(json_ptr), owner(false) {}
explicit JSON(const std::string &string);
explicit JSON(const char *c_str);
explicit JSON(std::istream &istream);
explicit JSON(const boost::filesystem::path &path);
JSON(JSON &&other)
noexcept;
JSON &operator=(JSON &&other) noexcept;
~JSON();
static JSON make_owner(JSON &&other) noexcept;
/// Use for instance std::setw(2) prior to this call to enable pretty printing.
friend std::ostream &operator<<(std::ostream &os, const JSON &json);
/// Set indent to for instance 2 to enable pretty printing.
std::string to_string(int indent = -1) const;
/// Set indent to for instance 2 to enable pretty printing.
void to_file(const boost::filesystem::path &path, int indent = -1) const;
void set(const std::string &key, std::string value) noexcept;
void set(const std::string &key, const char *value) noexcept;
void set(const std::string &key, int value) noexcept {
set(key, static_cast<long long>(value));
}
void set(const std::string &key, long value) noexcept {
set(key, static_cast<long long>(value));
}
void set(const std::string &key, long long value) noexcept;
void set(const std::string &key, unsigned value) noexcept {
set(key, static_cast<long long>(value));
}
void set(const std::string &key, unsigned long value) noexcept {
set(key, static_cast<long long>(value));
}
void set(const std::string &key, unsigned long long value) noexcept {
set(key, static_cast<long long>(value));
}
void set(const std::string &key, float value) noexcept {
set(key, static_cast<double>(value));
}
void set(const std::string &key, double value) noexcept;
void set(const std::string &key, bool value) noexcept;
void set(const std::string &key, JSON &&value) noexcept;
void set(const std::string &key, const JSON &value) noexcept;
/// Might invalidate JSON references returned by children(), if one of the these elements are removed.
void remove(const std::string &key) noexcept;
void emplace_back(JSON &&value);
boost::optional<JSON> child_optional(const std::string &key) const noexcept;
JSON child(const std::string &key) const;
boost::optional<std::vector<std::pair<std::string, JSON>>> children_optional(const std::string &key) const noexcept;
std::vector<std::pair<std::string, JSON>> children_or_empty(const std::string &key) const noexcept;
std::vector<std::pair<std::string, JSON>> children(const std::string &key) const;
boost::optional<std::vector<std::pair<std::string, JSON>>> children_optional() const noexcept;
std::vector<std::pair<std::string, JSON>> children_or_empty() const noexcept;
std::vector<std::pair<std::string, JSON>> children() const;
boost::optional<JSON> object_optional(const std::string &key) const noexcept;
JSON object(const std::string &key) const;
boost::optional<JSON> object_optional() const noexcept;
JSON object() const;
boost::optional<std::vector<JSON>> array_optional(const std::string &key) const noexcept;
std::vector<JSON> array_or_empty(const std::string &key) const noexcept;
std::vector<JSON> array(const std::string &key) const;
boost::optional<std::vector<JSON>> array_optional() const noexcept;
std::vector<JSON> array_or_empty() const noexcept;
std::vector<JSON> array() const;
boost::optional<std::string> string_optional(const std::string &key) const noexcept;
std::string string_or(const std::string &key, const std::string &default_value) const noexcept;
std::string string(const std::string &key) const;
boost::optional<std::string> string_optional() const noexcept;
std::string string_or(const std::string &default_value) const noexcept;
std::string string() const;
boost::optional<long long> integer_optional(const std::string &key, ParseOptions parse_options = ParseOptions::none) const noexcept;
long long integer_or(const std::string &key, long long default_value, ParseOptions parse_options = ParseOptions::none) const noexcept;
long long integer(const std::string &key, ParseOptions parse_options = ParseOptions::none) const;
boost::optional<long long> integer_optional(ParseOptions parse_options = ParseOptions::none) const noexcept;
long long integer_or(long long default_value, ParseOptions parse_options = ParseOptions::none) const noexcept;
long long integer(ParseOptions parse_options = ParseOptions::none) const;
boost::optional<double> floating_point_optional(const std::string &key, ParseOptions parse_options = ParseOptions::none) const noexcept;
double floating_point_or(const std::string &key, double default_value, ParseOptions parse_options = ParseOptions::none) const noexcept;
double floating_point(const std::string &key, ParseOptions parse_options = ParseOptions::none) const;
boost::optional<double> floating_point_optional(ParseOptions parse_options = ParseOptions::none) const noexcept;
double floating_point_or(double default_value, ParseOptions parse_options = ParseOptions::none) const noexcept;
double floating_point(ParseOptions parse_options = ParseOptions::none) const;
boost::optional<bool> boolean_optional(const std::string &key, ParseOptions parse_options = ParseOptions::none) const noexcept;
bool boolean_or(const std::string &key, bool default_value, ParseOptions parse_options = ParseOptions::none) const noexcept;
bool boolean(const std::string &key, ParseOptions parse_options = ParseOptions::none) const;
boost::optional<bool> boolean_optional(ParseOptions parse_options = ParseOptions::none) const noexcept;
bool boolean_or(bool default_value, ParseOptions parse_options = ParseOptions::none) const noexcept;
bool boolean(ParseOptions parse_options = ParseOptions::none) const;
};

30
src/juci.cpp

@ -4,6 +4,7 @@
#include "filesystem.hpp"
#include "menu.hpp"
#include "notebook.hpp"
#include "plugins.h"
#include "terminal.hpp"
#include "utility.hpp"
#include "window.hpp"
@ -38,7 +39,7 @@ int Application::on_command_line(const Glib::RefPtr<Gio::ApplicationCommandLine>
else if(boost::filesystem::is_directory(path, ec))
directories.emplace_back(path);
}
//Open new file if parent path exists
// Open new file if parent path exists
else {
if(path.is_absolute() && boost::filesystem::is_directory(path.parent_path(), ec))
files.emplace_back(path, 0);
@ -53,11 +54,10 @@ int Application::on_command_line(const Glib::RefPtr<Gio::ApplicationCommandLine>
void Application::on_activate() {
std::vector<std::pair<int, int>> file_offsets;
std::string current_file;
boost::filesystem::path current_file;
Window::get().init();
Window::get().load_session(directories, files, file_offsets, current_file, directories.empty() && files.empty());
Window::get().add_widgets();
add_window(Window::get());
Window::get().show();
@ -121,20 +121,18 @@ void Application::on_startup() {
Gtk::Application::on_startup();
Menu::get().build();
if(!Menu::get().juci_menu || !Menu::get().window_menu) {
std::cerr << "Menu not found." << std::endl;
}
else {
if(Menu::get().juci_menu)
set_app_menu(Menu::get().juci_menu);
if(!Menu::get().window_menu)
std::cerr << "Menu not found." << std::endl;
else
set_menubar(Menu::get().window_menu);
}
}
Application::Application() : Gtk::Application("no.sout.juci", Gio::APPLICATION_NON_UNIQUE | Gio::APPLICATION_HANDLES_COMMAND_LINE) {
Application::Application(Plugins &p) : Gtk::Application("no.sout.juci", Gio::APPLICATION_NON_UNIQUE | Gio::APPLICATION_HANDLES_COMMAND_LINE), window(p) {
Glib::set_application_name("juCi++");
//Gtk::MessageDialog without buttons caused text to be selected, this prevents that
// Gtk::MessageDialog without buttons caused text to be selected, this prevents that
Gtk::Settings::get_default()->property_gtk_label_select_on_focus() = false;
}
@ -142,5 +140,11 @@ int main(int argc, char *argv[]) {
#ifndef _WIN32
signal(SIGPIPE, SIG_IGN); // Do not terminate application when writing to a process fails
#endif
return Application().run(argc, argv);
auto &config = Config::get();
config.load();
Plugins plugins(config);
if(Config::get().plugins.enabled) {
plugins.load();
}
return Application(plugins).run(argc, argv);
}

9
src/juci.hpp

@ -1,4 +1,7 @@
#pragma once
#include "window.hpp"
#include <boost/filesystem.hpp>
#include <gtkmm.h>
/**
\mainpage
juCi++ is a lightweight C++ IDE written in C++
@ -25,12 +28,9 @@
[juCi++] --> [tiny-process-library] : use
\enduml
*/
#include <boost/filesystem.hpp>
#include <gtkmm.h>
class Application : public Gtk::Application {
public:
Application();
Application(Plugins &p);
int on_command_line(const Glib::RefPtr<Gio::ApplicationCommandLine> &cmd) override;
void on_activate() override;
void on_startup() override;
@ -39,4 +39,5 @@ private:
std::vector<boost::filesystem::path> directories;
std::vector<std::pair<boost::filesystem::path, size_t>> files;
std::vector<std::string> errors;
Window window;
};

151
src/menu.cpp

@ -1,8 +1,48 @@
#include "menu.hpp"
#include "config.hpp"
#include "terminal.hpp"
#include <iostream>
#include <pybind11/functional.h>
#include <string>
const Glib::ustring juci_section_xml = R"RAW(<section>
<item>
<attribute name='label' translatable='yes'>_About</attribute>
<attribute name='action'>app.about</attribute>
</item>
</section>
<section>
<item>
<attribute name='label' translatable='yes'>_Preferences</attribute>
<attribute name='action'>app.preferences</attribute>
</item>
</section>
<section>
<item>
<attribute name='label' translatable='yes'>_Snippets</attribute>
<attribute name='action'>app.snippets</attribute>
</item>
<item>
<attribute name='label' translatable='yes'>_Commands</attribute>
<attribute name='action'>app.commands</attribute>
</item>
</section>
<section>
<item>
<attribute name='label' translatable='yes'>_Quit</attribute>
<attribute name='action'>app.quit</attribute>
</item>
</section>
)RAW";
#ifdef __APPLE__
const Glib::ustring juci_menu_xml = "<menu id='juci-menu'>" + juci_section_xml + "</menu>";
const Glib::ustring juci_submenu_xml = "";
#else
const Glib::ustring juci_menu_xml = "";
const Glib::ustring juci_submenu_xml = "<submenu><attribute name='label' translatable='no'>juCi++</attribute>" + juci_section_xml + "</submenu>";
#endif
const Glib::ustring menu_xml = R"RAW(<interface>
<menu id='right-click-line-menu'>
<section>
@ -86,38 +126,11 @@ const Glib::ustring menu_xml = R"RAW(<interface>
</item>
</section>
</menu>
<menu id='juci-menu'>
<section>
<item>
<attribute name='label' translatable='yes'>_About</attribute>
<attribute name='action'>app.about</attribute>
</item>
</section>
<section>
<item>
<attribute name='label' translatable='yes'>_Preferences</attribute>
<attribute name='action'>app.preferences</attribute>
</item>
</section>
<section>
<item>
<attribute name='label' translatable='yes'>_Snippets</attribute>
<attribute name='action'>app.snippets</attribute>
</item>
<item>
<attribute name='label' translatable='yes'>_Commands</attribute>
<attribute name='action'>app.commands</attribute>
</item>
</section>
<section>
<item>
<attribute name='label' translatable='yes'>_Quit</attribute>
<attribute name='action'>app.quit</attribute>
</item>
</section>
</menu>
)RAW" + juci_menu_xml + R"RAW(
<menu id='window-menu'>
)RAW" + juci_submenu_xml + R"RAW(
<submenu>
<attribute name='label' translatable='yes'>_File</attribute>
<section>
@ -155,6 +168,16 @@ const Glib::ustring menu_xml = R"RAW(<interface>
<attribute name='action'>app.file_open_folder</attribute>
</item>
</section>
<section>
<item>
<attribute name='label' translatable='yes'>_Find _File</attribute>
<attribute name='action'>app.file_find_file</attribute>
</item>
<item>
<attribute name='label' translatable='yes'>_Switch _File _Type</attribute>
<attribute name='action'>app.file_switch_file_type</attribute>
</item>
</section>
<section>
<item>
<attribute name='label' translatable='yes'>_Reload _File</attribute>
@ -184,6 +207,10 @@ const Glib::ustring menu_xml = R"RAW(<interface>
<attribute name='label' translatable='yes'>_Close _Project</attribute>
<attribute name='action'>app.file_close_project</attribute>
</item>
<item>
<attribute name='label' translatable='yes'>_Close _Other _Files</attribute>
<attribute name='action'>app.file_close_other_files</attribute>
</item>
</section>
<section>
<item>
@ -318,10 +345,6 @@ const Glib::ustring menu_xml = R"RAW(<interface>
</submenu>
</section>
<section>
<item>
<attribute name='label' translatable='yes'>_Find _File</attribute>
<attribute name='action'>app.source_find_file</attribute>
</item>
<item>
<attribute name='label' translatable='yes'>_Find _Symbol</attribute>
<attribute name='action'>app.source_find_symbol</attribute>
@ -533,6 +556,10 @@ const Glib::ustring menu_xml = R"RAW(<interface>
<attribute name='label' translatable='yes'>_Toggle _Terminal</attribute>
<attribute name='action'>app.window_toggle_terminal</attribute>
</item>
<item>
<attribute name='label' translatable='yes'>_Toggle _Status</attribute>
<attribute name='action'>app.window_toggle_status</attribute>
</item>
<item>
<attribute name='label' translatable='yes'>_Toggle _Menu</attribute>
<attribute name='action'>app.window_toggle_menu</attribute>
@ -558,21 +585,38 @@ const Glib::ustring menu_xml = R"RAW(<interface>
)RAW";
void Menu::add_action(const std::string &name, const std::function<void()> &action) {
auto g_application = g_application_get_default();
auto gio_application = Glib::wrap(g_application, true);
auto application = Glib::RefPtr<Gtk::Application>::cast_static(gio_application);
actions[name] = application->add_action(name, action);
actions[name] = Glib::RefPtr<Gtk::Application>::cast_dynamic(Gtk::Application::get_default())->add_action(name, action);
}
void Menu::set_keys() {
auto g_application = g_application_get_default();
auto gio_application = Glib::wrap(g_application, true);
auto application = Glib::RefPtr<Gtk::Application>::cast_static(gio_application);
auto application = Glib::RefPtr<Gtk::Application>::cast_dynamic(Gtk::Application::get_default());
accelerators_with_multiple_actions.clear();
for(auto &action_and_key : Config::get().menu.keys) {
auto it = actions.find(action_and_key.first);
if(it != actions.end()) {
if(!action_and_key.second.empty()) {
application->set_accel_for_action("app." + action_and_key.first, action_and_key.second);
for(auto &key : Config::get().menu.keys) {
if(key.second.size() > 0 && actions.find(key.first) != actions.end())
application->set_accel_for_action("app." + key.first, key.second);
guint key = 0;
GdkModifierType modifier = static_cast<GdkModifierType>(0);
gtk_accelerator_parse(action_and_key.second.c_str(), &key, &modifier);
if(key == 0 && modifier == 0)
Terminal::get().async_print("\e[31mError\e[m: could not parse key string \"" + action_and_key.second + "\" for action " + action_and_key.first + "\n", true);
else
accelerators_with_multiple_actions[std::make_pair(key, modifier)].emplace_back(it->second);
}
else
application->unset_accels_for_action("app." + action_and_key.first);
}
}
for(auto it = accelerators_with_multiple_actions.begin(); it != accelerators_with_multiple_actions.end();) {
if(it->second.size() < 2)
it = accelerators_with_multiple_actions.erase(it);
else
++it;
}
}
@ -590,7 +634,20 @@ void Menu::build() {
ptr = Glib::RefPtr<Gio::Menu>::cast_dynamic(object);
right_click_selected_menu = std::make_unique<Gtk::Menu>(ptr);
}
catch(const Glib::Error &ex) {
std::cerr << "building menu failed: " << ex.what();
catch(const Glib::Error &e) {
std::cerr << "building menu failed: " << e.what();
}
}
void Menu::init_module(py::module &api) {
// TODO bind glib members
py::class_<Menu, std::unique_ptr<Menu, py::nodelete>>(api, "Menu")
.def(py::init([] { return &Menu::get(); }))
.def("add_action", &Menu::add_action,
py::arg("name"),
py::arg("action"))
.def("set_keys", &Menu::set_keys)
.def("build", &Menu::build)
;
}

3
src/menu.hpp

@ -1,4 +1,5 @@
#pragma once
#include "python_bind.h"
#include <functional>
#include <gtkmm.h>
#include <string>
@ -15,6 +16,7 @@ public:
void add_action(const std::string &name, const std::function<void()> &action);
std::unordered_map<std::string, Glib::RefPtr<Gio::SimpleAction>> actions;
std::map<std::pair<guint, GdkModifierType>, std::vector<Glib::RefPtr<Gio::SimpleAction>>> accelerators_with_multiple_actions;
void set_keys();
void build();
@ -24,6 +26,7 @@ public:
std::unique_ptr<Gtk::Menu> right_click_line_menu;
std::unique_ptr<Gtk::Menu> right_click_selected_menu;
std::function<void()> toggle_menu_items = [] {};
static void init_module(py::module &api);
private:
Glib::RefPtr<Gtk::Builder> builder;

26
src/meson.cpp

@ -3,20 +3,21 @@
#include "config.hpp"
#include "dialog.hpp"
#include "filesystem.hpp"
#include "json.hpp"
#include "terminal.hpp"
#include "utility.hpp"
#include <boost/optional.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <fstream>
#include <future>
#include <regex>
Meson::Meson(const boost::filesystem::path &path) {
const auto find_project = [](const boost::filesystem::path &file_path) {
std::ifstream input(file_path.string(), std::ofstream::binary);
std::ifstream input(file_path.string(), std::ios::binary);
if(input) {
std::string line;
while(std::getline(input, line)) {
const static std::regex project_regex("^ *project *\\(.*", std::regex::icase | std::regex::optimize);
const static std::regex project_regex("^ *project *\\(.*\r?$", std::regex::icase | std::regex::optimize);
std::smatch sm;
if(std::regex_match(line, sm, project_regex))
return true;
@ -134,7 +135,7 @@ boost::filesystem::path Meson::get_executable(const boost::filesystem::path &bui
for(auto &command : compile_commands.commands) {
auto source_file = filesystem::get_normal_path(command.file);
auto values = command.parameter_values("-o");
auto values = command.get_argument_values("-o");
if(!values.empty()) {
size_t pos;
if((pos = values[0].find('@')) != std::string::npos) {
@ -156,18 +157,17 @@ boost::filesystem::path Meson::get_executable(const boost::filesystem::path &bui
}
if(best_match_executable.empty()) { // Newer Meson outputs intro-targets.json that can be used to find executable
boost::property_tree::ptree pt;
try {
boost::property_tree::json_parser::read_json((build_path / "meson-info" / "intro-targets.json").string(), pt);
for(auto &target : pt) {
if(target.second.get<std::string>("type") == "executable") {
auto filenames = target.second.get_child("filename");
JSON targets(build_path / "meson-info" / "intro-targets.json");
for(auto &target : targets.array()) {
if(target.string("type") == "executable") {
auto filenames = target.array("filename");
if(filenames.empty()) // No executable file found
break;
auto executable = filesystem::get_normal_path(filenames.begin()->second.get<std::string>(""));
for(auto &target_source : target.second.get_child("target_sources")) {
for(auto &source : target_source.second.get_child("sources")) {
auto source_file = filesystem::get_normal_path(source.second.get<std::string>(""));
auto executable = filesystem::get_normal_path(filenames.begin()->string());
for(auto &target_source : target.array("target_sources")) {
for(auto &source : target_source.array("sources")) {
auto source_file = filesystem::get_normal_path(source.string());
if(source_file == file_path)
return executable;
auto source_file_directory = source_file.parent_path();

214
src/notebook.cpp

@ -1,15 +1,19 @@
#include "notebook.hpp"
#include "config.hpp"
#include "dialog.hpp"
#include "directories.hpp"
#include "filesystem.hpp"
#include "project.hpp"
#include "selection_dialog.hpp"
#include "source_clang.hpp"
#include "source_generic.hpp"
#include "source_language_protocol.hpp"
#include "utility.hpp"
#include <fstream>
#include <gtksourceview-3.0/gtksourceview/gtksourcemap.h>
#include <regex>
Notebook::TabLabel::TabLabel(const std::function<void()> &on_close) {
set_can_focus(false);
@ -66,6 +70,28 @@ Notebook::Notebook() : Gtk::Paned(), notebooks(2) {
});
}
pack1(notebooks[0], true, true);
if(filesystem::find_executable("rustup").empty()) {
// PATH might not be set (for instance after installation)
#ifdef _WIN32
boost::filesystem::path cargo_bin;
if(auto userprofile = std::getenv("USERPROFILE")) // rustup uses this environment variable on MSYS2 as well
cargo_bin = boost::filesystem::path(userprofile) / ".cargo" / "bin";
const char delimiter = ';';
#else
auto cargo_bin = filesystem::get_home_path() / ".cargo" / "bin";
const char delimiter = ':';
#endif
boost::system::error_code ec;
if(!cargo_bin.empty() && boost::filesystem::is_directory(cargo_bin, ec)) {
std::string env;
if(auto c_env = std::getenv("PATH"))
env = c_env;
Glib::setenv("PATH", !env.empty() ? env + delimiter + cargo_bin.string() : cargo_bin.string());
filesystem::rust_sysroot_path = {};
filesystem::executable_search_paths = {};
}
}
}
size_t Notebook::size() {
@ -89,7 +115,7 @@ Source::View *Notebook::get_current_view() {
if(view == current_view)
return view;
}
//In case there exist a tab that has not yet received focus again in a different notebook
// In case there exist a tab that has not yet received focus again in a different notebook
for(int notebook_index = 0; notebook_index < 2; ++notebook_index) {
auto page = notebooks[notebook_index].get_current_page();
if(page >= 0)
@ -123,6 +149,18 @@ bool Notebook::open(const boost::filesystem::path &file_path_, Position position
auto file_path = filesystem::get_normal_path(file_path_);
if(std::any_of(Config::get().project.open_with_default_application.begin(), Config::get().project.open_with_default_application.end(), [&file_path](const boost::filesystem::path &extension) {
return extension == file_path.extension();
})) {
#ifdef __APPLE__
Terminal::get().async_process("open " + filesystem::escape_argument(file_path.string()), "", nullptr, true);
#else
Terminal::get().async_process("xdg-open " + filesystem::escape_argument(file_path.string()), "", nullptr, true);
#endif
Directories::get().select(file_path);
return true;
}
if((position == Position::right || position == Position::split) && !split)
toggle_split();
@ -169,20 +207,75 @@ bool Notebook::open(const boost::filesystem::path &file_path_, Position position
}
size_t source_views_previous_size = source_views.size();
if(language_id == "chdr" || language_id == "cpphdr" || language_id == "c" || language_id == "cpp" || language_id == "objc")
source_views.emplace_back(new Source::ClangView(file_path, language));
if(language_id == "chdr" || language_id == "cpphdr" || language_id == "c" || language_id == "cpp" || language_id == "objc") {
if(!filesystem::find_executable("clang-language-server").empty())
source_views.emplace_back(new Source::LanguageProtocolView(file_path, language, language_protocol_language_id, "clang-language-server"));
else
source_views.emplace_back(new Source::ClangView(file_path, language));
}
else if(language && !language_protocol_language_id.empty() && !filesystem::find_executable(language_protocol_language_id + "-language-server").empty())
source_views.emplace_back(new Source::LanguageProtocolView(file_path, language, language_protocol_language_id, language_protocol_language_id + "-language-server"));
source_views.emplace_back(new Source::LanguageProtocolView(file_path, language, language_protocol_language_id, filesystem::escape_argument(language_protocol_language_id + "-language-server")));
else if(language && language_protocol_language_id == "rust") {
if(!filesystem::find_executable("rust-analyzer").empty())
source_views.emplace_back(new Source::LanguageProtocolView(file_path, language, language_protocol_language_id, "rust-analyzer"));
else {
auto sysroot = filesystem::get_rust_sysroot_path();
if(!sysroot.empty()) {
auto rust_analyzer = sysroot / "bin" / "rust-analyzer";
boost::system::error_code ec;
if(boost::filesystem::exists(rust_analyzer, ec))
source_views.emplace_back(new Source::LanguageProtocolView(file_path, language, language_protocol_language_id, rust_analyzer.string()));
if(filesystem::find_executable("rustup").empty())
install_rust();
if(!filesystem::find_executable("rustup").empty()) {
static auto rust_analyzer_installed = []() -> bool {
std::stringstream stdin_stream, stdout_stream;
auto exit_status = Terminal::get().process(stdin_stream, stdout_stream, "rustup component list --installed");
if(exit_status != 0)
return false;
std::string line;
while(std::getline(stdout_stream, line)) {
if(starts_with(line, "rust-analyzer"))
return true;
}
return false;
}();
if(rust_analyzer_installed)
source_views.emplace_back(new Source::LanguageProtocolView(file_path, language, language_protocol_language_id, filesystem::escape_argument((filesystem::get_rust_sysroot_path() / "bin" / "rust-analyzer").string())));
if(source_views_previous_size == source_views.size()) {
static bool show_dialog = true;
if(show_dialog) {
show_dialog = false;
// Install rust-analyzer
Gtk::MessageDialog dialog(*static_cast<Gtk::Window *>(get_toplevel()), "Install Rust language server?", false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO);
Gtk::Image image;
image.set_from_icon_name("dialog-question", Gtk::BuiltinIconSize::ICON_SIZE_DIALOG);
dialog.set_image(image);
dialog.set_default_response(Gtk::RESPONSE_YES);
dialog.set_secondary_text("Rust language server not found. Would you like to install rust-analyzer?");
dialog.show_all();
int result = dialog.run();
dialog.hide();
if(result == Gtk::RESPONSE_YES) {
bool canceled = false;
Dialog::Message message("Installing rust-analyzer", [&canceled] {
canceled = true;
});
boost::optional<int> exit_status;
std::string command = "rustup component add rust-analyzer";
Terminal::get().print("\e[2mRunning: " + command + "\e[m\n");
auto process = Terminal::get().async_process(command, "", [&exit_status](int exit_status_) {
exit_status = exit_status_;
});
bool killed = false;
while(!exit_status) {
if(canceled && !killed) {
process->kill();
killed = true;
show_dialog = true; // Show dialog again
}
while(Gtk::Main::events_pending())
Gtk::Main::iteration();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
if(exit_status == 0) {
Terminal::get().print("\e[32mSuccess\e[m: installed rust-analyzer\n");
rust_analyzer_installed = true;
source_views.emplace_back(new Source::LanguageProtocolView(file_path, language, language_protocol_language_id, filesystem::escape_argument((filesystem::get_rust_sysroot_path() / "bin" / "rust-analyzer").string())));
}
}
}
}
}
}
@ -202,7 +295,7 @@ bool Notebook::open(const boost::filesystem::path &file_path_, Position position
}
else if(language_id == "rust") {
auto rust_installed = !filesystem::get_rust_sysroot_path().empty();
Terminal::get().print(std::string("\e[33mWarning\e[m: could not find Rust ") + (rust_installed ? "language server" : "installation") + ".\n");
Terminal::get().print(std::string("\e[33mWarning\e[m: could not find Rust") + (rust_installed ? " language server" : "") + ".\n");
Terminal::get().print("For installation instructions please visit: https://gitlab.com/cppit/jucipp/-/blob/master/docs/language_servers.md#rust.\n");
if(!rust_installed)
Terminal::get().print("You will need to restart juCi++ after installing Rust.\n");
@ -265,8 +358,12 @@ bool Notebook::open(const boost::filesystem::path &file_path_, Position position
}
};
view->update_status_file_path = [this](Source::BaseView *view) {
if(get_current_view() == view)
status_file_path.set_text(' ' + filesystem::get_short_path(view->file_path).string());
if(get_current_view() == view) {
if(!Directories::get().path.empty() && filesystem::file_in_path(view->file_path, Directories::get().path))
status_file_path.set_text(' ' + filesystem::get_relative_path(view->file_path, Directories::get().path).string());
else
status_file_path.set_text(' ' + filesystem::get_short_path(view->file_path).string());
}
};
view->update_status_branch = [this](Source::BaseView *view) {
if(get_current_view() == view) {
@ -355,7 +452,7 @@ bool Notebook::open(const boost::filesystem::path &file_path_, Position position
configure(source_views.size() - 1);
//Set up tab label
// Set up tab label
tab_labels.emplace_back(new TabLabel([this, view]() {
close(view);
}));
@ -510,6 +607,67 @@ bool Notebook::open(const boost::filesystem::path &file_path_, Position position
return true;
}
void Notebook::install_rust() {
static bool show_dialog = true;
if(show_dialog) {
show_dialog = false;
Gtk::MessageDialog dialog(*static_cast<Gtk::Window *>(get_toplevel()), "Install Rust?", false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO);
Gtk::Image image;
image.set_from_icon_name("dialog-question", Gtk::BuiltinIconSize::ICON_SIZE_DIALOG);
dialog.set_image(image);
dialog.set_default_response(Gtk::RESPONSE_YES);
dialog.set_secondary_text("Rust not found. Would you like to install Rust?");
dialog.show_all();
int result = dialog.run();
dialog.hide();
if(result == Gtk::RESPONSE_YES) {
bool canceled = false;
Dialog::Message message("Installing Rust", [&canceled] {
canceled = true;
});
boost::optional<int> exit_status;
std::string command = "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y";
Terminal::get().print("\e[2mRunning: " + command + "\e[m\n");
auto process = Terminal::get().async_process(command, "", [&exit_status](int exit_status_) {
exit_status = exit_status_;
});
bool killed = false;
while(!exit_status) {
if(canceled && !killed) {
process->kill();
killed = true;
show_dialog = true; // Show dialog again
}
while(Gtk::Main::events_pending())
Gtk::Main::iteration();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
if(exit_status == 0) {
if(filesystem::find_executable("rustup").empty()) {
#ifdef _WIN32
boost::filesystem::path cargo_bin;
if(auto userprofile = std::getenv("USERPROFILE")) // rustup uses this environment variable on MSYS2 as well
cargo_bin = boost::filesystem::path(userprofile) / ".cargo" / "bin";
const char delimiter = ';';
#else
auto cargo_bin = filesystem::get_home_path() / ".cargo" / "bin";
const char delimiter = ':';
#endif
if(!cargo_bin.empty()) {
std::string env;
if(auto c_env = std::getenv("PATH"))
env = c_env;
Glib::setenv("PATH", !env.empty() ? env + delimiter + cargo_bin.string() : cargo_bin.string());
}
}
filesystem::rust_sysroot_path = {};
filesystem::executable_search_paths = {};
Terminal::get().print("\e[32mSuccess\e[m: installed Rust and updated PATH environment variable\n");
}
}
}
}
void Notebook::open_uri(const std::string &uri) {
#ifdef __APPLE__
Terminal::get().process("open " + filesystem::escape_argument(uri));
@ -666,8 +824,8 @@ void Notebook::toggle_split() {
pack2(notebooks[1], true, true);
set_position(get_width() / 2);
show_all();
//Make sure the position is correct
//TODO: report bug to gtk if it is not fixed in gtk3.22
// Make sure the position is correct
// TODO: report bug to gtk if it is not fixed in gtk3.22
Glib::signal_timeout().connect(
[this] {
set_position(get_width() / 2);
@ -766,17 +924,17 @@ void Notebook::set_current_view(Source::View *view) {
}
bool Notebook::save_modified_dialog(size_t index) {
Gtk::MessageDialog dialog(*static_cast<Gtk::Window *>(get_toplevel()), "Save file!", false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO);
Gtk::MessageDialog dialog(*static_cast<Gtk::Window *>(get_toplevel()), "Save file?", false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO);
Gtk::Image image;
image.set_from_icon_name("document-save", Gtk::BuiltinIconSize::ICON_SIZE_DIALOG);
dialog.set_image(image);
dialog.set_default_response(Gtk::RESPONSE_YES);
dialog.set_secondary_text("Do you want to save: " + get_view(index)->file_path.string() + " ?");
dialog.set_secondary_text("Do you want to save " + filesystem::get_short_path(get_view(index)->file_path).string() + "?");
dialog.show_all();
int result = dialog.run();
if(result == Gtk::RESPONSE_YES) {
if(result == Gtk::RESPONSE_YES)
return save(index);
}
else if(result == Gtk::RESPONSE_NO) {
else if(result == Gtk::RESPONSE_NO)
return true;
}
else {
return false;
}
return false;
}

3
src/notebook.hpp

@ -45,6 +45,7 @@ public:
};
bool open(Source::View *view);
bool open(const boost::filesystem::path &file_path, Position position = Position::infer);
void install_rust();
void open_uri(const std::string &uri);
void configure(size_t index);
bool save(size_t index);
@ -86,7 +87,7 @@ private:
/// Throws if view is not found
std::pair<size_t, int> get_notebook_page(Source::View *view);
std::vector<Source::View *> source_views; //Is NOT freed in destructor, this is intended for quick program exit.
std::vector<Source::View *> source_views; // Is NOT freed in destructor, this is intended for quick program exit.
std::vector<std::unique_ptr<Gtk::Widget>> source_maps;
std::vector<std::unique_ptr<Gtk::ScrolledWindow>> scrolled_windows;
std::vector<std::unique_ptr<Gtk::Box>> hboxes;

79
src/plugins.cc

@ -0,0 +1,79 @@
#include "plugins.h"
#include "config.hpp"
#include "python_module.h"
#include "terminal.hpp"
Plugins::Plugins(Config &config) : jucipp_module("Jucipp", Module::init_jucipp_module) {
#ifdef PYTHON_HOME_DIR
#ifdef _WIN32
const std::wstring python_home(PYTHON_HOME_DIR);
const std::wstring python_path(python_home + L";" + python_home + L"\\lib-dynload;" + python_home + L"\\site-packages");
Py_SetPythonHome(python_home.c_str());
Py_SetPath(python_path.c_str());
#endif
#endif
py::initialize_interpreter();
py::module::import("sys")
.attr("path")
.cast<py::list>()
.append(config.plugins.path);
}
Plugins::~Plugins() {
py::finalize_interpreter();
}
void Plugins::init_hook() {
for(auto &module_name : loaded_modules) {
auto module = py::module(py::handle(PyImport_GetModule(py::str(module_name.c_str()).ptr())), false);
if(py::hasattr(module, "init_hook")) {
py::object obj = module.attr("init_hook");
if(py::isinstance<py::function>(obj)) {
py::function func(obj);
try {
func();
}
catch(const py::error_already_set &err) {
std::cerr << err.what() << std::endl;
}
}
}
}
}
void Plugins::load() {
const auto &plugin_path = Config::get().plugins.path;
boost::system::error_code ec;
if(!boost::filesystem::exists(plugin_path, ec)) {
ec.clear();
boost::filesystem::create_directories(plugin_path, ec);
}
if(ec) {
std::cerr << ec.message() << std::endl;
return;
}
boost::filesystem::directory_iterator end_it;
for(boost::filesystem::directory_iterator it(plugin_path); it != end_it; it++) {
auto module_name = it->path().stem().string();
if(module_name.empty())
continue;
const auto is_directory = boost::filesystem::is_directory(it->path());
const auto has_py_extension = it->path().extension() == ".py";
const auto is_pycache = module_name == "__pycache__";
if((is_directory && !is_pycache) || has_py_extension) {
try {
auto module = py::module::import(module_name.c_str());
loaded_modules.push_back(module_name);
}
catch(py::error_already_set &error) {
std::cerr << "Error loading plugin `" << module_name << "`:\n"
<< error.what() << "\n";
}
}
}
}

18
src/plugins.h

@ -0,0 +1,18 @@
#pragma once
#include "config.hpp"
#include "python_bind.h"
#include <pybind11/embed.h>
class __attribute__((visibility("default")))
Plugins {
public:
Plugins(Config &config);
~Plugins();
void load();
void init_hook();
private:
py::detail::embedded_module jucipp_module;
std::vector<std::string> loaded_modules;
};

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save