From b75167c3c1d56d476abc06458a84ee84b7d907ca Mon Sep 17 00:00:00 2001 From: Asif Amin <122637911+asifamin13@users.noreply.github.com> Date: Mon, 30 Jan 2023 14:19:40 -0600 Subject: [PATCH] Bracket Colors Add bracketcolors plugin. Color {}, [], () based on nesting order Closes #1148 --- MAINTAINERS | 7 + Makefile.am | 4 + bracketcolors/AUTHORS | 1 + bracketcolors/COPYING | 339 +++++++ bracketcolors/ChangeLog | 0 bracketcolors/Makefile.am | 4 + bracketcolors/NEWS | 0 bracketcolors/README | 32 + bracketcolors/src/BracketMap.cc | 143 +++ bracketcolors/src/BracketMap.h | 76 ++ bracketcolors/src/Makefile.am | 15 + bracketcolors/src/bracketcolors.cc | 1386 ++++++++++++++++++++++++++++ build/ax_cxx_compile_stdcxx.m4 | 962 +++++++++++++++++++ build/ax_cxx_compile_stdcxx_17.m4 | 35 + build/bracketcolors.m4 | 31 + configure.ac | 2 + po/POTFILES.in | 3 + 17 files changed, 3040 insertions(+) create mode 100644 bracketcolors/AUTHORS create mode 100644 bracketcolors/COPYING create mode 100644 bracketcolors/ChangeLog create mode 100644 bracketcolors/Makefile.am create mode 100644 bracketcolors/NEWS create mode 100644 bracketcolors/README create mode 100644 bracketcolors/src/BracketMap.cc create mode 100644 bracketcolors/src/BracketMap.h create mode 100644 bracketcolors/src/Makefile.am create mode 100644 bracketcolors/src/bracketcolors.cc create mode 100644 build/ax_cxx_compile_stdcxx.m4 create mode 100644 build/ax_cxx_compile_stdcxx_17.m4 create mode 100644 build/bracketcolors.m4 diff --git a/MAINTAINERS b/MAINTAINERS index 90d8b8bef..64471f608 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -33,6 +33,13 @@ M: Pavel Roschin W: S: Maintained +bracketcolors +* P: Asif Amin +* g: @asifamin13 +* M: Asif Amin +* W: +* S: Maintained + codenav P: Federico Reghenzani g: diff --git a/Makefile.am b/Makefile.am index 2dc37e3a1..68693f102 100644 --- a/Makefile.am +++ b/Makefile.am @@ -21,6 +21,10 @@ if ENABLE_AUTOMARK SUBDIRS += automark endif +if ENABLE_BRACKETCOLORS +SUBDIRS += bracketcolors +endif + if ENABLE_CODENAV SUBDIRS += codenav endif diff --git a/bracketcolors/AUTHORS b/bracketcolors/AUTHORS new file mode 100644 index 000000000..284d02da0 --- /dev/null +++ b/bracketcolors/AUTHORS @@ -0,0 +1 @@ +Asif Amin diff --git a/bracketcolors/COPYING b/bracketcolors/COPYING new file mode 100644 index 000000000..d511905c1 --- /dev/null +++ b/bracketcolors/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/bracketcolors/ChangeLog b/bracketcolors/ChangeLog new file mode 100644 index 000000000..e69de29bb diff --git a/bracketcolors/Makefile.am b/bracketcolors/Makefile.am new file mode 100644 index 000000000..8b3dd53a5 --- /dev/null +++ b/bracketcolors/Makefile.am @@ -0,0 +1,4 @@ +include $(top_srcdir)/build/vars.auxfiles.mk + +SUBDIRS = src +plugin = bracketcolors diff --git a/bracketcolors/NEWS b/bracketcolors/NEWS new file mode 100644 index 000000000..e69de29bb diff --git a/bracketcolors/README b/bracketcolors/README new file mode 100644 index 000000000..f40ee099c --- /dev/null +++ b/bracketcolors/README @@ -0,0 +1,32 @@ +Color brackets, parenthesis, and braces +=================== + +.. contents:: + +About +----- + +This plugin enables bracket coloring features. Brackets are colored based on +nesting level + +Features +-------- + +* Color brackets for: { }, [ ], ( ) + +Usage +----- + +Install the plugin (https://plugins.geany.org/install.html) then +load it in Geany's plugin manager. + +Requirements +------------ + +* Geany >= 1.38 +* C++17 + +Contact developers +------------------ + +Asif Amin diff --git a/bracketcolors/src/BracketMap.cc b/bracketcolors/src/BracketMap.cc new file mode 100644 index 000000000..592838b30 --- /dev/null +++ b/bracketcolors/src/BracketMap.cc @@ -0,0 +1,143 @@ +/* + * BracketMap.cc + * + * Copyright 2013 Asif Amin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "BracketMap.h" + + +// ----------------------------------------------------------------------------- + BracketMap::BracketMap() +/* + Constructor +----------------------------------------------------------------------------- */ +{ + +} + + +// ----------------------------------------------------------------------------- + BracketMap::~BracketMap() +/* + Destructor +----------------------------------------------------------------------------- */ +{ + +} + + +// ----------------------------------------------------------------------------- + void BracketMap::Update(Index index, Length length) +/* + +----------------------------------------------------------------------------- */ +{ + auto it = mBracketMap.find(index); + if (it != mBracketMap.end()) { + auto &bracket = it->second; + GetLength(bracket) = length; + } + else { + mBracketMap.insert( + std::make_pair(index, std::make_tuple(length, 0)) + ); + } +} + + + +// ----------------------------------------------------------------------------- + void BracketMap::ComputeOrder() +/* + +----------------------------------------------------------------------------- */ +{ + std::stack orderStack; + + for (auto &it : mBracketMap) { + + const Index &startIndex = it.first; + Bracket &bracket = it.second; + Length length = GetLength(bracket); + Index endPos = startIndex + length; + + if (length == UNDEFINED) { + // Invalid brackets + GetOrder(bracket) = UNDEFINED; + continue; + } + + + if (orderStack.size() == 0) { + // First bracket + orderStack.push(endPos); + } + else if (startIndex > orderStack.top()) { + // not nested + while(orderStack.size() > 0 and orderStack.top() < startIndex) { + orderStack.pop(); + } + orderStack.push(endPos); + } + else { + // nested bracket + orderStack.push(endPos); + } + + GetOrder(bracket) = orderStack.size() - 1; + } +} + + + +// ----------------------------------------------------------------------------- + void BracketMap::Show() +/* + +----------------------------------------------------------------------------- */ +{ g_debug("%s: Showing bracket map ...", __FUNCTION__); + + for (const auto it : mBracketMap) { + + const Index &startIndex = it.first; + const Bracket &bracket = it.second; + + Length length = std::get<0>(bracket); + Order order = std::get<1>(bracket); + + Index end = -1; + if (length > 0) { + end = startIndex + length; + } + + g_debug( + "%s: Bracket at %d, Length: %d, End: %d, Order: %d", + __FUNCTION__, startIndex, length, end, order + ); + } + + g_debug("%s: ... Finished showing bracket map", __FUNCTION__); +} + diff --git a/bracketcolors/src/BracketMap.h b/bracketcolors/src/BracketMap.h new file mode 100644 index 000000000..55932315b --- /dev/null +++ b/bracketcolors/src/BracketMap.h @@ -0,0 +1,76 @@ +/* + * BracketMap.h + * + * Copyright 2013 Asif Amin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __BRACKET_MAP_H__ +#define __BRACKET_MAP_H__ + +#include +#include + +#include + + +// ----------------------------------------------------------------------------- + struct BracketMap +/* + Purpose: data structure which stores and computes nesting order +----------------------------------------------------------------------------- */ +{ + typedef gint Length, Order, Index; + typedef std::tuple Bracket; + std::map mBracketMap; + + BracketMap(); + ~BracketMap(); + + void Show(); + + void Update(Index index, Length length); + void ComputeOrder(); + + static const gint UNDEFINED = -1; + + static Length& GetLength(Bracket &bracket) { + return std::get<0>(bracket); + } + static Order& GetOrder(Bracket &bracket) { + return std::get<1>(bracket); + } + + static const Length& GetLength(const Bracket &bracket) { + return std::get<0>(bracket); + } + static const Order& GetOrder(const Bracket &bracket) { + return std::get<1>(bracket); + } +}; + + + + + + + + + + + + +#endif diff --git a/bracketcolors/src/Makefile.am b/bracketcolors/src/Makefile.am new file mode 100644 index 000000000..9e9afa949 --- /dev/null +++ b/bracketcolors/src/Makefile.am @@ -0,0 +1,15 @@ +include $(top_srcdir)/build/vars.build.mk +plugin = bracketcolors + +geanyplugins_LTLIBRARIES = bracketcolors.la + +bracketcolors_srcs = \ + bracketcolors.cc \ + BracketMap.cc \ + BracketMap.h + +bracketcolors_la_SOURCES = $(bracketcolors_srcs) +bracketcolors_la_CXXFLAGS = $(AM_CXXFLAGS) $(AM_CFLAGS) -DG_LOG_DOMAIN=\"BracketColors\" +bracketcolors_la_LIBADD = $(COMMONLIBS) + +include $(top_srcdir)/build/cppcheck.mk diff --git a/bracketcolors/src/bracketcolors.cc b/bracketcolors/src/bracketcolors.cc new file mode 100644 index 000000000..f00221b60 --- /dev/null +++ b/bracketcolors/src/bracketcolors.cc @@ -0,0 +1,1386 @@ +/* + * bracketcolors.cc + * + * Copyright 2013 Asif Amin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + + +/* --------------------------------- INCLUDES ------------------------------- */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#ifdef HAVE_LOCALE_H +# include +#endif + +#include +#include +#include +#include + +#include + +#include +#include "sciwrappers.h" + +#include "BracketMap.h" + +#define BC_NUM_COLORS 3 +#define BC_NO_ARG 0 +#define BC_STOP_ACTION TRUE +#define BC_CONTINUE_ACTION FALSE + +#define SSM(s, m, w, l) scintilla_send_message(s, m, w, l) + + +/* --------------------------------- CONSTANTS ------------------------------ */ + + typedef std::array BracketColorArray; + + /* + * These were copied from VS Code + * TODO: Make this user configurable, get from theme? + */ + + static const BracketColorArray sDarkBackgroundColors = { + "#FF00FF", "#FFFF00", "#00FFFF" + }; + + static const BracketColorArray sLightBackgroundColors = { + "#008000", "#000080", "#800000" + }; + + static const gchar *sPluginName = "bracketcolors"; + + // start index of indicators our plugin will use + static const guint sIndicatorIndex = INDICATOR_IME - BC_NUM_COLORS; + +/* ----------------------------------- TYPES -------------------------------- */ + + enum BracketType { + PAREN = 0, + BRACE, + BRACKET, + ANGLE, + COUNT + }; + + struct BracketColorsData { + + /* + * Associated with every document + */ + + GeanyDocument *doc; + + guint32 backgroundColor; + BracketColorArray bracketColors; + + gboolean init; + + guint computeTimeoutID, computeInterval; + guint drawTimeoutID; + + gboolean updateUI; + std::set recomputeIndicies, redrawIndicies; + + gboolean bracketColorsEnable[BracketType::COUNT]; + BracketMap bracketMaps[BracketType::COUNT]; + + BracketColorsData() : + doc(NULL), + bracketColors(sLightBackgroundColors), + init(FALSE), + computeTimeoutID(0), + computeInterval(500), + drawTimeoutID(0), + updateUI(FALSE) + { + for (guint i = 0; i < BracketType::COUNT; i++) { + bracketColorsEnable[i] = TRUE; + } + + /* + * color matching angle brackets seems to cause + * more confusion than its worth + */ + + bracketColorsEnable[BracketType::ANGLE] = FALSE; + } + + ~BracketColorsData() {} + + void RemoveFromQueues(BracketMap::Index index); + void StartTimers(); + void StopTimers(); + }; + +/* ---------------------------------- EXTERNS ------------------------------- */ + + GeanyPlugin *geany_plugin; + GeanyData *geany_data; + +/* --------------------------------- PROTOTYPES ----------------------------- */ + + static gboolean recompute_brackets_timeout(gpointer user_data); + static gboolean render_brackets_timeout(gpointer user_data); + +/* ------------------------------ IMPLEMENTATION ---------------------------- */ + + +// ----------------------------------------------------------------------------- + void BracketColorsData::StartTimers() + +/* + +----------------------------------------------------------------------------- */ +{ + if (computeTimeoutID == 0) { + computeTimeoutID = g_timeout_add_full( + G_PRIORITY_LOW, + 20, + recompute_brackets_timeout, + this, + NULL + ); + } + else { + //g_debug("%s: computeTimeoutID already set", __FUNCTION__); + } + + if (drawTimeoutID == 0) { + drawTimeoutID = g_timeout_add_full( + G_PRIORITY_LOW, + 100, + render_brackets_timeout, + this, + NULL + ); + } + else { + //g_debug("%s: drawTimeoutID already set", __FUNCTION__); + } +} + + + +// ----------------------------------------------------------------------------- + void BracketColorsData::StopTimers() + +/* + +----------------------------------------------------------------------------- */ +{ + if (computeTimeoutID > 0) { + g_source_remove(computeTimeoutID); + computeTimeoutID = 0; + } + else { + //g_debug("%s: computeTimeoutID already 0", __FUNCTION__); + } + + if (drawTimeoutID > 0) { + g_source_remove(drawTimeoutID); + drawTimeoutID = 0; + } + else { + //g_debug("%s: drawTimeoutID already 0", __FUNCTION__); + } +} + + + +// ----------------------------------------------------------------------------- + void BracketColorsData::RemoveFromQueues(BracketMap::Index index) + +/* + +----------------------------------------------------------------------------- */ +{ + { + auto it = recomputeIndicies.find(index); + if (it != recomputeIndicies.end()) { + recomputeIndicies.erase(it); + } + } + { + auto it = redrawIndicies.find(index); + if (it != redrawIndicies.end()) { + redrawIndicies.erase(it); + } + } +} + + +// ----------------------------------------------------------------------------- + static gboolean utils_is_dark(guint32 color) + +/* + +----------------------------------------------------------------------------- */ +{ + guint8 b = color >> 16; + guint8 g = color >> 8; + guint8 r = color; + + // https://stackoverflow.com/questions/596216/formula-to-determine-perceived-brightness-of-rgb-color + guint8 y = ((r << 1) + r + (g << 2) + b) >> 3; + + if (y < 125) { + return TRUE; + } + + return FALSE; +} + + + +// ----------------------------------------------------------------------------- + static gboolean utils_parse_color( + const gchar *spec, + GdkColor *color + ) +/* + +----------------------------------------------------------------------------- */ +{ + gchar buf[64] = {0}; + + g_return_val_if_fail(spec != NULL, -1); + + if (spec[0] == '0' && (spec[1] == 'x' || spec[1] == 'X')) + { + /* convert to # format for GDK to understand it */ + buf[0] = '#'; + strncpy(buf + 1, spec + 2, sizeof(buf) - 2); + spec = buf; + } + + return gdk_color_parse(spec, color); +} + + + +// ----------------------------------------------------------------------------- + static gint utils_color_to_bgr(const GdkColor *c) +/* + +----------------------------------------------------------------------------- */ +{ + g_return_val_if_fail(c != NULL, -1); + return (c->red / 256) | ((c->green / 256) << 8) | ((c->blue / 256) << 16); +} + + + +// ----------------------------------------------------------------------------- + static gint utils_parse_color_to_bgr(const gchar *spec) +/* + +----------------------------------------------------------------------------- */ +{ + GdkColor color; + if (utils_parse_color(spec, &color)) { + return utils_color_to_bgr(&color); + } + else { + return -1; + } +} + + + +// ----------------------------------------------------------------------------- + static gchar char_at(ScintillaObject *sci, gint pos) +/* + +----------------------------------------------------------------------------- */ +{ + return sci_get_char_at(sci, pos); +} + + + +// ----------------------------------------------------------------------------- + static void assign_indicator_colors( + BracketColorsData *data + ) +/* + +----------------------------------------------------------------------------- */ +{ + ScintillaObject *sci = data->doc->editor->sci; + + for (guint i = 0; i < data->bracketColors.size(); i++) { + guint index = sIndicatorIndex + i; + std::string spec = data->bracketColors.at(i); + gint color = utils_parse_color_to_bgr(spec.c_str()); + SSM(sci, SCI_INDICSETSTYLE, index, INDIC_TEXTFORE); + SSM(sci, SCI_INDICSETFORE, index, color); + } +} + + + +// ----------------------------------------------------------------------------- + static gboolean hasDocument(void) +/* + sanity check +----------------------------------------------------------------------------- */ +{ + GtkNotebook *notebook = GTK_NOTEBOOK(geany_data->main_widgets->notebook); + gint currPage = gtk_notebook_get_current_page(notebook); + return currPage >= 0 ? TRUE : FALSE; +} + + + +// ----------------------------------------------------------------------------- + static gboolean isCurrDocument( + BracketColorsData *data + ) +/* + check if this document is currently opened +----------------------------------------------------------------------------- */ +{ + GtkNotebook *notebook = GTK_NOTEBOOK(geany_data->main_widgets->notebook); + gint currPage = gtk_notebook_get_current_page(notebook); + GeanyDocument *currDoc = document_get_from_page(currPage); + + if (currDoc != NULL and currDoc == data->doc) { + return TRUE; + } + + return FALSE; +} + + + +// ----------------------------------------------------------------------------- + static void bracket_colors_data_free(gpointer data) +/* + +----------------------------------------------------------------------------- */ +{ + // g_debug( + // "%s: Purging a BracketColorsData", + // __FUNCTION__ + // ); + + BracketColorsData *bcd = reinterpret_cast(data); + delete bcd; +} + + + +// ----------------------------------------------------------------------------- + static BracketColorsData* bracket_colors_data_new(GeanyDocument *doc) +/* + +----------------------------------------------------------------------------- */ +{ + BracketColorsData *newBCD = new BracketColorsData(); + + plugin_set_document_data_full( + geany_plugin, + doc, + sPluginName, + newBCD, + bracket_colors_data_free + ); + + return newBCD; +} + + + +// ----------------------------------------------------------------------------- + static gboolean is_bracket_type( + gchar ch, + BracketType type + ) +/* + check if char is bracket type +----------------------------------------------------------------------------- */ +{ + static const std::set sAllBrackets { + '(', ')', + '[', ']', + '{', '}', + '<', '>', + }; + + switch (type) { + case (BracketType::PAREN): { + if (ch == '(' or ch == ')') { + return TRUE; + } + return FALSE; + } + case(BracketType::BRACE): { + if (ch == '[' or ch == ']') { + return TRUE; + } + return FALSE; + } + case(BracketType::BRACKET): { + if (ch == '{' or ch == '}') { + return TRUE; + } + return FALSE; + } + case(BracketType::ANGLE): { + if (ch == '<' or ch == '>') { + return TRUE; + } + return FALSE; + } + case(BracketType::COUNT): { + return sAllBrackets.find(ch) != sAllBrackets.end() ? TRUE : FALSE; + } + default: + return FALSE; + } + +} + + + +// ----------------------------------------------------------------------------- + static gboolean is_open_bracket( + gchar ch, + BracketType type + ) +/* + check if char is open bracket type +----------------------------------------------------------------------------- */ +{ + static const std::set sAllOpenBrackets { + '(', + '[', + '{', + '<', + }; + + switch (type) { + case (BracketType::PAREN): { + if (ch == '(') { + return TRUE; + } + return FALSE; + } + case(BracketType::BRACE): { + if (ch == '[') { + return TRUE; + } + return FALSE; + } + case(BracketType::BRACKET): { + if (ch == '{') { + return TRUE; + } + return FALSE; + } + case(BracketType::ANGLE): { + if (ch == '<') { + return TRUE; + } + return FALSE; + } + case(BracketType::COUNT): { + return sAllOpenBrackets.find(ch) != sAllOpenBrackets.end() ? TRUE : FALSE; + } + default: + return FALSE; + } + +} + + + +// ----------------------------------------------------------------------------- + static gint compute_bracket_at( + ScintillaObject *sci, + BracketMap &bracketMap, + gint position, + bool updateInvalidMapping = true + ) +/* + compute bracket at position +----------------------------------------------------------------------------- */ +{ + gint matchedBrace = SSM(sci, SCI_BRACEMATCH, position, BC_NO_ARG); + gint braceIdentity = position; + + if (matchedBrace != -1) { + + gint length = matchedBrace - position; + + // g_debug( + // "%s: bracket at %d matched at %d", + // __FUNCTION__, position, matchedBrace + // ); + + if (length > 0) { + // matched from start brace + bracketMap.Update(position, length); + } + else { + // matched from end brace + length = -length; + braceIdentity = position - length; + bracketMap.Update(braceIdentity, length); + } + } + else { + // invalid mapping + // g_debug("%s: bracket at %d invalid", __FUNCTION__, position); + + if (is_open_bracket(char_at(sci, position), BracketType::COUNT)) { + if (updateInvalidMapping) { + bracketMap.Update(position, BracketMap::UNDEFINED); + } + } + else { + // unknown start brace + braceIdentity = -1; + } + } + + return braceIdentity; +} + + + +// ----------------------------------------------------------------------------- + static gboolean isIgnoreStyle( + BracketColorsData *data, + gint position + ) +/* + check if position is part of non source section +----------------------------------------------------------------------------- */ +{ + ScintillaObject *sci = data->doc->editor->sci; + + gint style = SSM(sci, SCI_GETSTYLEAT, position, BC_NO_ARG); + gint lexer = sci_get_lexer(sci); + + return not highlighting_is_code_style(lexer, style); +} + + + +// ----------------------------------------------------------------------------- + static void find_all_brackets( + BracketColorsData &data + ) +/* + brute force search for brackets +----------------------------------------------------------------------------- */ +{ + ScintillaObject *sci = data.doc->editor->sci; + + gint length = sci_get_length(sci); + for (gint i = 0; i < length; i++) { + gchar ch = char_at(sci, i); + if (is_bracket_type(ch, BracketType::COUNT)) { + for (gint bracketType = 0; bracketType < BracketType::COUNT; bracketType++) { + if (data.bracketColorsEnable[bracketType] == TRUE) { + if (is_bracket_type(ch, static_cast(bracketType))) { + data.recomputeIndicies.insert(i); + data.updateUI = TRUE; + break; + } + } + } + } + } +} + + + +// ----------------------------------------------------------------------------- + static void remove_bc_indicators( + ScintillaObject *sci + ) +/* + remove indicators associated with this plugin +----------------------------------------------------------------------------- */ +{ + gint length = sci_get_length(sci); + for (gint i = 0; i < BC_NUM_COLORS; i++) { + SSM(sci, SCI_SETINDICATORCURRENT, sIndicatorIndex + i, BC_NO_ARG); + SSM(sci, SCI_INDICATORCLEARRANGE, 0, length); + } +} + + + +// ----------------------------------------------------------------------------- + static void set_bc_indicators_at( + ScintillaObject *sci, + const BracketColorsData &data, + gint index + ) +/* + assign indicator at position, check if already correct +----------------------------------------------------------------------------- */ +{ + for (gint i = 0; i < BracketType::COUNT; i++) { + + const BracketMap &bracketMap = data.bracketMaps[i]; + + auto it = bracketMap.mBracketMap.find(index); + if (it == bracketMap.mBracketMap.end()) { + continue; + } + + auto bracket = it->second; + + if (BracketMap::GetLength(bracket) != BracketMap::UNDEFINED) { + + std::array positions { + { index, index + BracketMap::GetLength(bracket) } + }; + + for (auto position : positions) { + + unsigned correctIndicatorIndex = sIndicatorIndex + \ + ((BracketMap::GetOrder(bracket) + i) % BC_NUM_COLORS); + + gint curr = SSM(sci, SCI_INDICATORVALUEAT, correctIndicatorIndex, position); + if (not curr) { + // g_debug( + // "%s: Setting indicator %d at %d", + // __FUNCTION__, + // correctIndicatorIndex, position + // ); + SSM( + sci, + SCI_SETINDICATORCURRENT, + correctIndicatorIndex, + BC_NO_ARG + ); + SSM(sci, SCI_INDICATORFILLRANGE, position, 1); + } + + // make sure there arent any other indicators at position + for ( + guint indicatorIndex = sIndicatorIndex; + indicatorIndex < sIndicatorIndex + BC_NUM_COLORS; + indicatorIndex++ + ) + { + if (indicatorIndex == correctIndicatorIndex) { + continue; + } + + gint hasIndicator = SSM(sci, SCI_INDICATORVALUEAT, indicatorIndex, position); + if (hasIndicator) { + SSM( + sci, + SCI_SETINDICATORCURRENT, + indicatorIndex, + BC_NO_ARG + ); + SSM(sci, SCI_INDICATORCLEARRANGE, position, 1); + } + } + } + } + } +} + + + + +// ----------------------------------------------------------------------------- + static void clear_bc_indicators( + ScintillaObject *sci, + gint position, gint length + ) +/* + clear bracket indicators in range +----------------------------------------------------------------------------- */ +{ + for (gint i = position; i < position + length; i++) { + for ( + guint indicatorIndex = sIndicatorIndex; + indicatorIndex < sIndicatorIndex + BC_NUM_COLORS; + indicatorIndex++ + ) + { + gint hasIndicator = SSM(sci, SCI_INDICATORVALUEAT, indicatorIndex, i); + // g_debug("%s: Indicator %d: %d", __FUNCTION__, indicatorIndex, hasIndicator); + if (hasIndicator) { + // g_debug("%s: Clearing bracket at %d", __FUNCTION__, i); + SSM( + sci, + SCI_SETINDICATORCURRENT, + indicatorIndex, + BC_NO_ARG + ); + SSM(sci, SCI_INDICATORCLEARRANGE, i, 1); + } + } + } +} + + + +// ----------------------------------------------------------------------------- + static gboolean move_brackets( + ScintillaObject *sci, + BracketColorsData &bracketColorsData, + gint position, gint length, + BracketType type + ) +/* + handle when text is added +----------------------------------------------------------------------------- */ +{ + if (bracketColorsData.bracketColorsEnable[type] == FALSE) { + return FALSE; + } + + BracketMap &bracketMap = bracketColorsData.bracketMaps[type]; + + std::set indiciesToAdjust, indiciesToRecompute; + + /* + * Look through existing bracket map and check if addition of characters + * will require adjustment + */ + + for (const auto &it : bracketMap.mBracketMap) { + const auto &bracket = it.second; + gint endPos = it.first + BracketMap::GetLength(bracket); + if (it.first >= position) { + indiciesToAdjust.insert(it.first); + } + else if ( + endPos >= position or + BracketMap::GetLength(bracket) == BracketMap::UNDEFINED + ) { + indiciesToRecompute.insert(it.first); + } + } + + gboolean madeChange = FALSE; + + // Check if the new characters that are added were brackets + for (gint i = position; i < position + length; i++) { + gchar newChar = char_at(sci, i); + if (is_bracket_type(newChar, type)) { + // g_debug("%s: Handling new bracket character", __FUNCTION__); + madeChange = TRUE; + bracketColorsData.recomputeIndicies.insert(i); + } + } + + // g_debug( + // "%s: Need to adjust %d brackets, recompute %d brackets", + // __FUNCTION__, indiciesToAdjust.size(), indiciesToRecompute.size() + // ); + + if (not indiciesToAdjust.size() and not indiciesToRecompute.size()) { + //g_debug("%s: Nothing to do", __FUNCTION__); + return madeChange; + } + + for (const auto &it : indiciesToRecompute) { + //g_debug("%s: Recomputing bracket at %d", __FUNCTION__, it); + bracketColorsData.recomputeIndicies.insert(it); + } + + for (const auto &it : indiciesToAdjust) { + //g_debug("%s: Moved brace at %d to %d", __FUNCTION__, it, it + length); + bracketMap.mBracketMap.insert( + std::make_pair( + it + length, + bracketMap.mBracketMap.at(it) + ) + ); + + bracketMap.mBracketMap.erase(it); + bracketColorsData.RemoveFromQueues(it); + } + + return TRUE; +} + + + +// ----------------------------------------------------------------------------- + static gboolean remove_brackets( + ScintillaObject *sci, + BracketColorsData &bracketColorsData, + gint position, gint length, + BracketType type + ) +/* + handle when text is removed +----------------------------------------------------------------------------- */ +{ + if (bracketColorsData.bracketColorsEnable[type] == FALSE) { + return FALSE; + } + + BracketMap &bracketMap = bracketColorsData.bracketMaps[type]; + + std::set indiciesToRemove, indiciesToRecompute; + + for (const auto &it : bracketMap.mBracketMap) { + const auto &bracket = it.second; + gint endPos = it.first + BracketMap::GetLength(bracket); + // start bracket was deleted + if ( (it.first >= position) and (it.first < position + length) ) { + indiciesToRemove.insert(it.first); + } + // end bracket removed or space removed + else if (it.first >= position or endPos >= position) { + indiciesToRecompute.insert(it.first); + } + } + + // g_debug( + // "%s: Need to remove %d brackets, adjust %d brackets", + // __FUNCTION__, indiciesToRemove.size(), indiciesToRecompute.size() + // ); + + if ( + not indiciesToRemove.size() and + not indiciesToRecompute.size() + ) { + //g_debug("%s: Nothing to do", __FUNCTION__); + return FALSE; + } + + for (const auto &it : indiciesToRemove) { + //g_debug("%s: Removing brace at %d", __FUNCTION__, it); + bracketMap.mBracketMap.erase(it); + bracketColorsData.RemoveFromQueues(it); + } + + for (const auto &it : indiciesToRecompute) { + // first bracket was moved backwards + if (it >= position) { + //g_debug("%s: Moved brace at %d to %d", __FUNCTION__, it, it - length); + bracketMap.mBracketMap.insert( + std::make_pair( + it - length, + bracketMap.mBracketMap.at(it) + ) + ); + bracketMap.mBracketMap.erase(it); + bracketColorsData.RemoveFromQueues(it); + } + // last bracket was moved + else { + //g_debug("%s: Recomputing bracket at %d", __FUNCTION__, it); + bracketColorsData.recomputeIndicies.insert(it); + } + } + + return TRUE; +} + + + +// ----------------------------------------------------------------------------- + static void render_document( + ScintillaObject *sci, + BracketColorsData *data + ) +/* + +----------------------------------------------------------------------------- */ +{ + if (data->updateUI) { + // g_debug( + // "%s: Need to update %d indicies", + // __FUNCTION__, data->redrawIndicies.size() + // ); + + for ( + auto position = data->redrawIndicies.begin(); + position != data->redrawIndicies.end(); + position++ + ) + { + // if this bracket has been reinserted into the work queue, ignore + if (data->recomputeIndicies.find(*position) == data->recomputeIndicies.end()) { + set_bc_indicators_at(sci, *data, *position); + } + } + + data->redrawIndicies.clear(); + data->updateUI = FALSE; + } +} + + + +// ----------------------------------------------------------------------------- + static void on_sci_notify( + ScintillaObject *sci, + gint scn, + SCNotification *nt, + gpointer user_data + ) +/* + +----------------------------------------------------------------------------- */ +{ + BracketColorsData *data = reinterpret_cast(user_data); + + switch(nt->nmhdr.code) { + + case(SCN_UPDATEUI): { + + if (nt->updated & SC_UPDATE_CONTENT) { + + if (isCurrDocument(data)) { + render_document(sci, data); + } + } + + break; + } + + case(SCN_MODIFIED): + { + if (nt->modificationType & SC_MOD_INSERTTEXT) { + // g_debug( + // "%s: Text added. Position: %d, Length: %d", + // __FUNCTION__, nt->position, nt->length + // ); + + // if we insert into position that had bracket + clear_bc_indicators(sci, nt->position, nt->length); + + /* + * Check to adjust current bracket positions + */ + + for (gint bracketType = 0; bracketType < BracketType::COUNT; bracketType++) { + if ( + move_brackets( + sci, + *data, + nt->position, nt->length, + static_cast(bracketType) + ) + ) { + data->updateUI = TRUE; + } + } + } + + if (nt->modificationType & SC_MOD_DELETETEXT) { + // g_debug( + // "%s: Text removed. Position: %d, Length: %d", + // __FUNCTION__, nt->position, nt->length + // ); + + for (gint bracketType = 0; bracketType < BracketType::COUNT; bracketType++) { + if ( + remove_brackets( + sci, + *data, + nt->position, nt->length, + static_cast(bracketType) + ) + ) { + data->updateUI = TRUE; + } + } + } + + if (nt->modificationType & SC_MOD_CHANGESTYLE) { + // g_debug( + // "%s: Style change. Position: %d, Length: %d", + // __FUNCTION__, nt->position, nt->length + // ); + + if (data->init == TRUE) { + for (gint bracketType = 0; bracketType < BracketType::COUNT; bracketType++) { + if (data->bracketColorsEnable[bracketType] == FALSE) { + continue; + } + for (gint i = nt->position; i < nt->position + nt->length; i++) { + gchar currChar = char_at(sci, i); + if (is_bracket_type(currChar, static_cast(bracketType))) { + //g_debug("%s: Handling style change for bracket at %d", __FUNCTION__, i); + data->recomputeIndicies.insert(i); + } + } + } + } + } + + break; + } + } +} + + + +// ----------------------------------------------------------------------------- + gboolean render_brackets_timeout( + gpointer user_data + ) +/* + +----------------------------------------------------------------------------- */ +{ + if (not hasDocument()) { + return FALSE; + } + + BracketColorsData *data = reinterpret_cast(user_data); + + if (not isCurrDocument(data)) { + data->StopTimers(); + return FALSE; + } + + /* + * check if background color changed + */ + + ScintillaObject *sci = data->doc->editor->sci; + guint32 currBGColor = SSM(sci, SCI_STYLEGETBACK, STYLE_DEFAULT, BC_NO_ARG); + if (currBGColor != data->backgroundColor) { + g_debug("%s: background color changed: %#04x", __FUNCTION__, currBGColor); + + gboolean currDark = utils_is_dark(currBGColor); + gboolean wasDark = utils_is_dark(data->backgroundColor); + + if (currDark != wasDark) { + g_debug("%s: Need to change colors scheme!", __FUNCTION__); + data->bracketColors = currDark ? sDarkBackgroundColors : sLightBackgroundColors; + assign_indicator_colors(data); + } + + data->backgroundColor = currBGColor; + } + + if (data->updateUI) { + render_document(sci, data); + } + + return TRUE; +} + + + +// ----------------------------------------------------------------------------- + gboolean recompute_brackets_timeout( + gpointer user_data + ) +/* + +----------------------------------------------------------------------------- */ +{ + static const unsigned sIterationLimit = 50; + + if (not hasDocument()) { + return FALSE; + } + + BracketColorsData *data = reinterpret_cast(user_data); + if (not isCurrDocument(data)) { + data->StopTimers(); + return FALSE; + } + + if (data->init == FALSE) { + find_all_brackets(*data); + data->init = TRUE; + } + + if (data->recomputeIndicies.size()) { + + ScintillaObject *sci = data->doc->editor->sci; + + // g_debug( + // "%s: have to recompute %d indicies", + // __FUNCTION__, data->recomputeIndicies.size() + // ); + + unsigned numIterations = 0; + for ( + auto position = data->recomputeIndicies.begin(); + position != data->recomputeIndicies.end(); + numIterations++ + ) + { + for (gint bracketType = 0; bracketType < BracketType::COUNT; bracketType++) { + + BracketMap &bracketMap = data->bracketMaps[bracketType]; + + if ( + is_bracket_type( + char_at(sci, *position), + static_cast(bracketType) + ) + ) { + // check if in a comment + if (isIgnoreStyle(data, *position)) { + bracketMap.mBracketMap.erase(*position); + clear_bc_indicators(sci, *position, 1); + } + else { + gint brace = compute_bracket_at(sci, bracketMap, *position); + if (brace >= 0) { + data->redrawIndicies.insert(brace); + } + data->updateUI = TRUE; + } + } + + bracketMap.ComputeOrder(); + } + + position = data->recomputeIndicies.erase(position); + if (numIterations >= sIterationLimit) { + break; + } + } + } + + return TRUE; +} + + + +// ----------------------------------------------------------------------------- + static void on_document_close( + GObject *obj, + GeanyDocument *doc, + gpointer user_data + ) +/* + +----------------------------------------------------------------------------- */ +{ + g_return_if_fail(DOC_VALID(doc)); + //g_debug("%s: closing document '%d'", __FUNCTION__, doc->id); + + gpointer pluginData = plugin_get_document_data(geany_plugin, doc, sPluginName); + if (pluginData != NULL) { + BracketColorsData *data = reinterpret_cast(pluginData); + //g_debug("%s: Closing doc ID: %d", __FUNCTION__, data->doc->id); + data->StopTimers(); + } + + ScintillaObject *sci = doc->editor->sci; + remove_bc_indicators(sci); + + //g_debug("%s: finished close routine", __FUNCTION__); +} + + + +// ----------------------------------------------------------------------------- + static void on_document_activate( + GObject *obj, + GeanyDocument *doc, + gpointer user_data + ) +/* + +----------------------------------------------------------------------------- */ +{ + //g_debug("%s: handling document activate", __FUNCTION__); + gpointer pluginData = plugin_get_document_data(geany_plugin, doc, sPluginName); + if (pluginData != NULL) { + BracketColorsData *data = reinterpret_cast(pluginData); + //g_debug("%s: got page switch to doc ID: %d", __FUNCTION__, data->doc->id); + data->StartTimers(); + } +} + + + +// ----------------------------------------------------------------------------- + static void on_startup_complete( + GObject *obj, + gpointer user_data + ) +/* + +----------------------------------------------------------------------------- */ +{ + //g_debug("%s: handling startup complete", __FUNCTION__); + GeanyDocument *currDoc = document_get_current(); + if (currDoc != NULL) { + gpointer pluginData = plugin_get_document_data(geany_plugin, currDoc, sPluginName); + if (pluginData != NULL) { + BracketColorsData *data = reinterpret_cast(pluginData); + g_debug("%s: starting on doc ID: %d", __FUNCTION__, data->doc->id); + data->StartTimers(); + } + } +} + + + + +// ----------------------------------------------------------------------------- + static void on_document_open( + GObject *obj, + GeanyDocument *doc, + gpointer user_data + ) +/* + +----------------------------------------------------------------------------- */ +{ + g_return_if_fail(DOC_VALID(doc)); + //g_debug("%s: opening document '%d'", __FUNCTION__, doc->id); + + BracketColorsData *data = bracket_colors_data_new(doc); + ScintillaObject *sci = doc->editor->sci; + data->doc = doc; + + plugin_signal_connect( + geany_plugin, + G_OBJECT(sci), "sci-notify", + FALSE, + G_CALLBACK(on_sci_notify), data + ); + + /* + * Setup our bracket indicators + */ + + data->backgroundColor = SSM(sci, SCI_STYLEGETBACK, STYLE_DEFAULT, BC_NO_ARG); + if (utils_is_dark(data->backgroundColor)) { + data->bracketColors = sDarkBackgroundColors; + } + + assign_indicator_colors(data); + + if (user_data == NULL) { + data->StartTimers(); + } + +} + + + +// ----------------------------------------------------------------------------- + static gboolean plugin_bracketcolors_init ( + GeanyPlugin *plugin, + gpointer pdata + ) +/* + +----------------------------------------------------------------------------- */ +{ + g_debug("%s: seting up plugin", __FUNCTION__); + + geany_plugin = plugin; + geany_data = plugin->geany_data; + + gboolean inInit = TRUE; + + guint i = 0; + foreach_document(i) + { + on_document_open(NULL, documents[i], (gpointer) &inInit); + } + + plugin_signal_connect( + plugin, + NULL, "document-activate", + FALSE, + G_CALLBACK(on_document_activate), NULL + ); + + on_startup_complete(NULL, (gpointer) &inInit); + + return TRUE; +} + + + +// ----------------------------------------------------------------------------- + static void plugin_bracketcolors_cleanup ( + GeanyPlugin *plugin, + gpointer pdata + ) +/* + +----------------------------------------------------------------------------- */ +{ + g_debug("%s: leaving bracket colors", __FUNCTION__); + + guint i = 0; + foreach_document(i) + { + on_document_close(NULL, documents[i], NULL); + } +} + + + +// ----------------------------------------------------------------------------- + static PluginCallback plugin_bracketcolors_callbacks[] = +/* + +----------------------------------------------------------------------------- */ +{ + { "document-open", (GCallback) &on_document_open, FALSE, NULL }, + { "document-new", (GCallback) &on_document_open, FALSE, NULL }, + { "document-close", (GCallback) &on_document_close, FALSE, NULL }, + { "geany-startup-complete", (GCallback) &on_startup_complete, FALSE, NULL }, + { NULL, NULL, FALSE, NULL } +}; + + + + + +// ----------------------------------------------------------------------------- + extern "C" void geany_load_module(GeanyPlugin *plugin) +/* + Load module +----------------------------------------------------------------------------- */ +{ + main_locale_init(LOCALEDIR, GETTEXT_PACKAGE); + + /* Set metadata */ + plugin->info->name = _("Bracket Colors"); + plugin->info->description = _("Color nested brackets, braces, parenthesis"); + plugin->info->version = "0.1"; + plugin->info->author = "Asif Amin "; + + /* Set functions */ + plugin->funcs->init = plugin_bracketcolors_init; + plugin->funcs->cleanup = plugin_bracketcolors_cleanup; + plugin->funcs->callbacks = plugin_bracketcolors_callbacks; + + /* Register! */ + GEANY_PLUGIN_REGISTER(plugin, 226); +} diff --git a/build/ax_cxx_compile_stdcxx.m4 b/build/ax_cxx_compile_stdcxx.m4 new file mode 100644 index 000000000..9413da624 --- /dev/null +++ b/build/ax_cxx_compile_stdcxx.m4 @@ -0,0 +1,962 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional]) +# +# DESCRIPTION +# +# Check for baseline language coverage in the compiler for the specified +# version of the C++ standard. If necessary, add switches to CXX and +# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard) +# or '14' (for the C++14 standard). +# +# The second argument, if specified, indicates whether you insist on an +# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. +# -std=c++11). If neither is specified, you get whatever works, with +# preference for no added switch, and then for an extended mode. +# +# The third argument, if specified 'mandatory' or if left unspecified, +# indicates that baseline support for the specified C++ standard is +# required and that the macro should error out if no mode with that +# support is found. If specified 'optional', then configuration proceeds +# regardless, after defining HAVE_CXX${VERSION} if and only if a +# supporting mode is found. +# +# LICENSE +# +# Copyright (c) 2008 Benjamin Kosnik +# Copyright (c) 2012 Zack Weinberg +# Copyright (c) 2013 Roy Stogner +# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov +# Copyright (c) 2015 Paul Norman +# Copyright (c) 2015 Moritz Klammler +# Copyright (c) 2016, 2018 Krzesimir Nowak +# Copyright (c) 2019 Enji Cooper +# Copyright (c) 2020 Jason Merrill +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 12 + +dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro +dnl (serial version number 13). + +AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl + m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"], + [$1], [14], [ax_cxx_compile_alternatives="14 1y"], + [$1], [17], [ax_cxx_compile_alternatives="17 1z"], + [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl + m4_if([$2], [], [], + [$2], [ext], [], + [$2], [noext], [], + [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl + m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true], + [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true], + [$3], [optional], [ax_cxx_compile_cxx$1_required=false], + [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])]) + AC_LANG_PUSH([C++])dnl + ac_success=no + + m4_if([$2], [], [dnl + AC_CACHE_CHECK(whether $CXX supports C++$1 features by default, + ax_cv_cxx_compile_cxx$1, + [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [ax_cv_cxx_compile_cxx$1=yes], + [ax_cv_cxx_compile_cxx$1=no])]) + if test x$ax_cv_cxx_compile_cxx$1 = xyes; then + ac_success=yes + fi]) + + m4_if([$2], [noext], [], [dnl + if test x$ac_success = xno; then + for alternative in ${ax_cxx_compile_alternatives}; do + switch="-std=gnu++${alternative}" + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, + $cachevar, + [ac_save_CXX="$CXX" + CXX="$CXX $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXX="$ac_save_CXX"]) + if eval test x\$$cachevar = xyes; then + CXX="$CXX $switch" + if test -n "$CXXCPP" ; then + CXXCPP="$CXXCPP $switch" + fi + ac_success=yes + break + fi + done + fi]) + + m4_if([$2], [ext], [], [dnl + if test x$ac_success = xno; then + dnl HP's aCC needs +std=c++11 according to: + dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf + dnl Cray's crayCC needs "-h std=c++11" + for alternative in ${ax_cxx_compile_alternatives}; do + for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, + $cachevar, + [ac_save_CXX="$CXX" + CXX="$CXX $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXX="$ac_save_CXX"]) + if eval test x\$$cachevar = xyes; then + CXX="$CXX $switch" + if test -n "$CXXCPP" ; then + CXXCPP="$CXXCPP $switch" + fi + ac_success=yes + break + fi + done + if test x$ac_success = xyes; then + break + fi + done + fi]) + AC_LANG_POP([C++]) + if test x$ax_cxx_compile_cxx$1_required = xtrue; then + if test x$ac_success = xno; then + AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.]) + fi + fi + if test x$ac_success = xno; then + HAVE_CXX$1=0 + AC_MSG_NOTICE([No compiler with C++$1 support was found]) + else + HAVE_CXX$1=1 + AC_DEFINE(HAVE_CXX$1,1, + [define if the compiler supports basic C++$1 syntax]) + fi + AC_SUBST(HAVE_CXX$1) +]) + + +dnl Test body for checking C++11 support + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 +) + + +dnl Test body for checking C++14 support + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 +) + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_17 +) + +dnl Tests for new features in C++11 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[ + +// If the compiler admits that it is not ready for C++11, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201103L + +#error "This is not a C++11 compiler" + +#else + +namespace cxx11 +{ + + namespace test_static_assert + { + + template + struct check + { + static_assert(sizeof(int) <= sizeof(T), "not big enough"); + }; + + } + + namespace test_final_override + { + + struct Base + { + virtual ~Base() {} + virtual void f() {} + }; + + struct Derived : public Base + { + virtual ~Derived() override {} + virtual void f() override {} + }; + + } + + namespace test_double_right_angle_brackets + { + + template < typename T > + struct check {}; + + typedef check single_type; + typedef check> double_type; + typedef check>> triple_type; + typedef check>>> quadruple_type; + + } + + namespace test_decltype + { + + int + f() + { + int a = 1; + decltype(a) b = 2; + return a + b; + } + + } + + namespace test_type_deduction + { + + template < typename T1, typename T2 > + struct is_same + { + static const bool value = false; + }; + + template < typename T > + struct is_same + { + static const bool value = true; + }; + + template < typename T1, typename T2 > + auto + add(T1 a1, T2 a2) -> decltype(a1 + a2) + { + return a1 + a2; + } + + int + test(const int c, volatile int v) + { + static_assert(is_same::value == true, ""); + static_assert(is_same::value == false, ""); + static_assert(is_same::value == false, ""); + auto ac = c; + auto av = v; + auto sumi = ac + av + 'x'; + auto sumf = ac + av + 1.0; + static_assert(is_same::value == true, ""); + static_assert(is_same::value == true, ""); + static_assert(is_same::value == true, ""); + static_assert(is_same::value == false, ""); + static_assert(is_same::value == true, ""); + return (sumf > 0.0) ? sumi : add(c, v); + } + + } + + namespace test_noexcept + { + + int f() { return 0; } + int g() noexcept { return 0; } + + static_assert(noexcept(f()) == false, ""); + static_assert(noexcept(g()) == true, ""); + + } + + namespace test_constexpr + { + + template < typename CharT > + unsigned long constexpr + strlen_c_r(const CharT *const s, const unsigned long acc) noexcept + { + return *s ? strlen_c_r(s + 1, acc + 1) : acc; + } + + template < typename CharT > + unsigned long constexpr + strlen_c(const CharT *const s) noexcept + { + return strlen_c_r(s, 0UL); + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("1") == 1UL, ""); + static_assert(strlen_c("example") == 7UL, ""); + static_assert(strlen_c("another\0example") == 7UL, ""); + + } + + namespace test_rvalue_references + { + + template < int N > + struct answer + { + static constexpr int value = N; + }; + + answer<1> f(int&) { return answer<1>(); } + answer<2> f(const int&) { return answer<2>(); } + answer<3> f(int&&) { return answer<3>(); } + + void + test() + { + int i = 0; + const int c = 0; + static_assert(decltype(f(i))::value == 1, ""); + static_assert(decltype(f(c))::value == 2, ""); + static_assert(decltype(f(0))::value == 3, ""); + } + + } + + namespace test_uniform_initialization + { + + struct test + { + static const int zero {}; + static const int one {1}; + }; + + static_assert(test::zero == 0, ""); + static_assert(test::one == 1, ""); + + } + + namespace test_lambdas + { + + void + test1() + { + auto lambda1 = [](){}; + auto lambda2 = lambda1; + lambda1(); + lambda2(); + } + + int + test2() + { + auto a = [](int i, int j){ return i + j; }(1, 2); + auto b = []() -> int { return '0'; }(); + auto c = [=](){ return a + b; }(); + auto d = [&](){ return c; }(); + auto e = [a, &b](int x) mutable { + const auto identity = [](int y){ return y; }; + for (auto i = 0; i < a; ++i) + a += b--; + return x + identity(a + b); + }(0); + return a + b + c + d + e; + } + + int + test3() + { + const auto nullary = [](){ return 0; }; + const auto unary = [](int x){ return x; }; + using nullary_t = decltype(nullary); + using unary_t = decltype(unary); + const auto higher1st = [](nullary_t f){ return f(); }; + const auto higher2nd = [unary](nullary_t f1){ + return [unary, f1](unary_t f2){ return f2(unary(f1())); }; + }; + return higher1st(nullary) + higher2nd(nullary)(unary); + } + + } + + namespace test_variadic_templates + { + + template + struct sum; + + template + struct sum + { + static constexpr auto value = N0 + sum::value; + }; + + template <> + struct sum<> + { + static constexpr auto value = 0; + }; + + static_assert(sum<>::value == 0, ""); + static_assert(sum<1>::value == 1, ""); + static_assert(sum<23>::value == 23, ""); + static_assert(sum<1, 2>::value == 3, ""); + static_assert(sum<5, 5, 11>::value == 21, ""); + static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); + + } + + // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae + // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function + // because of this. + namespace test_template_alias_sfinae + { + + struct foo {}; + + template + using member = typename T::member_type; + + template + void func(...) {} + + template + void func(member*) {} + + void test(); + + void test() { func(0); } + + } + +} // namespace cxx11 + +#endif // __cplusplus >= 201103L + +]]) + + +dnl Tests for new features in C++14 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[ + +// If the compiler admits that it is not ready for C++14, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201402L + +#error "This is not a C++14 compiler" + +#else + +namespace cxx14 +{ + + namespace test_polymorphic_lambdas + { + + int + test() + { + const auto lambda = [](auto&&... args){ + const auto istiny = [](auto x){ + return (sizeof(x) == 1UL) ? 1 : 0; + }; + const int aretiny[] = { istiny(args)... }; + return aretiny[0]; + }; + return lambda(1, 1L, 1.0f, '1'); + } + + } + + namespace test_binary_literals + { + + constexpr auto ivii = 0b0000000000101010; + static_assert(ivii == 42, "wrong value"); + + } + + namespace test_generalized_constexpr + { + + template < typename CharT > + constexpr unsigned long + strlen_c(const CharT *const s) noexcept + { + auto length = 0UL; + for (auto p = s; *p; ++p) + ++length; + return length; + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("x") == 1UL, ""); + static_assert(strlen_c("test") == 4UL, ""); + static_assert(strlen_c("another\0test") == 7UL, ""); + + } + + namespace test_lambda_init_capture + { + + int + test() + { + auto x = 0; + const auto lambda1 = [a = x](int b){ return a + b; }; + const auto lambda2 = [a = lambda1(x)](){ return a; }; + return lambda2(); + } + + } + + namespace test_digit_separators + { + + constexpr auto ten_million = 100'000'000; + static_assert(ten_million == 100000000, ""); + + } + + namespace test_return_type_deduction + { + + auto f(int& x) { return x; } + decltype(auto) g(int& x) { return x; } + + template < typename T1, typename T2 > + struct is_same + { + static constexpr auto value = false; + }; + + template < typename T > + struct is_same + { + static constexpr auto value = true; + }; + + int + test() + { + auto x = 0; + static_assert(is_same::value, ""); + static_assert(is_same::value, ""); + return x; + } + + } + +} // namespace cxx14 + +#endif // __cplusplus >= 201402L + +]]) + + +dnl Tests for new features in C++17 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[ + +// If the compiler admits that it is not ready for C++17, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201703L + +#error "This is not a C++17 compiler" + +#else + +#include +#include +#include + +namespace cxx17 +{ + + namespace test_constexpr_lambdas + { + + constexpr int foo = [](){return 42;}(); + + } + + namespace test::nested_namespace::definitions + { + + } + + namespace test_fold_expression + { + + template + int multiply(Args... args) + { + return (args * ... * 1); + } + + template + bool all(Args... args) + { + return (args && ...); + } + + } + + namespace test_extended_static_assert + { + + static_assert (true); + + } + + namespace test_auto_brace_init_list + { + + auto foo = {5}; + auto bar {5}; + + static_assert(std::is_same, decltype(foo)>::value); + static_assert(std::is_same::value); + } + + namespace test_typename_in_template_template_parameter + { + + template typename X> struct D; + + } + + namespace test_fallthrough_nodiscard_maybe_unused_attributes + { + + int f1() + { + return 42; + } + + [[nodiscard]] int f2() + { + [[maybe_unused]] auto unused = f1(); + + switch (f1()) + { + case 17: + f1(); + [[fallthrough]]; + case 42: + f1(); + } + return f1(); + } + + } + + namespace test_extended_aggregate_initialization + { + + struct base1 + { + int b1, b2 = 42; + }; + + struct base2 + { + base2() { + b3 = 42; + } + int b3; + }; + + struct derived : base1, base2 + { + int d; + }; + + derived d1 {{1, 2}, {}, 4}; // full initialization + derived d2 {{}, {}, 4}; // value-initialized bases + + } + + namespace test_general_range_based_for_loop + { + + struct iter + { + int i; + + int& operator* () + { + return i; + } + + const int& operator* () const + { + return i; + } + + iter& operator++() + { + ++i; + return *this; + } + }; + + struct sentinel + { + int i; + }; + + bool operator== (const iter& i, const sentinel& s) + { + return i.i == s.i; + } + + bool operator!= (const iter& i, const sentinel& s) + { + return !(i == s); + } + + struct range + { + iter begin() const + { + return {0}; + } + + sentinel end() const + { + return {5}; + } + }; + + void f() + { + range r {}; + + for (auto i : r) + { + [[maybe_unused]] auto v = i; + } + } + + } + + namespace test_lambda_capture_asterisk_this_by_value + { + + struct t + { + int i; + int foo() + { + return [*this]() + { + return i; + }(); + } + }; + + } + + namespace test_enum_class_construction + { + + enum class byte : unsigned char + {}; + + byte foo {42}; + + } + + namespace test_constexpr_if + { + + template + int f () + { + if constexpr(cond) + { + return 13; + } + else + { + return 42; + } + } + + } + + namespace test_selection_statement_with_initializer + { + + int f() + { + return 13; + } + + int f2() + { + if (auto i = f(); i > 0) + { + return 3; + } + + switch (auto i = f(); i + 4) + { + case 17: + return 2; + + default: + return 1; + } + } + + } + + namespace test_template_argument_deduction_for_class_templates + { + + template + struct pair + { + pair (T1 p1, T2 p2) + : m1 {p1}, + m2 {p2} + {} + + T1 m1; + T2 m2; + }; + + void f() + { + [[maybe_unused]] auto p = pair{13, 42u}; + } + + } + + namespace test_non_type_auto_template_parameters + { + + template + struct B + {}; + + B<5> b1; + B<'a'> b2; + + } + + namespace test_structured_bindings + { + + int arr[2] = { 1, 2 }; + std::pair pr = { 1, 2 }; + + auto f1() -> int(&)[2] + { + return arr; + } + + auto f2() -> std::pair& + { + return pr; + } + + struct S + { + int x1 : 2; + volatile double y1; + }; + + S f3() + { + return {}; + } + + auto [ x1, y1 ] = f1(); + auto& [ xr1, yr1 ] = f1(); + auto [ x2, y2 ] = f2(); + auto& [ xr2, yr2 ] = f2(); + const auto [ x3, y3 ] = f3(); + + } + + namespace test_exception_spec_type_system + { + + struct Good {}; + struct Bad {}; + + void g1() noexcept; + void g2(); + + template + Bad + f(T*, T*); + + template + Good + f(T1*, T2*); + + static_assert (std::is_same_v); + + } + + namespace test_inline_variables + { + + template void f(T) + {} + + template inline T g(T) + { + return T{}; + } + + template<> inline void f<>(int) + {} + + template<> int g<>(int) + { + return 5; + } + + } + +} // namespace cxx17 + +#endif // __cplusplus < 201703L + +]]) diff --git a/build/ax_cxx_compile_stdcxx_17.m4 b/build/ax_cxx_compile_stdcxx_17.m4 new file mode 100644 index 000000000..a68341717 --- /dev/null +++ b/build/ax_cxx_compile_stdcxx_17.m4 @@ -0,0 +1,35 @@ +# ============================================================================= +# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_17.html +# ============================================================================= +# +# SYNOPSIS +# +# AX_CXX_COMPILE_STDCXX_17([ext|noext], [mandatory|optional]) +# +# DESCRIPTION +# +# Check for baseline language coverage in the compiler for the C++17 +# standard; if necessary, add switches to CXX and CXXCPP to enable +# support. +# +# This macro is a convenience alias for calling the AX_CXX_COMPILE_STDCXX +# macro with the version set to C++17. The two optional arguments are +# forwarded literally as the second and third argument respectively. +# Please see the documentation for the AX_CXX_COMPILE_STDCXX macro for +# more information. If you want to use this macro, you also need to +# download the ax_cxx_compile_stdcxx.m4 file. +# +# LICENSE +# +# Copyright (c) 2015 Moritz Klammler +# Copyright (c) 2016 Krzesimir Nowak +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 2 + +AX_REQUIRE_DEFINED([AX_CXX_COMPILE_STDCXX]) +AC_DEFUN([AX_CXX_COMPILE_STDCXX_17], [AX_CXX_COMPILE_STDCXX([17], [$1], [$2])]) diff --git a/build/bracketcolors.m4 b/build/bracketcolors.m4 new file mode 100644 index 000000000..61ba44ae9 --- /dev/null +++ b/build/bracketcolors.m4 @@ -0,0 +1,31 @@ +AC_DEFUN([GP_CHECK_BRACKETCOLORS], +[ + GP_ARG_DISABLE([Bracketcolors], [auto]) + GP_CHECK_PLUGIN_GTK3_ONLY([Bracketcolors]) + GP_CHECK_PLUGIN_DEPS([Bracketcolors], [BRACKETCOLORS], [geany >= 1.38]) + + dnl Check for c++17. We really only need c++11 but scintilla is + dnl compiled with c++17 so just use that + + AS_IF( + [test x$enable_bracketcolors = xauto], [ + AX_CXX_COMPILE_STDCXX_17(, optional) + AS_IF( + [test ${HAVE_CXX17} = 1], [ + enable_bracketcolors=yes + ], + [enable_bracketcolors=no] + ) + ], + [test x$enable_bracketcolors = xyes], [ + AX_CXX_COMPILE_STDCXX_17(, mandatory) + ] + ) + + GP_COMMIT_PLUGIN_STATUS([Bracketcolors]) + AC_CONFIG_FILES([ + bracketcolors/Makefile + bracketcolors/src/Makefile + ]) +]) + diff --git a/configure.ac b/configure.ac index d9fe4be06..aeef50925 100644 --- a/configure.ac +++ b/configure.ac @@ -13,6 +13,7 @@ AC_USE_SYSTEM_EXTENSIONS AC_PROG_CC AC_PROG_CC_C99 AM_PROG_CC_C_O +AC_PROG_CXX AC_DISABLE_STATIC AC_PROG_LIBTOOL @@ -33,6 +34,7 @@ dnl plugin checks GP_CHECK_ADDONS GP_CHECK_AUTOCLOSE GP_CHECK_AUTOMARK +GP_CHECK_BRACKETCOLORS GP_CHECK_CODENAV GP_CHECK_COMMANDER GP_CHECK_DEBUGGER diff --git a/po/POTFILES.in b/po/POTFILES.in index 6e310f9b2..b34e0014b 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -19,6 +19,9 @@ autoclose/src/autoclose.c # Automark automark/src/automark.c +# bracketcolors +bracketcolors/src/bracketcolors.cc + # codenav codenav/src/codenavigation.c codenav/src/goto_file.c