Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:Leap:16.0:FactoryCandidates
libspelling
libspelling-0.4.4.obscpio
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File libspelling-0.4.4.obscpio of Package libspelling
07070100000000000081A40000000000000000000000016712C6CB00000AD9000000000000000000000000000000000000002100000000libspelling-0.4.4/.gitlab-ci.ymlstages: - docs - deploy variables: FEDORA_IMAGE: "registry.fedoraproject.org/fedora:41" reference: image: $FEDORA_IMAGE stage: docs needs: [] variables: MESON_FLAGS: "-Denchant=enabled -Dvapi=true" script: - sudo dnf install -y gtk4-devel ccache desktop-file-utils editorconfig-devel enchant2-devel expat-devel gcc gcc-c++ gettext gi-docgen gi-docgen-fonts git glib2-devel gobject-introspection-devel gtk4-devel gtksourceview5-devel itstool libadwaita-devel libicu-devel libjpeg-turbo-devel libpng-devel libxkbcommon-devel libXrandr-devel libXi-devel libXcursor-devel libXdamage-devel libXinerama-devel libdrm-devel libglvnd-devel glslc mesa-vulkan-drivers meson ninja-build redhat-rpm-config shared-mime-info vala xorg-x11-server-Xvfb vulkan-devel vulkan-headers vulkan-loader-devel - mkdir -p pfx/ - meson ${MESON_FLAGS} --prefix=${PWD}/pfx _build -Dwerror=false -Dglib:werror=false -Dgtk:broadway-backend=false -Dgtk:build-examples=false -Dgtk:build-tests=false -Dgtk:build-testsuite=false -Dgtk:build-demos=false -Dgtk:media-gstreamer=disabled -Dgtk:werror=false -Dgtksourceview:documentation=false -Dgtksourceview:introspection=disabled -Dgtksourceview:vapi=false -Dgtksourceview:werror=false -Dicu:werror=false -Dpango:gtk_doc=false -Dpango:introspection=disabled -Dpango:werror=false -Dwayland-protocols:tests=false -Dwayland:documentation=false -Dwayland:dtd_validation=false -Dwayland:tests=false - ninja -C _build install - mkdir -p _reference/ - mv pfx/share/doc/libspelling-1/ _reference/ artifacts: paths: - _reference pages: stage: deploy needs: ['reference'] script: - mv _reference public/ artifacts: paths: - public only: - main 07070100000001000081A40000000000000000000000016712C6CB000067A2000000000000000000000000000000000000001A00000000libspelling-0.4.4/COPYING GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 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. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, 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 and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, 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 library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete 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 distribute a copy of this License along with the Library. 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 Library or any portion of it, thus forming a work based on the Library, 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) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, 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 Library, 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 Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you 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. If distribution of 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 satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be 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. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library 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. 9. 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 Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library 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 with this License. 11. 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 Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library 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 Library. 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. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library 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. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser 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 Library 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 Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, 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 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "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 LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. 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 LIBRARY 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 LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. 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. <one line to give the library's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; 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. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. <signature of Ty Coon>, 1 April 1990 Ty Coon, President of Vice That's all there is to it! 07070100000002000081A40000000000000000000000016712C6CB00000D6C000000000000000000000000000000000000001700000000libspelling-0.4.4/NEWSlibspelling 0.4.4 ================= This is a bug-fix release for GNOME 47 * Adds a few dozen translations for menu items libspelling 0.4.3 ================= This is a bug-fix release for GNOME 47 * Fix build to disable assertions in release builds * Fix an off-by-one in assertion * Add i18n for translations of menu entries * Ignore empty strings when building job list libspelling 0.4.2 ================= This is a bug-fix release for GNOME 47 * Limit the number of corrections to 10 in enchant backend to help with dictionary configurations which produce many results. * Hide add/ignore menu items when disabled. * Don't show word corrections when spellcheck is disabled. libspelling 0.4.1 ================= This is a bug-fix release for GNOME 47 * Fixes an important issue tracking weak pointers from a GtkTextBuffer. * Fixes an important issue where we could get invalid text iters from GtkTextBuffer::delete-range() after the deletion occurred when other signal handlers are in play. libspelling 0.4.0 ================= This is a stable release for GNOME 47.0 * Protect the SpellingEngine from systems with misconfigured dictionaries. * Protect the SpellingTextBufferAdapter from NULL language codes. * Protect the SpellingJob from uncooperative break chars which could result in zero length runs. * Fix some incorrect licenses. libspelling 0.3.1 ================= This is an unstable release for GNOME 47.rc. * Immediately clear tags for invalidated regions without round-tripping to the checker thread. * Bump soname for ABI changes in 0.3 * Fix licenses to be LGPLv2.1+ * Add sysprof profiler marks for performance profiling * Update example so people know to copy the button-press work to update the menu immediately. * Fix pkg-config fields * Fix libspelling usage from subproject * Make introspection building optional * Documentation improvements libspelling 0.3.0 ================= This is an unstable release for GNOME 47.beta. The highlight for this release is a new threaded spellchecking engine. It performs text analysis and dictionary lookups in bulk off the UI thread. Care is taken to catch collisions in the face of user editing while spellchecking operations are in flight. I expect a significant reduction in initial spellchecking time after opening a document. Opening `gtktextbuffer.c` in Text Editor was more than 8x faster. Currently, libspelling relies on GTK main until 4.15.5 is released so do keep this in mind if you are a distributor. Some API has changed, though that is unlikely to affect any known applications using libspelling. * Many new unit tests are part of the testsuite * SpellingLanguage was renamed to SpellingDictionary * SpellingLanguageInfo was renamed to SpellingLanguage * Various helpers were added to SpellingTextBufferAdapter so that they can update spelling menus. Applications that update the cursor position on clicks before showing menus may want to force the menu updating before `menu.popup`. * SpellingTextBufferAdapter now uses the new GtkTextBufferCommitNotify API in GTK 4.15.5 and newer libspelling 0.2.1 ================= * Fix licenses to all be LGPLv2.1+ * Fix handling of cursor-moved signal libspelling 0.2.0 ================= This is the first stable release of libspelling, for GNOME 45 While I don't promise that we won't break ABI before 1.0, we do aim to try for that. 07070100000003000081A40000000000000000000000016712C6CB00000730000000000000000000000000000000000000001C00000000libspelling-0.4.4/README.md# libspelling A spellcheck library for GTK 4. This library is heavily based upon GNOME Text Editor and GNOME Builder's spellcheck implementation. However, it is licensed LGPLv2.1+. ## Documentation [Our documentation](https://gnome.pages.gitlab.gnome.org/libspelling/libspelling-1/) is updated on every commit. ## Example ### In C ```c SpellingChecker *checker = spelling_checker_get_default (); g_autoptr(SpellingTextBufferAdapter) adapter = spelling_text_buffer_adapter_new (source_buffer, checker); GMenuModel *extra_menu = spelling_text_buffer_adapter_get_menu_model (adapter); gtk_text_view_set_extra_menu (GTK_TEXT_VIEW (source_view), extra_menu); gtk_widget_insert_action_group (GTK_WIDGET (source_view), "spelling", G_ACTION_GROUP (adapter)); spelling_text_buffer_adapter_set_enabled (adapter, TRUE); ``` ### In Python ``` from gi.repository import Spelling checker = Spelling.Checker.get_default() adapter = Spelling.TextBufferAdapter.new(buffer, checker) extra_menu = adapter.get_menu_model() view.set_extra_menu(extra_menu) view.insert_action_group('spelling', adapter) adapter.set_enabled(True) ``` ### In JavaScript ``` const Spelling = imports.gi.Spelling; let checker = Spelling.Checker.get_default() let adapter = Spelling.TextBufferAdapter.new(buffer, checker) let extra_menu = adapter.get_menu_model() view.set_extra_menu(extra_menu) view.insert_action_group('spelling', adapter) adapter.set_enabled(true) ``` ### In Rust Add the [bindings dependency](https://crates.io/crates/libspelling) to your Cargo.toml ```rust let checker = libspelling:::Checker::default(); let adapter = libspelling::TextBufferAdapter::new(&buffer, &checker); let extra_menu = adapter.menu_model(); view.set_extra_menu(Some(&extra_menu)); view.insert_action_group("spelling", Some(&adapter)); adapter.set_enabled(true); ``` 07070100000004000041ED0000000000000000000000026712C6CB00000000000000000000000000000000000000000000001700000000libspelling-0.4.4/data07070100000005000041ED0000000000000000000000026712C6CB00000000000000000000000000000000000000000000001D00000000libspelling-0.4.4/data/icons07070100000006000041ED0000000000000000000000026712C6CB00000000000000000000000000000000000000000000002200000000libspelling-0.4.4/data/icons/apps07070100000007000041ED0000000000000000000000026712C6CB00000000000000000000000000000000000000000000002B00000000libspelling-0.4.4/data/icons/apps/symbolic07070100000008000081A40000000000000000000000016712C6CB0000058B000000000000000000000000000000000000004400000000libspelling-0.4.4/data/icons/apps/symbolic/libspelling-symbolic.svg<svg version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" width="256" height="256"><style>.a{fill:#f00}.b{fill:#fff}.c{fill:none;stroke:#fff;stroke-width:16}</style><filter id="f0"><feFlood flood-color="#ffffff" flood-opacity="1" /><feBlend mode="normal" in2="SourceGraphic"/><feComposite in2="SourceAlpha" operator="in" /></filter><g filter="url(#f0)"><path class="a" d="m98 152.5c0-1.9 1.6-3.5 3.5-3.5h9c1.9 0 3.5 1.6 3.5 3.5 0 1.9-1.6 3.5-3.5 3.5h-9c-1.9 0-3.5-1.6-3.5-3.5z"/><path class="a" d="m120 152.5c0-1.9 1.6-3.5 3.5-3.5h9c1.9 0 3.5 1.6 3.5 3.5 0 1.9-1.6 3.5-3.5 3.5h-9c-1.9 0-3.5-1.6-3.5-3.5z"/><path class="a" d="m142 152.5c0-1.9 1.6-3.5 3.5-3.5h9c1.9 0 3.5 1.6 3.5 3.5 0 1.9-1.6 3.5-3.5 3.5h-9c-1.9 0-3.5-1.6-3.5-3.5z"/></g><path class="b" d="m101 137l13.2-33.3h6.5l13.2 33.3h-6.1l-3.7-8.7h-13.6l-3.7 8.7zm11.5-14.1h9.4l-4.7-12.4zm33.4-5.5q2.1 0 3.7 0.4 1.6 0.3 2.6 1 0.9 0.6 1.5 1.5 0.7 1 0.9 1.9 0.2 1 0.2 2.2v12.6h-3.6l-0.5-1.2q-2.6 1.6-6 1.6-3.6 0-5.8-1.7-2.3-1.7-2.3-4 0-1.4 0.8-2.5 0.7-1.1 1.9-1.7 1.2-0.7 3-1.1 1.8-0.4 3.6-0.5 1.8-0.2 4.1-0.2v-0.7q0-0.7-0.2-1.2-0.1-0.5-0.5-1-0.4-0.6-1.5-0.9-0.9-0.3-2.5-0.3-2.2 0-5.8 0.9l-0.8 0.2-1-3.8 0.7-0.3q3.6-1.2 7.5-1.2zm4.1 14.7v-2.8h-0.5q-3.9 0-5.9 0.5-1.9 0.5-1.9 1.6 0 0.7 1.2 1.3 1.2 0.6 2.9 0.6 2.4 0 4.2-1.2z"/><path class="c" d="m64 90c0-9.9 8.1-18 18-18h92c9.9 0 18 8.1 18 18v76c0 9.9-8.1 18-18 18h-92c-9.9 0-18-8.1-18-18z"/></svg>07070100000009000041ED0000000000000000000000026712C6CB00000000000000000000000000000000000000000000001700000000libspelling-0.4.4/docs0707010000000A000081A40000000000000000000000016712C6CB0000063F000000000000000000000000000000000000002800000000libspelling-0.4.4/docs/Spelling.toml.in[library] version = "@version@" browse_url = "https://gitlab.gnome.org/GNOME/libspelling/" repository_url = "https://gitlab.gnome.org/GNOME/libspelling.git" website_url = "https://gitlab.gnome.org/GNOME/libspelling/" docs_url = "https://gnome.pages.gitlab.gnome.org/libspelling/libspelling-1/" authors = "Christian Hergert" license = "LGPL-2.1-or-later" description = "A GNOME library for spellchecking" dependencies = ["Gio-2.0", "Gtk-4.0", "GtkSource-5"] devhelp = true search_index = true logo_url = "libspelling.svg" [dependencies."GObject-2.0"] name = "GObject" description = "The base type system library" docs_url = "https://docs.gtk.org/gobject/" [dependencies."GLib-2.0"] name = "GLib" description = "The base type system library" docs_url = "https://docs.gtk.org/glib/" [dependencies."Gio-2.0"] name = "GIO" description = "GObject Interfaces and Objects, Networking, IPC, and I/O" docs_url = "https://docs.gtk.org/gio/" [dependencies."Gtk-4.0"] name = "GTK" description = "The GTK toolkit" docs_url = "https://docs.gtk.org/gtk4/" [dependencies."GtkSource-5"] name = "GtkSourceView" description = "The GTK source editor widget" docs_url = "https://gnome.pages.gitlab.gnome.org/gtksourceview/gtksourceview5/" [theme] name = "basic" show_index_summary = true show_class_hierarchy = true [source-location] base_url = "https://gitlab.gnome.org/GNOME/libspelling/-/blob/main/" [extra] # The same order will be used when generating the index content_files = [ "overview.md", ] content_images = [ "libspelling.svg", ] urlmap_file = "urlmap.js" 0707010000000B000081A40000000000000000000000016712C6CB0000084E000000000000000000000000000000000000002700000000libspelling-0.4.4/docs/libspelling.svg<?xml version="1.0" encoding="UTF-8"?> <svg width="256" height="256" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <defs> <linearGradient id="Gradient" x1="0" x2="0" y1="0" y2="1"> <stop offset="0%" style="stop-color:#FF7800;stop-opacity:1" /> <stop offset="100%" style="stop-color:#E01B24;stop-opacity:1" /> </linearGradient> <filter id="alpha-to-white"> <feColorMatrix in="SourceGraphic" type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/> </filter> <g id="child-svg"><svg xmlns="http://www.w3.org/2000/svg" version="1.2" viewBox="0 0 144 144" width="144" height="144"><style>.a{fill:none;stroke:#fff;stroke-width:16}.b{fill:#fff}</style><path fill-rule="evenodd" class="a" d="m8 34c0-9.9 8.1-18 18-18h92c9.9 0 18 8.1 18 18v76c0 9.9-8.1 18-18 18h-92c-9.9 0-18-8.1-18-18z" /><path class="b" d="m42 96.5c0-1.9 1.6-3.5 3.5-3.5h9c1.9 0 3.5 1.6 3.5 3.5 0 1.9-1.6 3.5-3.5 3.5h-9c-1.9 0-3.5-1.6-3.5-3.5z" /><path class="b" d="m64 96.5c0-1.9 1.6-3.5 3.5-3.5h9c1.9 0 3.5 1.6 3.5 3.5 0 1.9-1.6 3.5-3.5 3.5h-9c-1.9 0-3.5-1.6-3.5-3.5z" /><path class="b" d="m86 96.5c0-1.9 1.6-3.5 3.5-3.5h9c1.9 0 3.5 1.6 3.5 3.5 0 1.9-1.6 3.5-3.5 3.5h-9c-1.9 0-3.5-1.6-3.5-3.5z" /><path fill-rule="evenodd" class="b" d="m45 81l13.2-33.3h6.5l13.2 33.3h-6.1l-3.7-8.7h-13.6l-3.7 8.7zm11.5-14.1h9.4l-4.7-12.4zm33.4-5.5q2.1 0 3.7 0.4 1.6 0.3 2.6 1 0.9 0.6 1.5 1.5 0.7 1 0.9 1.9 0.2 1 0.2 2.2v12.6h-3.6l-0.5-1.2q-2.6 1.6-6 1.6-3.6 0-5.8-1.7-2.3-1.7-2.3-4 0-1.4 0.8-2.5 0.7-1.1 1.9-1.7 1.2-0.7 3-1.1 1.8-0.4 3.6-0.5 1.8-0.2 4.1-0.2v-0.7q0-0.7-0.2-1.2-0.1-0.5-0.5-1-0.4-0.6-1.5-0.9-0.9-0.3-2.5-0.3-2.2 0-5.8 0.9l-0.8 0.2-1-3.8 0.7-0.3q3.6-1.2 7.5-1.2zm4.1 14.7v-2.8h-0.5q-3.9 0-5.9 0.5-1.9 0.5-1.9 1.6 0 0.7 1.2 1.3 1.2 0.6 2.9 0.6 2.4 0 4.2-1.2z" /></svg></g> </defs> <rect width="256" height="256" fill="url(#Gradient)" ry="0" x="0" y="0" /> <use xlink:href="#child-svg" filter="url(#alpha-to-white)" transform="matrix(0.8888888888888888,0,0,0.8888888888888888,64,64)" /> </svg> 0707010000000C000081A40000000000000000000000016712C6CB00000389000000000000000000000000000000000000002300000000libspelling-0.4.4/docs/meson.buildtoml_conf = configuration_data() toml_conf.set('version', meson.project_version()) gidocgen = find_program('gi-docgen') gidocgen_common_args = [ '--quiet', '--no-namespace-dir', ] if get_option('werror') gidocgen_common_args += ['--fatal-warnings'] endif docs_dir = get_option('datadir') / 'doc' source_toml = configure_file( input: 'Spelling.toml.in', output: 'Spelling.toml', configuration: toml_conf, install: true, install_dir: docs_dir / 'libspelling-@0@'.format(api_version), ) custom_target('spelling-doc', input: [ source_toml, libspelling_gir[0] ], output: 'libspelling-@0@'.format(api_version), command: [ gidocgen, 'generate', gidocgen_common_args, '--config=@INPUT0@', '--output-dir=@OUTPUT@', '--content-dir=@0@'.format(meson.current_source_dir()), '@INPUT1@', ], build_by_default: true, install: true, install_dir: docs_dir, ) 0707010000000D000081A40000000000000000000000016712C6CB00000173000000000000000000000000000000000000002300000000libspelling-0.4.4/docs/overview.mdTitle: Overview # Overview Spelling is a [GNOME](https://www.gnome.org/) library that provides spellchecking for `GtkTextView` widgets. ## pkg-config name To build a program that uses Spelling, you can use the following command to get the cflags and libraries necessary to compile and link. ```sh gcc hello.c `pkg-config --cflags --libs libspelling-1` -o hello ``` 0707010000000E000081A40000000000000000000000016712C6CB0000016E000000000000000000000000000000000000002100000000libspelling-0.4.4/docs/urlmap.js // A map between namespaces and base URLs for their online documentation baseURLs = [ [ 'GLib', 'https://docs.gtk.org/glib/' ], [ 'GObject', 'https://docs.gtk.org/gobject/' ], [ 'Gio', 'https://docs.gtk.org/gio/' ], [ 'Gtk', 'https://docs.gtk.org/gtk4/' ], [ 'GtkSource', 'https://gnome.pages.gitlab.gnome.org/gtksourceview/gtksourceview5/'], ] 0707010000000F000041ED0000000000000000000000026712C6CB00000000000000000000000000000000000000000000001600000000libspelling-0.4.4/lib07070100000010000081A40000000000000000000000016712C6CB00009674000000000000000000000000000000000000002600000000libspelling-0.4.4/lib/cjhtextregion.c/* cjhtextregion.c * * Copyright 2021 Christian Hergert <chergert@redhat.com> * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include "cjhtextregionprivate.h" #include "cjhtextregionbtree.h" /** * SECTION:cjhtextregion * @Title: CjhTextRegion * @Short_description: track regions of text with a hybrid B+Tree and piecetable * * This data-structure is a hybrid between a PieceTable and a B+Tree, which I've * decided to call a Piece+Tree. It allows for very fast tracking of regions of * text (in a single dimension, meaning no sub-regions). * * This is very useful for tracking where work still needs to be done in a text * buffer such as for spelling mistakes, syntax highlighting, error checking, or * multi-device synchronization. * * See_also: https://blogs.gnome.org/chergert/2021/03/26/bplustree_augmented_piecetable/ */ #ifndef G_DISABLE_ASSERT # define DEBUG_VALIDATE(a,b) G_STMT_START { if (a) cjh_text_region_node_validate(a,b); } G_STMT_END #else # define DEBUG_VALIDATE(a,b) G_STMT_START { } G_STMT_END #endif static inline void cjh_text_region_invalid_cache (CjhTextRegion *region) { region->cached_result = NULL; region->cached_result_offset = 0; } G_GNUC_UNUSED static void cjh_text_region_node_validate (CjhTextRegionNode *node, CjhTextRegionNode *parent) { gsize length = 0; gsize length_in_parent = 0; g_assert (node != NULL); g_assert (UNTAG (node->tagged_parent) == parent); g_assert (cjh_text_region_node_is_leaf (node) || UNTAG (node->tagged_parent) == node->tagged_parent); g_assert (!parent || !cjh_text_region_node_is_leaf (parent)); g_assert (!parent || !SORTED_ARRAY_IS_EMPTY (&parent->branch.children)); if (parent != NULL) { SORTED_ARRAY_FOREACH (&parent->branch.children, CjhTextRegionChild, child, { if (child->node == node) { length_in_parent = child->length; goto found; } }); g_assert_not_reached (); } found: if (parent != NULL) g_assert_cmpint (length_in_parent, ==, cjh_text_region_node_length (node)); for (CjhTextRegionNode *iter = parent; iter != NULL; iter = cjh_text_region_node_get_parent (iter)) g_assert_false (cjh_text_region_node_is_leaf (iter)); if (cjh_text_region_node_is_leaf (node)) { SORTED_ARRAY_FOREACH (&node->leaf.runs, CjhTextRegionRun, run, { g_assert_cmpint (run->length, >, 0); length += run->length; }); if (node->leaf.prev != NULL) g_assert_true (cjh_text_region_node_is_leaf (node->leaf.prev)); if (node->leaf.next != NULL) g_assert_true (cjh_text_region_node_is_leaf (node->leaf.next)); } else { SORTED_ARRAY_FOREACH (&node->branch.children, CjhTextRegionChild, child, { CjhTextRegionChild *next = SORTED_ARRAY_FOREACH_PEEK (&node->branch.children); g_assert_nonnull (child->node); g_assert_cmpint (child->length, >, 0); g_assert_cmpint (child->length, ==, cjh_text_region_node_length (child->node)); g_assert_true (cjh_text_region_node_get_parent (child->node) == node); length += child->length; if (next != NULL && next->node) { g_assert_cmpint (cjh_text_region_node_is_leaf (child->node), ==, cjh_text_region_node_is_leaf (next->node)); if (cjh_text_region_node_is_leaf (child->node)) { g_assert_true (child->node->leaf.next == next->node); g_assert_true (child->node == next->node->leaf.prev); } else { g_assert_true (child->node->branch.next == next->node); g_assert_true (child->node == next->node->branch.prev); } } }); } if (parent != NULL) g_assert_cmpint (length_in_parent, ==, length); } static void cjh_text_region_split (CjhTextRegion *region, gsize offset, const CjhTextRegionRun *run, CjhTextRegionRun *left, CjhTextRegionRun *right) { if (region->split_func != NULL) region->split_func (offset, run, left, right); } static CjhTextRegionNode * cjh_text_region_node_new (CjhTextRegionNode *parent, gboolean is_leaf) { CjhTextRegionNode *node; g_assert (UNTAG (parent) == parent); node = g_new0 (CjhTextRegionNode, 1); node->tagged_parent = TAG (parent, is_leaf); if (is_leaf) { SORTED_ARRAY_INIT (&node->leaf.runs); node->leaf.prev = NULL; node->leaf.next = NULL; } else { SORTED_ARRAY_INIT (&node->branch.children); } g_assert (cjh_text_region_node_get_parent (node) == parent); return node; } static void cjh_text_region_subtract_from_parents (CjhTextRegion *region, CjhTextRegionNode *node, gsize length) { CjhTextRegionNode *parent = cjh_text_region_node_get_parent (node); if (parent == NULL || length == 0) return; cjh_text_region_invalid_cache (region); SORTED_ARRAY_FOREACH (&parent->branch.children, CjhTextRegionChild, child, { if (child->node == node) { g_assert (length <= child->length); child->length -= length; cjh_text_region_subtract_from_parents (region, parent, length); return; } }); g_assert_not_reached (); } static void cjh_text_region_add_to_parents (CjhTextRegion *region, CjhTextRegionNode *node, gsize length) { CjhTextRegionNode *parent = cjh_text_region_node_get_parent (node); if (parent == NULL || length == 0) return; cjh_text_region_invalid_cache (region); SORTED_ARRAY_FOREACH (&parent->branch.children, CjhTextRegionChild, child, { if (child->node == node) { child->length += length; cjh_text_region_add_to_parents (region, parent, length); return; } }); g_assert_not_reached (); } static inline gboolean cjh_text_region_node_is_root (CjhTextRegionNode *node) { return node != NULL && cjh_text_region_node_get_parent (node) == NULL; } static CjhTextRegionNode * cjh_text_region_node_search_recurse (CjhTextRegionNode *node, gsize offset, gsize *offset_within_node) { CjhTextRegionChild *last_child = NULL; g_assert (node != NULL); g_assert (offset_within_node != NULL); /* If we reached a leaf, that is all we need to do */ if (cjh_text_region_node_is_leaf (node)) { *offset_within_node = offset; return node; } g_assert (!cjh_text_region_node_is_leaf (node)); g_assert (!SORTED_ARRAY_IS_EMPTY (&node->branch.children)); g_assert (offset <= cjh_text_region_node_length (node)); SORTED_ARRAY_FOREACH (&node->branch.children, CjhTextRegionChild, child, { g_assert (child->length > 0); g_assert (child->node != NULL); if (offset < child->length) return cjh_text_region_node_search_recurse (child->node, offset, offset_within_node); offset -= child->length; last_child = child; }); /* We're right-most, so it belongs at the end. Add back the length we removed * while trying to resolve within the parent branch. */ g_assert (last_child != NULL); g_assert (node->branch.next == NULL); return cjh_text_region_node_search_recurse (last_child->node, offset + last_child->length, offset_within_node); } static CjhTextRegionNode * cjh_text_region_search (CjhTextRegion *region, gsize offset, gsize *offset_within_node) { CjhTextRegionNode *result; *offset_within_node = 0; g_assert (region->cached_result == NULL || cjh_text_region_node_is_leaf (region->cached_result)); /* Try to reuse cached node to avoid traversal since in most cases * an insert will be followed by another insert. */ if (region->cached_result != NULL && offset >= region->cached_result_offset) { gsize calc_offset = region->cached_result_offset + cjh_text_region_node_length (region->cached_result); if (offset < calc_offset || (offset == calc_offset && region->cached_result->leaf.next == NULL)) { *offset_within_node = offset - region->cached_result_offset; return region->cached_result; } } if (offset == 0) result = _cjh_text_region_get_first_leaf (region); else result = cjh_text_region_node_search_recurse (®ion->root, offset, offset_within_node); /* Now save it for cached reuse */ if (result != NULL) { region->cached_result = result; region->cached_result_offset = offset - *offset_within_node; } return result; } static void cjh_text_region_root_split (CjhTextRegion *region, CjhTextRegionNode *root) { CjhTextRegionNode *left; CjhTextRegionNode *right; CjhTextRegionChild new_child; g_assert (region != NULL); g_assert (!cjh_text_region_node_is_leaf (root)); g_assert (cjh_text_region_node_is_root (root)); g_assert (!SORTED_ARRAY_IS_EMPTY (&root->branch.children)); left = cjh_text_region_node_new (root, FALSE); right = cjh_text_region_node_new (root, FALSE); left->branch.next = right; right->branch.prev = left; SORTED_ARRAY_SPLIT2 (&root->branch.children, &left->branch.children, &right->branch.children); SORTED_ARRAY_FOREACH (&left->branch.children, CjhTextRegionChild, child, { cjh_text_region_node_set_parent (child->node, left); }); SORTED_ARRAY_FOREACH (&right->branch.children, CjhTextRegionChild, child, { cjh_text_region_node_set_parent (child->node, right); }); g_assert (SORTED_ARRAY_IS_EMPTY (&root->branch.children)); new_child.node = right; new_child.length = cjh_text_region_node_length (right); SORTED_ARRAY_PUSH_HEAD (&root->branch.children, new_child); new_child.node = left; new_child.length = cjh_text_region_node_length (left); SORTED_ARRAY_PUSH_HEAD (&root->branch.children, new_child); g_assert (SORTED_ARRAY_LENGTH (&root->branch.children) == 2); DEBUG_VALIDATE (root, NULL); DEBUG_VALIDATE (left, root); DEBUG_VALIDATE (right, root); } static CjhTextRegionNode * cjh_text_region_branch_split (CjhTextRegion *region, CjhTextRegionNode *left) { G_GNUC_UNUSED gsize old_length; CjhTextRegionNode *parent; CjhTextRegionNode *right; gsize right_length = 0; gsize left_length = 0; guint i = 0; g_assert (region != NULL); g_assert (left != NULL); g_assert (!cjh_text_region_node_is_leaf (left)); g_assert (!cjh_text_region_node_is_root (left)); old_length = cjh_text_region_node_length (left); /* * This operation should not change the height of the tree. Only * splitting the root node can change the height of the tree. So * here we add a new right node, and update the parent to point to * it right after our node. * * Since no new items are added, lengths do not change and we do * not need to update lengths up the hierarchy except for our two * effected nodes (and their direct parent). */ parent = cjh_text_region_node_get_parent (left); /* Create a new node to split half the items into */ right = cjh_text_region_node_new (parent, FALSE); /* Insert node into branches linked list */ right->branch.next = left->branch.next; right->branch.prev = left; if (right->branch.next != NULL) right->branch.next->branch.prev = right; left->branch.next = right; SORTED_ARRAY_SPLIT (&left->branch.children, &right->branch.children); SORTED_ARRAY_FOREACH (&right->branch.children, CjhTextRegionChild, child, { cjh_text_region_node_set_parent (child->node, right); }); #ifndef G_DISABLE_ASSERT SORTED_ARRAY_FOREACH (&left->branch.children, CjhTextRegionChild, child, { g_assert (cjh_text_region_node_get_parent (child->node) == left); }); #endif right_length = cjh_text_region_node_length (right); left_length = cjh_text_region_node_length (left); g_assert (right_length + left_length == old_length); g_assert (SORTED_ARRAY_LENGTH (&parent->branch.children) < SORTED_ARRAY_CAPACITY (&parent->branch.children)); SORTED_ARRAY_FOREACH (&parent->branch.children, CjhTextRegionChild, child, { i++; if (child->node == left) { CjhTextRegionChild right_child; right_child.node = right; right_child.length = right_length; child->length = left_length; SORTED_ARRAY_INSERT_VAL (&parent->branch.children, i, right_child); DEBUG_VALIDATE (left, parent); DEBUG_VALIDATE (right, parent); DEBUG_VALIDATE (parent, cjh_text_region_node_get_parent (parent)); return right; } }); g_assert_not_reached (); } static CjhTextRegionNode * cjh_text_region_leaf_split (CjhTextRegion *region, CjhTextRegionNode *left) { G_GNUC_UNUSED gsize length; CjhTextRegionNode *parent; CjhTextRegionNode *right; gsize right_length; guint i; g_assert (region != NULL); g_assert (left != NULL); g_assert (cjh_text_region_node_is_leaf (left)); parent = cjh_text_region_node_get_parent (left); g_assert (parent != left); g_assert (!cjh_text_region_node_is_leaf (parent)); g_assert (!SORTED_ARRAY_IS_EMPTY (&parent->branch.children)); g_assert (!SORTED_ARRAY_IS_FULL (&parent->branch.children)); length = cjh_text_region_node_length (left); g_assert (length > 0); DEBUG_VALIDATE (parent, cjh_text_region_node_get_parent (parent)); DEBUG_VALIDATE (left, parent); right = cjh_text_region_node_new (parent, TRUE); SORTED_ARRAY_SPLIT (&left->leaf.runs, &right->leaf.runs); right_length = cjh_text_region_node_length (right); g_assert (length == right_length + cjh_text_region_node_length (left)); g_assert (cjh_text_region_node_is_leaf (left)); g_assert (cjh_text_region_node_is_leaf (right)); i = 0; SORTED_ARRAY_FOREACH (&parent->branch.children, CjhTextRegionChild, child, { G_GNUC_UNUSED const CjhTextRegionChild *next = SORTED_ARRAY_FOREACH_PEEK (&parent->branch.children); ++i; g_assert (cjh_text_region_node_is_leaf (child->node)); g_assert (next == NULL || cjh_text_region_node_is_leaf (next->node)); if (child->node == left) { CjhTextRegionChild right_child; g_assert (child->length >= right_length); g_assert (next == NULL || left->leaf.next == next->node); if (left->leaf.next != NULL) left->leaf.next->leaf.prev = right; right->leaf.prev = left; right->leaf.next = left->leaf.next; left->leaf.next = right; right_child.node = right; right_child.length = right_length; child->length -= right_length; g_assert (child->length > 0); g_assert (right_child.length > 0); SORTED_ARRAY_INSERT_VAL (&parent->branch.children, i, right_child); g_assert (right != NULL); g_assert (cjh_text_region_node_is_leaf (right)); g_assert (right->leaf.prev == left); g_assert (left->leaf.next == right); DEBUG_VALIDATE (left, parent); DEBUG_VALIDATE (right, parent); DEBUG_VALIDATE (parent, cjh_text_region_node_get_parent (parent)); return right; } }); g_assert_not_reached (); } static inline gboolean cjh_text_region_node_needs_split (CjhTextRegionNode *node) { /* * We want to split the tree node if there is not enough space to * split a single entry into two AND add a new entry. That means we * need two empty slots before we ever perform an insert. */ if (!cjh_text_region_node_is_leaf (node)) return SORTED_ARRAY_LENGTH (&node->branch.children) >= (SORTED_ARRAY_CAPACITY (&node->branch.children) - 2); else return SORTED_ARRAY_LENGTH (&node->leaf.runs) >= (SORTED_ARRAY_CAPACITY (&node->leaf.runs) - 2); } static inline CjhTextRegionNode * cjh_text_region_node_split (CjhTextRegion *region, CjhTextRegionNode *node) { CjhTextRegionNode *parent; g_assert (node != NULL); cjh_text_region_invalid_cache (region); parent = cjh_text_region_node_get_parent (node); if (parent != NULL && cjh_text_region_node_needs_split (parent)) cjh_text_region_node_split (region, parent); if (!cjh_text_region_node_is_leaf (node)) { if (cjh_text_region_node_is_root (node)) { cjh_text_region_root_split (region, node); return ®ion->root; } return cjh_text_region_branch_split (region, node); } else { return cjh_text_region_leaf_split (region, node); } } CjhTextRegion * _cjh_text_region_new (CjhTextRegionJoinFunc join_func, CjhTextRegionSplitFunc split_func) { CjhTextRegion *self; CjhTextRegionNode *leaf; CjhTextRegionChild child; self = g_new0 (CjhTextRegion, 1); self->length = 0; self->join_func = join_func; self->split_func = split_func; /* The B+Tree has a root node (a branch) and a single leaf * as a child to simplify how we do splits/rotations/etc. */ leaf = cjh_text_region_node_new (&self->root, TRUE); child.node = leaf; child.length = 0; SORTED_ARRAY_INIT (&self->root.branch.children); SORTED_ARRAY_PUSH_HEAD (&self->root.branch.children, child); return self; } static void cjh_text_region_node_free (CjhTextRegionNode *node) { if (node == NULL) return; if (!cjh_text_region_node_is_leaf (node)) { SORTED_ARRAY_FOREACH (&node->branch.children, CjhTextRegionChild, child, { cjh_text_region_node_free (child->node); }); } g_free (node); } void _cjh_text_region_free (CjhTextRegion *region) { if (region != NULL) { g_assert (cjh_text_region_node_is_root (®ion->root)); g_assert (!SORTED_ARRAY_IS_EMPTY (®ion->root.branch.children)); SORTED_ARRAY_FOREACH (®ion->root.branch.children, CjhTextRegionChild, child, { cjh_text_region_node_free (child->node); }); g_free (region); } } static inline gboolean join_run (CjhTextRegion *region, gsize offset, const CjhTextRegionRun *left, const CjhTextRegionRun *right, CjhTextRegionRun *joined) { gboolean join; g_assert (region != NULL); g_assert (left != NULL); g_assert (right != NULL); g_assert (joined != NULL); if (region->join_func != NULL) join = region->join_func (offset, left, right); else join = FALSE; if (join) { joined->length = left->length + right->length; joined->data = left->data; return TRUE; } return FALSE; } void _cjh_text_region_insert (CjhTextRegion *region, gsize offset, gsize length, gpointer data) { CjhTextRegionRun to_insert = { length, data }; CjhTextRegionNode *target; CjhTextRegionNode *node; CjhTextRegionNode *parent; gsize offset_within_node = offset; guint i; g_assert (region != NULL); g_assert (offset <= region->length); if (length == 0) return; target = cjh_text_region_search (region, offset, &offset_within_node); g_assert (cjh_text_region_node_is_leaf (target)); g_assert (offset_within_node <= cjh_text_region_node_length (target)); /* We should only hit this if we have an empty tree. */ if G_UNLIKELY (SORTED_ARRAY_IS_EMPTY (&target->leaf.runs)) { g_assert (offset == 0); SORTED_ARRAY_PUSH_HEAD (&target->leaf.runs, to_insert); g_assert (cjh_text_region_node_length (target) == length); goto inserted; } /* Split up to region->root if necessary */ if (cjh_text_region_node_needs_split (target)) { DEBUG_VALIDATE (target, cjh_text_region_node_get_parent (target)); /* Split the target into two and then re-locate our position as * we might need to be in another node. * * TODO: Potentially optimization here to look at prev/next to * locate which we need. Complicated though since we don't * have real offsets. */ cjh_text_region_node_split (region, target); target = cjh_text_region_search (region, offset, &offset_within_node); g_assert (cjh_text_region_node_is_leaf (target)); g_assert (offset_within_node <= cjh_text_region_node_length (target)); DEBUG_VALIDATE (target, cjh_text_region_node_get_parent (target)); } i = 0; SORTED_ARRAY_FOREACH (&target->leaf.runs, CjhTextRegionRun, run, { /* * If this insert request would happen immediately after this run, * we want to see if we can chain it to this run or the beginning * of the next run. * * Note: We coudld also follow the the B+tree style linked-leaf to * the next leaf and compare against it's first item. But that is * out of scope for this prototype. */ if (offset_within_node == 0) { if (!join_run (region, offset, &to_insert, run, run)) SORTED_ARRAY_INSERT_VAL (&target->leaf.runs, i, to_insert); goto inserted; } else if (offset_within_node == run->length) { CjhTextRegionRun *next = SORTED_ARRAY_FOREACH_PEEK (&target->leaf.runs); /* Try to chain to the end of this run or the beginning of the next */ if (!join_run (region, offset, run, &to_insert, run) && (next == NULL || !join_run (region, offset, &to_insert, next, next))) SORTED_ARRAY_INSERT_VAL (&target->leaf.runs, i + 1, to_insert); goto inserted; } else if (offset_within_node < run->length) { CjhTextRegionRun left; CjhTextRegionRun right; left.length = offset_within_node; left.data = run->data; right.length = run->length - offset_within_node; right.data = run->data; cjh_text_region_split (region, offset - offset_within_node, run, &left, &right); *run = left; if (!join_run (region, offset, &to_insert, &right, &to_insert)) SORTED_ARRAY_INSERT_VAL (&target->leaf.runs, i + 1, right); if (!join_run (region, offset - offset_within_node, run, &to_insert, run)) SORTED_ARRAY_INSERT_VAL (&target->leaf.runs, i + 1, to_insert); goto inserted; } offset_within_node -= run->length; i++; }); g_assert_not_reached (); inserted: g_assert (target != NULL); /* * Now update each of the parent nodes in the tree so that they have * an apprporiate length along with the child pointer. This allows them * to calculate offsets while walking the tree (without derefrencing the * child node) at the cost of us walking back up the tree. */ for (parent = cjh_text_region_node_get_parent (target), node = target; parent != NULL; node = parent, parent = cjh_text_region_node_get_parent (node)) { SORTED_ARRAY_FOREACH (&parent->branch.children, CjhTextRegionChild, child, { if (child->node == node) { child->length += length; goto found_in_parent; } }); g_assert_not_reached (); found_in_parent: DEBUG_VALIDATE (node, parent); continue; } region->length += length; g_assert (region->length == cjh_text_region_node_length (®ion->root)); } void _cjh_text_region_replace (CjhTextRegion *region, gsize offset, gsize length, gpointer data) { g_assert (region != NULL); if (length == 0) return; /* TODO: This could be optimized to avoid possible splits * by merging adjoining runs. */ _cjh_text_region_remove (region, offset, length); _cjh_text_region_insert (region, offset, length, data); g_assert (region->length == cjh_text_region_node_length (®ion->root)); } guint _cjh_text_region_get_length (CjhTextRegion *region) { g_assert (region != NULL); return region->length; } static void cjh_text_region_branch_compact (CjhTextRegion *region, CjhTextRegionNode *node) { CjhTextRegionNode *parent; CjhTextRegionNode *left; CjhTextRegionNode *right; CjhTextRegionNode *target; gsize added = 0; gsize length; g_assert (region != NULL); g_assert (node != NULL); g_assert (!cjh_text_region_node_is_leaf (node)); SORTED_ARRAY_FOREACH (&node->branch.children, CjhTextRegionChild, child, { if (child->node == NULL) { g_assert (child->length == 0); SORTED_ARRAY_FOREACH_REMOVE (&node->branch.children); } }); if (cjh_text_region_node_is_root (node)) return; parent = cjh_text_region_node_get_parent (node); g_assert (parent != NULL); g_assert (!cjh_text_region_node_is_leaf (parent)); /* Reparent child in our stead if we can remove this node */ if (SORTED_ARRAY_LENGTH (&node->branch.children) == 1 && SORTED_ARRAY_LENGTH (&parent->branch.children) == 1) { CjhTextRegionChild *descendant = &SORTED_ARRAY_PEEK_HEAD (&node->branch.children); g_assert (parent->branch.prev == NULL); g_assert (parent->branch.next == NULL); g_assert (node->branch.prev == NULL); g_assert (node->branch.next == NULL); g_assert (descendant->node != NULL); SORTED_ARRAY_FOREACH (&parent->branch.children, CjhTextRegionChild, child, { if (child->node == node) { child->node = descendant->node; cjh_text_region_node_set_parent (child->node, parent); descendant->node = NULL; descendant->length = 0; goto compact_parent; } }); g_assert_not_reached (); } if (node->branch.prev == NULL && node->branch.next == NULL) return; if (SORTED_ARRAY_LENGTH (&node->branch.children) >= CJH_TEXT_REGION_MIN_BRANCHES) return; length = cjh_text_region_node_length (node); cjh_text_region_subtract_from_parents (region, node, length); /* Remove this node, we'll reparent the children with edges */ SORTED_ARRAY_FOREACH (&parent->branch.children, CjhTextRegionChild, child, { if (child->node == node) { SORTED_ARRAY_FOREACH_REMOVE (&parent->branch.children); goto found; } }); g_assert_not_reached (); found: left = node->branch.prev; right = node->branch.next; if (left != NULL) left->branch.next = right; if (right != NULL) right->branch.prev = left; if (left == NULL || (right != NULL && SORTED_ARRAY_LENGTH (&left->branch.children) > SORTED_ARRAY_LENGTH (&right->branch.children))) { target = right; g_assert (target->branch.prev == left); SORTED_ARRAY_FOREACH_REVERSE (&node->branch.children, CjhTextRegionChild, child, { if (SORTED_ARRAY_LENGTH (&target->branch.children) >= CJH_TEXT_REGION_MAX_BRANCHES-1) { cjh_text_region_add_to_parents (region, target, added); added = 0; cjh_text_region_branch_split (region, target); g_assert (target->branch.prev == left); } cjh_text_region_node_set_parent (child->node, target); added += child->length; SORTED_ARRAY_PUSH_HEAD (&target->branch.children, *child); child->node = NULL; child->length = 0; }); cjh_text_region_add_to_parents (region, target, added); } else { target = left; g_assert (target->branch.next == right); SORTED_ARRAY_FOREACH (&node->branch.children, CjhTextRegionChild, child, { if (SORTED_ARRAY_LENGTH (&target->branch.children) >= CJH_TEXT_REGION_MAX_BRANCHES-1) { cjh_text_region_add_to_parents (region, target, added); added = 0; target = cjh_text_region_branch_split (region, target); } cjh_text_region_node_set_parent (child->node, target); added += child->length; SORTED_ARRAY_PUSH_TAIL (&target->branch.children, *child); child->node = NULL; child->length = 0; }); cjh_text_region_add_to_parents (region, target, added); } DEBUG_VALIDATE (left, cjh_text_region_node_get_parent (left)); DEBUG_VALIDATE (right, cjh_text_region_node_get_parent (right)); DEBUG_VALIDATE (parent, cjh_text_region_node_get_parent (parent)); compact_parent: if (parent != NULL) cjh_text_region_branch_compact (region, parent); cjh_text_region_node_free (node); } static void cjh_text_region_leaf_compact (CjhTextRegion *region, CjhTextRegionNode *node) { CjhTextRegionNode *parent; CjhTextRegionNode *target; CjhTextRegionNode *left; CjhTextRegionNode *right; gsize added = 0; g_assert (region != NULL); g_assert (node != NULL); g_assert (cjh_text_region_node_is_leaf (node)); g_assert (SORTED_ARRAY_LENGTH (&node->leaf.runs) < CJH_TEXT_REGION_MIN_RUNS); /* Short-circuit if we are the only node */ if (node->leaf.prev == NULL && node->leaf.next == NULL) return; parent = cjh_text_region_node_get_parent (node); left = node->leaf.prev; right = node->leaf.next; g_assert (parent != NULL); g_assert (!cjh_text_region_node_is_leaf (parent)); g_assert (left == NULL || cjh_text_region_node_is_leaf (left)); g_assert (right == NULL || cjh_text_region_node_is_leaf (right)); SORTED_ARRAY_FOREACH (&parent->branch.children, CjhTextRegionChild, child, { if (child->node == node) { cjh_text_region_subtract_from_parents (region, node, child->length); g_assert (child->length == 0); SORTED_ARRAY_FOREACH_REMOVE (&parent->branch.children); goto found; } }); g_assert_not_reached (); found: if (left != NULL) left->leaf.next = right; if (right != NULL) right->leaf.prev = left; node->leaf.next = NULL; node->leaf.prev = NULL; if (left == NULL || (right != NULL && SORTED_ARRAY_LENGTH (&left->leaf.runs) > SORTED_ARRAY_LENGTH (&right->leaf.runs))) { target = right; g_assert (target->leaf.prev == left); SORTED_ARRAY_FOREACH_REVERSE (&node->leaf.runs, CjhTextRegionRun, run, { if (SORTED_ARRAY_LENGTH (&target->leaf.runs) >= CJH_TEXT_REGION_MAX_RUNS-1) { cjh_text_region_add_to_parents (region, target, added); added = 0; cjh_text_region_node_split (region, target); g_assert (target->leaf.prev == left); } added += run->length; SORTED_ARRAY_PUSH_HEAD (&target->leaf.runs, *run); }); cjh_text_region_add_to_parents (region, target, added); } else { target = left; g_assert (target->leaf.next == right); SORTED_ARRAY_FOREACH (&node->leaf.runs, CjhTextRegionRun, run, { if (SORTED_ARRAY_LENGTH (&target->leaf.runs) >= CJH_TEXT_REGION_MAX_RUNS-1) { cjh_text_region_add_to_parents (region, target, added); added = 0; target = cjh_text_region_node_split (region, target); left = target; } added += run->length; SORTED_ARRAY_PUSH_TAIL (&target->leaf.runs, *run); }); cjh_text_region_add_to_parents (region, target, added); } DEBUG_VALIDATE (left, cjh_text_region_node_get_parent (left)); DEBUG_VALIDATE (right, cjh_text_region_node_get_parent (right)); DEBUG_VALIDATE (parent, cjh_text_region_node_get_parent (parent)); cjh_text_region_branch_compact (region, parent); cjh_text_region_node_free (node); } void _cjh_text_region_remove (CjhTextRegion *region, gsize offset, gsize length) { CjhTextRegionNode *target; gsize offset_within_node; gsize to_remove = length; gsize calc_offset; guint i; g_assert (region != NULL); g_assert (length <= region->length); g_assert (offset < region->length); g_assert (length <= region->length - offset); if (length == 0) return; target = cjh_text_region_search (region, offset, &offset_within_node); g_assert (target != NULL); g_assert (cjh_text_region_node_is_leaf (target)); g_assert (SORTED_ARRAY_LENGTH (&target->leaf.runs) > 0); g_assert (offset >= offset_within_node); calc_offset = offset - offset_within_node; i = 0; SORTED_ARRAY_FOREACH (&target->leaf.runs, CjhTextRegionRun, run, { ++i; g_assert (to_remove > 0); if (offset_within_node >= run->length) { offset_within_node -= run->length; calc_offset += run->length; } else if (offset_within_node > 0 && to_remove >= run->length - offset_within_node) { CjhTextRegionRun left; CjhTextRegionRun right; left.length = offset_within_node; left.data = run->data; right.length = run->length - left.length; right.data = run->data; cjh_text_region_split (region, calc_offset, run, &left, &right); to_remove -= right.length; calc_offset += left.length; offset_within_node = 0; *run = left; if (to_remove == 0) break; } else if (offset_within_node > 0 && to_remove < run->length - offset_within_node) { CjhTextRegionRun left; CjhTextRegionRun right; CjhTextRegionRun right2; CjhTextRegionRun center; left.length = offset_within_node; left.data = run->data; right.length = run->length - left.length; right.data = run->data; cjh_text_region_split (region, calc_offset, run, &left, &right); center.length = to_remove; center.data = run->data; right2.length = run->length - offset_within_node - to_remove; right2.data = run->data; cjh_text_region_split (region, calc_offset + left.length, &right, ¢er, &right2); *run = left; if (!join_run (region, calc_offset, run, &right2, run)) SORTED_ARRAY_INSERT_VAL (&target->leaf.runs, i, right2); offset_within_node = 0; to_remove = 0; break; } else if (offset_within_node == 0 && to_remove < run->length) { CjhTextRegionRun left; CjhTextRegionRun right; left.length = to_remove; left.data = run->data; right.length = run->length - to_remove; right.data = run->data; cjh_text_region_split (region, calc_offset, run, &left, &right); to_remove = 0; offset_within_node = 0; *run = right; break; } else if (offset_within_node == 0 && to_remove >= run->length) { to_remove -= run->length; SORTED_ARRAY_FOREACH_REMOVE (&target->leaf.runs); if (to_remove == 0) break; } else { g_assert_not_reached (); } g_assert (to_remove > 0); }); region->length -= length - to_remove; cjh_text_region_subtract_from_parents (region, target, length - to_remove); if (SORTED_ARRAY_LENGTH (&target->leaf.runs) < CJH_TEXT_REGION_MIN_RUNS) cjh_text_region_leaf_compact (region, target); g_assert (region->length == cjh_text_region_node_length (®ion->root)); if (to_remove > 0) _cjh_text_region_remove (region, offset, to_remove); } void _cjh_text_region_foreach (CjhTextRegion *region, CjhTextRegionForeachFunc func, gpointer user_data) { CjhTextRegionNode *leaf; gsize offset = 0; g_return_if_fail (region != NULL); g_return_if_fail (func != NULL); for (leaf = _cjh_text_region_get_first_leaf (region); leaf != NULL; leaf = leaf->leaf.next) { g_assert (leaf->leaf.next == NULL || leaf->leaf.next->leaf.prev == leaf); SORTED_ARRAY_FOREACH (&leaf->leaf.runs, CjhTextRegionRun, run, { if (func (offset, run, user_data)) return; offset += run->length; }); } } void _cjh_text_region_foreach_in_range (CjhTextRegion *region, gsize begin, gsize end, CjhTextRegionForeachFunc func, gpointer user_data) { CjhTextRegionNode *leaf; gsize position; gsize offset_within_node = 0; g_return_if_fail (region != NULL); g_return_if_fail (func != NULL); g_return_if_fail (begin <= region->length); g_return_if_fail (end <= region->length); g_return_if_fail (begin <= end); if (begin == end || begin == region->length) return; if (begin == 0) leaf = _cjh_text_region_get_first_leaf (region); else leaf = cjh_text_region_search (region, begin, &offset_within_node); g_assert (offset_within_node < cjh_text_region_node_length (leaf)); position = begin - offset_within_node; while (position < end) { SORTED_ARRAY_FOREACH (&leaf->leaf.runs, CjhTextRegionRun, run, { if (offset_within_node >= run->length) { offset_within_node -= run->length; } else { offset_within_node = 0; if (func (position, run, user_data)) return; } position += run->length; if (position >= end) break; }); leaf = leaf->leaf.next; } } 07070100000011000081A40000000000000000000000016712C6CB00007BEF000000000000000000000000000000000000002B00000000libspelling-0.4.4/lib/cjhtextregionbtree.h/* cjhtextregionbtree.h * * Copyright 2021 Christian Hergert <chergert@redhat.com> * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "cjhtextregionprivate.h" G_BEGIN_DECLS /* The following set of macros are used to create a queue similar to a * double-ended linked list but using integers as indexes for items within the * queue. Doing so allows for inserting or removing items from a b+tree node * without having to memmove() data to maintain sorting orders. */ #define VAL_QUEUE_INVALID(Node) ((glib_typeof((Node)->head))-1) #define VAL_QUEUE_LENGTH(Node) ((Node)->length) #define VAL_QUEUE_EMPTY(Node) ((Node)->head == VAL_QUEUE_INVALID(Node)) #define VAL_QUEUE_PEEK_HEAD(Node) ((Node)->head) #define VAL_QUEUE_PEEK_TAIL(Node) ((Node)->tail) #define VAL_QUEUE_IS_VALID(Node, ID) ((ID) != VAL_QUEUE_INVALID(Node)) #define VAL_QUEUE_NODE(Type, N_Items) \ struct { \ Type length; \ Type head; \ Type tail; \ struct { \ Type prev; \ Type next; \ } items[N_Items]; \ } #define VAL_QUEUE_INIT(Node) \ G_STMT_START { \ (Node)->length = 0; \ (Node)->head = VAL_QUEUE_INVALID(Node); \ (Node)->tail = VAL_QUEUE_INVALID(Node); \ for (guint _i = 0; _i < G_N_ELEMENTS ((Node)->items); _i++) \ { \ (Node)->items[_i].next = VAL_QUEUE_INVALID(Node); \ (Node)->items[_i].prev = VAL_QUEUE_INVALID(Node); \ } \ } G_STMT_END #ifndef G_DISABLE_ASSERT # define _VAL_QUEUE_VALIDATE(Node) \ G_STMT_START { \ glib_typeof((Node)->head) count = 0; \ \ if ((Node)->tail != VAL_QUEUE_INVALID(Node)) \ g_assert_cmpint((Node)->items[(Node)->tail].next, ==, VAL_QUEUE_INVALID(Node)); \ if ((Node)->head != VAL_QUEUE_INVALID(Node)) \ g_assert_cmpint((Node)->items[(Node)->head].prev , ==, VAL_QUEUE_INVALID(Node)); \ \ for (glib_typeof((Node)->head) _viter = (Node)->head; \ VAL_QUEUE_IS_VALID(Node, _viter); \ _viter = (Node)->items[_viter].next) \ { \ count++; \ } \ \ g_assert_cmpint(count, ==, (Node)->length); \ } G_STMT_END #else # define _VAL_QUEUE_VALIDATE(Node) G_STMT_START { } G_STMT_END #endif #define VAL_QUEUE_PUSH_HEAD(Node, ID) \ G_STMT_START { \ (Node)->items[ID].prev = VAL_QUEUE_INVALID(Node); \ (Node)->items[ID].next = (Node)->head; \ if (VAL_QUEUE_IS_VALID(Node, (Node)->head)) \ (Node)->items[(Node)->head].prev = ID; \ (Node)->head = ID; \ if (!VAL_QUEUE_IS_VALID(Node, (Node)->tail)) \ (Node)->tail = ID; \ (Node)->length++; \ _VAL_QUEUE_VALIDATE(Node); \ } G_STMT_END #define VAL_QUEUE_PUSH_TAIL(Node, ID) \ G_STMT_START { \ (Node)->items[ID].prev = (Node)->tail; \ (Node)->items[ID].next = VAL_QUEUE_INVALID(Node); \ if (VAL_QUEUE_IS_VALID (Node, (Node)->tail)) \ (Node)->items[(Node)->tail].next = ID; \ (Node)->tail = ID; \ if (!VAL_QUEUE_IS_VALID(Node, (Node)->head)) \ (Node)->head = ID; \ (Node)->length++; \ _VAL_QUEUE_VALIDATE(Node); \ } G_STMT_END #define VAL_QUEUE_INSERT(Node, Nth, Val) \ G_STMT_START { \ g_assert_cmpint (VAL_QUEUE_LENGTH(Node),<,G_N_ELEMENTS((Node)->items)); \ \ if ((Nth) == 0) \ { \ VAL_QUEUE_PUSH_HEAD(Node, Val); \ } \ else if ((Nth) == (Node)->length) \ { \ VAL_QUEUE_PUSH_TAIL(Node, Val); \ } \ else \ { \ glib_typeof((Node)->head) ID; \ glib_typeof((Node)->head) _nth; \ \ g_assert_cmpint (VAL_QUEUE_LENGTH(Node), >, 0); \ g_assert (VAL_QUEUE_IS_VALID(Node, (Node)->head)); \ g_assert (VAL_QUEUE_IS_VALID(Node, (Node)->tail)); \ \ for (ID = (Node)->head, _nth = 0; \ _nth < (Nth) && VAL_QUEUE_IS_VALID(Node, ID); \ ID = (Node)->items[ID].next, ++_nth) \ { /* Do Nothing */ } \ \ g_assert (VAL_QUEUE_IS_VALID(Node, ID)); \ g_assert (VAL_QUEUE_IS_VALID(Node, (Node)->items[ID].prev)); \ \ (Node)->items[Val].prev = (Node)->items[ID].prev; \ (Node)->items[Val].next = ID; \ (Node)->items[(Node)->items[ID].prev].next = Val; \ (Node)->items[ID].prev = Val; \ \ (Node)->length++; \ \ _VAL_QUEUE_VALIDATE(Node); \ } \ } G_STMT_END #define VAL_QUEUE_POP_HEAD(Node,_pos) VAL_QUEUE_POP_NTH((Node), 0, _pos) #define VAL_QUEUE_POP_TAIL(Node,_pos) VAL_QUEUE_POP_NTH((Node), (Node)->length - 1, _pos) #define VAL_QUEUE_POP_AT(Node, _pos) \ G_STMT_START { \ g_assert (_pos != VAL_QUEUE_INVALID(Node)); \ g_assert (_pos < G_N_ELEMENTS ((Node)->items)); \ \ if ((Node)->items[_pos].prev != VAL_QUEUE_INVALID(Node)) \ (Node)->items[(Node)->items[_pos].prev].next = (Node)->items[_pos].next; \ if ((Node)->items[_pos].next != VAL_QUEUE_INVALID(Node)) \ (Node)->items[(Node)->items[_pos].next].prev = (Node)->items[_pos].prev; \ if ((Node)->head == _pos) \ (Node)->head = (Node)->items[_pos].next; \ if ((Node)->tail == _pos) \ (Node)->tail = (Node)->items[_pos].prev; \ \ (Node)->items[_pos].prev = VAL_QUEUE_INVALID((Node)); \ (Node)->items[_pos].next = VAL_QUEUE_INVALID((Node)); \ \ (Node)->length--; \ \ _VAL_QUEUE_VALIDATE(Node); \ } G_STMT_END #define VAL_QUEUE_POP_NTH(Node, Nth, _pos) \ G_STMT_START { \ _pos = VAL_QUEUE_INVALID(Node); \ \ if (Nth == 0) \ _pos = (Node)->head; \ else if (Nth >= (((Node)->length) - 1)) \ _pos = (Node)->tail; \ else \ VAL_QUEUE_NTH (Node, Nth, _pos); \ \ if (_pos != VAL_QUEUE_INVALID(Node)) \ VAL_QUEUE_POP_AT (Node, _pos); \ } G_STMT_END #define VAL_QUEUE_NTH(Node, Nth, _iter) \ G_STMT_START { \ glib_typeof((Node)->head) _nth; \ if (Nth == 0) \ _iter = (Node)->head; \ else if (Nth >= (((Node)->length) - 1)) \ _iter = (Node)->tail; \ else \ { \ for (_iter = (Node)->head, _nth = 0; \ _nth < (Nth); \ _iter = (Node)->items[_iter].next, ++_nth) \ { \ /* Do Nothing */ \ g_assert (_iter != VAL_QUEUE_INVALID(Node)); \ } \ } \ } G_STMT_END #define _VAL_QUEUE_MOVE(Node, Old, New) \ G_STMT_START { \ (Node)->items[New] = (Node)->items[Old]; \ if ((Node)->items[New].prev != VAL_QUEUE_INVALID(Node)) \ (Node)->items[(Node)->items[New].prev].next = New; \ if ((Node)->items[New].next != VAL_QUEUE_INVALID(Node)) \ (Node)->items[(Node)->items[New].next].prev = New; \ if ((Node)->head == Old) \ (Node)->head = New; \ if ((Node)->tail == Old) \ (Node)->tail = New; \ } G_STMT_END /* * SORTED_ARRAY_FIELD: * @TYPE: The type of the structure used by elements in the array * @N_ITEMS: The maximum number of items in the array * * This creates a new inline structure that can be embedded within * other super-structures. * * @N_ITEMS must be <= 254 or this macro will fail. */ #define SORTED_ARRAY_FIELD(TYPE,N_ITEMS) \ struct { \ TYPE items[N_ITEMS]; \ VAL_QUEUE_NODE(guint8, N_ITEMS) q; \ } /* * SORTED_ARRAY_INIT: * @FIELD: A pointer to a SortedArray * * This will initialize a node that has been previously registered * using %SORTED_ARRAY_FIELD(). You must call this macro before * using the SortedArray structure. */ #define SORTED_ARRAY_INIT(FIELD) \ G_STMT_START { \ G_STATIC_ASSERT (G_N_ELEMENTS((FIELD)->items) < 255); \ VAL_QUEUE_INIT(&(FIELD)->q); \ } G_STMT_END /* * SORTED_ARRAY_LENGTH: * @FIELD: A pointer to the SortedArray field. * * This macro will evaluate to the number of items inserted into * the SortedArray. */ #define SORTED_ARRAY_LENGTH(FIELD) (VAL_QUEUE_LENGTH(&(FIELD)->q)) /* * SORTED_ARRAY_CAPACITY: * @FIELD: A pointer to the SortedArray field. * * This macro will evaluate to the number of elements in the SortedArray. * This is dependent on how the SortedArray was instantiated using * the %SORTED_ARRAY_FIELD() macro. */ #define SORTED_ARRAY_CAPACITY(FIELD) (G_N_ELEMENTS((FIELD)->items)) /* * SORTED_ARRAY_IS_FULL: * @FIELD: A pointer to the SortedArray field. * * This macro will evaluate to 1 if the SortedArray is at capacity. * Otherwise, the macro will evaluate to 0. */ #define SORTED_ARRAY_IS_FULL(FIELD) (SORTED_ARRAY_LENGTH(FIELD) == SORTED_ARRAY_CAPACITY(FIELD)) /* * SORTED_ARRAY_IS_EMPTY: * @FIELD: A SortedArray field * * This macro will evaluate to 1 if the SortedArray contains zero children. */ #define SORTED_ARRAY_IS_EMPTY(FIELD) (SORTED_ARRAY_LENGTH(FIELD) == 0) /* * SORTED_ARRAY_INSERT_VAL: * @FIELD: A pointer to a SortedArray field. * @POSITION: the logical position at which to insert * @ELEMENT: The element to insert * * This will insert a new item into the array. It is invalid API use * to call this function while the SortedArray is at capacity. Check * SORTED_ARRAY_IS_FULL() before using this function to be certain. */ #define SORTED_ARRAY_INSERT_VAL(FIELD,POSITION,ELEMENT) \ G_STMT_START { \ guint8 _pos; \ \ g_assert (POSITION <= SORTED_ARRAY_LENGTH(FIELD)); \ \ _pos = VAL_QUEUE_LENGTH(&(FIELD)->q); \ g_assert (_pos != VAL_QUEUE_INVALID(&(FIELD)->q)); \ (FIELD)->items[_pos] = ELEMENT; \ VAL_QUEUE_INSERT(&(FIELD)->q, POSITION, _pos); \ } G_STMT_END #define SORTED_ARRAY_REMOVE_INDEX(FIELD,POSITION,_ele) \ G_STMT_START { \ guint8 _pos; \ guint8 _len; \ \ VAL_QUEUE_POP_NTH(&(FIELD)->q, POSITION, _pos); \ if (_pos == VAL_QUEUE_INVALID(&(FIELD)->q)) \ { \ g_assert_not_reached (); \ break; \ } \ \ _ele = (FIELD)->items[_pos]; \ _len = VAL_QUEUE_LENGTH(&(FIELD)->q); \ \ /* We must preserve our invariant of having no empty gaps \ * in the array so that se can place new items always at the \ * end (to avoid scanning for an empty spot). \ * Therefore we move our tail item into the removed slot and \ * adjust the iqueue positions (which are all O(1). \ */ \ \ if (_pos < _len) \ { \ (FIELD)->items[_pos] = (FIELD)->items[_len]; \ _VAL_QUEUE_MOVE(&(FIELD)->q, _len, _pos); \ } \ } G_STMT_END /* SORTED_ARRAY_FOREACH_REMOVE: * * This a form of SORTED_ARRAY_REMOVE_INDEX but to be used when you * are within a SORTED_ARRAY_FOREACH() to avoid extra scanning. */ #define SORTED_ARRAY_FOREACH_REMOVE(FIELD) \ G_STMT_START { \ guint8 _pos = _current; \ guint8 _len = VAL_QUEUE_LENGTH(&(FIELD)->q); \ \ g_assert (_len > 0); \ g_assert (_pos < _len); \ VAL_QUEUE_POP_AT(&(FIELD)->q, _pos); \ g_assert (VAL_QUEUE_LENGTH(&(FIELD)->q) == _len-1); \ _len--; \ \ /* We must preserve our invariant of having no empty gaps \ * in the array so that se can place new items always at the \ * end (to avoid scanning for an empty spot). \ * Therefore we move our tail item into the removed slot and \ * adjust the iqueue positions (which are all O(1). \ */ \ \ if (_pos < _len) \ { \ (FIELD)->items[_pos] = (FIELD)->items[_len]; \ _VAL_QUEUE_MOVE(&(FIELD)->q, _len, _pos); \ \ /* We might need to change the iter if next position moved */ \ if (_aiter == _len) \ _aiter = _pos; \ } \ \ } G_STMT_END /* * SORTED_ARRAY_FOREACH: * @FIELD: A pointer to a SortedArray * @Element: The type of the elements in @FIELD * @Name: the name for a pointer of type @Element * @LABlock: a {} tyle block to execute for each item. You may use * "break" to exit the foreach. * * Calls @Block for every element stored in @FIELD. A pointer to * each element will be provided as a variable named @Name. */ #define SORTED_ARRAY_FOREACH(FIELD, Element, Name, LABlock) \ G_STMT_START { \ for (glib_typeof((FIELD)->q.head) _aiter = (FIELD)->q.head; \ _aiter != VAL_QUEUE_INVALID(&(FIELD)->q); \ /* Do Nothing */) \ { \ G_GNUC_UNUSED glib_typeof((FIELD)->q.head) _current = _aiter; \ Element * Name = &(FIELD)->items[_aiter]; \ _aiter = (FIELD)->q.items[_aiter].next; \ LABlock \ } \ } G_STMT_END #define SORTED_ARRAY_FOREACH_REVERSE(FIELD, Element, Name, LABlock) \ G_STMT_START { \ for (glib_typeof((FIELD)->q.head) _aiter = (FIELD)->q.tail; \ _aiter != VAL_QUEUE_INVALID(&(FIELD)->q); \ /* Do Nothing */) \ { \ G_GNUC_UNUSED glib_typeof((FIELD)->q.head) _current = _aiter; \ Element * Name = &(FIELD)->items[_aiter]; \ _aiter = (FIELD)->q.items[_aiter].prev; \ LABlock \ } \ } G_STMT_END #define SORTED_ARRAY_FOREACH_PEEK(FIELD) \ (((FIELD)->q.items[_current].next != VAL_QUEUE_INVALID(&(FIELD)->q)) \ ? &(FIELD)->items[(FIELD)->q.items[_current].next] : NULL) #define SORTED_ARRAY_SPLIT(FIELD, SPLIT) \ G_STMT_START { \ guint8 _mid; \ \ SORTED_ARRAY_INIT(SPLIT); \ \ _mid = SORTED_ARRAY_LENGTH(FIELD) / 2; \ \ for (guint8 _z = 0; _z < _mid; _z++) \ { \ glib_typeof((FIELD)->items[0]) ele; \ SORTED_ARRAY_POP_TAIL(FIELD, ele); \ SORTED_ARRAY_PUSH_HEAD(SPLIT, ele); \ } \ } G_STMT_END #define SORTED_ARRAY_SPLIT2(FIELD, LEFT, RIGHT) \ G_STMT_START { \ guint8 mid; \ \ SORTED_ARRAY_INIT(LEFT); \ SORTED_ARRAY_INIT(RIGHT); \ \ mid = SORTED_ARRAY_LENGTH(FIELD) / 2; \ \ for (guint8 i = 0; i < mid; i++) \ { \ glib_typeof((FIELD)->items[0]) ele; \ SORTED_ARRAY_POP_TAIL(FIELD, ele); \ SORTED_ARRAY_PUSH_HEAD(RIGHT, ele); \ } \ \ while (!SORTED_ARRAY_IS_EMPTY(FIELD)) \ { \ glib_typeof((FIELD)->items[0]) ele; \ SORTED_ARRAY_POP_TAIL(FIELD, ele); \ SORTED_ARRAY_PUSH_HEAD(LEFT, ele); \ } \ } G_STMT_END #define SORTED_ARRAY_PEEK_HEAD(FIELD) ((FIELD)->items[VAL_QUEUE_PEEK_HEAD(&(FIELD)->q)]) #define SORTED_ARRAY_POP_HEAD(FIELD,_ele) SORTED_ARRAY_REMOVE_INDEX(FIELD, 0, _ele) #define SORTED_ARRAY_POP_TAIL(FIELD,_ele) SORTED_ARRAY_REMOVE_INDEX(FIELD, SORTED_ARRAY_LENGTH(FIELD)-1, _ele) #define SORTED_ARRAY_PUSH_HEAD(FIELD, ele) \ G_STMT_START { \ guint8 _pos = VAL_QUEUE_LENGTH(&(FIELD)->q); \ g_assert_cmpint (_pos, <, G_N_ELEMENTS ((FIELD)->items)); \ (FIELD)->items[_pos] = ele; \ VAL_QUEUE_PUSH_HEAD(&(FIELD)->q, _pos); \ } G_STMT_END #define SORTED_ARRAY_PUSH_TAIL(FIELD, ele) \ G_STMT_START { \ guint8 _pos = VAL_QUEUE_LENGTH(&(FIELD)->q); \ g_assert_cmpint (_pos, <, G_N_ELEMENTS ((FIELD)->items)); \ (FIELD)->items[_pos] = ele; \ VAL_QUEUE_PUSH_TAIL(&(FIELD)->q, _pos); \ } G_STMT_END #define CJH_TEXT_REGION_MAX_BRANCHES 26 #define CJH_TEXT_REGION_MIN_BRANCHES (CJH_TEXT_REGION_MAX_BRANCHES/3) #define CJH_TEXT_REGION_MAX_RUNS 26 #define CJH_TEXT_REGION_MIN_RUNS (CJH_TEXT_REGION_MAX_RUNS/3) typedef union _CjhTextRegionNode CjhTextRegionNode; typedef struct _CjhTextRegionBranch CjhTextRegionBranch; typedef struct _CjhTextRegionLeaf CjhTextRegionLeaf; typedef struct _CjhTextRegionChild CjhTextRegionChild; struct _CjhTextRegionChild { CjhTextRegionNode *node; gsize length; }; struct _CjhTextRegionBranch { CjhTextRegionNode *tagged_parent; CjhTextRegionNode *prev; CjhTextRegionNode *next; SORTED_ARRAY_FIELD (CjhTextRegionChild, CJH_TEXT_REGION_MAX_BRANCHES) children; }; struct _CjhTextRegionLeaf { CjhTextRegionNode *tagged_parent; CjhTextRegionNode *prev; CjhTextRegionNode *next; SORTED_ARRAY_FIELD (CjhTextRegionRun, CJH_TEXT_REGION_MAX_RUNS) runs; }; union _CjhTextRegionNode { /* pointer to the parent, low bit 0x1 means leaf node */ CjhTextRegionNode *tagged_parent; struct _CjhTextRegionLeaf leaf; struct _CjhTextRegionBranch branch; }; struct _CjhTextRegion { CjhTextRegionNode root; CjhTextRegionJoinFunc join_func; CjhTextRegionSplitFunc split_func; gsize length; CjhTextRegionNode *cached_result; gsize cached_result_offset; }; #define TAG(ptr,val) GSIZE_TO_POINTER(GPOINTER_TO_SIZE(ptr)|(gsize)val) #define UNTAG(ptr) GSIZE_TO_POINTER(GPOINTER_TO_SIZE(ptr) & ~(gsize)1) static inline CjhTextRegionNode * cjh_text_region_node_get_parent (CjhTextRegionNode *node) { if (node == NULL) return NULL; return UNTAG (node->tagged_parent); } static inline gboolean cjh_text_region_node_is_leaf (CjhTextRegionNode *node) { CjhTextRegionNode *parent = cjh_text_region_node_get_parent (node); return parent != NULL && node->tagged_parent != parent; } static inline void cjh_text_region_node_set_parent (CjhTextRegionNode *node, CjhTextRegionNode *parent) { node->tagged_parent = TAG (parent, cjh_text_region_node_is_leaf (node)); } static inline gsize cjh_text_region_node_length (CjhTextRegionNode *node) { gsize length = 0; g_assert (node != NULL); if (cjh_text_region_node_is_leaf (node)) { SORTED_ARRAY_FOREACH (&node->leaf.runs, CjhTextRegionRun, run, { length += run->length; }); } else { SORTED_ARRAY_FOREACH (&node->branch.children, CjhTextRegionChild, child, { length += child->length; }); } return length; } static inline CjhTextRegionNode * _cjh_text_region_get_first_leaf (CjhTextRegion *self) { for (CjhTextRegionNode *iter = &self->root; iter; iter = SORTED_ARRAY_PEEK_HEAD (&iter->branch.children).node) { if (cjh_text_region_node_is_leaf (iter)) return iter; } g_assert_not_reached (); } G_END_DECLS 07070100000012000081A40000000000000000000000016712C6CB0000191D000000000000000000000000000000000000002D00000000libspelling-0.4.4/lib/cjhtextregionprivate.h/* cjhtextregionprivate.h * * Copyright 2021-2023 Christian Hergert <chergert@redhat.com> * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <glib.h> G_BEGIN_DECLS typedef struct _CjhTextRegion CjhTextRegion; typedef struct _CjhTextRegionRun { gsize length; gpointer data; } CjhTextRegionRun; /* * CjhTextRegionForeachFunc: * @offset: the offset in characters within the text region * @run: the run of text and data pointer * @user_data: user data supplied * * Function callback to iterate through runs within a text region. * * Returns: %FALSE to continue iteration, otherwise %TRUE to stop. */ typedef gboolean (*CjhTextRegionForeachFunc) (gsize offset, const CjhTextRegionRun *run, gpointer user_data); /* * CjhTextRegionJoinFunc: * * This callback is used to determine if two runs can be joined together. * This is useful when you have similar data pointers between two runs * and seeing them as one run is irrelevant to the code using the * text region. * * The default calllback for joining will return %FALSE so that no joins * may occur. * * Returns: %TRUE if the runs can be joined; otherwise %FALSE */ typedef gboolean (*CjhTextRegionJoinFunc) (gsize offset, const CjhTextRegionRun *left, const CjhTextRegionRun *right); /* * CjhTextRegionSplitFunc: * * This function is responsible for splitting a run into two runs. * This can happen a delete happens in the middle of a run. * * By default, @left will contain the run prior to the delete, and * @right will contain the run after the delete. * * You can use the run lengths to determine where the delete was made * using @offset which is an absolute offset from the beginning of the * region. * * If you would like to keep a single run after the deletion, then * set @right to contain a length of zero and add it's previous * length to @left. * * All the length in @left and @right must be accounted for. * * This function is useful when using CjhTextRegion as a piecetable * where you want to adjust the data pointer to point at a new * section of an original or change buffer. */ typedef void (*CjhTextRegionSplitFunc) (gsize offset, const CjhTextRegionRun *run, CjhTextRegionRun *left, CjhTextRegionRun *right); CjhTextRegion *_cjh_text_region_new (CjhTextRegionJoinFunc join_func, CjhTextRegionSplitFunc split_func); void _cjh_text_region_insert (CjhTextRegion *region, gsize offset, gsize length, gpointer data); void _cjh_text_region_replace (CjhTextRegion *region, gsize offset, gsize length, gpointer data); void _cjh_text_region_remove (CjhTextRegion *region, gsize offset, gsize length); guint _cjh_text_region_get_length (CjhTextRegion *region); void _cjh_text_region_foreach (CjhTextRegion *region, CjhTextRegionForeachFunc func, gpointer user_data); void _cjh_text_region_foreach_in_range (CjhTextRegion *region, gsize begin, gsize end, CjhTextRegionForeachFunc func, gpointer user_data); void _cjh_text_region_free (CjhTextRegion *region); static inline gboolean _cjh_text_region_get_run_at_offset_cb (gsize offset, const CjhTextRegionRun *run, gpointer user_data) { struct { const CjhTextRegionRun *run; gsize real_offset; } *state = user_data; state->run = run; state->real_offset = offset; return TRUE; } static inline const CjhTextRegionRun * _cjh_text_region_get_run_at_offset (CjhTextRegion *region, gsize offset, gsize *real_offset) { struct { const CjhTextRegionRun *run; gsize real_offset; } state = { NULL, 0 }; gsize lo = offset; gsize hi = MIN (offset + 1, _cjh_text_region_get_length (region)); if (offset == _cjh_text_region_get_length (region)) { *real_offset = offset; return NULL; } _cjh_text_region_foreach_in_range (region, lo, hi, _cjh_text_region_get_run_at_offset_cb, &state); *real_offset = state.real_offset; return state.run; } G_END_DECLS 07070100000013000081A40000000000000000000000016712C6CB00004310000000000000000000000000000000000000002900000000libspelling-0.4.4/lib/egg-action-group.h/* egg-action-group.h * * Copyright 2017-2023 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <gio/gio.h> G_BEGIN_DECLS #define EGG_DEFINE_ACTION_GROUP(Type, prefix, ...) \ struct _##Type##ActionEntry { \ const gchar *name; \ void (*activate) (Type *self, GVariant *param); \ const gchar *parameter_type; \ const gchar *state; \ void (*change_state) (Type *self, GVariant *state); \ } prefix##_actions[] = __VA_ARGS__; \ \ typedef struct { \ GVariant *state; \ GVariant *state_hint; \ guint enabled : 1; \ } Type##ActionInfo; \ \ static gboolean \ _##prefix##_has_action (GActionGroup *group, \ const gchar *name) \ { \ for (guint i = 0; i < G_N_ELEMENTS(prefix##_actions); i++) \ { \ if (g_strcmp0 (name, prefix##_actions[i].name) == 0) \ return TRUE; \ } \ return FALSE; \ } \ \ static gchar ** \ _##prefix##_list_actions (GActionGroup *group) \ { \ GPtrArray *ar = g_ptr_array_new (); \ \ for (guint i = 0; i < G_N_ELEMENTS(prefix##_actions); i++) \ g_ptr_array_add (ar, g_strdup (prefix##_actions[i].name)); \ g_ptr_array_add (ar, NULL); \ \ return (gchar **)g_ptr_array_free (ar, FALSE); \ } \ \ static void \ _##prefix##_action_info_free (gpointer data) \ { \ Type##ActionInfo *info = data; \ g_clear_pointer (&info->state, g_variant_unref); \ g_clear_pointer (&info->state_hint, g_variant_unref); \ g_slice_free (Type##ActionInfo, info); \ } \ \ static Type##ActionInfo * \ _##prefix##_get_action_info (GActionGroup *group, \ const gchar *name) \ { \ g_autofree gchar *fullname = g_strdup_printf ("ACTION-INFO:%s", name); \ Type##ActionInfo *info = g_object_get_data (G_OBJECT (group), fullname); \ if (info == NULL) \ { \ info = g_slice_new0 (Type##ActionInfo); \ info->enabled = TRUE; \ for (guint i = 0; i < G_N_ELEMENTS(prefix##_actions); i++) \ { \ if (g_strcmp0 (prefix##_actions[i].name, name) == 0) \ { \ if (prefix##_actions[i].state != NULL) \ info->state = g_variant_parse ( \ NULL, prefix##_actions[i].state, NULL, NULL, NULL); \ break; \ } \ } \ g_object_set_data_full (G_OBJECT (group), fullname, info, \ _##prefix##_action_info_free); \ } \ return info; \ } \ \ G_GNUC_UNUSED static inline GVariant * \ prefix##_get_action_state (Type *self, \ const gchar *name) \ { \ Type##ActionInfo *info = _##prefix##_get_action_info (G_ACTION_GROUP (self), \ name); \ return info->state; \ } \ \ G_GNUC_UNUSED static inline void \ prefix##_set_action_state (Type *self, \ const gchar *name, \ GVariant *state) \ { \ Type##ActionInfo *info = _##prefix##_get_action_info (G_ACTION_GROUP (self), \ name); \ if (state != info->state) \ { \ g_clear_pointer (&info->state, g_variant_unref); \ info->state = state ? g_variant_ref_sink (state) : NULL; \ g_action_group_action_state_changed (G_ACTION_GROUP (self), name, state); \ } \ } \ \ G_GNUC_UNUSED static inline void \ prefix##_set_action_enabled (Type *self, \ const gchar *name, \ gboolean enabled) \ { \ Type##ActionInfo *info = _##prefix##_get_action_info (G_ACTION_GROUP (self), \ name); \ if (enabled != info->enabled) \ { \ info->enabled = !!enabled; \ g_action_group_action_enabled_changed (G_ACTION_GROUP (self), \ name, enabled); \ } \ } \ \ static void \ _##prefix##_change_action_state (GActionGroup *group, \ const gchar *name, \ GVariant *state) \ { \ for (guint i = 0; i < G_N_ELEMENTS(prefix##_actions); i++) \ { \ if (g_strcmp0 (name, prefix##_actions[i].name) == 0) \ { \ if (prefix##_actions[i].change_state) \ prefix##_actions[i].change_state ((Type*)group, state); \ return; \ } \ } \ } \ \ static void \ _##prefix##_activate_action (GActionGroup *group, \ const gchar *name, \ GVariant *param) \ { \ for (guint i = 0; i < G_N_ELEMENTS(prefix##_actions); i++) \ { \ if (g_strcmp0 (name, prefix##_actions[i].name) == 0) \ { \ if (prefix##_actions[i].activate) \ prefix##_actions[i].activate ((Type*)group, param); \ return; \ } \ } \ } \ \ static gboolean \ _##prefix##_query_action (GActionGroup *group, \ const gchar *name, \ gboolean *enabled, \ const GVariantType **parameter_type, \ const GVariantType **state_type, \ GVariant **state_hint, \ GVariant **state) \ { \ if (enabled) *enabled = FALSE; \ if (parameter_type) *parameter_type = NULL ; \ if (state_type) *state_type = NULL ; \ if (state_hint) *state_hint = NULL ; \ if (state) *state = NULL ; \ for (guint i = 0; i < G_N_ELEMENTS(prefix##_actions); i++) \ { \ if (g_strcmp0 (name, prefix##_actions[i].name) == 0) \ { \ Type##ActionInfo *info = _##prefix##_get_action_info(group, name); \ if (prefix##_actions[i].change_state && state_type) \ *state_type = prefix##_actions[i].parameter_type ? \ G_VARIANT_TYPE(prefix##_actions[i].parameter_type) : \ NULL; \ else if (prefix##_actions[i].activate && parameter_type) \ *parameter_type = prefix##_actions[i].parameter_type ? \ G_VARIANT_TYPE(prefix##_actions[i].parameter_type) :\ NULL; \ if (state_hint) \ *state_hint = info->state_hint != NULL ? \ g_variant_ref (info->state_hint) : NULL; \ if (state) \ *state = info->state != NULL ? \ g_variant_ref (info->state) : NULL; \ if (enabled) \ *enabled = info->enabled; \ return TRUE; \ } \ } \ return FALSE; \ } \ \ static void \ prefix##_init_action_group (GActionGroupInterface *iface) \ { \ iface->has_action = _##prefix##_has_action; \ iface->list_actions = _##prefix##_list_actions; \ iface->change_action_state = _##prefix##_change_action_state; \ iface->activate_action = _##prefix##_activate_action; \ iface->query_action = _##prefix##_query_action; \ } G_END_DECLS 07070100000014000041ED0000000000000000000000026712C6CB00000000000000000000000000000000000000000000001E00000000libspelling-0.4.4/lib/enchant07070100000015000081A40000000000000000000000016712C6CB000000EC000000000000000000000000000000000000002A00000000libspelling-0.4.4/lib/enchant/meson.buildlibenchant_dep = dependency('enchant-2') libicu_dep = dependency('icu-uc') libspelling_deps += [libenchant_dep, libicu_dep] libspelling_private_sources += files([ 'spelling-enchant-dictionary.c', 'spelling-enchant-provider.c', ]) 07070100000016000081A40000000000000000000000016712C6CB000027A0000000000000000000000000000000000000003C00000000libspelling-0.4.4/lib/enchant/spelling-enchant-dictionary.c/* spelling-enchant-dictionary.c * * Copyright 2021-2023 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <pango/pango.h> #include <enchant.h> #include "spelling-enchant-dictionary.h" #define MAX_RESULTS 10 struct _SpellingEnchantDictionary { SpellingDictionary parent_instance; PangoLanguage *language; EnchantDict *native; char *extra_word_chars; }; G_DEFINE_FINAL_TYPE (SpellingEnchantDictionary, spelling_enchant_dictionary, SPELLING_TYPE_DICTIONARY) enum { PROP_0, PROP_NATIVE, N_PROPS }; static GParamSpec *properties [N_PROPS]; /** * spelling_enchant_dictionary_new: * * Create a new `SpellingEnchantDictionary`. * * Returns: (transfer full): a newly created `SpellingEnchantDictionary` */ SpellingDictionary * spelling_enchant_dictionary_new (const char *code, gpointer native) { return g_object_new (SPELLING_TYPE_ENCHANT_DICTIONARY, "code", code, "native", native, NULL); } static inline gboolean word_is_number (const char *word, gsize word_len) { g_assert (word_len > 0); for (gsize i = 0; i < word_len; i++) { if (word[i] < '0' || word[i] > '9') return FALSE; } return TRUE; } static gboolean spelling_enchant_dictionary_contains_word (SpellingDictionary *dictionary, const char *word, gssize word_len) { SpellingEnchantDictionary *self = (SpellingEnchantDictionary *)dictionary; g_assert (SPELLING_IS_ENCHANT_DICTIONARY (self)); g_assert (word != NULL); g_assert (word_len >= 0); if (word_is_number (word, word_len)) return TRUE; return enchant_dict_check (self->native, word, word_len) == 0; } static char ** strv_copy_n (const char * const *strv, gsize n) { char **copy = g_new (char *, n + 1); for (gsize i = 0; i < n; i++) copy[i] = g_strdup (strv[i]); copy[n] = NULL; return copy; } static char ** spelling_enchant_dictionary_list_corrections (SpellingDictionary *dictionary, const char *word, gssize word_len) { SpellingEnchantDictionary *self = (SpellingEnchantDictionary *)dictionary; size_t count = 0; char **tmp; char **ret = NULL; g_assert (SPELLING_IS_ENCHANT_DICTIONARY (self)); g_assert (word != NULL); g_assert (word_len > 0); if ((tmp = enchant_dict_suggest (self->native, word, word_len, &count)) && count > 0) { if (g_strv_length (tmp) <= MAX_RESULTS) ret = g_strdupv (tmp); else ret = strv_copy_n ((const char * const *)tmp, MAX_RESULTS); enchant_dict_free_string_list (self->native, tmp); } return g_steal_pointer (&ret); } static char ** spelling_enchant_dictionary_split (SpellingEnchantDictionary *self, const char *words) { PangoLogAttr *attrs; GArray *ar; gsize n_chars; g_assert (SPELLING_IS_ENCHANT_DICTIONARY (self)); if (words == NULL || self->language == NULL) return NULL; /* We don't care about splitting obnoxious stuff */ if ((n_chars = g_utf8_strlen (words, -1)) > 1024) return NULL; attrs = g_newa (PangoLogAttr, n_chars + 1); pango_get_log_attrs (words, -1, -1, self->language, attrs, n_chars + 1); ar = g_array_new (TRUE, FALSE, sizeof (char*)); for (gsize i = 0; i < n_chars + 1; i++) { if (attrs[i].is_word_start) { for (gsize j = i + 1; j < n_chars + 1; j++) { if (attrs[j].is_word_end) { char *substr = g_utf8_substring (words, i, j); g_array_append_val (ar, substr); i = j; break; } } } } return (char **)(gpointer)g_array_free (ar, FALSE); } static void spelling_enchant_dictionary_add_all_to_session (SpellingEnchantDictionary *self, const char * const *words) { g_assert (SPELLING_IS_ENCHANT_DICTIONARY (self)); if (words == NULL || words[0] == NULL) return; for (guint i = 0; words[i]; i++) enchant_dict_add_to_session (self->native, words[i], -1); } static void spelling_enchant_dictionary_add_word (SpellingDictionary *dictionary, const char *word) { SpellingEnchantDictionary *self = (SpellingEnchantDictionary *)dictionary; g_assert (SPELLING_IS_ENCHANT_DICTIONARY (self)); g_assert (word != NULL); enchant_dict_add (self->native, word, -1); } static void spelling_enchant_dictionary_ignore_word (SpellingDictionary *dictionary, const char *word) { SpellingEnchantDictionary *self = (SpellingEnchantDictionary *)dictionary; g_assert (SPELLING_IS_ENCHANT_DICTIONARY (self)); g_assert (word != NULL); enchant_dict_add_to_session (self->native, word, -1); } static const char * spelling_enchant_dictionary_get_extra_word_chars (SpellingDictionary *dictionary) { SpellingEnchantDictionary *self = (SpellingEnchantDictionary *)dictionary; g_assert (SPELLING_IS_ENCHANT_DICTIONARY (self)); return self->extra_word_chars; } static void spelling_enchant_dictionary_constructed (GObject *object) { SpellingEnchantDictionary *self = (SpellingEnchantDictionary *)object; g_auto(GStrv) split = NULL; const char *extra_word_chars; const char *code; g_assert (SPELLING_IS_ENCHANT_DICTIONARY (self)); G_OBJECT_CLASS (spelling_enchant_dictionary_parent_class)->constructed (object); code = spelling_dictionary_get_code (SPELLING_DICTIONARY (self)); self->language = pango_language_from_string (code); if ((split = spelling_enchant_dictionary_split (self, g_get_real_name ()))) spelling_enchant_dictionary_add_all_to_session (self, (const char * const *)split); if ((extra_word_chars = enchant_dict_get_extra_word_characters (self->native))) { const char *end_pos = NULL; /* Sometimes we get invalid UTF-8 from enchant, so handle that directly. * In particular, the data seems corrupted from Fedora. */ if (g_utf8_validate (extra_word_chars, -1, &end_pos)) self->extra_word_chars = g_strdup (extra_word_chars); else self->extra_word_chars = g_strndup (extra_word_chars, end_pos - extra_word_chars); } } static void spelling_enchant_dictionary_finalize (GObject *object) { SpellingEnchantDictionary *self = (SpellingEnchantDictionary *)object; /* Owned by provider */ self->native = NULL; G_OBJECT_CLASS (spelling_enchant_dictionary_parent_class)->finalize (object); } static void spelling_enchant_dictionary_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { SpellingEnchantDictionary *self = SPELLING_ENCHANT_DICTIONARY (object); switch (prop_id) { case PROP_NATIVE: g_value_set_pointer (value, self->native); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void spelling_enchant_dictionary_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { SpellingEnchantDictionary *self = SPELLING_ENCHANT_DICTIONARY (object); switch (prop_id) { case PROP_NATIVE: self->native = g_value_get_pointer (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void spelling_enchant_dictionary_class_init (SpellingEnchantDictionaryClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); SpellingDictionaryClass *dictionary_class = SPELLING_DICTIONARY_CLASS (klass); object_class->constructed = spelling_enchant_dictionary_constructed; object_class->finalize = spelling_enchant_dictionary_finalize; object_class->get_property = spelling_enchant_dictionary_get_property; object_class->set_property = spelling_enchant_dictionary_set_property; dictionary_class->contains_word = spelling_enchant_dictionary_contains_word; dictionary_class->list_corrections = spelling_enchant_dictionary_list_corrections; dictionary_class->add_word = spelling_enchant_dictionary_add_word; dictionary_class->ignore_word = spelling_enchant_dictionary_ignore_word; dictionary_class->get_extra_word_chars = spelling_enchant_dictionary_get_extra_word_chars; properties[PROP_NATIVE] = g_param_spec_pointer ("native", "Native", "The native enchant dictionary", (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_properties (object_class, N_PROPS, properties); } static void spelling_enchant_dictionary_init (SpellingEnchantDictionary *self) { } gpointer spelling_enchant_dictionary_get_native (SpellingEnchantDictionary *self) { g_return_val_if_fail (SPELLING_IS_ENCHANT_DICTIONARY (self), NULL); return self->native; } 07070100000017000081A40000000000000000000000016712C6CB00000576000000000000000000000000000000000000003C00000000libspelling-0.4.4/lib/enchant/spelling-enchant-dictionary.h/* spelling-enchant-dictionary.h * * Copyright 2021-2023 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "spelling-dictionary-internal.h" G_BEGIN_DECLS #define SPELLING_TYPE_ENCHANT_DICTIONARY (spelling_enchant_dictionary_get_type()) G_DECLARE_FINAL_TYPE (SpellingEnchantDictionary, spelling_enchant_dictionary, SPELLING, ENCHANT_DICTIONARY, SpellingDictionary) SpellingDictionary *spelling_enchant_dictionary_new (const char *code, gpointer native); gpointer spelling_enchant_dictionary_get_native (SpellingEnchantDictionary *self); G_END_DECLS 07070100000018000081A40000000000000000000000016712C6CB000015B1000000000000000000000000000000000000003A00000000libspelling-0.4.4/lib/enchant/spelling-enchant-provider.c/* spelling-enchant-provider.c * * Copyright 2021-2023 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <enchant.h> #include <locale.h> #include <unicode/uloc.h> #include <gio/gio.h> #include "spelling-language-private.h" #include "spelling-enchant-dictionary.h" #include "spelling-enchant-provider.h" struct _SpellingEnchantProvider { SpellingProvider parent_instance; }; G_DEFINE_FINAL_TYPE (SpellingEnchantProvider, spelling_enchant_provider, SPELLING_TYPE_PROVIDER) static GHashTable *dictionaries; static EnchantBroker * get_broker (void) { static EnchantBroker *broker; if (broker == NULL) broker = enchant_broker_init (); return broker; } static char * _icu_uchar_to_char (const UChar *input, gsize max_input_len) { GString *str; g_assert (input != NULL); g_assert (max_input_len > 0); if (input[0] == 0) return NULL; str = g_string_new (NULL); for (gsize i = 0; i < max_input_len; i++) { if (input[i] == 0) break; g_string_append_unichar (str, input[i]); } return g_string_free (str, FALSE); } static char * get_display_name (const char *code) { const char * const *names = g_get_language_names (); for (guint i = 0; names[i]; i++) { UChar ret[256]; UErrorCode status = U_ZERO_ERROR; uloc_getDisplayName (code, names[i], ret, G_N_ELEMENTS (ret), &status); if (status == U_ZERO_ERROR) return _icu_uchar_to_char (ret, G_N_ELEMENTS (ret)); } return NULL; } static char * get_display_language (const char *code) { const char * const *names = g_get_language_names (); for (guint i = 0; names[i]; i++) { UChar ret[256]; UErrorCode status = U_ZERO_ERROR; uloc_getDisplayLanguage (code, names[i], ret, G_N_ELEMENTS (ret), &status); if (status == U_ZERO_ERROR) return _icu_uchar_to_char (ret, G_N_ELEMENTS (ret)); } return NULL; } /** * spelling_enchant_provider_new: * * Create a new `SpellingEnchantProvider`. * * Returns: (transfer full): a newly created `SpellingEnchantProvider` */ SpellingProvider * spelling_enchant_provider_new (void) { return g_object_new (SPELLING_TYPE_ENCHANT_PROVIDER, "display-name", "Enchant", NULL); } static gboolean spelling_enchant_provider_supports_language (SpellingProvider *provider, const char *language) { g_assert (SPELLING_IS_ENCHANT_PROVIDER (provider)); g_assert (language != NULL); return enchant_broker_dict_exists (get_broker (), language); } static void list_languages_cb (const char * const lang_tag, const char * const provider_name, const char * const provider_desc, const char * const provider_file, gpointer user_data) { GListStore *store = user_data; char *name = get_display_name (lang_tag); char *group = get_display_language (lang_tag); if (name != NULL) { g_autoptr(SpellingLanguage) language = spelling_language_new (name, lang_tag, group); g_list_store_append (store, language); } g_free (name); g_free (group); } static GListModel * spelling_enchant_provider_list_languages (SpellingProvider *provider) { EnchantBroker *broker = get_broker (); GListStore *store = g_list_store_new (SPELLING_TYPE_LANGUAGE); enchant_broker_list_dicts (broker, list_languages_cb, store); return G_LIST_MODEL (store); } static SpellingDictionary * spelling_enchant_provider_load_dictionary (SpellingProvider *provider, const char *language) { SpellingDictionary *ret; g_assert (SPELLING_IS_ENCHANT_PROVIDER (provider)); g_assert (language != NULL); if (dictionaries == NULL) dictionaries = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref); if (!(ret = g_hash_table_lookup (dictionaries, language))) { EnchantDict *dict = enchant_broker_request_dict (get_broker (), language); if (dict == NULL) return NULL; ret = spelling_enchant_dictionary_new (language, dict); g_hash_table_insert (dictionaries, (char *)g_intern_string (language), ret); } return ret ? g_object_ref (ret) : NULL; } static void spelling_enchant_provider_class_init (SpellingEnchantProviderClass *klass) { SpellingProviderClass *spell_provider_class = SPELLING_PROVIDER_CLASS (klass); spell_provider_class->supports_language = spelling_enchant_provider_supports_language; spell_provider_class->list_languages = spelling_enchant_provider_list_languages; spell_provider_class->load_dictionary= spelling_enchant_provider_load_dictionary; } static void spelling_enchant_provider_init (SpellingEnchantProvider *self) { } 07070100000019000081A40000000000000000000000016712C6CB00000482000000000000000000000000000000000000003A00000000libspelling-0.4.4/lib/enchant/spelling-enchant-provider.h/* spelling-enchant-provider.h * * Copyright 2021-2023 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "spelling-provider-internal.h" G_BEGIN_DECLS #define SPELLING_TYPE_ENCHANT_PROVIDER (spelling_enchant_provider_get_type()) G_DECLARE_FINAL_TYPE (SpellingEnchantProvider, spelling_enchant_provider, SPELLING, ENCHANT_PROVIDER, SpellingProvider) SpellingProvider *spelling_enchant_provider_new (void); G_END_DECLS 0707010000001A000081A40000000000000000000000016712C6CB000015E5000000000000000000000000000000000000002500000000libspelling-0.4.4/lib/gconstructor.h/* GLIB - Library of useful routines for C programming * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see <http://www.gnu.org/licenses/>. */ /* * Modified by the GLib Team and others 1997-2000. See the AUTHORS * file for a list of people on the GLib Team. See the ChangeLog * files for a list of changes. These files are distributed with * GLib at ftp://ftp.gtk.org/pub/gtk/. */ #ifndef __G_CONSTRUCTOR_H__ #define __G_CONSTRUCTOR_H__ /* If G_HAS_CONSTRUCTORS is true then the compiler support *both* constructors and destructors, in a usable way, including e.g. on library unload. If not you're on your own. Some compilers need #pragma to handle this, which does not work with macros, so the way you need to use this is (for constructors): #ifdef G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA #pragma G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(my_constructor) #endif G_DEFINE_CONSTRUCTOR(my_constructor) static void my_constructor(void) { ... } */ #ifndef __GTK_DOC_IGNORE__ #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7) #define G_HAS_CONSTRUCTORS 1 #define G_DEFINE_CONSTRUCTOR(_func) static void __attribute__((constructor)) _func (void); #define G_DEFINE_DESTRUCTOR(_func) static void __attribute__((destructor)) _func (void); #elif defined (_MSC_VER) && (_MSC_VER >= 1500) /* Visual studio 2008 and later has _Pragma */ /* * Only try to include gslist.h if not already included via glib.h, * so that items using gconstructor.h outside of GLib (such as * GResources) continue to build properly. */ #ifndef __G_LIB_H__ #include "gslist.h" #endif #include <stdlib.h> #define G_HAS_CONSTRUCTORS 1 /* We do some weird things to avoid the constructors being optimized * away on VS2015 if WholeProgramOptimization is enabled. First we * make a reference to the array from the wrapper to make sure its * references. Then we use a pragma to make sure the wrapper function * symbol is always included at the link stage. Also, the symbols * need to be extern (but not dllexport), even though they are not * really used from another object file. */ /* We need to account for differences between the mangling of symbols * for x86 and x64/ARM/ARM64 programs, as symbols on x86 are prefixed * with an underscore but symbols on x64/ARM/ARM64 are not. */ #ifdef _M_IX86 #define G_MSVC_SYMBOL_PREFIX "_" #else #define G_MSVC_SYMBOL_PREFIX "" #endif #define G_DEFINE_CONSTRUCTOR(_func) G_MSVC_CTOR (_func, G_MSVC_SYMBOL_PREFIX) #define G_DEFINE_DESTRUCTOR(_func) G_MSVC_DTOR (_func, G_MSVC_SYMBOL_PREFIX) #define G_MSVC_CTOR(_func,_sym_prefix) \ static void _func(void); \ extern int (* _array ## _func)(void); \ int _func ## _wrapper(void) { _func(); g_slist_find (NULL, _array ## _func); return 0; } \ __pragma(comment(linker,"/include:" _sym_prefix # _func "_wrapper")) \ __pragma(section(".CRT$XCU",read)) \ __declspec(allocate(".CRT$XCU")) int (* _array ## _func)(void) = _func ## _wrapper; #define G_MSVC_DTOR(_func,_sym_prefix) \ static void _func(void); \ extern int (* _array ## _func)(void); \ int _func ## _constructor(void) { atexit (_func); g_slist_find (NULL, _array ## _func); return 0; } \ __pragma(comment(linker,"/include:" _sym_prefix # _func "_constructor")) \ __pragma(section(".CRT$XCU",read)) \ __declspec(allocate(".CRT$XCU")) int (* _array ## _func)(void) = _func ## _constructor; #elif defined (_MSC_VER) #define G_HAS_CONSTRUCTORS 1 /* Pre Visual studio 2008 must use #pragma section */ #define G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA 1 #define G_DEFINE_DESTRUCTOR_NEEDS_PRAGMA 1 #define G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(_func) \ section(".CRT$XCU",read) #define G_DEFINE_CONSTRUCTOR(_func) \ static void _func(void); \ static int _func ## _wrapper(void) { _func(); return 0; } \ __declspec(allocate(".CRT$XCU")) static int (*p)(void) = _func ## _wrapper; #define G_DEFINE_DESTRUCTOR_PRAGMA_ARGS(_func) \ section(".CRT$XCU",read) #define G_DEFINE_DESTRUCTOR(_func) \ static void _func(void); \ static int _func ## _constructor(void) { atexit (_func); return 0; } \ __declspec(allocate(".CRT$XCU")) static int (* _array ## _func)(void) = _func ## _constructor; #elif defined(__SUNPRO_C) /* This is not tested, but i believe it should work, based on: * http://opensource.apple.com/source/OpenSSL098/OpenSSL098-35/src/fips/fips_premain.c */ #define G_HAS_CONSTRUCTORS 1 #define G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA 1 #define G_DEFINE_DESTRUCTOR_NEEDS_PRAGMA 1 #define G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(_func) \ init(_func) #define G_DEFINE_CONSTRUCTOR(_func) \ static void _func(void); #define G_DEFINE_DESTRUCTOR_PRAGMA_ARGS(_func) \ fini(_func) #define G_DEFINE_DESTRUCTOR(_func) \ static void _func(void); #else /* constructors not supported for this compiler */ #endif #endif /* __GTK_DOC_IGNORE__ */ #endif /* __G_CONSTRUCTOR_H__ */ 0707010000001B000081A40000000000000000000000016712C6CB00001F8D000000000000000000000000000000000000002500000000libspelling-0.4.4/lib/gdkarrayimpl.c/* * Copyright © 2020 Benjamin Otte * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see <http://www.gnu.org/licenses/>. * * Authors: Benjamin Otte <otte@gnome.org> */ #include <glib.h> G_BEGIN_DECLS #ifndef GDK_ARRAY_TYPE_NAME #define GDK_ARRAY_TYPE_NAME GdkArray #endif #ifndef GDK_ARRAY_NAME #define GDK_ARRAY_NAME gdk_array #endif #ifndef GDK_ARRAY_ELEMENT_TYPE #define GDK_ARRAY_ELEMENT_TYPE gpointer #endif #ifdef GDK_ARRAY_PREALLOC #if GDK_ARRAY_PREALLOC == 0 #undef GDK_ARRAY_PREALLOC #endif #endif #ifdef GDK_ARRAY_NULL_TERMINATED #define GDK_ARRAY_REAL_SIZE(_size) ((_size) + 1) #define GDK_ARRAY_MAX_SIZE (G_MAXSIZE / sizeof (_T_) - 1) #else #define GDK_ARRAY_REAL_SIZE(_size) (_size) #define GDK_ARRAY_MAX_SIZE (G_MAXSIZE / sizeof (_T_)) #endif /* make this readable */ #define _T_ GDK_ARRAY_ELEMENT_TYPE #define GdkArray GDK_ARRAY_TYPE_NAME #define gdk_array_paste_more(GDK_ARRAY_NAME, func_name) GDK_ARRAY_NAME ## _ ## func_name #define gdk_array_paste(GDK_ARRAY_NAME, func_name) gdk_array_paste_more (GDK_ARRAY_NAME, func_name) #define gdk_array(func_name) gdk_array_paste (GDK_ARRAY_NAME, func_name) typedef struct GdkArray GdkArray; struct GdkArray { _T_ *start; _T_ *end; _T_ *end_allocation; #ifdef GDK_ARRAY_PREALLOC _T_ preallocated[GDK_ARRAY_REAL_SIZE(GDK_ARRAY_PREALLOC)]; #endif }; /* no G_GNUC_UNUSED here, if you don't use an array type, remove it. */ static inline void gdk_array(init) (GdkArray *self) { #ifdef GDK_ARRAY_PREALLOC self->start = self->preallocated; self->end = self->start; self->end_allocation = self->start + GDK_ARRAY_PREALLOC; #ifdef GDK_ARRAY_NULL_TERMINATED *self->start = *(_T_[1]) { 0 }; #endif #else self->start = NULL; self->end = NULL; self->end_allocation = NULL; #endif } G_GNUC_UNUSED static inline gsize gdk_array(get_capacity) (const GdkArray *self) { return self->end_allocation - self->start; } G_GNUC_UNUSED static inline gsize gdk_array(get_size) (const GdkArray *self) { return self->end - self->start; } static inline void gdk_array(free_elements) (_T_ *start, _T_ *end) { #ifdef GDK_ARRAY_FREE_FUNC _T_ *e; for (e = start; e < end; e++) #ifdef GDK_ARRAY_BY_VALUE GDK_ARRAY_FREE_FUNC (e); #else GDK_ARRAY_FREE_FUNC (*e); #endif #endif } /* no G_GNUC_UNUSED here */ static inline void gdk_array(clear) (GdkArray *self) { gdk_array(free_elements) (self->start, self->end); #ifdef GDK_ARRAY_PREALLOC if (self->start != self->preallocated) #endif g_free (self->start); gdk_array(init) (self); } /* * gdk_array_steal: * @self: the array * * Steals all data in the array and clears the array. * * If you need to know the size of the data, you should query it * beforehand. * * Returns: The array's data **/ G_GNUC_UNUSED static inline _T_ * gdk_array(steal) (GdkArray *self) { _T_ *result; #ifdef GDK_ARRAY_PREALLOC if (self->start == self->preallocated) { gsize size = GDK_ARRAY_REAL_SIZE (gdk_array(get_size) (self)); result = g_new (_T_, size); memcpy (result, self->preallocated, sizeof (_T_) * size); } else #endif result = self->start; gdk_array(init) (self); return result; } G_GNUC_UNUSED static inline _T_ * gdk_array(get_data) (const GdkArray *self) { return self->start; } G_GNUC_UNUSED static inline _T_ * gdk_array(index) (const GdkArray *self, gsize pos) { return self->start + pos; } G_GNUC_UNUSED static inline gboolean gdk_array(is_empty) (const GdkArray *self) { return self->end == self->start; } G_GNUC_UNUSED static inline void gdk_array(reserve) (GdkArray *self, gsize n) { gsize new_capacity, size, capacity; if (G_UNLIKELY (n > GDK_ARRAY_MAX_SIZE)) g_error ("requesting array size of %zu, but maximum size is %zu", n, GDK_ARRAY_MAX_SIZE); capacity = gdk_array(get_capacity) (self); if (n <= capacity) return; size = gdk_array(get_size) (self); /* capacity * 2 can overflow, that's why we MAX() */ new_capacity = MAX (GDK_ARRAY_REAL_SIZE (n), capacity * 2); #ifdef GDK_ARRAY_PREALLOC if (self->start == self->preallocated) { self->start = g_new (_T_, new_capacity); memcpy (self->start, self->preallocated, sizeof (_T_) * GDK_ARRAY_REAL_SIZE (size)); } else #endif #ifdef GDK_ARRAY_NULL_TERMINATED if (self->start == NULL) { self->start = g_new (_T_, new_capacity); *self->start = *(_T_[1]) { 0 }; } else #endif self->start = g_renew (_T_, self->start, new_capacity); self->end = self->start + size; self->end_allocation = self->start + new_capacity; #ifdef GDK_ARRAY_NULL_TERMINATED self->end_allocation--; #endif } G_GNUC_UNUSED static inline void gdk_array(splice) (GdkArray *self, gsize pos, gsize removed, gboolean stolen, #ifdef GDK_ARRAY_BY_VALUE const _T_ *additions, #else _T_ *additions, #endif gsize added) { gsize size; gsize remaining; size = gdk_array(get_size) (self); g_assert (pos + removed <= size); remaining = size - pos - removed; if (!stolen) gdk_array(free_elements) (gdk_array(index) (self, pos), gdk_array(index) (self, pos + removed)); gdk_array(reserve) (self, size - removed + added); if (GDK_ARRAY_REAL_SIZE (remaining) && removed != added) memmove (gdk_array(index) (self, pos + added), gdk_array(index) (self, pos + removed), GDK_ARRAY_REAL_SIZE (remaining) * sizeof (_T_)); if (added) { if (additions) memcpy (gdk_array(index) (self, pos), additions, added * sizeof (_T_)); #ifndef GDK_ARRAY_NO_MEMSET else memset (gdk_array(index) (self, pos), 0, added * sizeof (_T_)); #endif } /* might overflow, but does the right thing */ self->end += added - removed; } G_GNUC_UNUSED static void gdk_array(set_size) (GdkArray *self, gsize new_size) { gsize old_size = gdk_array(get_size) (self); if (new_size > old_size) gdk_array(splice) (self, old_size, 0, FALSE, NULL, new_size - old_size); else gdk_array(splice) (self, new_size, old_size - new_size, FALSE, NULL, 0); } G_GNUC_UNUSED static void gdk_array(append) (GdkArray *self, #ifdef GDK_ARRAY_BY_VALUE _T_ *value) #else _T_ value) #endif { gdk_array(splice) (self, gdk_array(get_size) (self), 0, FALSE, #ifdef GDK_ARRAY_BY_VALUE value, #else &value, #endif 1); } #ifdef GDK_ARRAY_BY_VALUE G_GNUC_UNUSED static _T_ * gdk_array(get) (const GdkArray *self, gsize pos) { return gdk_array(index) (self, pos); } #else G_GNUC_UNUSED static _T_ gdk_array(get) (const GdkArray *self, gsize pos) { return *gdk_array(index) (self, pos); } #endif #ifndef GDK_ARRAY_NO_UNDEF #undef _T_ #undef GdkArray #undef gdk_array_paste_more #undef gdk_array_paste #undef gdk_array #undef GDK_ARRAY_REAL_SIZE #undef GDK_ARRAY_MAX_SIZE #undef GDK_ARRAY_BY_VALUE #undef GDK_ARRAY_ELEMENT_TYPE #undef GDK_ARRAY_FREE_FUNC #undef GDK_ARRAY_NAME #undef GDK_ARRAY_NULL_TERMINATED #undef GDK_ARRAY_PREALLOC #undef GDK_ARRAY_TYPE_NAME #undef GDK_ARRAY_NO_MEMSET #endif G_END_DECLS 0707010000001C000081A40000000000000000000000016712C6CB000004F5000000000000000000000000000000000000002400000000libspelling-0.4.4/lib/libspelling.h/* libspelling.h * * Copyright 2023 Christian Hergert * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <gtk/gtk.h> G_BEGIN_DECLS #define LIBSPELLING_INSIDE # include "spelling-checker.h" # include "spelling-dictionary.h" # include "spelling-init.h" # include "spelling-language.h" # include "spelling-provider.h" # include "spelling-text-buffer-adapter.h" # include "spelling-types.h" # include "spelling-version.h" # include "spelling-version-macros.h" #undef LIBSPELLING_INSIDE G_END_DECLS 0707010000001D000081A40000000000000000000000016712C6CB00000FA4000000000000000000000000000000000000002200000000libspelling-0.4.4/lib/meson.buildapi_version = '1' so_version = '2' libspelling_private_sources = [ 'cjhtextregion.c', 'spelling-cursor.c', 'spelling-empty-provider.c', 'spelling-engine.c', 'spelling-job.c', 'spelling-menu.c', ] libspelling_public_sources = [ 'spelling-init.c', 'spelling-checker.c', 'spelling-dictionary.c', 'spelling-language.c', 'spelling-provider.c', 'spelling-text-buffer-adapter.c', ] libspelling_public_headers = [ 'libspelling.h', 'spelling-checker.h', 'spelling-dictionary.h', 'spelling-init.h', 'spelling-language.h', 'spelling-provider.h', 'spelling-text-buffer-adapter.h', 'spelling-types.h', 'spelling-version-macros.h', ] libspelling_deps = [ libgio_dep, libgtk_dep, gtksource_dep, ] if profiler_enabled libspelling_deps += [libsysprof_capture_dep] endif if get_option('enchant').enabled() subdir('enchant') endif libspelling_sources = libspelling_private_sources + libspelling_public_sources version_split = meson.project_version().split('.') version_conf = configuration_data() version_conf.set('VERSION', meson.project_version()) version_conf.set('MAJOR_VERSION', version_split[0]) version_conf.set('MINOR_VERSION', version_split[1]) version_conf.set('MICRO_VERSION', version_split[2]) configure_file( input: 'spelling-version.h.in', output: 'spelling-version.h', configuration: version_conf, install: true, install_dir: join_paths(get_option('includedir'), 'libspelling-' + api_version) ) libspelling_static = static_library( 'spelling-' + api_version, libspelling_sources, dependencies: libspelling_deps, gnu_symbol_visibility: 'hidden', c_args: ['-DLIBSPELLING_COMPILATION'], include_directories: [include_directories('..'), include_directories('.')], ) libspelling_static_dep = declare_dependency( link_whole: libspelling_static, dependencies: libspelling_deps, include_directories: [include_directories('.')], ) libspelling = shared_library('spelling-' + api_version, dependencies: [libspelling_static_dep], gnu_symbol_visibility: 'hidden', soversion: so_version, version: '@0@.0.0'.format(so_version), darwin_versions: '@0@.0'.format(so_version), install: true, c_args: ['-DLIBSPELLING_COMPILATION'] + release_args, ) libspelling_dep = declare_dependency( link_with: libspelling, dependencies: libspelling_deps, include_directories: [include_directories('.')], ) meson.override_dependency('libspelling-' + api_version, libspelling_dep) install_headers(libspelling_public_headers, subdir: 'libspelling-' + api_version) pkg = import('pkgconfig') pkg.generate( description: 'A spellcheck library for GTK', libraries: libspelling, name: 'libspelling-' + api_version, filebase: 'libspelling-' + api_version, version: meson.project_version(), subdirs: 'libspelling-' + api_version, requires: ['gio-2.0', 'gtk4', 'gtksourceview-5'], install_dir: join_paths(get_option('libdir'), 'pkgconfig') ) if get_option('introspection').enabled() gnome = import('gnome') libspelling_gir = gnome.generate_gir(libspelling, nsversion: api_version, sources: [libspelling_public_sources, libspelling_public_headers], namespace: 'Spelling', symbol_prefix: 'spelling', identifier_prefix: 'Spelling', includes: ['Gio-2.0', 'Gtk-4.0', 'GtkSource-5'], export_packages: 'libspelling-' + api_version, install: true, header: 'libspelling.h', extra_args: '-DLIBSPELLING_COMPILATION', ) if get_option('vapi') spelling_vapi = gnome.generate_vapi('libspelling-' + api_version, sources: libspelling_gir.get(0), install: true, install_dir: join_paths(datadir, 'vala', 'vapi'), packages: ['gio-2.0', 'gtk4', 'gtksourceview-5'], ) endif endif 0707010000001E000081A40000000000000000000000016712C6CB00000430000000000000000000000000000000000000003100000000libspelling-0.4.4/lib/spelling-checker-private.h/* spelling-checker-private.h * * Copyright 2024 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <pango/pango.h> #include "spelling-checker.h" G_BEGIN_DECLS PangoLanguage *_spelling_checker_get_pango_language (SpellingChecker *self); SpellingDictionary *_spelling_checker_get_dictionary (SpellingChecker *self); G_END_DECLS 0707010000001F000081A40000000000000000000000016712C6CB00002908000000000000000000000000000000000000002900000000libspelling-0.4.4/lib/spelling-checker.c/* * spelling-checker.c * * Copyright 2021-2023 Christian Hergert <chergert@redhat.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <string.h> #include "spelling-checker-private.h" #include "spelling-dictionary.h" #include "spelling-provider.h" /** * SpellingChecker: * * `SpellingChecker` is the core class of libspelling. It provides high-level * APIs for spellchecking. */ struct _SpellingChecker { GObject parent_instance; SpellingProvider *provider; SpellingDictionary *dictionary; PangoLanguage *language; }; G_DEFINE_FINAL_TYPE (SpellingChecker, spelling_checker, G_TYPE_OBJECT) enum { PROP_0, PROP_LANGUAGE, PROP_PROVIDER, N_PROPS }; static GParamSpec *properties[N_PROPS]; /** * spelling_checker_new: * * Create a new `SpellingChecker`. * * Returns: (transfer full): a newly created `SpellingChecker` */ SpellingChecker * spelling_checker_new (SpellingProvider *provider, const char *language) { g_return_val_if_fail (!provider || SPELLING_IS_PROVIDER (provider), NULL); if (provider == NULL) provider = spelling_provider_get_default (); if (language == NULL) language = spelling_provider_get_default_code (provider); return g_object_new (SPELLING_TYPE_CHECKER, "provider", provider, "language", language, NULL); } static void spelling_checker_constructed (GObject *object) { SpellingChecker *self = (SpellingChecker *)object; g_assert (SPELLING_IS_CHECKER (self)); G_OBJECT_CLASS (spelling_checker_parent_class)->constructed (object); if (self->provider == NULL) self->provider = spelling_provider_get_default (); } static void spelling_checker_finalize (GObject *object) { SpellingChecker *self = (SpellingChecker *)object; g_clear_object (&self->provider); g_clear_object (&self->dictionary); G_OBJECT_CLASS (spelling_checker_parent_class)->finalize (object); } static void spelling_checker_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { SpellingChecker *self = SPELLING_CHECKER (object); switch (prop_id) { case PROP_PROVIDER: g_value_set_object (value, spelling_checker_get_provider (self)); break; case PROP_LANGUAGE: g_value_set_string (value, spelling_checker_get_language (self)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void spelling_checker_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { SpellingChecker *self = SPELLING_CHECKER (object); switch (prop_id) { case PROP_PROVIDER: self->provider = g_value_dup_object (value); break; case PROP_LANGUAGE: spelling_checker_set_language (self, g_value_get_string (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void spelling_checker_class_init (SpellingCheckerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->constructed = spelling_checker_constructed; object_class->finalize = spelling_checker_finalize; object_class->get_property = spelling_checker_get_property; object_class->set_property = spelling_checker_set_property; /** * SpellingChecker:language: * * The "language" to use when checking words with the configured * `SpellingProvider`. For example, `en_US`. */ properties[PROP_LANGUAGE] = g_param_spec_string ("language", NULL, NULL, NULL, (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); /** * SpellingChecker:provider: * * The "provider" property contains the provider that is providing * information to the spell checker. * * Currently, only Enchant is supported, and requires using the * `SpellingEnchantProvider`. Setting this to %NULL will get * the default provider. */ properties[PROP_PROVIDER] = g_param_spec_object ("provider", NULL, NULL, SPELLING_TYPE_PROVIDER, (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_properties (object_class, N_PROPS, properties); } static void spelling_checker_init (SpellingChecker *self) { } /** * spelling_checker_get_language: * * Gets the language being used by the spell checker. * * Returns: (nullable): a string describing the current language. */ const char * spelling_checker_get_language (SpellingChecker *self) { g_return_val_if_fail (SPELLING_IS_CHECKER (self), NULL); return self->dictionary ? spelling_dictionary_get_code (self->dictionary) : NULL; } /** * spelling_checker_set_language: * @self: a `SpellingChecker` * @language: the language to use * * Sets the language code to use when communicating with the provider, * such as `en_US`. */ void spelling_checker_set_language (SpellingChecker *self, const char *language) { g_return_if_fail (SPELLING_IS_CHECKER (self)); if (g_strcmp0 (language, spelling_checker_get_language (self)) != 0) { self->language = pango_language_from_string (language); g_clear_object (&self->dictionary); self->dictionary = spelling_provider_load_dictionary (self->provider, language); g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LANGUAGE]); } } /** * spelling_checker_get_provider: * * Gets the spell provider used by the spell checker. * * Currently, only Enchant-2 is supported. * * Returns: (transfer none) (not nullable): a `SpellingProvider` */ SpellingProvider * spelling_checker_get_provider (SpellingChecker *self) { g_return_val_if_fail (SPELLING_IS_CHECKER (self), NULL); return self->provider; } /** * spelling_checker_check_word: * @self: a `SpellingChecker` * @word: a word to be checked * @word_len: length of the word, in bytes * * Checks if the active dictionary contains @word. * * Returns: %TRUE if the dictionary contains the word */ gboolean spelling_checker_check_word (SpellingChecker *self, const char *word, gssize word_len) { g_return_val_if_fail (SPELLING_IS_CHECKER (self), FALSE); if (word == NULL || word_len == 0) return FALSE; if (self->dictionary == NULL) return TRUE; if (word_len < 0) word_len = strlen (word); return spelling_dictionary_contains_word (self->dictionary, word, word_len); } /** * spelling_checker_list_corrections: * @self: a `SpellingChecker` * @word: a word to be checked * * Retrieves a list of possible corrections for @word. * * Returns: (nullable) (transfer full) (array zero-terminated=1) (type utf8): * A list of possible corrections, or %NULL. */ char ** spelling_checker_list_corrections (SpellingChecker *self, const char *word) { g_return_val_if_fail (SPELLING_IS_CHECKER (self), NULL); g_return_val_if_fail (word != NULL, NULL); if (self->dictionary == NULL) return NULL; return spelling_dictionary_list_corrections (self->dictionary, word, -1); } /** * spelling_checker_add_word: * @self: a `SpellingChecker` * @word: a word to be added * * Adds @word to the active dictionary. */ void spelling_checker_add_word (SpellingChecker *self, const char *word) { g_return_if_fail (SPELLING_IS_CHECKER (self)); g_return_if_fail (word != NULL); if (self->dictionary != NULL) spelling_dictionary_add_word (self->dictionary, word); } /** * spelling_checker_ignore_word: * @self: a `SpellingChecker` * @word: a word to be ignored * * Requests the active dictionary to ignore @word. */ void spelling_checker_ignore_word (SpellingChecker *self, const char *word) { g_return_if_fail (SPELLING_IS_CHECKER (self)); g_return_if_fail (word != NULL); if (self->dictionary != NULL) spelling_dictionary_ignore_word (self->dictionary, word); } /** * spelling_checker_get_extra_word_chars: * @self: a `SpellingChecker` * * Gets the extra word characters of the active dictionary. * * Returns: (transfer none): extra word characters */ const char * spelling_checker_get_extra_word_chars (SpellingChecker *self) { g_return_val_if_fail (SPELLING_IS_CHECKER (self), NULL); if (self->dictionary != NULL) return spelling_dictionary_get_extra_word_chars (self->dictionary); return ""; } /** * spelling_checker_get_default: * * Gets a default `SpellingChecker` using the default provider and language. * * Returns: (transfer none): a `SpellingChecker` */ SpellingChecker * spelling_checker_get_default (void) { static SpellingChecker *instance; if (instance == NULL) { SpellingProvider *provider = spelling_provider_get_default (); const char *code = spelling_provider_get_default_code (provider); instance = spelling_checker_new (provider, code); g_object_add_weak_pointer (G_OBJECT (instance), (gpointer *)&instance); } return instance; } PangoLanguage * _spelling_checker_get_pango_language (SpellingChecker *self) { g_return_val_if_fail (SPELLING_IS_CHECKER (self), NULL); if (self->language == NULL) return pango_language_get_default (); return self->language; } SpellingDictionary * _spelling_checker_get_dictionary (SpellingChecker *self) { g_return_val_if_fail (SPELLING_IS_CHECKER (self), NULL); return self->dictionary; } 07070100000020000081A40000000000000000000000016712C6CB00000B68000000000000000000000000000000000000002900000000libspelling-0.4.4/lib/spelling-checker.h/* * spelling-checker.h * * Copyright 2021-2023 Christian Hergert <chergert@redhat.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #if !defined(LIBSPELLING_INSIDE) && !defined(LIBSPELLING_COMPILATION) # error "Only <libspelling.h> can be included directly." #endif #include <glib-object.h> #include "spelling-types.h" #include "spelling-version-macros.h" G_BEGIN_DECLS #define SPELLING_TYPE_CHECKER (spelling_checker_get_type()) SPELLING_AVAILABLE_IN_ALL G_DECLARE_FINAL_TYPE (SpellingChecker, spelling_checker, SPELLING, CHECKER, GObject) SPELLING_AVAILABLE_IN_ALL SpellingChecker *spelling_checker_get_default (void); SPELLING_AVAILABLE_IN_ALL SpellingChecker *spelling_checker_new (SpellingProvider *provider, const char *language); SPELLING_AVAILABLE_IN_ALL SpellingProvider *spelling_checker_get_provider (SpellingChecker *self); SPELLING_AVAILABLE_IN_ALL const char *spelling_checker_get_language (SpellingChecker *self); SPELLING_AVAILABLE_IN_ALL void spelling_checker_set_language (SpellingChecker *self, const char *language); SPELLING_AVAILABLE_IN_ALL gboolean spelling_checker_check_word (SpellingChecker *self, const char *word, gssize word_len); SPELLING_AVAILABLE_IN_ALL char **spelling_checker_list_corrections (SpellingChecker *self, const char *word); SPELLING_AVAILABLE_IN_ALL void spelling_checker_add_word (SpellingChecker *self, const char *word); SPELLING_AVAILABLE_IN_ALL void spelling_checker_ignore_word (SpellingChecker *self, const char *word); SPELLING_AVAILABLE_IN_ALL const char *spelling_checker_get_extra_word_chars (SpellingChecker *self); G_END_DECLS 07070100000021000081A40000000000000000000000016712C6CB000004D5000000000000000000000000000000000000003000000000libspelling-0.4.4/lib/spelling-compat-private.h/* spelling-compat-private.h * * Copyright 2023 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <glib.h> G_BEGIN_DECLS #if !GLIB_CHECK_VERSION(2,76,0) static inline gboolean g_set_str (char **str_pointer, const char *new_str) { char *copy; if (*str_pointer == new_str || (*str_pointer && new_str && strcmp (*str_pointer, new_str) == 0)) return FALSE; copy = g_strdup (new_str); g_free (*str_pointer); *str_pointer = copy; return TRUE; } #endif G_END_DECLS 07070100000022000081A40000000000000000000000016712C6CB000007C6000000000000000000000000000000000000003000000000libspelling-0.4.4/lib/spelling-cursor-private.h/* spelling-cursor-private.h * * Copyright 2021-2023 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <gtk/gtk.h> G_BEGIN_DECLS typedef struct _SpellingCursor SpellingCursor; typedef struct _CjhTextRegion CjhTextRegion; SpellingCursor *spelling_cursor_new (GtkTextBuffer *buffer, CjhTextRegion *region, GtkTextTag *no_spell_check_tag, const char *extra_word_chars); void spelling_cursor_free (SpellingCursor *cursor); gboolean spelling_cursor_next (SpellingCursor *cursor, GtkTextIter *word_begin, GtkTextIter *word_end); gboolean spelling_iter_forward_word_end (GtkTextIter *iter, const char *extra_word_chars); gboolean spelling_iter_backward_word_start (GtkTextIter *iter, const char *extra_word_chars); G_DEFINE_AUTOPTR_CLEANUP_FUNC (SpellingCursor, spelling_cursor_free) G_END_DECLS 07070100000023000081A40000000000000000000000016712C6CB00002572000000000000000000000000000000000000002800000000libspelling-0.4.4/lib/spelling-cursor.c/* * spelling-cursor.c * * Copyright 2021-2023 Christian Hergert <chergert@redhat.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include "cjhtextregionprivate.h" #include "spelling-cursor-private.h" #define RUN_UNCHECKED NULL #if 1 # define RETURN(r) G_STMT_START { return (r); } G_STMT_END #else # define RETURN(r) G_STMT_START { typeof(r) _r = (r); g_debug (" EXIT: %s(): %s", G_STRFUNC, G_STRLOC); return (_r); } G_STMT_END #endif typedef struct { CjhTextRegion *region; GtkTextBuffer *buffer; gssize pos; } RegionIter; typedef struct { GtkTextBuffer *buffer; GtkTextTag *tag; GtkTextIter pos; } TagIter; typedef struct { GtkTextBuffer *buffer; GtkTextIter word_begin; GtkTextIter word_end; } WordIter; struct _SpellingCursor { RegionIter region; TagIter tag; WordIter word; const char *extra_word_chars; }; static void region_iter_init (RegionIter *self, GtkTextBuffer *buffer, CjhTextRegion *region) { self->region = region; self->buffer = buffer; self->pos = -1; } static gboolean region_iter_next_cb (gsize position, const CjhTextRegionRun *run, gpointer user_data) { if (run->data == RUN_UNCHECKED) { gsize *pos = user_data; *pos = position; RETURN (TRUE); } RETURN (FALSE); } static gboolean region_iter_next (RegionIter *self, GtkTextIter *iter) { gsize pos, new_pos; if (self->pos >= (gssize)_cjh_text_region_get_length (self->region)) { gtk_text_buffer_get_end_iter (self->buffer, iter); RETURN (FALSE); } if (self->pos < 0) pos = 0; else pos = self->pos; _cjh_text_region_foreach_in_range (self->region, pos, _cjh_text_region_get_length (self->region), region_iter_next_cb, &new_pos); pos = MAX (pos, new_pos); gtk_text_buffer_get_iter_at_offset (self->buffer, iter, pos); self->pos = pos; RETURN (TRUE); } static void region_iter_seek (RegionIter *self, const GtkTextIter *iter) { /* Move to position past the word */ self->pos = gtk_text_iter_get_offset (iter) + 1; } static void tag_iter_init (TagIter *self, GtkTextBuffer *buffer, GtkTextTag *tag) { self->buffer = buffer; self->tag = tag; gtk_text_buffer_get_start_iter (buffer, &self->pos); } static gboolean tag_iter_next (TagIter *self, GtkTextIter *pos) { if (self->tag && gtk_text_iter_has_tag (&self->pos, self->tag)) { /* Should always succeed because we are within the tag */ gtk_text_iter_forward_to_tag_toggle (&self->pos, self->tag); } *pos = self->pos; RETURN (TRUE); } static void tag_iter_seek (TagIter *self, const GtkTextIter *iter) { self->pos = *iter; } static inline gboolean utf8_contains_unichar (const char *utf8, gunichar ch) { if (ch == 0) RETURN (FALSE); for (const char *c = utf8; *c; c = g_utf8_next_char (c)) { if (ch == g_utf8_get_char (c)) RETURN (TRUE); } RETURN (FALSE); } static inline gboolean is_word_char (const GtkTextIter *iter, const char *extra_word_chars) { if (gtk_text_iter_starts_word (iter)) RETURN (TRUE); if (gtk_text_iter_inside_word (iter)) RETURN (TRUE); if (extra_word_chars != NULL) { if (utf8_contains_unichar (extra_word_chars, gtk_text_iter_get_char (iter))) RETURN (TRUE); } RETURN (FALSE); } gboolean spelling_iter_forward_word_end (GtkTextIter *iter, const char *extra_word_chars) { GtkTextIter orig = *iter; GtkTextIter peek; if (!gtk_text_iter_forward_word_end (iter)) { /* We might get here if we moved but are at the end of the buffer. * If the previous character ends a word, that is fine too. */ if (gtk_text_iter_is_end (iter) && !gtk_text_iter_equal (iter, &orig)) RETURN (gtk_text_iter_ends_word (iter)); RETURN (FALSE); } if (!is_word_char (iter, extra_word_chars)) RETURN (TRUE); peek = *iter; while (gtk_text_iter_forward_char (&peek) && is_word_char (&peek, extra_word_chars)) { /* Do nothing */ } *iter = peek; RETURN (TRUE); } gboolean spelling_iter_backward_word_start (GtkTextIter *iter, const char *extra_word_chars) { GtkTextIter peek; if (gtk_text_iter_is_start (iter)) RETURN (FALSE); if (!gtk_text_iter_backward_word_start (iter)) RETURN (gtk_text_iter_starts_word (iter)); peek = *iter; while (is_word_char (&peek, extra_word_chars) && gtk_text_iter_backward_char (&peek)) { /* Do Nothing */ } if (!gtk_text_iter_is_start (&peek) || !is_word_char (&peek, extra_word_chars)) { if (!gtk_text_iter_equal (&peek, iter)) gtk_text_iter_forward_char (&peek); } *iter = peek; RETURN (TRUE); } static void word_iter_init (WordIter *self, GtkTextBuffer *buffer) { self->buffer = buffer; gtk_text_buffer_get_start_iter (buffer, &self->word_begin); self->word_end = self->word_begin; } static gboolean word_iter_next (WordIter *self, GtkTextIter *word_begin, GtkTextIter *word_end, const char *extra_word_chars) { if (!spelling_iter_forward_word_end (&self->word_end, extra_word_chars)) { *word_begin = self->word_end; *word_end = self->word_end; RETURN (FALSE); } self->word_begin = self->word_end; if (!spelling_iter_backward_word_start (&self->word_begin, extra_word_chars)) { *word_begin = self->word_end; *word_end = self->word_end; RETURN (FALSE); } *word_begin = self->word_begin; *word_end = self->word_end; RETURN (TRUE); } static void word_iter_seek (WordIter *self, const GtkTextIter *iter) { self->word_begin = *iter; self->word_end = *iter; } SpellingCursor * spelling_cursor_new (GtkTextBuffer *buffer, CjhTextRegion *region, GtkTextTag *no_spell_check_tag, const char *extra_word_chars) { SpellingCursor *self; g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL); g_return_val_if_fail (region != NULL, NULL); g_return_val_if_fail (!no_spell_check_tag || GTK_IS_TEXT_TAG (no_spell_check_tag), NULL); self = g_rc_box_new0 (SpellingCursor); region_iter_init (&self->region, buffer, region); tag_iter_init (&self->tag, buffer, no_spell_check_tag); word_iter_init (&self->word, buffer); self->extra_word_chars = extra_word_chars ? g_intern_string (extra_word_chars) : ""; return self; } void spelling_cursor_free (SpellingCursor *self) { g_rc_box_release (self); } static gboolean contains_tag (const GtkTextIter *word_begin, const GtkTextIter *word_end, GtkTextTag *tag) { GtkTextIter toggle_iter; if (tag == NULL) RETURN (FALSE); if (gtk_text_iter_has_tag (word_begin, tag)) RETURN (TRUE); toggle_iter = *word_begin; if (!gtk_text_iter_forward_to_tag_toggle (&toggle_iter, tag)) RETURN (FALSE); RETURN (gtk_text_iter_compare (word_end, &toggle_iter) > 0); } gboolean spelling_cursor_next (SpellingCursor *self, GtkTextIter *word_begin, GtkTextIter *word_end) { /* Try to advance skipping any checked region in the buffer */ if (!region_iter_next (&self->region, word_end)) { *word_begin = *word_end; RETURN (FALSE); } /* Pass that position to the next iter, so it can skip * past anything that is already checked. Then try to move * forward so that we can skip past regions in the text * buffer that are to be ignored by spellcheck. */ tag_iter_seek (&self->tag, word_end); if (!tag_iter_next (&self->tag, word_end)) { *word_begin = *word_end; RETURN (FALSE); } /* Now pass that information to the word iter, so that it can * jump forward to the next word starting from our tag/region * positions. */ word_iter_seek (&self->word, word_end); if (!word_iter_next (&self->word, word_begin, word_end, self->extra_word_chars)) RETURN (FALSE); /* Now pass our new position to the region so that it will * skip past the word when advancing. */ region_iter_seek (&self->region, word_end); /* If this word contains the no-spell-check tag, then try * again to skip past even more content. */ if (contains_tag (word_begin, word_end, self->tag.tag)) RETURN (spelling_cursor_next (self, word_begin, word_end)); RETURN (TRUE); } 07070100000024000081A40000000000000000000000016712C6CB000009D5000000000000000000000000000000000000003500000000libspelling-0.4.4/lib/spelling-dictionary-internal.h/* spelling-dictionary-internal.h * * Copyright 2023 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <gtk/gtk.h> #include "spelling-dictionary.h" G_BEGIN_DECLS #define SPELLING_DICTIONARY_GET_CLASS(obj) G_TYPE_INSTANCE_GET_CLASS(obj, SPELLING_TYPE_DICTIONARY, SpellingDictionaryClass) typedef struct _SpellingBoundary { guint offset; guint length; guint byte_offset; guint byte_length; } SpellingBoundary; struct _SpellingDictionary { GObject parent_instance; const char *code; GMutex mutex; }; struct _SpellingDictionaryClass { GObjectClass parent_class; void (*lock) (SpellingDictionary *self); void (*unlock) (SpellingDictionary *self); gboolean (*contains_word) (SpellingDictionary *self, const char *word, gssize word_len); char **(*list_corrections) (SpellingDictionary *self, const char *word, gssize word_len); void (*add_word) (SpellingDictionary *self, const char *word); void (*ignore_word) (SpellingDictionary *self, const char *word); const char *(*get_extra_word_chars) (SpellingDictionary *self); }; GtkBitset *_spelling_dictionary_check_words (SpellingDictionary *self, const char *text, const SpellingBoundary *positions, guint n_positions); G_END_DECLS 07070100000025000081A40000000000000000000000016712C6CB00002236000000000000000000000000000000000000002C00000000libspelling-0.4.4/lib/spelling-dictionary.c/* spelling-dictionary.c * * Copyright 2021-2023 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <string.h> #include "spelling-dictionary-internal.h" /** * SpellingDictionary: * * Abstract base class for spellchecking dictionaries. */ G_DEFINE_ABSTRACT_TYPE (SpellingDictionary, spelling_dictionary, G_TYPE_OBJECT) enum { PROP_0, PROP_CODE, N_PROPS }; static GParamSpec *properties[N_PROPS]; static void spelling_dictionary_real_lock (SpellingDictionary *self) { g_mutex_lock (&self->mutex); } static void spelling_dictionary_real_unlock (SpellingDictionary *self) { g_mutex_unlock (&self->mutex); } static inline void spelling_dictionary_lock (SpellingDictionary *self) { SPELLING_DICTIONARY_GET_CLASS (self)->lock (self); } static inline void spelling_dictionary_unlock (SpellingDictionary *self) { SPELLING_DICTIONARY_GET_CLASS (self)->unlock (self); } static void spelling_dictionary_finalize (GObject *object) { SpellingDictionary *self = (SpellingDictionary *)object; self->code = NULL; g_mutex_clear (&self->mutex); G_OBJECT_CLASS (spelling_dictionary_parent_class)->finalize (object); } static void spelling_dictionary_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { SpellingDictionary *self = SPELLING_DICTIONARY (object); switch (prop_id) { case PROP_CODE: g_value_set_string (value, spelling_dictionary_get_code (self)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void spelling_dictionary_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { SpellingDictionary *self = SPELLING_DICTIONARY (object); switch (prop_id) { case PROP_CODE: self->code = g_intern_string (g_value_get_string (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void spelling_dictionary_class_init (SpellingDictionaryClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = spelling_dictionary_finalize; object_class->get_property = spelling_dictionary_get_property; object_class->set_property = spelling_dictionary_set_property; klass->lock = spelling_dictionary_real_lock; klass->unlock = spelling_dictionary_real_unlock; /** * SpellingDictionary:code: * * The language code, for example `en_US`. */ properties[PROP_CODE] = g_param_spec_string ("code", NULL, NULL, NULL, (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_properties (object_class, N_PROPS, properties); } static void spelling_dictionary_init (SpellingDictionary *self) { g_mutex_init (&self->mutex); } /** * spelling_dictionary_get_code: * @self: a `SpellingDictionary` * * Gets the language code of the dictionary, or %NULL if undefined. * * Returns: (transfer none) (nullable): the language code of the dictionary */ const char * spelling_dictionary_get_code (SpellingDictionary *self) { g_return_val_if_fail (SPELLING_IS_DICTIONARY (self), NULL); return self->code; } /** * spelling_dictionary_contains_word: * @self: a `SpellingDictionary` * @word: a word to be checked * @word_len: length of the word, in bytes * * Checks if the dictionary contains @word. * * Returns: %TRUE if the dictionary contains the word */ gboolean spelling_dictionary_contains_word (SpellingDictionary *self, const char *word, gssize word_len) { gboolean ret; g_return_val_if_fail (SPELLING_IS_DICTIONARY (self), FALSE); g_return_val_if_fail (word != NULL, FALSE); if (word_len < 0) word_len = strlen (word); spelling_dictionary_lock (self); ret = SPELLING_DICTIONARY_GET_CLASS (self)->contains_word (self, word, word_len); spelling_dictionary_unlock (self); return ret; } /** * spelling_dictionary_list_corrections: * @self: a `SpellingDictionary` * @word: a word to be checked * @word_len: the length of @word, or -1 if @word is zero-terminated * * Retrieves a list of possible corrections for @word. * * Returns: (nullable) (transfer full) (array zero-terminated=1) (type utf8): * A list of possible corrections, or %NULL. */ char ** spelling_dictionary_list_corrections (SpellingDictionary *self, const char *word, gssize word_len) { char **ret; g_return_val_if_fail (SPELLING_IS_DICTIONARY (self), NULL); g_return_val_if_fail (word != NULL, NULL); g_return_val_if_fail (word != NULL || word_len == 0, NULL); if (word_len < 0) word_len = strlen (word); if (word_len == 0) return NULL; spelling_dictionary_lock (self); ret = SPELLING_DICTIONARY_GET_CLASS (self)->list_corrections (self, word, word_len); spelling_dictionary_unlock (self); return ret; } /** * spelling_dictionary_add_word: * @self: a `SpellingDictionary` * @word: a word to be added * * Adds @word to the dictionary. */ void spelling_dictionary_add_word (SpellingDictionary *self, const char *word) { g_return_if_fail (SPELLING_IS_DICTIONARY (self)); g_return_if_fail (word != NULL); if (SPELLING_DICTIONARY_GET_CLASS (self)->add_word) { spelling_dictionary_lock (self); SPELLING_DICTIONARY_GET_CLASS (self)->add_word (self, word); spelling_dictionary_unlock (self); } } /** * spelling_dictionary_ignore_word: * @self: a `SpellingDictionary` * @word: a word to be ignored * * Requests the dictionary to ignore @word. */ void spelling_dictionary_ignore_word (SpellingDictionary *self, const char *word) { g_return_if_fail (SPELLING_IS_DICTIONARY (self)); g_return_if_fail (word != NULL); if (SPELLING_DICTIONARY_GET_CLASS (self)->ignore_word) { spelling_dictionary_lock (self); SPELLING_DICTIONARY_GET_CLASS (self)->ignore_word (self, word); spelling_dictionary_unlock (self); } } /** * spelling_dictionary_get_extra_word_chars: * @self: a `SpellingDictionary` * * Gets the extra word characters of the dictionary. * * Returns: (transfer none): extra word characters */ const char * spelling_dictionary_get_extra_word_chars (SpellingDictionary *self) { const char *ret = ""; g_return_val_if_fail (SPELLING_IS_DICTIONARY (self), NULL); if (SPELLING_DICTIONARY_GET_CLASS (self)->get_extra_word_chars) { spelling_dictionary_lock (self); ret = SPELLING_DICTIONARY_GET_CLASS (self)->get_extra_word_chars (self); spelling_dictionary_unlock (self); } return ret; } GtkBitset * _spelling_dictionary_check_words (SpellingDictionary *self, const char *text, const SpellingBoundary *positions, guint n_positions) { gboolean (*contains_word) (SpellingDictionary *, const char *, gssize); GtkBitset *bitset; g_return_val_if_fail (SPELLING_IS_DICTIONARY (self), NULL); g_return_val_if_fail (text != NULL, NULL); bitset = gtk_bitset_new_empty (); contains_word = SPELLING_DICTIONARY_GET_CLASS (self)->contains_word; spelling_dictionary_lock (self); for (guint i = 0; i < n_positions; i++) { const char *word = &text[positions[i].byte_offset]; guint wordlen = positions[i].byte_length; if (!(*contains_word) (self, word, wordlen)) gtk_bitset_add (bitset, i); } spelling_dictionary_unlock (self); return bitset; } 07070100000026000081A40000000000000000000000016712C6CB00000B7E000000000000000000000000000000000000002C00000000libspelling-0.4.4/lib/spelling-dictionary.h/* * spelling-dictionary.h * * Copyright 2021-2023 Christian Hergert <chergert@redhat.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #if !defined(LIBSPELLING_INSIDE) && !defined(LIBSPELLING_COMPILATION) # error "Only <libspelling.h> can be included directly." #endif #include <glib-object.h> #include "spelling-types.h" #include "spelling-version-macros.h" G_BEGIN_DECLS #define SPELLING_TYPE_DICTIONARY (spelling_dictionary_get_type()) #define SPELLING_IS_DICTIONARY(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, SPELLING_TYPE_DICTIONARY)) #define SPELLING_DICTIONARY(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, SPELLING_TYPE_DICTIONARY, SpellingDictionary)) #define SPELLING_DICTIONARY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST(klass, SPELLING_TYPE_DICTIONARY, SpellingDictionaryClass)) typedef struct _SpellingDictionary SpellingDictionary; typedef struct _SpellingDictionaryClass SpellingDictionaryClass; SPELLING_AVAILABLE_IN_ALL GType spelling_dictionary_get_type (void) G_GNUC_CONST; SPELLING_AVAILABLE_IN_ALL const char *spelling_dictionary_get_code (SpellingDictionary *self); SPELLING_AVAILABLE_IN_ALL gboolean spelling_dictionary_contains_word (SpellingDictionary *self, const char *word, gssize word_len); SPELLING_AVAILABLE_IN_ALL char **spelling_dictionary_list_corrections (SpellingDictionary *self, const char *word, gssize word_len); SPELLING_AVAILABLE_IN_ALL void spelling_dictionary_add_word (SpellingDictionary *self, const char *word); SPELLING_AVAILABLE_IN_ALL void spelling_dictionary_ignore_word (SpellingDictionary *self, const char *word); SPELLING_AVAILABLE_IN_ALL const char *spelling_dictionary_get_extra_word_chars (SpellingDictionary *self); G_DEFINE_AUTOPTR_CLEANUP_FUNC (SpellingDictionary, g_object_unref) G_END_DECLS 07070100000027000081A40000000000000000000000016712C6CB0000047D000000000000000000000000000000000000003800000000libspelling-0.4.4/lib/spelling-empty-provider-private.h/* spelling-empty-provider-private.h * * Copyright 2023 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "spelling-provider-internal.h" G_BEGIN_DECLS #define SPELLING_TYPE_EMPTY_PROVIDER (spelling_empty_provider_get_type()) G_DECLARE_FINAL_TYPE (SpellingEmptyProvider, spelling_empty_provider, SPELLING, EMPTY_SPELL_PROVIDER, SpellingProvider) SpellingProvider *spelling_empty_provider_new (void); G_END_DECLS 07070100000028000081A40000000000000000000000016712C6CB000007FC000000000000000000000000000000000000003000000000libspelling-0.4.4/lib/spelling-empty-provider.c/* spelling-empty-provider.c * * Copyright 2023 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include "spelling-empty-provider-private.h" #include "spelling-language.h" struct _SpellingEmptyProvider { SpellingProvider parent_instance; }; G_DEFINE_FINAL_TYPE (SpellingEmptyProvider, spelling_empty_provider, SPELLING_TYPE_PROVIDER) SpellingProvider * spelling_empty_provider_new (void) { return g_object_new (SPELLING_TYPE_EMPTY_PROVIDER, NULL); } static GListModel * empty_list_languages (SpellingProvider *provider) { return G_LIST_MODEL (g_list_store_new (SPELLING_TYPE_LANGUAGE)); } static SpellingDictionary * empty_load_dictionary (SpellingProvider *provider, const char *language) { return NULL; } static gboolean empty_supports_language (SpellingProvider *provider, const char *language) { return FALSE; } static void spelling_empty_provider_class_init (SpellingEmptyProviderClass *klass) { SpellingProviderClass *provider_class = SPELLING_PROVIDER_CLASS (klass); provider_class->list_languages = empty_list_languages; provider_class->load_dictionary = empty_load_dictionary; provider_class->supports_language = empty_supports_language; } static void spelling_empty_provider_init (SpellingEmptyProvider *self) { } 07070100000029000081A40000000000000000000000016712C6CB00000FD9000000000000000000000000000000000000003000000000libspelling-0.4.4/lib/spelling-engine-private.h/* spelling-engine-private.h * * Copyright 2024 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <gtk/gtk.h> #include "spelling-dictionary-internal.h" G_BEGIN_DECLS #define SPELLING_TYPE_ENGINE (spelling_engine_get_type()) typedef struct _SpellingAdapter { gboolean (*check_enabled) (gpointer instance); guint (*get_cursor) (gpointer instance); char *(*copy_text) (gpointer instance, guint position, guint length); void (*apply_tag) (gpointer instance, guint position, guint length); void (*clear_tag) (gpointer instance, guint position, guint length); gboolean (*backward_word_start) (gpointer instance, guint *position); gboolean (*forward_word_end) (gpointer instance, guint *position); void (*intersect_spellcheck_region) (gpointer instance, GtkBitset *region); PangoLanguage *(*get_language) (gpointer instance); SpellingDictionary *(*get_dictionary) (gpointer instance); } SpellingAdapter; G_DECLARE_FINAL_TYPE (SpellingEngine, spelling_engine, SPELLING, ENGINE, GObject) SpellingEngine *spelling_engine_new (const SpellingAdapter *adapter, GObject *instance); void spelling_engine_before_insert_text (SpellingEngine *self, guint position, guint length); void spelling_engine_after_insert_text (SpellingEngine *self, guint position, guint length); void spelling_engine_before_delete_range (SpellingEngine *self, guint position, guint length); void spelling_engine_after_delete_range (SpellingEngine *self, guint position); void spelling_engine_iteration (SpellingEngine *self); void spelling_engine_invalidate (SpellingEngine *self, guint position, guint length); void spelling_engine_invalidate_all (SpellingEngine *self); G_END_DECLS 0707010000002A000081A40000000000000000000000016712C6CB00003D11000000000000000000000000000000000000002800000000libspelling-0.4.4/lib/spelling-engine.c/* spelling-engine.c * * Copyright 2024 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <gtksourceview/gtksource.h> #include "cjhtextregionprivate.h" #include "spelling-engine-private.h" #include "spelling-job-private.h" #define TAG_NEEDS_CHECK GUINT_TO_POINTER(1) #define TAG_CHECKED GUINT_TO_POINTER(0) #define INVALIDATE_DELAY_MSECS 100 #define WATERMARK_PER_JOB 1000 struct _SpellingEngine { GObject parent_instance; CjhTextRegion *region; GObject *instance; SpellingJob *active; SpellingAdapter adapter; guint queued_update_handler; }; typedef struct { SpellingEngine *self; GtkBitset *bitset; GtkBitset *all; guint size; } CollectRanges; G_DEFINE_FINAL_TYPE (SpellingEngine, spelling_engine, G_TYPE_OBJECT) static void spelling_engine_queue_update (SpellingEngine *self, guint delay_msec); static gboolean spelling_engine_check_enabled (SpellingEngine *self) { if (self->instance != NULL) return self->adapter.check_enabled (self->instance); return FALSE; } static gboolean has_unchecked_regions_cb (gsize offset, const CjhTextRegionRun *run, gpointer user_data) { gboolean *ret = user_data; *ret |= run->data == TAG_NEEDS_CHECK; return *ret; } static gboolean spelling_engine_has_unchecked_regions (SpellingEngine *self) { gboolean ret = FALSE; _cjh_text_region_foreach (self->region, has_unchecked_regions_cb, &ret); return ret; } static gboolean spelling_engine_extend_range (SpellingEngine *self, guint *begin, guint *end) { guint tmp; g_assert (SPELLING_IS_ENGINE (self)); g_assert (begin != NULL); g_assert (end != NULL); tmp = *begin; if (self->adapter.backward_word_start (self->instance, &tmp)) *begin = tmp; tmp = *end; if (self->adapter.forward_word_end (self->instance, &tmp)) *end = tmp; return *begin != *end; } static void spelling_engine_add_fragment (SpellingEngine *self, SpellingJob *job, GtkBitset *bitset, guint begin, guint end) { g_autoptr(GBytes) bytes = NULL; char *text; g_assert (SPELLING_IS_ENGINE (self)); g_assert (bitset != NULL); g_assert (end >= begin); text = self->adapter.copy_text (self->instance, begin, end - begin + 1); bytes = g_bytes_new_take (text, strlen (text)); spelling_job_add_fragment (job, bytes, begin, end - begin + 1); } static void spelling_engine_add_fragments (SpellingEngine *self, SpellingJob *job, GtkBitset *bitset) { GtkBitsetIter iter; guint pos; g_assert (SPELLING_IS_ENGINE (self)); g_assert (SPELLING_IS_JOB (job)); g_assert (bitset != NULL); if (gtk_bitset_iter_init_first (&iter, bitset, &pos)) { guint begin = pos; guint end = pos; while (gtk_bitset_iter_next (&iter, &pos)) { if (pos != end + 1) { spelling_engine_add_fragment (self, job, bitset, begin, end); begin = end = pos; } else { end = pos; } } spelling_engine_add_fragment (self, job, bitset, begin, end); } } static gsize spelling_engine_add_range (SpellingEngine *self, guint begin, guint end, GtkBitset *all, GtkBitset *bitset) { gsize ret; g_assert (SPELLING_IS_ENGINE (self)); g_assert (self->active != NULL); g_assert (SPELLING_IS_JOB (self->active)); g_assert (begin <= end); g_assert (all != NULL); g_assert (bitset != NULL); /* Track this range in "all" as we'll need to clear the areas * that have "no-spell-check" in our textregion too. We can * figure that out by subtracting bitset from all. */ gtk_bitset_add_range (all, begin, end - begin); /* Track what the adapter thinks should be in this run */ gtk_bitset_add_range (bitset, begin, end - begin); self->adapter.intersect_spellcheck_region (self->instance, bitset); /* And now subtract that from the all to cover the gaps */ gtk_bitset_subtract (all, bitset); /* Add fragments for the sub-regions we need to check */ spelling_engine_add_fragments (self, self->active, bitset); /* Track the size so we can bail after sufficent data to check */ ret = gtk_bitset_get_size (bitset); /* Reset bitset for next run */ gtk_bitset_remove_all (bitset); return ret; } static gboolean collect_ranges (gsize offset, const CjhTextRegionRun *run, gpointer user_data) { CollectRanges *collect = user_data; guint begin; guint end; if (run->data != TAG_NEEDS_CHECK) return FALSE; begin = offset; end = offset + run->length; spelling_engine_extend_range (collect->self, &begin, &end); collect->size += spelling_engine_add_range (collect->self, begin, end, collect->all, collect->bitset); return collect->size >= WATERMARK_PER_JOB; } static void spelling_engine_job_finished (GObject *object, GAsyncResult *result, gpointer user_data) { SpellingJob *job = (SpellingJob *)object; g_autoptr(SpellingEngine) self = user_data; g_autofree SpellingBoundary *fragments = NULL; g_autofree SpellingMistake *mistakes = NULL; guint n_fragments = 0; guint n_mistakes = 0; g_assert (SPELLING_IS_JOB (job)); g_assert (G_IS_ASYNC_RESULT (result)); g_assert (SPELLING_IS_ENGINE (self)); g_clear_object (&self->active); if (!spelling_engine_check_enabled (self)) return; spelling_job_run_finish (job, result, &fragments, &n_fragments, &mistakes, &n_mistakes); for (guint f = 0; f < n_fragments; f++) { self->adapter.clear_tag (self->instance, fragments[f].offset, fragments[f].length); _cjh_text_region_replace (self->region, fragments[f].offset, fragments[f].length, TAG_CHECKED); } for (guint m = 0; m < n_mistakes; m++) self->adapter.apply_tag (self->instance, mistakes[m].offset, mistakes[m].length); /* Check immediately if there is more */ if (spelling_engine_has_unchecked_regions (self)) spelling_engine_queue_update (self, 0); } static void spelling_engine_clear_runs (SpellingEngine *self, GtkBitset *bitset) { GtkBitsetIter iter; guint pos; g_assert (SPELLING_IS_ENGINE (self)); g_assert (bitset != NULL); if (gtk_bitset_iter_init_first (&iter, bitset, &pos)) { guint begin = pos; guint end = pos; while (gtk_bitset_iter_next (&iter, &pos)) { if (pos == end + 1) { end++; continue; } _cjh_text_region_replace (self->region, begin, end - begin + 1, TAG_CHECKED); begin = pos; end = pos; } _cjh_text_region_replace (self->region, begin, end - begin + 1, TAG_CHECKED); } } static gboolean spelling_engine_tick (gpointer data) { SpellingEngine *self = data; g_autoptr(GtkBitset) bitset = NULL; g_autoptr(GtkBitset) all = NULL; const CjhTextRegionRun *run; SpellingDictionary *dictionary; PangoLanguage *language; CollectRanges collect; gsize real_offset; guint cursor; g_assert (SPELLING_IS_ENGINE (self)); g_assert (self->active == NULL); /* Be safe against weak-pointer lost or bad dictionary installations */ if (self->instance == NULL || !(dictionary = self->adapter.get_dictionary (self->instance)) || !(language = self->adapter.get_language (self->instance))) { g_clear_handle_id (&self->queued_update_handler, g_source_remove); return G_SOURCE_REMOVE; } self->active = spelling_job_new (dictionary, language); bitset = gtk_bitset_new_empty (); all = gtk_bitset_new_empty (); /* Always check the cursor location so that spellcheck feels snappy */ cursor = self->adapter.get_cursor (self->instance); run = _cjh_text_region_get_run_at_offset (self->region, cursor, &real_offset); if (run == NULL || run->data == TAG_NEEDS_CHECK) { guint begin = cursor; guint end = cursor; if (spelling_engine_extend_range (self, &begin, &end)) spelling_engine_add_range (self, begin, end, all, bitset); } collect.self = self; collect.bitset = bitset; collect.all = all; collect.size = 0; _cjh_text_region_foreach (self->region, collect_ranges, &collect); /* We need to clear everything from our textregion that is still * in @all as those are gaps in what should be checked, such as * no-spell-check regions. */ spelling_engine_clear_runs (self, all); spelling_job_run (self->active, spelling_engine_job_finished, g_object_ref (self)); g_clear_handle_id (&self->queued_update_handler, g_source_remove); return G_SOURCE_REMOVE; } static void spelling_engine_queue_update (SpellingEngine *self, guint delay_msec) { g_assert (SPELLING_IS_ENGINE (self)); if (self->active != NULL) return; if (!spelling_engine_check_enabled (self)) return; if (self->queued_update_handler == 0) self->queued_update_handler = g_timeout_add_full (G_PRIORITY_LOW, delay_msec, spelling_engine_tick, self, NULL); } static gboolean spelling_engine_join_range (gsize offset, const CjhTextRegionRun *left, const CjhTextRegionRun *right) { return left->data == right->data; } static void spelling_engine_split_range (gsize offset, const CjhTextRegionRun *run, CjhTextRegionRun *left, CjhTextRegionRun *right) { /* We could potentially scan forward/back here from the offset * where the split occurs to mark the sub-regions as needing * to be checked. However we do that at another layer currently * so no need to do it here. * * It's also a bit difficult since you can't split the edge * runs into more than two runs and that would be necessary * if they were more than a couple words. */ } static void spelling_engine_dispose (GObject *object) { SpellingEngine *self = (SpellingEngine *)object; g_clear_object (&self->active); g_clear_handle_id (&self->queued_update_handler, g_source_remove); g_clear_weak_pointer (&self->instance); G_OBJECT_CLASS (spelling_engine_parent_class)->dispose (object); } static void spelling_engine_class_init (SpellingEngineClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->dispose = spelling_engine_dispose; } static void spelling_engine_init (SpellingEngine *self) { self->region = _cjh_text_region_new (spelling_engine_join_range, spelling_engine_split_range); } SpellingEngine * spelling_engine_new (const SpellingAdapter *adapter, GObject *instance) { SpellingEngine *self; g_return_val_if_fail (adapter != NULL, NULL); g_return_val_if_fail (G_IS_OBJECT (instance), NULL); self = g_object_new (SPELLING_TYPE_ENGINE, NULL); g_set_weak_pointer (&self->instance, instance); self->adapter = *adapter; return self; } void spelling_engine_before_insert_text (SpellingEngine *self, guint position, guint length) { g_return_if_fail (SPELLING_IS_ENGINE (self)); g_return_if_fail (self->instance != NULL); if (length == 0) return; if (self->active) spelling_job_notify_insert (self->active, position, length); _cjh_text_region_insert (self->region, position, length, TAG_NEEDS_CHECK); } void spelling_engine_after_insert_text (SpellingEngine *self, guint position, guint length) { g_return_if_fail (SPELLING_IS_ENGINE (self)); g_return_if_fail (self->instance != NULL); if (length == 0) return; spelling_engine_invalidate (self, position, length); } void spelling_engine_before_delete_range (SpellingEngine *self, guint position, guint length) { g_return_if_fail (SPELLING_IS_ENGINE (self)); g_return_if_fail (self->instance != NULL); if (length == 0) return; if (self->active) spelling_job_notify_delete (self->active, position, length); _cjh_text_region_remove (self->region, position, length); } void spelling_engine_after_delete_range (SpellingEngine *self, guint position) { g_return_if_fail (SPELLING_IS_ENGINE (self)); g_return_if_fail (self->instance != NULL); spelling_engine_invalidate (self, position, 0); } void spelling_engine_iteration (SpellingEngine *self) { g_return_if_fail (SPELLING_IS_ENGINE (self)); if (self->active == NULL) spelling_engine_tick (self); } void spelling_engine_invalidate_all (SpellingEngine *self) { guint length; g_return_if_fail (SPELLING_IS_ENGINE (self)); g_clear_object (&self->active); g_clear_handle_id (&self->queued_update_handler, g_source_remove); length = _cjh_text_region_get_length (self->region); if (length > 0) { _cjh_text_region_replace (self->region, 0, length, TAG_NEEDS_CHECK); self->adapter.clear_tag (self->instance, 0, length); } spelling_engine_queue_update (self, 0); } void spelling_engine_invalidate (SpellingEngine *self, guint position, guint length) { g_assert (SPELLING_IS_ENGINE (self)); g_assert (self->instance != NULL); if (self->active) spelling_job_invalidate (self->active, position, length); _cjh_text_region_replace (self->region, position, length, TAG_NEEDS_CHECK); self->adapter.clear_tag (self->instance, position, length); spelling_engine_queue_update (self, 0); } 0707010000002B000081A40000000000000000000000016712C6CB00000782000000000000000000000000000000000000002600000000libspelling-0.4.4/lib/spelling-init.c/* libspelling.c * * Copyright 2023 Christian Hergert * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <glib/gi18n.h> #include "spelling-checker.h" #include "spelling-dictionary.h" #include "spelling-init.h" #include "spelling-language.h" #include "spelling-provider.h" #include "spelling-text-buffer-adapter.h" #include "gconstructor.h" G_DEFINE_CONSTRUCTOR (_spelling_init) static void _spelling_init (void) { static gsize initialized; if (g_once_init_enter (&initialized)) { bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR); g_type_ensure (SPELLING_TYPE_CHECKER); g_type_ensure (SPELLING_TYPE_DICTIONARY); g_type_ensure (SPELLING_TYPE_LANGUAGE); g_type_ensure (SPELLING_TYPE_PROVIDER); g_type_ensure (SPELLING_TYPE_TEXT_BUFFER_ADAPTER); g_once_init_leave (&initialized, TRUE); } } /** * spelling_init: * * Call this function before using any other libspelling functions in your * applications. It will initialize everything needed to operate the library. */ void spelling_init (void) { _spelling_init (); } 0707010000002C000081A40000000000000000000000016712C6CB0000042A000000000000000000000000000000000000002600000000libspelling-0.4.4/lib/spelling-init.h/* spelling-init.h * * Copyright 2023 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #if !defined(LIBSPELLING_INSIDE) && !defined(LIBSPELLING_COMPILATION) # error "Only <libspelling.h> can be included directly." #endif #include "spelling-version-macros.h" G_BEGIN_DECLS SPELLING_AVAILABLE_IN_ALL void spelling_init (void); G_END_DECLS 0707010000002D000081A40000000000000000000000016712C6CB00000D56000000000000000000000000000000000000002D00000000libspelling-0.4.4/lib/spelling-job-private.h/* spelling-job-private.h * * Copyright 2024 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <gio/gio.h> #include "spelling-dictionary-internal.h" G_BEGIN_DECLS typedef struct _SpellingMistake { guint offset; guint length; } SpellingMistake; #define SPELLING_TYPE_JOB (spelling_job_get_type()) G_DECLARE_FINAL_TYPE (SpellingJob, spelling_job, SPELLING, JOB, GObject) SpellingJob *spelling_job_new (SpellingDictionary *dictionary, PangoLanguage *language); void spelling_job_discard (SpellingJob *self); void spelling_job_run (SpellingJob *self, GAsyncReadyCallback callback, gpointer user_data); void spelling_job_run_finish (SpellingJob *self, GAsyncResult *result, SpellingBoundary **fragments, guint *n_fragments, SpellingMistake **mistakes, guint *n_mistakes); void spelling_job_run_sync (SpellingJob *self, SpellingBoundary **fragments, guint *n_fragments, SpellingMistake **mistakes, guint *n_mistakes); void spelling_job_add_fragment (SpellingJob *self, GBytes *bytes, guint position, guint length); void spelling_job_notify_delete (SpellingJob *self, guint position, guint length); void spelling_job_notify_insert (SpellingJob *self, guint position, guint length); void spelling_job_invalidate (SpellingJob *self, guint position, guint length); G_END_DECLS 0707010000002E000081A40000000000000000000000016712C6CB00004A1F000000000000000000000000000000000000002500000000libspelling-0.4.4/lib/spelling-job.c/* spelling-job.c * * Copyright 2024 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <pango/pango.h> #include "spelling-dictionary-internal.h" #include "spelling-job-private.h" #include "spelling-trace.h" #define GDK_ARRAY_NAME spelling_boundaries #define GDK_ARRAY_TYPE_NAME SpellingBoundaries #define GDK_ARRAY_ELEMENT_TYPE SpellingBoundary #define GDK_ARRAY_BY_VALUE 1 #define GDK_ARRAY_PREALLOC 8 #define GDK_ARRAY_NO_MEMSET #include "gdkarrayimpl.c" typedef struct _SpellingFragment { GBytes *bytes; guint position; guint length; gboolean must_discard; } SpellingFragment; typedef struct _SpellingMistakes { const SpellingFragment *fragment; GArray *boundaries; } SpellingMistakes; struct _SpellingJob { GObject parent_instance; SpellingDictionary *dictionary; PangoLanguage *language; char *extra_word_chars; GArray *fragments; guint frozen : 1; }; enum { PROP_0, PROP_DICTIONARY, PROP_LANGUAGE, PROP_EXTRA_WORD_CHARS, N_PROPS }; G_DEFINE_FINAL_TYPE (SpellingJob, spelling_job, G_TYPE_OBJECT) static GParamSpec *properties[N_PROPS]; static void clear_fragment (gpointer data) { SpellingFragment *fragment = data; g_clear_pointer (&fragment->bytes, g_bytes_unref); } static void spelling_job_dispose (GObject *object) { SpellingJob *self = (SpellingJob *)object; g_clear_object (&self->dictionary); g_clear_pointer (&self->fragments, g_array_unref); g_clear_pointer (&self->extra_word_chars, g_free); self->language = NULL; G_OBJECT_CLASS (spelling_job_parent_class)->dispose (object); } static void spelling_job_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { SpellingJob *self = SPELLING_JOB (object); switch (prop_id) { case PROP_DICTIONARY: g_value_set_object (value, self->dictionary); break; case PROP_EXTRA_WORD_CHARS: g_value_set_string (value, self->extra_word_chars); break; case PROP_LANGUAGE: g_value_set_pointer (value, self->language); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void spelling_job_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { SpellingJob *self = SPELLING_JOB (object); switch (prop_id) { case PROP_DICTIONARY: self->dictionary = g_value_dup_object (value); break; case PROP_EXTRA_WORD_CHARS: self->extra_word_chars = g_value_dup_string (value); break; case PROP_LANGUAGE: self->language = g_value_get_pointer (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void spelling_job_class_init (SpellingJobClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->dispose = spelling_job_dispose; object_class->get_property = spelling_job_get_property; object_class->set_property = spelling_job_set_property; properties[PROP_DICTIONARY] = g_param_spec_object ("dictionary", NULL, NULL, SPELLING_TYPE_DICTIONARY, (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); properties[PROP_EXTRA_WORD_CHARS] = g_param_spec_string ("extra-word-chars", NULL, NULL, NULL, (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); properties[PROP_LANGUAGE] = g_param_spec_pointer ("language", NULL, NULL, (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_properties (object_class, N_PROPS, properties); } static void spelling_job_init (SpellingJob *self) { self->fragments = g_array_new (FALSE, FALSE, sizeof (SpellingFragment)); g_array_set_clear_func (self->fragments, clear_fragment); } SpellingJob * spelling_job_new (SpellingDictionary *dictionary, PangoLanguage *language) { const char *extra_word_chars; g_return_val_if_fail (SPELLING_IS_DICTIONARY (dictionary), NULL); g_return_val_if_fail (language != NULL, NULL); extra_word_chars = spelling_dictionary_get_extra_word_chars (dictionary); return g_object_new (SPELLING_TYPE_JOB, "dictionary", dictionary, "extra-word-chars", extra_word_chars, "language", language, NULL); } static inline gboolean is_extra_word_char (const char *extra_word_chars, gunichar ch) { if (extra_word_chars == NULL) return FALSE; for (const char *c = extra_word_chars; *c; c = g_utf8_next_char (c)) { if (ch == g_utf8_get_char (c)) return TRUE; } return FALSE; } static gboolean find_word_start (const char **textptr, gsize *iptr, const PangoLogAttr *attrs, gsize attrslen, const char *extra_word_chars) { while (*iptr < attrslen) { if (attrs[*iptr].is_word_start) return TRUE; if (!attrs[*iptr].is_white) { gunichar ch = g_utf8_get_char (*textptr); if (is_extra_word_char (extra_word_chars, ch)) return TRUE; } *textptr = g_utf8_next_char (*textptr); (*iptr)++; } return FALSE; } static gboolean find_word_end (const char **textptr, gsize *iptr, const PangoLogAttr *attrs, gsize attrslen, const char *extra_word_chars) { while (*iptr < attrslen) { if (attrs[*iptr].is_word_end) { gboolean skipped = FALSE; /* We're at a word boundary, but we might have an extra word * char here. If so, skip past the word char. */ while (*iptr < attrslen && !attrs[*iptr].is_white && is_extra_word_char (extra_word_chars, g_utf8_get_char (*textptr))) { skipped = TRUE; *textptr = g_utf8_next_char (*textptr); (*iptr)++; } /* If we landed on a word start we must continue as it might * be something like `words's` where `'` is the extra word char * but `s` is not in extra_word_chars. */ if (skipped && *iptr < attrslen && attrs[*iptr].is_word_start) (void)find_word_end (textptr, iptr, attrs, attrslen, extra_word_chars); return TRUE; } *textptr = g_utf8_next_char (*textptr); (*iptr)++; } return FALSE; } static void clear_mistakes (gpointer data) { SpellingMistakes *mistakes = data; mistakes->fragment = NULL; g_clear_pointer (&mistakes->boundaries, g_array_unref); } static void spelling_job_check (GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable) { SpellingJob *self = source_object; SpellingBoundaries boundaries; g_autoptr(GArray) result = NULL; g_assert (G_IS_TASK (task)); g_assert (SPELLING_IS_JOB (self)); g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); spelling_boundaries_init (&boundaries); result = g_array_new (FALSE, FALSE, sizeof (SpellingMistakes)); g_array_set_clear_func (result, clear_mistakes); SPELLING_PROFILER_LOG ("Checking %u fragments", self->fragments->len); for (guint f = 0; f < self->fragments->len; f++) { const SpellingFragment *fragment = &g_array_index (self->fragments, SpellingFragment, f); G_GNUC_UNUSED g_autofree char *message = NULL; g_autoptr(GtkBitset) bitset = NULL; g_autofree PangoLogAttr *attrs = NULL; SpellingMistakes mistakes; const char *text; const char *p; GtkBitsetIter iter; gsize textlen; gsize attrslen; gsize i; guint pos; SPELLING_PROFILER_BEGIN_MARK spelling_boundaries_clear (&boundaries); mistakes.fragment = fragment; mistakes.boundaries = NULL; text = g_bytes_get_data (fragment->bytes, &textlen); attrslen = g_utf8_strlen (text, textlen) + 1; attrs = g_new0 (PangoLogAttr, attrslen); g_assert (textlen <= G_MAXINT); g_assert (attrslen <= G_MAXINT); pango_get_log_attrs (text, (int)textlen, -1, self->language, attrs, attrslen); p = text; i = 0; for (gsize count = 0; TRUE; count++) { SpellingBoundary boundary; const char *before = p; /* Occasionally check to break out of large runs */ if ((count & 0xFF) == 0 && g_atomic_int_get (&fragment->must_discard)) break; /* Find next word start */ if (!find_word_start (&p, &i, attrs, attrslen-1, self->extra_word_chars)) break; boundary.byte_offset = p - text; boundary.offset = i; /* Ensure we've moved at least one character as find_word_end() may stop * on the current character it is on. */ if (p == before) { p = g_utf8_next_char (p); i++; } if (!find_word_end (&p, &i, attrs, attrslen-1, self->extra_word_chars)) break; boundary.length = i - boundary.offset; boundary.byte_length = p - text - boundary.byte_offset; if (boundary.byte_length > 0) spelling_boundaries_append (&boundaries, &boundary); } if (g_atomic_int_get (&fragment->must_discard)) continue; bitset = _spelling_dictionary_check_words (self->dictionary, text, spelling_boundaries_index (&boundaries, 0), spelling_boundaries_get_size (&boundaries)); if (gtk_bitset_iter_init_first (&iter, bitset, &pos)) { mistakes.boundaries = g_array_new (FALSE, FALSE, sizeof (SpellingBoundary)); do { const SpellingBoundary *b = spelling_boundaries_index (&boundaries, pos); g_array_append_vals (mistakes.boundaries, b, 1); } while (gtk_bitset_iter_next (&iter, &pos)); g_array_append_val (result, mistakes); } if G_UNLIKELY (SPELLING_PROFILER_ACTIVE) message = g_strdup_printf ("%u chars, %u bytes, %u mistakes", (guint)attrslen, (guint)textlen, mistakes.boundaries ? mistakes.boundaries->len : 0); SPELLING_PROFILER_END_MARK ("Check", message); } spelling_boundaries_clear (&boundaries); g_task_return_pointer (task, g_steal_pointer (&result), (GDestroyNotify)g_array_unref); } void spelling_job_run (SpellingJob *self, GAsyncReadyCallback callback, gpointer user_data) { g_autoptr(GTask) task = NULL; g_return_if_fail (SPELLING_IS_JOB (self)); self->frozen = TRUE; task = g_task_new (self, NULL, callback, user_data); g_task_set_source_tag (task, spelling_job_run); g_task_run_in_thread (task, spelling_job_check); } void spelling_job_run_finish (SpellingJob *self, GAsyncResult *result, SpellingBoundary **fragments, guint *n_fragments, SpellingMistake **mistakes, guint *n_mistakes) { g_autoptr(GArray) ar = NULL; g_return_if_fail (SPELLING_IS_JOB (self)); g_return_if_fail (G_IS_TASK (result)); g_return_if_fail (n_fragments != NULL || fragments == NULL); g_return_if_fail (mistakes != NULL); g_return_if_fail (n_mistakes != NULL); *n_mistakes = 0; *mistakes = NULL; if (n_fragments != NULL) *n_fragments = 0; if (fragments != NULL) *fragments = NULL; if (n_fragments != NULL) { for (guint i = 0; i < self->fragments->len; i++) { const SpellingFragment *fragment = &g_array_index (self->fragments, SpellingFragment, i); if (fragment->must_discard) continue; (*n_fragments)++; } } if (fragments != NULL) { guint pos = 0; *fragments = g_new0 (SpellingBoundary, *n_fragments); for (guint i = 0; i < self->fragments->len; i++) { const SpellingFragment *fragment = &g_array_index (self->fragments, SpellingFragment, i); if (fragment->must_discard) continue; (*fragments)[pos].offset = fragment->position; (*fragments)[pos].length = fragment->length; pos++; } } ar = g_task_propagate_pointer (G_TASK (result), NULL); if (ar != NULL) { guint pos = 0; for (guint i = 0; i < ar->len; i++) { const SpellingMistakes *m = &g_array_index (ar, SpellingMistakes, i); if (m->fragment->must_discard) continue; *n_mistakes += m->boundaries->len; } if (*n_mistakes == 0) return; *mistakes = g_new0 (SpellingMistake, *n_mistakes); for (guint i = 0; i < ar->len; i++) { const SpellingMistakes *m = &g_array_index (ar, SpellingMistakes, i); if (m->fragment->must_discard) continue; for (guint j = 0; j < m->boundaries->len; j++) { const SpellingBoundary *boundary = &g_array_index (m->boundaries, SpellingBoundary, j); (*mistakes)[pos].offset = m->fragment->position + boundary->offset; (*mistakes)[pos].length = boundary->length; pos++; } } g_assert (pos == *n_mistakes); } } void spelling_job_run_sync (SpellingJob *self, SpellingBoundary **fragments, guint *n_fragments, SpellingMistake **mistakes, guint *n_mistakes) { g_autoptr(GTask) task = NULL; g_return_if_fail (SPELLING_IS_JOB (self)); g_return_if_fail (n_fragments != NULL || fragments == NULL); g_return_if_fail (mistakes != NULL); g_return_if_fail (n_mistakes != NULL); self->frozen = TRUE; task = g_task_new (self, NULL, NULL, NULL); g_task_set_source_tag (task, spelling_job_run); spelling_job_check (task, self, NULL, NULL); spelling_job_run_finish (self, G_ASYNC_RESULT (task), fragments, n_fragments, mistakes, n_mistakes); } void spelling_job_add_fragment (SpellingJob *self, GBytes *bytes, guint position, guint length) { SpellingFragment fragment = {0}; g_return_if_fail (SPELLING_IS_JOB (self)); g_return_if_fail (bytes != NULL); g_return_if_fail (self->frozen == FALSE); fragment.bytes = g_bytes_ref (bytes); fragment.position = position; fragment.length = length; fragment.must_discard = FALSE; g_array_append_val (self->fragments, fragment); } void spelling_job_notify_insert (SpellingJob *self, guint position, guint length) { g_return_if_fail (SPELLING_IS_JOB (self)); for (guint i = 0; i < self->fragments->len; i++) { SpellingFragment *fragment = &g_array_index (self->fragments, SpellingFragment, i); if (fragment->must_discard) continue; /* Inserts after w/ at least 1 position after fragment */ if (position > fragment->position + fragment->length) continue; /* Inserts before w/ at least 1 position before fragment */ if (position < fragment->position) { fragment->position += length; continue; } g_atomic_int_set (&fragment->must_discard, TRUE); } } void spelling_job_notify_delete (SpellingJob *self, guint position, guint length) { g_return_if_fail (SPELLING_IS_JOB (self)); for (guint i = 0; i < self->fragments->len; i++) { SpellingFragment *fragment = &g_array_index (self->fragments, SpellingFragment, i); if (fragment->must_discard) continue; /* Deletes after w/ at least 1 position after fragment */ if (position > fragment->position + fragment->length) continue; /* If we had the ability to look back at text to see if a boundary * character was before the cursor here, we could potentially avoid * bailing. But that is more effort than it's worth when we can just * recheck things. */ /* Deletes before w/ at least 1 position before fragment */ if (position + length < fragment->position) { fragment->position -= length; continue; } g_atomic_int_set (&fragment->must_discard, TRUE); } } void spelling_job_invalidate (SpellingJob *self, guint position, guint length) { g_return_if_fail (SPELLING_IS_JOB (self)); for (guint i = 0; i < self->fragments->len; i++) { SpellingFragment *fragment = &g_array_index (self->fragments, SpellingFragment, i); if (fragment->must_discard) continue; if (position > fragment->position + fragment->length) continue; if (position + length < fragment->position) continue; g_atomic_int_set (&fragment->must_discard, TRUE); } } 0707010000002F000081A40000000000000000000000016712C6CB00000431000000000000000000000000000000000000003200000000libspelling-0.4.4/lib/spelling-language-private.h/* * spelling-language-private.h * * Copyright 2023 Christian Hergert <chergert@redhat.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "spelling-language.h" G_BEGIN_DECLS SpellingLanguage *spelling_language_new (const char *name, const char *code, const char *group); G_END_DECLS 07070100000030000081A40000000000000000000000016712C6CB0000165D000000000000000000000000000000000000002A00000000libspelling-0.4.4/lib/spelling-language.c/* * spelling-language.c * * Copyright 2021-2023 Christian Hergert <chergert@redhat.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include "spelling-language-private.h" /** * SpellingLanguage: * * Represents a spellchecking language. */ struct _SpellingLanguage { GObject parent_instance; char *name; char *code; char *group; }; G_DEFINE_FINAL_TYPE (SpellingLanguage, spelling_language, G_TYPE_OBJECT) enum { PROP_0, PROP_CODE, PROP_GROUP, PROP_NAME, N_PROPS }; static GParamSpec *properties[N_PROPS]; /** * spelling_language_new: * * Create a new `SpellingLanguage`. * * Returns: (transfer full): a newly created `SpellingLanguage` */ SpellingLanguage * spelling_language_new (const char *name, const char *code, const char *group) { return g_object_new (SPELLING_TYPE_LANGUAGE, "name", name, "code", code, "group", group, NULL); } static void spelling_language_finalize (GObject *object) { SpellingLanguage *self = (SpellingLanguage *)object; g_clear_pointer (&self->name, g_free); g_clear_pointer (&self->code, g_free); g_clear_pointer (&self->group, g_free); G_OBJECT_CLASS (spelling_language_parent_class)->finalize (object); } static void spelling_language_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { SpellingLanguage *self = SPELLING_LANGUAGE (object); switch (prop_id) { case PROP_NAME: g_value_set_string (value, spelling_language_get_name (self)); break; case PROP_CODE: g_value_set_string (value, spelling_language_get_code (self)); break; case PROP_GROUP: g_value_set_string (value, spelling_language_get_group (self)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void spelling_language_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { SpellingLanguage *self = SPELLING_LANGUAGE (object); switch (prop_id) { case PROP_NAME: self->name = g_value_dup_string (value); break; case PROP_CODE: self->code = g_value_dup_string (value); break; case PROP_GROUP: self->group = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void spelling_language_class_init (SpellingLanguageClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = spelling_language_finalize; object_class->get_property = spelling_language_get_property; object_class->set_property = spelling_language_set_property; /** * SpellingLanguage:name: * * The name of the language. */ properties[PROP_NAME] = g_param_spec_string ("name", NULL, NULL, NULL, (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); /** * SpellingLanguage:code: * * The language code. */ properties[PROP_CODE] = g_param_spec_string ("code", NULL, NULL, NULL, (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); /** * SpellingLanguage:group: * * A group for sorting, usually the country name. */ properties[PROP_GROUP] = g_param_spec_string ("group", NULL, NULL, NULL, (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_properties (object_class, N_PROPS, properties); } static void spelling_language_init (SpellingLanguage *self) { } /** * spelling_language_get_name: * @self: a `SpellingLanguage` * * Gets the name of the language, or %NULL if undefined. * * Returns: (transfer none) (nullable): the name of the language */ const char * spelling_language_get_name (SpellingLanguage *self) { g_return_val_if_fail (SPELLING_IS_LANGUAGE (self), NULL); return self->name; } /** * spelling_language_get_code: * @self: a `SpellingLanguage` * * Gets the code of the language, or %NULL if undefined. * * Returns: (transfer none) (nullable): the code of the language */ const char * spelling_language_get_code (SpellingLanguage *self) { g_return_val_if_fail (SPELLING_IS_LANGUAGE (self), NULL); return self->code; } /** * spelling_language_get_group: * @self: a `SpellingLanguage` * * Gets the group of the language, or %NULL if undefined. * * Returns: (transfer none) (nullable): the group of the language */ const char * spelling_language_get_group (SpellingLanguage *self) { g_return_val_if_fail (SPELLING_IS_LANGUAGE (self), NULL); return self->group; } 07070100000031000081A40000000000000000000000016712C6CB000005E8000000000000000000000000000000000000002A00000000libspelling-0.4.4/lib/spelling-language.h/* * spelling-language.h * * Copyright 2021-2023 Christian Hergert <chergert@redhat.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #if !defined(LIBSPELLING_INSIDE) && !defined(LIBSPELLING_COMPILATION) # error "Only <libspelling.h> can be included directly." #endif #include <glib-object.h> #include "spelling-version-macros.h" G_BEGIN_DECLS #define SPELLING_TYPE_LANGUAGE (spelling_language_get_type()) SPELLING_AVAILABLE_IN_ALL G_DECLARE_FINAL_TYPE (SpellingLanguage, spelling_language, SPELLING, LANGUAGE, GObject) SPELLING_AVAILABLE_IN_ALL const char *spelling_language_get_group (SpellingLanguage *self); SPELLING_AVAILABLE_IN_ALL const char *spelling_language_get_name (SpellingLanguage *self); SPELLING_AVAILABLE_IN_ALL const char *spelling_language_get_code (SpellingLanguage *self); G_END_DECLS 07070100000032000081A40000000000000000000000016712C6CB00000478000000000000000000000000000000000000002E00000000libspelling-0.4.4/lib/spelling-menu-private.h/* * spelling-menu-private.h * * Copyright 2021-2023 Christian Hergert <chergert@redhat.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <gio/gio.h> G_BEGIN_DECLS GMenuModel *spelling_menu_new (void); void spelling_menu_set_corrections (GMenuModel *menu, const char *word, const char * const *words); G_END_DECLS 07070100000033000081A40000000000000000000000016712C6CB00002743000000000000000000000000000000000000002600000000libspelling-0.4.4/lib/spelling-menu.c/* * spelling-menu.c * * Copyright 2021-2023 Christian Hergert <chergert@redhat.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <glib/gi18n-lib.h> #include "spelling-compat-private.h" #include "spelling-language.h" #include "spelling-menu-private.h" #include "spelling-provider.h" #define MAX_CORRECTIONS 5 #define SPELLING_CORRECTIONS_MENU "SPELLING_CORRECTIONS_MENU" #define SPELLING_TYPE_CORRECTIONS (spelling_corrections_get_type()) G_DECLARE_FINAL_TYPE (SpellingCorrections, spelling_corrections, SPELLING, CORRECTIONS, GMenuModel) struct _SpellingCorrections { GMenuModel parent_instance; char *word; char **corrections; }; G_DEFINE_FINAL_TYPE (SpellingCorrections, spelling_corrections, G_TYPE_MENU_MODEL) static int spelling_corrections_get_n_items (GMenuModel *model) { SpellingCorrections *self = SPELLING_CORRECTIONS (model); return self->corrections ? g_strv_length (self->corrections) : 0; } static gboolean spelling_corrections_is_mutable (GMenuModel *model) { return TRUE; } static GMenuModel * spelling_corrections_get_item_link (GMenuModel *model, int position, const char *link) { return NULL; } static void spelling_corrections_get_item_links (GMenuModel *model, int position, GHashTable **links) { *links = NULL; } static void spelling_corrections_get_item_attributes (GMenuModel *model, int position, GHashTable **attributes) { SpellingCorrections *self = SPELLING_CORRECTIONS (model); const char *correction; GHashTable *ht; g_assert (G_IS_MENU_MODEL (model)); g_assert (attributes != NULL); *attributes = NULL; if (position < 0 || self->corrections == NULL || position >= g_strv_length (self->corrections)) return; correction = self->corrections[position]; g_assert (correction != NULL); ht = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_variant_unref); g_hash_table_insert (ht, g_strdup (G_MENU_ATTRIBUTE_ACTION), g_variant_ref_sink (g_variant_new_string ("spelling.correct"))); g_hash_table_insert (ht, g_strdup (G_MENU_ATTRIBUTE_TARGET), g_variant_ref_sink (g_variant_new_string (correction))); g_hash_table_insert (ht, g_strdup (G_MENU_ATTRIBUTE_LABEL), g_variant_ref_sink (g_variant_new_string (correction))); *attributes = ht; } static void spelling_menu_dispose (GObject *object) { SpellingCorrections *self = (SpellingCorrections *)object; g_clear_pointer (&self->word, g_free); g_clear_pointer (&self->corrections, g_strfreev); G_OBJECT_CLASS (spelling_corrections_parent_class)->dispose (object); } static void spelling_corrections_class_init (SpellingCorrectionsClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GMenuModelClass *menu_model_class = G_MENU_MODEL_CLASS (klass); object_class->dispose = spelling_menu_dispose; menu_model_class->get_n_items = spelling_corrections_get_n_items; menu_model_class->is_mutable = spelling_corrections_is_mutable; menu_model_class->get_item_link = spelling_corrections_get_item_link; menu_model_class->get_item_links = spelling_corrections_get_item_links; menu_model_class->get_item_attributes = spelling_corrections_get_item_attributes; } static void spelling_corrections_init (SpellingCorrections *self) { } static void spelling_corrections_set (SpellingCorrections *self, const char *word, const char * const *corrections) { char **copy; guint removed = 0; guint added = 0; g_assert (SPELLING_IS_CORRECTIONS (self)); g_set_str (&self->word, word); if (self->corrections == NULL && corrections == NULL) return; if (corrections != NULL && self->corrections != NULL && g_strv_equal (corrections, (const char * const *)self->corrections)) return; if (self->corrections != NULL) removed = g_strv_length (self->corrections); if (corrections != NULL) added = g_strv_length ((char **)corrections); copy = g_strdupv ((char **)corrections); g_strfreev (self->corrections); self->corrections = copy; g_menu_model_items_changed (G_MENU_MODEL (self), 0, removed, added); } static GMenuModel * spelling_corrections_new (void) { return g_object_new (SPELLING_TYPE_CORRECTIONS, NULL); } static int count_groups (GListModel *model) { g_autoptr(GHashTable) groups = g_hash_table_new (g_str_hash, g_str_equal); guint n_items; g_assert (G_IS_LIST_MODEL (model)); n_items = g_list_model_get_n_items (model); for (guint i = 0; i < n_items; i++) { g_autoptr(SpellingLanguage) language = g_list_model_get_item (model, i); const char *group = spelling_language_get_group (language); if (group != NULL && group[0] != 0 && !g_hash_table_contains (groups, group)) g_hash_table_insert (groups, (char *)group, NULL); } return g_hash_table_size (groups); } static void populate_languages (GMenu *menu) { SpellingProvider *provider = spelling_provider_get_default (); g_autoptr(GListModel) languages = spelling_provider_list_languages (provider); g_autoptr(GHashTable) groups = NULL; guint n_items; if (languages == NULL) return; groups = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); n_items = g_list_model_get_n_items (languages); /* First setup our groups. We do that up front so we can avoid * checking below, but also so we can hoist a single group up * into the parent menu if necessary. */ if (count_groups (languages) > 1) { for (guint i = 0; i < n_items; i++) { g_autoptr(SpellingLanguage) language = g_list_model_get_item (languages, i); const char *group = spelling_language_get_group (language); GMenu *group_menu; if (group == NULL || group[0] == 0) continue; if (!g_hash_table_contains (groups, group)) { group_menu = g_menu_new (); g_menu_append_submenu (menu, group, G_MENU_MODEL (group_menu)); g_hash_table_insert (groups, g_strdup (group), g_steal_pointer (&group_menu)); } } } for (guint i = 0; i < n_items; i++) { g_autoptr(SpellingLanguage) language = g_list_model_get_item (languages, i); const char *name = spelling_language_get_name (language); const char *group = spelling_language_get_group (language); const char *code = spelling_language_get_code (language); g_autoptr(GMenuItem) item = NULL; GMenu *group_menu; if (group == NULL || !(group_menu = g_hash_table_lookup (groups, group))) group_menu = menu; g_assert (G_IS_MENU (group_menu)); item = g_menu_item_new (name, NULL); g_menu_item_set_action_and_target (item, "spelling.language", "s", code); g_menu_append_item (group_menu, item); } } /** * spelling_menu_new: * * Creates a new menu which can be updated with spelling options. * * Returns: (transfer full): a `GMenuModel` */ GMenuModel * spelling_menu_new (void) { static GMenu *languages_menu; static GMenuItem *languages_item; g_autoptr(GMenu) menu = g_menu_new (); g_autoptr(GMenuModel) corrections_menu = spelling_corrections_new (); g_autoptr(GMenuItem) add_item = g_menu_item_new (_("Add to Dictionary"), "spelling.add"); g_autoptr(GMenuItem) ignore_item = g_menu_item_new (_("Ignore"), "spelling.ignore"); g_autoptr(GMenuItem) check_item = g_menu_item_new (_("Check Spelling"), "spelling.enabled"); if (languages_menu == NULL) { languages_menu = g_menu_new (); populate_languages (languages_menu); } if (languages_item == NULL) languages_item = g_menu_item_new_submenu (_("Languages"), G_MENU_MODEL (languages_menu)); g_menu_item_set_attribute (add_item, "hidden-when", "s", "action-disabled"); g_menu_item_set_attribute (ignore_item, "hidden-when", "s", "action-disabled"); g_menu_item_set_attribute (check_item, "role", "s", "check"); g_menu_item_set_attribute (languages_item, "submenu-action", "s", "spellcheck.enabled"); g_menu_append_section (menu, NULL, G_MENU_MODEL (corrections_menu)); g_menu_append_item (menu, add_item); g_menu_append_item (menu, ignore_item); g_menu_append_item (menu, check_item); g_menu_append_item (menu, languages_item); g_object_set_data_full (G_OBJECT (menu), SPELLING_CORRECTIONS_MENU, g_object_ref (corrections_menu), g_object_unref); return G_MENU_MODEL (g_steal_pointer (&menu)); } void spelling_menu_set_corrections (GMenuModel *menu, const char *word, const char * const *words) { SpellingCorrections *corrections_menu; g_return_if_fail (G_IS_MENU_MODEL (menu)); if ((corrections_menu = g_object_get_data (G_OBJECT (menu), SPELLING_CORRECTIONS_MENU))) { g_assert (SPELLING_IS_CORRECTIONS (corrections_menu)); spelling_corrections_set (corrections_menu, word, words); } } 07070100000034000081A40000000000000000000000016712C6CB00000631000000000000000000000000000000000000003300000000libspelling-0.4.4/lib/spelling-provider-internal.h/* * spelling-provider-internal.h * * Copyright 2021-2023 Christian Hergert <chergert@redhat.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "spelling-provider.h" G_BEGIN_DECLS #define SPELLING_PROVIDER_GET_CLASS(obj) G_TYPE_INSTANCE_GET_CLASS(obj, SPELLING_TYPE_PROVIDER, SpellingProviderClass) struct _SpellingProvider { GObject parent_instance; char *display_name; }; struct _SpellingProviderClass { GObjectClass parent_class; GListModel *(*list_languages) (SpellingProvider *self); gboolean (*supports_language) (SpellingProvider *self, const char *language); SpellingDictionary *(*load_dictionary) (SpellingProvider *self, const char *language); const char *(*get_default_code) (SpellingProvider *self); }; G_END_DECLS 07070100000035000081A40000000000000000000000016712C6CB00001CCA000000000000000000000000000000000000002A00000000libspelling-0.4.4/lib/spelling-provider.c/* * spelling-provider.c * * Copyright 2021-2023 Christian Hergert <chergert@redhat.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include "spelling-dictionary.h" #include "spelling-empty-provider-private.h" #include "spelling-provider-internal.h" #ifdef HAVE_ENCHANT # include "enchant/spelling-enchant-provider.h" #endif /** * SpellingProvider: * * Abstract base class for spellchecking providers. */ G_DEFINE_ABSTRACT_TYPE (SpellingProvider, spelling_provider, G_TYPE_OBJECT) enum { PROP_0, PROP_DISPLAY_NAME, N_PROPS }; static GParamSpec *properties [N_PROPS]; static void spelling_provider_finalize (GObject *object) { SpellingProvider *self = (SpellingProvider *)object; g_clear_pointer (&self->display_name, g_free); G_OBJECT_CLASS (spelling_provider_parent_class)->finalize (object); } static void spelling_provider_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { SpellingProvider *self = SPELLING_PROVIDER (object); switch (prop_id) { case PROP_DISPLAY_NAME: g_value_set_string (value, spelling_provider_get_display_name (self)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void spelling_provider_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { SpellingProvider *self = SPELLING_PROVIDER (object); switch (prop_id) { case PROP_DISPLAY_NAME: self->display_name = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void spelling_provider_class_init (SpellingProviderClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = spelling_provider_finalize; object_class->get_property = spelling_provider_get_property; object_class->set_property = spelling_provider_set_property; /** * SpellingProvider:display-name: * * The display name. */ properties [PROP_DISPLAY_NAME] = g_param_spec_string ("display-name", "Display Name", "Display Name", NULL, (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_properties (object_class, N_PROPS, properties); } static void spelling_provider_init (SpellingProvider *self) { } /** * spelling_provider_get_display_name: * @self: a `SpellingProvider` * * Gets the display name of the provider, or %NULL if undefined. * * Returns: (transfer none) (nullable): the display name of the provider */ const char * spelling_provider_get_display_name (SpellingProvider *self) { g_return_val_if_fail (SPELLING_IS_PROVIDER (self), NULL); return self->display_name; } /** * spelling_provider_get_default: * * Gets the default spell provider. * * Returns: (transfer none): a `SpellingProvider` */ SpellingProvider * spelling_provider_get_default (void) { static SpellingProvider *instance; if (instance == NULL) { #ifdef HAVE_ENCHANT instance = spelling_enchant_provider_new (); #endif if (instance == NULL) instance = spelling_empty_provider_new (); g_set_weak_pointer (&instance, instance); } return instance; } /** * spelling_provider_supports_language: * @self: a `SpellingProvider` * @language: the language such as `en_US`. * * Checks of @language is supported by the provider. * * Returns: %TRUE if @language is supported, otherwise %FALSE */ gboolean spelling_provider_supports_language (SpellingProvider *self, const char *language) { g_return_val_if_fail (SPELLING_IS_PROVIDER (self), FALSE); g_return_val_if_fail (language != NULL, FALSE); return SPELLING_PROVIDER_GET_CLASS (self)->supports_language (self, language); } /** * spelling_provider_list_languages: * @self: a `SpellingProvider` * * Gets a `GListModel` of languages supported by the provider. * * Returns: (transfer full): a `GListModel` of `SpellingLanguage` */ GListModel * spelling_provider_list_languages (SpellingProvider *self) { GListModel *ret; g_return_val_if_fail (SPELLING_IS_PROVIDER (self), NULL); ret = SPELLING_PROVIDER_GET_CLASS (self)->list_languages (self); g_return_val_if_fail (!ret || G_IS_LIST_MODEL (ret), NULL); return ret; } /** * spelling_provider_load_dictionary: * @self: a `SpellingProvider` * @language: the language to load such as `en_US`. * * Gets a `SpellingDictionary` for the requested language, or %NULL * if the language is not supported. * * Returns: (transfer full) (nullable): a `SpellingDictionary` or %NULL */ SpellingDictionary * spelling_provider_load_dictionary (SpellingProvider *self, const char *language) { SpellingDictionary *ret; g_return_val_if_fail (SPELLING_IS_PROVIDER (self), NULL); g_return_val_if_fail (language != NULL, NULL); ret = SPELLING_PROVIDER_GET_CLASS (self)->load_dictionary (self, language); g_return_val_if_fail (!ret || SPELLING_IS_DICTIONARY (ret), NULL); return ret; } /** * spelling_provider_get_default_code: * @self: a `SpellingProvider` * * Gets the default language code for the detected system locales, or %NULL * if the provider doesn't support any of them. * * Returns: (transfer none) (nullable): the default language code */ const char * spelling_provider_get_default_code (SpellingProvider *self) { const char * const *langs; const char *ret; g_return_val_if_fail (SPELLING_IS_PROVIDER (self), NULL); if (SPELLING_PROVIDER_GET_CLASS (self)->get_default_code && (ret = SPELLING_PROVIDER_GET_CLASS (self)->get_default_code (self))) return ret; langs = g_get_language_names (); if (langs != NULL) { for (guint i = 0; langs[i]; i++) { /* Skip past things like "thing.utf8" since we'll * prefer to just have "thing" as it ensures we're * more likely to get code matches elsewhere. */ if (strchr (langs[i], '.') != NULL) continue; if (spelling_provider_supports_language (self, langs[i])) return langs[i]; } } if (spelling_provider_supports_language (self, "en_US")) return "en_US"; if (spelling_provider_supports_language (self, "C")) return "C"; return NULL; } 07070100000036000081A40000000000000000000000016712C6CB00000A06000000000000000000000000000000000000002A00000000libspelling-0.4.4/lib/spelling-provider.h/* * spelling-provider.h * * Copyright 2021-2023 Christian Hergert <chergert@redhat.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #if !defined(LIBSPELLING_INSIDE) && !defined(LIBSPELLING_COMPILATION) # error "Only <libspelling.h> can be included directly." #endif #include <gio/gio.h> #include "spelling-types.h" #include "spelling-version-macros.h" G_BEGIN_DECLS #define SPELLING_TYPE_PROVIDER (spelling_provider_get_type()) #define SPELLING_IS_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, SPELLING_TYPE_PROVIDER)) #define SPELLING_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, SPELLING_TYPE_PROVIDER, SpellingProvider)) #define SPELLING_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST(klass, SPELLING_TYPE_PROVIDER, SpellingProviderClass)) typedef struct _SpellingProvider SpellingProvider; typedef struct _SpellingProviderClass SpellingProviderClass; SPELLING_AVAILABLE_IN_ALL GType spelling_provider_get_type (void) G_GNUC_CONST; SPELLING_AVAILABLE_IN_ALL SpellingProvider *spelling_provider_get_default (void); SPELLING_AVAILABLE_IN_ALL const char *spelling_provider_get_default_code (SpellingProvider *self); SPELLING_AVAILABLE_IN_ALL const char *spelling_provider_get_display_name (SpellingProvider *self); SPELLING_AVAILABLE_IN_ALL gboolean spelling_provider_supports_language (SpellingProvider *self, const char *language); SPELLING_AVAILABLE_IN_ALL GListModel *spelling_provider_list_languages (SpellingProvider *self); SPELLING_AVAILABLE_IN_ALL SpellingDictionary *spelling_provider_load_dictionary (SpellingProvider *self, const char *language); G_DEFINE_AUTOPTR_CLEANUP_FUNC (SpellingProvider, g_object_unref) G_END_DECLS 07070100000037000081A40000000000000000000000016712C6CB00009C66000000000000000000000000000000000000003500000000libspelling-0.4.4/lib/spelling-text-buffer-adapter.c/* * spelling-text-buffer-adapter.c * * Copyright 2021-2023 Christian Hergert <chergert@redhat.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include "egg-action-group.h" #include "spelling-compat-private.h" #include "spelling-checker-private.h" #include "spelling-cursor-private.h" #include "spelling-engine-private.h" #include "spelling-menu-private.h" #include "spelling-text-buffer-adapter.h" /** * SpellingTextBufferAdapter: * * `SpellingTextBufferAdapter` implements helpers to easily add spellchecking * capabilities to a `GtkSourceBuffer`. */ #define INVALIDATE_DELAY_MSECS 100 #define MAX_WORD_CHARS 100 struct _SpellingTextBufferAdapter { GObject parent_instance; SpellingEngine *engine; GSignalGroup *buffer_signals; GtkSourceBuffer *buffer; SpellingChecker *checker; GtkTextTag *no_spell_check_tag; GMenuModel *menu; GMenu *top_menu; char *word_under_cursor; /* Borrowed pointers */ GtkTextMark *insert_mark; GtkTextTag *tag; guint commit_handler; guint cursor_position; guint incoming_cursor_position; guint queued_cursor_moved; guint enabled : 1; }; static void spelling_add_action (SpellingTextBufferAdapter *self, GVariant *param); static void spelling_ignore_action (SpellingTextBufferAdapter *self, GVariant *param); static void spelling_enabled_action (SpellingTextBufferAdapter *self, GVariant *param); static void spelling_correct_action (SpellingTextBufferAdapter *self, GVariant *param); static void spelling_language_action (SpellingTextBufferAdapter *self, GVariant *param); EGG_DEFINE_ACTION_GROUP (SpellingTextBufferAdapter, spelling_text_buffer_adapter, { { "add", spelling_add_action }, { "correct", spelling_correct_action, "s" }, { "enabled", spelling_enabled_action, NULL, "false" }, { "ignore", spelling_ignore_action }, { "language", spelling_language_action, "s", "''" }, }) G_DEFINE_FINAL_TYPE_WITH_CODE (SpellingTextBufferAdapter, spelling_text_buffer_adapter, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, spelling_text_buffer_adapter_init_action_group)) enum { PROP_0, PROP_BUFFER, PROP_CHECKER, PROP_ENABLED, PROP_LANGUAGE, N_PROPS }; static GParamSpec *properties[N_PROPS]; static void spelling_text_buffer_adapter_commit_notify (GtkTextBuffer *buffer, GtkTextBufferNotifyFlags flags, guint position, guint length, gpointer user_data) { SpellingTextBufferAdapter *self = user_data; g_assert (GTK_IS_TEXT_BUFFER (buffer)); g_assert (SPELLING_IS_TEXT_BUFFER_ADAPTER (self)); if (flags == GTK_TEXT_BUFFER_NOTIFY_BEFORE_INSERT) spelling_engine_before_insert_text (self->engine, position, length); else if (flags == GTK_TEXT_BUFFER_NOTIFY_AFTER_INSERT) spelling_engine_after_insert_text (self->engine, position, length); else if (flags == GTK_TEXT_BUFFER_NOTIFY_BEFORE_DELETE) spelling_engine_before_delete_range (self->engine, position, length); else if (flags == GTK_TEXT_BUFFER_NOTIFY_AFTER_DELETE) spelling_engine_after_delete_range (self->engine, position); } static gboolean spelling_text_buffer_adapter_check_enabled (gpointer instance) { SpellingTextBufferAdapter *self = instance; if (self->buffer == NULL) return FALSE; #if GTK_SOURCE_CHECK_VERSION(5, 9, 0) if (gtk_source_buffer_get_loading (self->buffer)) return FALSE; #endif return self->enabled; } static guint spelling_text_buffer_adapter_get_cursor (gpointer instance) { SpellingTextBufferAdapter *self = instance; GtkTextIter iter; gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (self->buffer), &iter, self->insert_mark); return gtk_text_iter_get_offset (&iter); } static char * spelling_text_buffer_adapter_copy_text (gpointer instance, guint position, guint length) { SpellingTextBufferAdapter *self = instance; GtkTextIter begin; GtkTextIter end; gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (self->buffer), &begin, position); gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (self->buffer), &end, position + length); return gtk_text_iter_get_slice (&begin, &end); } static void spelling_text_buffer_adapter_apply_tag (gpointer instance, guint position, guint length) { SpellingTextBufferAdapter *self = instance; GtkTextIter begin; GtkTextIter end; if (self->tag == NULL) return; /* If the position overlaps our cursor position, ignore it. We don't * want to show that to the user while they are typing and will * instead deal with it when the cursor leaves the word. */ if (position <= self->cursor_position && position + length >= self->cursor_position) return; gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (self->buffer), &begin, position); gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (self->buffer), &end, position + length); gtk_text_buffer_apply_tag (GTK_TEXT_BUFFER (self->buffer), self->tag, &begin, &end); } static void spelling_text_buffer_adapter_clear_tag (gpointer instance, guint position, guint length) { SpellingTextBufferAdapter *self = instance; GtkTextIter begin; GtkTextIter end; if (self->tag == NULL) return; gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (self->buffer), &begin, position); gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (self->buffer), &end, position + length); gtk_text_buffer_remove_tag (GTK_TEXT_BUFFER (self->buffer), self->tag, &begin, &end); } static gboolean spelling_text_buffer_adapter_backward_word_start (gpointer instance, guint *position) { SpellingTextBufferAdapter *self = instance; const char *extra_word_chars = NULL; GtkTextIter iter; guint prev = *position; if (self->checker != NULL) extra_word_chars = spelling_checker_get_extra_word_chars (self->checker); gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (self->buffer), &iter, *position); spelling_iter_backward_word_start (&iter, extra_word_chars); *position = gtk_text_iter_get_offset (&iter); return prev != *position; } static gboolean spelling_text_buffer_adapter_forward_word_end (gpointer instance, guint *position) { SpellingTextBufferAdapter *self = instance; const char *extra_word_chars = NULL; GtkTextIter iter; guint prev = *position; if (self->checker != NULL) extra_word_chars = spelling_checker_get_extra_word_chars (self->checker); gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (self->buffer), &iter, *position); spelling_iter_forward_word_end (&iter, extra_word_chars); *position = gtk_text_iter_get_offset (&iter); return prev != *position; } static PangoLanguage * spelling_text_buffer_adapter_get_pango_language (gpointer instance) { SpellingTextBufferAdapter *self = instance; return _spelling_checker_get_pango_language (self->checker); } static SpellingDictionary * spelling_text_buffer_adapter_get_dictionary (gpointer instance) { SpellingTextBufferAdapter *self = instance; return _spelling_checker_get_dictionary (self->checker); } static void spelling_text_buffer_adapter_intersect_spellcheck_region (gpointer instance, GtkBitset *region) { SpellingTextBufferAdapter *self = instance; GtkTextIter begin; GtkTextIter end; GtkTextIter iter; g_assert (SPELLING_IS_TEXT_BUFFER_ADAPTER (self)); if (self->no_spell_check_tag == NULL || gtk_bitset_is_empty (region)) return; gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (self->buffer), &begin, gtk_bitset_get_minimum (region)); gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (self->buffer), &end, gtk_bitset_get_maximum (region)); if (gtk_text_iter_has_tag (&begin, self->no_spell_check_tag)) { if (!gtk_text_iter_starts_tag (&begin, self->no_spell_check_tag)) gtk_text_iter_backward_to_tag_toggle (&begin, self->no_spell_check_tag); } else { gtk_text_iter_forward_to_tag_toggle (&begin, self->no_spell_check_tag); } /* At this point we either have a no-spell-check tag or the * @begin iter will be at the end of the file and we can be * certain it will be >= 0. */ while (gtk_text_iter_compare (&begin, &end) < 0) { iter = begin; gtk_text_iter_forward_to_tag_toggle (&iter, self->no_spell_check_tag); g_assert (gtk_text_iter_compare (&begin, &iter) < 0); g_assert (gtk_text_iter_has_tag (&begin, self->no_spell_check_tag)); g_assert (!gtk_text_iter_has_tag (&iter, self->no_spell_check_tag)); #if 0 g_print ("%u:%u to %u:%u: NO SPELL CHECK (%u -> %u)\n", gtk_text_iter_get_line (&begin) + 1, gtk_text_iter_get_line_offset (&begin) + 1, gtk_text_iter_get_line (&iter) + 1, gtk_text_iter_get_line_offset (&iter) + 1, gtk_text_iter_get_offset (&begin), gtk_text_iter_get_offset (&iter)); #endif gtk_bitset_remove_range_closed (region, gtk_text_iter_get_offset (&begin), gtk_text_iter_get_offset (&iter) - 1); begin = iter; gtk_text_iter_forward_to_tag_toggle (&begin, self->no_spell_check_tag); } } static const SpellingAdapter adapter_funcs = { .check_enabled = spelling_text_buffer_adapter_check_enabled, .get_cursor = spelling_text_buffer_adapter_get_cursor, .copy_text = spelling_text_buffer_adapter_copy_text, .apply_tag = spelling_text_buffer_adapter_apply_tag, .clear_tag = spelling_text_buffer_adapter_clear_tag, .backward_word_start = spelling_text_buffer_adapter_backward_word_start, .forward_word_end = spelling_text_buffer_adapter_forward_word_end, .get_language = spelling_text_buffer_adapter_get_pango_language, .get_dictionary = spelling_text_buffer_adapter_get_dictionary, .intersect_spellcheck_region = spelling_text_buffer_adapter_intersect_spellcheck_region, }; static inline gboolean forward_word_end (SpellingTextBufferAdapter *self, GtkTextIter *iter) { const char *extra_word_chars = NULL; if (self->checker != NULL) extra_word_chars = spelling_checker_get_extra_word_chars (self->checker); return spelling_iter_forward_word_end (iter, extra_word_chars); } static inline gboolean backward_word_start (SpellingTextBufferAdapter *self, GtkTextIter *iter) { const char *extra_word_chars = NULL; if (self->checker != NULL) extra_word_chars = spelling_checker_get_extra_word_chars (self->checker); return spelling_iter_backward_word_start (iter, extra_word_chars); } static gboolean get_word_at_position (SpellingTextBufferAdapter *self, guint position, GtkTextIter *begin, GtkTextIter *end) { gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (self->buffer), begin, position); *end = *begin; if (gtk_text_iter_ends_word (end)) { backward_word_start (self, begin); return TRUE; } if (!gtk_text_iter_starts_word (begin)) { if (!gtk_text_iter_inside_word (begin)) return FALSE; backward_word_start (self, begin); } if (!gtk_text_iter_ends_word (end)) forward_word_end (self, end); return TRUE; } /** * spelling_text_buffer_adapter_new: * @buffer: (not nullable): a `GtkSourceBuffer` * @checker: a `SpellingChecker` * * Create a new `SpellingTextBufferAdapter`. * * Returns: (transfer full): a newly created `SpellingTextBufferAdapter` */ SpellingTextBufferAdapter * spelling_text_buffer_adapter_new (GtkSourceBuffer *buffer, SpellingChecker *checker) { g_return_val_if_fail (GTK_SOURCE_IS_BUFFER (buffer), NULL); g_return_val_if_fail (!checker || SPELLING_IS_CHECKER (checker), NULL); return g_object_new (SPELLING_TYPE_TEXT_BUFFER_ADAPTER, "buffer", buffer, "checker", checker, NULL); } /** * spelling_text_buffer_adapter_invalidate_all: * @self: a `SpellingTextBufferAdapter` * * Invalidate the spelling engine, to force parsing again. * * Invalidation is automatically done on [property@GtkSource.Buffer:loading] * change. */ void spelling_text_buffer_adapter_invalidate_all (SpellingTextBufferAdapter *self) { g_assert (SPELLING_IS_TEXT_BUFFER_ADAPTER (self)); spelling_engine_invalidate_all (self->engine); } static void on_tag_added_cb (SpellingTextBufferAdapter *self, GtkTextTag *tag, GtkTextTagTable *tag_table) { g_autofree char *name = NULL; g_assert (SPELLING_IS_TEXT_BUFFER_ADAPTER (self)); g_assert (GTK_IS_TEXT_TAG (tag)); g_assert (GTK_IS_TEXT_TAG_TABLE (tag_table)); g_object_get (tag, "name", &name, NULL); if (name && strcmp (name, "gtksourceview:context-classes:no-spell-check") == 0) { g_set_object (&self->no_spell_check_tag, tag); spelling_text_buffer_adapter_invalidate_all (self); } } static void on_tag_removed_cb (SpellingTextBufferAdapter *self, GtkTextTag *tag, GtkTextTagTable *tag_table) { g_assert (SPELLING_IS_TEXT_BUFFER_ADAPTER (self)); g_assert (GTK_IS_TEXT_TAG (tag)); g_assert (GTK_IS_TEXT_TAG_TABLE (tag_table)); if (tag == self->no_spell_check_tag) { g_clear_object (&self->no_spell_check_tag); spelling_text_buffer_adapter_invalidate_all (self); } } static void invalidate_tag_region_cb (SpellingTextBufferAdapter *self, GtkTextTag *tag, GtkTextIter *begin, GtkTextIter *end, GtkTextBuffer *buffer) { g_assert (SPELLING_IS_TEXT_BUFFER_ADAPTER (self)); g_assert (GTK_IS_TEXT_TAG (tag)); g_assert (GTK_IS_TEXT_BUFFER (buffer)); if (tag == self->no_spell_check_tag) { gtk_text_iter_order (begin, end); spelling_engine_invalidate (self->engine, gtk_text_iter_get_offset (begin), gtk_text_iter_get_offset (end) - gtk_text_iter_get_offset (begin)); } } static void apply_error_style_cb (GtkSourceBuffer *buffer, GParamSpec *pspec, GtkTextTag *tag) { GtkSourceStyleScheme *scheme; GtkSourceStyle *style; static GdkRGBA error_color; g_assert (GTK_SOURCE_IS_BUFFER (buffer)); g_assert (GTK_IS_TEXT_TAG (tag)); if G_UNLIKELY (error_color.alpha == .0) gdk_rgba_parse (&error_color, "#e01b24"); g_object_set (tag, "underline", PANGO_UNDERLINE_ERROR_LINE, "underline-rgba", &error_color, "background-set", FALSE, "foreground-set", FALSE, "weight-set", FALSE, "variant-set", FALSE, "style-set", FALSE, "indent-set", FALSE, "size-set", FALSE, NULL); if ((scheme = gtk_source_buffer_get_style_scheme (buffer))) { if ((style = gtk_source_style_scheme_get_style (scheme, "def:misspelled-word"))) gtk_source_style_apply (style, tag); } } static void spelling_text_buffer_adapter_set_buffer (SpellingTextBufferAdapter *self, GtkSourceBuffer *buffer) { GtkTextIter begin, end; GtkTextTagTable *tag_table; guint offset; guint length; g_assert (SPELLING_IS_TEXT_BUFFER_ADAPTER (self)); g_assert (GTK_SOURCE_IS_BUFFER (buffer)); g_assert (self->buffer == NULL); g_set_weak_pointer (&self->buffer, buffer); self->insert_mark = gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (buffer)); self->commit_handler = gtk_text_buffer_add_commit_notify (GTK_TEXT_BUFFER (buffer), (GTK_TEXT_BUFFER_NOTIFY_BEFORE_INSERT | GTK_TEXT_BUFFER_NOTIFY_AFTER_INSERT | GTK_TEXT_BUFFER_NOTIFY_BEFORE_DELETE | GTK_TEXT_BUFFER_NOTIFY_AFTER_DELETE), spelling_text_buffer_adapter_commit_notify, self, NULL); g_signal_group_set_target (self->buffer_signals, buffer); gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (buffer), &begin, &end); offset = gtk_text_iter_get_offset (&begin); length = gtk_text_iter_get_offset (&end) - offset; if (length > 0) { spelling_engine_before_insert_text (self->engine, offset, length); spelling_engine_after_insert_text (self->engine, offset, length); } self->tag = gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (buffer), NULL, "underline", PANGO_UNDERLINE_ERROR, NULL); g_signal_connect_object (buffer, "notify::style-scheme", G_CALLBACK (apply_error_style_cb), self->tag, 0); apply_error_style_cb (GTK_SOURCE_BUFFER (buffer), NULL, self->tag); /* Track tag changes from the tag table and extract "no-spell-check" * tag from GtkSourceView so that we can avoid words with that tag. */ tag_table = gtk_text_buffer_get_tag_table (GTK_TEXT_BUFFER (buffer)); g_signal_connect_object (tag_table, "tag-added", G_CALLBACK (on_tag_added_cb), self, G_CONNECT_SWAPPED); g_signal_connect_object (tag_table, "tag-removed", G_CALLBACK (on_tag_removed_cb), self, G_CONNECT_SWAPPED); g_signal_connect_object (buffer, "apply-tag", G_CALLBACK (invalidate_tag_region_cb), self, G_CONNECT_SWAPPED); g_signal_connect_object (buffer, "remove-tag", G_CALLBACK (invalidate_tag_region_cb), self, G_CONNECT_SWAPPED); } static void remember_word_under_cursor (SpellingTextBufferAdapter *self) { g_autofree char *word = NULL; g_auto(GStrv) corrections = NULL; GtkTextBuffer *buffer; GtkTextMark *insert; GtkTextIter iter, begin, end; g_assert (SPELLING_IS_TEXT_BUFFER_ADAPTER (self)); g_clear_pointer (&self->word_under_cursor, g_free); if (self->buffer == NULL || self->checker == NULL) goto cleanup; buffer = GTK_TEXT_BUFFER (self->buffer); insert = gtk_text_buffer_get_insert (buffer); gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert); if (get_word_at_position (self, gtk_text_iter_get_offset (&iter), &begin, &end)) { word = gtk_text_iter_get_slice (&begin, &end); if (spelling_checker_check_word (self->checker, word, -1)) g_clear_pointer (&word, g_free); else corrections = spelling_checker_list_corrections (self->checker, word); } cleanup: g_set_str (&self->word_under_cursor, word); spelling_text_buffer_adapter_set_action_enabled (self, "add", !!word); spelling_text_buffer_adapter_set_action_enabled (self, "ignore", !!word); if (self->menu) spelling_menu_set_corrections (self->menu, word, (const char * const *)corrections); } /** * spelling_text_buffer_adapter_set_enabled: * @self: a `SpellingTextBufferAdapter` * @enabled: whether the spellcheck is enabled * * If %TRUE spellcheck is enabled. */ void spelling_text_buffer_adapter_set_enabled (SpellingTextBufferAdapter *self, gboolean enabled) { g_assert (SPELLING_IS_TEXT_BUFFER_ADAPTER (self)); enabled = !!enabled; if (enabled != self->enabled) { self->enabled = enabled; spelling_text_buffer_adapter_set_action_state (self, "enabled", g_variant_new_boolean (enabled)); if (!enabled) { spelling_text_buffer_adapter_set_action_enabled (self, "add", FALSE); spelling_text_buffer_adapter_set_action_enabled (self, "ignore", FALSE); if (self->menu) spelling_menu_set_corrections (self->menu, NULL, NULL); } else { remember_word_under_cursor (self); } g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ENABLED]); spelling_engine_invalidate_all (self->engine); } } static gboolean spelling_text_buffer_adapter_cursor_moved_cb (gpointer data) { SpellingTextBufferAdapter *self = data; GtkTextIter begin, end; gboolean enabled; g_assert (SPELLING_IS_TEXT_BUFFER_ADAPTER (self)); self->queued_cursor_moved = 0; /* Protect against weak-pointer lost */ if (self->buffer == NULL) return G_SOURCE_REMOVE; enabled = spelling_text_buffer_adapter_get_enabled (self); /* Invalidate the old position */ if (enabled && get_word_at_position (self, self->cursor_position, &begin, &end)) spelling_engine_invalidate (self->engine, gtk_text_iter_get_offset (&begin), gtk_text_iter_get_offset (&end) - gtk_text_iter_get_offset (&begin)); self->cursor_position = self->incoming_cursor_position; /* Invalidate word at new position */ if (enabled && get_word_at_position (self, self->cursor_position, &begin, &end)) spelling_engine_invalidate (self->engine, gtk_text_iter_get_offset (&begin), gtk_text_iter_get_offset (&end) - gtk_text_iter_get_offset (&begin)); remember_word_under_cursor (self); return G_SOURCE_REMOVE; } static void spelling_text_buffer_adapter_cursor_moved (SpellingTextBufferAdapter *self, GtkSourceBuffer *buffer) { GtkTextIter iter; g_assert (SPELLING_IS_TEXT_BUFFER_ADAPTER (self)); g_assert (GTK_SOURCE_IS_BUFFER (buffer)); gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer), &iter, self->insert_mark); self->incoming_cursor_position = gtk_text_iter_get_offset (&iter); g_clear_handle_id (&self->queued_cursor_moved, g_source_remove); if (!spelling_text_buffer_adapter_check_enabled (self)) return; self->queued_cursor_moved = g_timeout_add_full (G_PRIORITY_LOW, INVALIDATE_DELAY_MSECS, spelling_text_buffer_adapter_cursor_moved_cb, g_object_ref (self), g_object_unref); } #if GTK_SOURCE_CHECK_VERSION(5, 9, 0) static void spelling_text_buffer_adapter_notify_loading_cb (SpellingTextBufferAdapter *self, GParamSpec *pspec, GtkSourceBuffer *buffer) { g_assert (SPELLING_IS_TEXT_BUFFER_ADAPTER (self)); g_assert (GTK_SOURCE_IS_BUFFER (buffer)); if (self->engine != NULL) spelling_engine_invalidate_all (self->engine); } #endif static void spelling_text_buffer_adapter_finalize (GObject *object) { SpellingTextBufferAdapter *self = (SpellingTextBufferAdapter *)object; self->tag = NULL; self->insert_mark = NULL; g_clear_pointer (&self->word_under_cursor, g_free); g_clear_object (&self->checker); g_clear_object (&self->no_spell_check_tag); g_clear_object (&self->buffer_signals); G_OBJECT_CLASS (spelling_text_buffer_adapter_parent_class)->finalize (object); } static void spelling_text_buffer_adapter_dispose (GObject *object) { SpellingTextBufferAdapter *self = (SpellingTextBufferAdapter *)object; if (self->buffer != NULL) { gtk_text_buffer_remove_commit_notify (GTK_TEXT_BUFFER (self->buffer), self->commit_handler); self->commit_handler = 0; g_clear_weak_pointer (&self->buffer); } g_signal_group_set_target (self->buffer_signals, NULL); g_clear_object (&self->engine); g_clear_object (&self->menu); g_clear_object (&self->top_menu); G_OBJECT_CLASS (spelling_text_buffer_adapter_parent_class)->dispose (object); } static void spelling_text_buffer_adapter_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { SpellingTextBufferAdapter *self = SPELLING_TEXT_BUFFER_ADAPTER (object); switch (prop_id) { case PROP_BUFFER: g_value_set_object (value, self->buffer); break; case PROP_CHECKER: g_value_set_object (value, spelling_text_buffer_adapter_get_checker (self)); break; case PROP_ENABLED: g_value_set_boolean (value, spelling_text_buffer_adapter_get_enabled (self)); break; case PROP_LANGUAGE: g_value_set_string (value, spelling_text_buffer_adapter_get_language (self)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void spelling_text_buffer_adapter_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { SpellingTextBufferAdapter *self = SPELLING_TEXT_BUFFER_ADAPTER (object); switch (prop_id) { case PROP_BUFFER: spelling_text_buffer_adapter_set_buffer (self, g_value_get_object (value)); break; case PROP_CHECKER: spelling_text_buffer_adapter_set_checker (self, g_value_get_object (value)); break; case PROP_ENABLED: spelling_text_buffer_adapter_set_enabled (self, g_value_get_boolean (value)); break; case PROP_LANGUAGE: spelling_text_buffer_adapter_set_language (self, g_value_get_string (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void spelling_text_buffer_adapter_class_init (SpellingTextBufferAdapterClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->dispose = spelling_text_buffer_adapter_dispose; object_class->finalize = spelling_text_buffer_adapter_finalize; object_class->get_property = spelling_text_buffer_adapter_get_property; object_class->set_property = spelling_text_buffer_adapter_set_property; /** * SpellingTextBufferAdapter:buffer: * * The [class@GtkSource.Buffer]. */ properties[PROP_BUFFER] = g_param_spec_object ("buffer", NULL, NULL, GTK_SOURCE_TYPE_BUFFER, (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); /** * SpellingTextBufferAdapter:checker: * * The [class@Spelling.Checker]. */ properties[PROP_CHECKER] = g_param_spec_object ("checker", NULL, NULL, SPELLING_TYPE_CHECKER, (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /** * SpellingTextBufferAdapter:enabled: * * Whether spellcheck is enabled. */ properties[PROP_ENABLED] = g_param_spec_boolean ("enabled", NULL, NULL, TRUE, (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); /** * SpellingTextBufferAdapter:language: * * The language code, such as `en_US`. */ properties[PROP_LANGUAGE] = g_param_spec_string ("language", NULL, NULL, NULL, (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); g_object_class_install_properties (object_class, N_PROPS, properties); } static void spelling_text_buffer_adapter_init (SpellingTextBufferAdapter *self) { self->enabled = TRUE; spelling_text_buffer_adapter_set_action_state (self, "enabled", g_variant_new_boolean (TRUE)); self->buffer_signals = g_signal_group_new (GTK_SOURCE_TYPE_BUFFER); g_signal_group_connect_object (self->buffer_signals, "cursor-moved", G_CALLBACK (spelling_text_buffer_adapter_cursor_moved), self, G_CONNECT_SWAPPED); #if GTK_SOURCE_CHECK_VERSION(5, 9, 0) g_signal_group_connect_object (self->buffer_signals, "notify::loading", G_CALLBACK (spelling_text_buffer_adapter_notify_loading_cb), self, G_CONNECT_SWAPPED); #endif self->engine = spelling_engine_new (&adapter_funcs, G_OBJECT (self)); } /** * spelling_text_buffer_adapter_get_checker: * @self: a `SpellingTextBufferAdapter` * * Gets the checker used by the adapter. * * Returns: (transfer none) (nullable): a `SpellingChecker` or %NULL */ SpellingChecker * spelling_text_buffer_adapter_get_checker (SpellingTextBufferAdapter *self) { g_return_val_if_fail (SPELLING_IS_TEXT_BUFFER_ADAPTER (self), NULL); return self->checker; } static void spelling_text_buffer_adapter_checker_notify_language (SpellingTextBufferAdapter *self, GParamSpec *pspec, SpellingChecker *checker) { const char *code; g_assert (SPELLING_IS_TEXT_BUFFER_ADAPTER (self)); g_assert (SPELLING_IS_CHECKER (checker)); if (!(code = spelling_checker_get_language (checker))) code = ""; spelling_text_buffer_adapter_set_action_state (self, "language", g_variant_new_string (code)); } /** * spelling_text_buffer_adapter_set_checker: * @self: a `SpellingTextBufferAdapter` * @checker: a `SpellingChecker` * * Set the [class@Spelling.Checker] used for spellchecking. */ void spelling_text_buffer_adapter_set_checker (SpellingTextBufferAdapter *self, SpellingChecker *checker) { const char *code = ""; g_return_if_fail (SPELLING_IS_TEXT_BUFFER_ADAPTER (self)); g_return_if_fail (!checker || SPELLING_IS_CHECKER (checker)); if (self->checker == checker) return; if (self->checker) g_signal_handlers_disconnect_by_func (self->checker, G_CALLBACK (spelling_text_buffer_adapter_checker_notify_language), self); g_set_object (&self->checker, checker); if (checker) { g_signal_connect_object (self->checker, "notify::language", G_CALLBACK (spelling_text_buffer_adapter_checker_notify_language), self, G_CONNECT_SWAPPED); if (!(code = spelling_checker_get_language (checker))) code = ""; } spelling_engine_invalidate_all (self->engine); spelling_text_buffer_adapter_set_action_state (self, "language", g_variant_new_string (code)); g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CHECKER]); g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LANGUAGE]); } /** * spelling_text_buffer_adapter_get_buffer: * @self: a `SpellingTextBufferAdapter` * * Gets the underlying buffer for the adapter. * * Returns: (transfer none) (nullable): a `GtkSourceBuffer` */ GtkSourceBuffer * spelling_text_buffer_adapter_get_buffer (SpellingTextBufferAdapter *self) { g_return_val_if_fail (SPELLING_IS_TEXT_BUFFER_ADAPTER (self), NULL); return self->buffer; } /** * spelling_text_buffer_adapter_get_language: * @self: a `SpellingTextBufferAdapter` * * Gets the checker language. * * Returns: (transfer none) (nullable): a language code */ const char * spelling_text_buffer_adapter_get_language (SpellingTextBufferAdapter *self) { g_return_val_if_fail (SPELLING_IS_TEXT_BUFFER_ADAPTER (self), NULL); return self->checker ? spelling_checker_get_language (self->checker) : NULL; } /** * spelling_text_buffer_adapter_set_language: * @self: a `SpellingTextBufferAdapter` * @language: the language to use * * Sets the language code to use by the checker, such as `en_US`. */ void spelling_text_buffer_adapter_set_language (SpellingTextBufferAdapter *self, const char *language) { g_return_if_fail (SPELLING_IS_TEXT_BUFFER_ADAPTER (self)); if (self->checker == NULL && language == NULL) return; if (self->checker == NULL) { self->checker = spelling_checker_new (NULL, language); g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CHECKER]); g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LANGUAGE]); } else if (g_strcmp0 (language, spelling_text_buffer_adapter_get_language (self)) != 0) { spelling_checker_set_language (self->checker, language); g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LANGUAGE]); } spelling_text_buffer_adapter_invalidate_all (self); } /** * spelling_text_buffer_adapter_get_tag: * @self: a `SpellingTextBufferAdapter` * * Gets the tag used for potentially misspelled words. * * Returns: (transfer none) (nullable): a `GtkTextTag` or %NULL */ GtkTextTag * spelling_text_buffer_adapter_get_tag (SpellingTextBufferAdapter *self) { g_return_val_if_fail (SPELLING_IS_TEXT_BUFFER_ADAPTER (self), NULL); return self->tag; } /** * spelling_text_buffer_adapter_get_enabled: * @self: a `SpellingTextBufferAdapter` * * Gets if the spellcheck is enabled. * * Returns: %TRUE if enabled */ gboolean spelling_text_buffer_adapter_get_enabled (SpellingTextBufferAdapter *self) { g_return_val_if_fail (!self || SPELLING_IS_TEXT_BUFFER_ADAPTER (self), FALSE); if (self == NULL) return FALSE; return self->enabled; } /** * spelling_text_buffer_adapter_get_menu_model: * @self: a `SpellingTextBufferAdapter` * * Gets the menu model containing corrections * * Returns: (transfer none): a `GMenuModel` */ GMenuModel * spelling_text_buffer_adapter_get_menu_model (SpellingTextBufferAdapter *self) { g_return_val_if_fail (SPELLING_IS_TEXT_BUFFER_ADAPTER (self), NULL); if (self->menu == NULL) { self->menu = spelling_menu_new (); self->top_menu = g_menu_new (); g_menu_append_section (self->top_menu, NULL, self->menu); } return G_MENU_MODEL (self->top_menu); } static void spelling_add_action (SpellingTextBufferAdapter *self, GVariant *param) { g_assert (SPELLING_IS_TEXT_BUFFER_ADAPTER (self)); g_assert (self->word_under_cursor != NULL); if (self->checker != NULL) { spelling_checker_add_word (self->checker, self->word_under_cursor); spelling_text_buffer_adapter_invalidate_all (self); } } static void spelling_ignore_action (SpellingTextBufferAdapter *self, GVariant *param) { g_assert (SPELLING_IS_TEXT_BUFFER_ADAPTER (self)); g_assert (self->word_under_cursor != NULL); if (self->checker != NULL) { spelling_checker_ignore_word (self->checker, self->word_under_cursor); spelling_text_buffer_adapter_invalidate_all (self); } } static void spelling_enabled_action (SpellingTextBufferAdapter *self, GVariant *param) { g_assert (SPELLING_IS_TEXT_BUFFER_ADAPTER (self)); spelling_text_buffer_adapter_set_enabled (self, !spelling_text_buffer_adapter_get_enabled (self)); } static void spelling_correct_action (SpellingTextBufferAdapter *self, GVariant *param) { g_autofree char *slice = NULL; GtkTextBuffer *buffer; const char *word; GtkTextIter begin, end; g_assert (SPELLING_IS_TEXT_BUFFER_ADAPTER (self)); g_assert (g_variant_is_of_type (param, G_VARIANT_TYPE_STRING)); if (self->buffer == NULL) return; buffer = GTK_TEXT_BUFFER (self->buffer); word = g_variant_get_string (param, NULL); /* We don't deal with selections (yet?) */ if (gtk_text_buffer_get_selection_bounds (buffer, &begin, &end)) return; if (!get_word_at_position (self, gtk_text_iter_get_offset (&begin), &begin, &end)) return; slice = gtk_text_iter_get_slice (&begin, &end); if (g_strcmp0 (slice, self->word_under_cursor) != 0) { g_debug ("Words do not match, will not replace."); return; } gtk_text_buffer_begin_user_action (buffer); gtk_text_buffer_delete (buffer, &begin, &end); gtk_text_buffer_insert (buffer, &begin, word, -1); gtk_text_buffer_end_user_action (buffer); } static void spelling_language_action (SpellingTextBufferAdapter *self, GVariant *param) { const char *code; g_assert (SPELLING_IS_TEXT_BUFFER_ADAPTER (self)); g_assert (g_variant_is_of_type (param, G_VARIANT_TYPE_STRING)); code = g_variant_get_string (param, NULL); if (self->checker) spelling_checker_set_language (self->checker, code); } /** * spelling_text_buffer_adapter_update_corrections: * @self: a `SpellingTextBufferAdapter` * * Looks at the current cursor position and updates the list of * corrections based on the current word. * * Use this to force an update immediately rather than after the * automatic timeout caused by cursor movements. */ void spelling_text_buffer_adapter_update_corrections (SpellingTextBufferAdapter *self) { g_return_if_fail (SPELLING_IS_TEXT_BUFFER_ADAPTER (self)); if (self->enabled == FALSE) return; remember_word_under_cursor (self); } 07070100000038000081A40000000000000000000000016712C6CB00000CD8000000000000000000000000000000000000003500000000libspelling-0.4.4/lib/spelling-text-buffer-adapter.h/* * spelling-text-buffer-adapter.h * * Copyright 2021-2023 Christian Hergert <chergert@redhat.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <gtksourceview/gtksource.h> #include "spelling-types.h" #include "spelling-version-macros.h" G_BEGIN_DECLS #define SPELLING_TYPE_TEXT_BUFFER_ADAPTER (spelling_text_buffer_adapter_get_type()) SPELLING_AVAILABLE_IN_ALL G_DECLARE_FINAL_TYPE (SpellingTextBufferAdapter, spelling_text_buffer_adapter, SPELLING, TEXT_BUFFER_ADAPTER, GObject) SPELLING_AVAILABLE_IN_ALL SpellingTextBufferAdapter *spelling_text_buffer_adapter_new (GtkSourceBuffer *buffer, SpellingChecker *checker); SPELLING_AVAILABLE_IN_ALL GtkSourceBuffer *spelling_text_buffer_adapter_get_buffer (SpellingTextBufferAdapter *self); SPELLING_AVAILABLE_IN_ALL gboolean spelling_text_buffer_adapter_get_enabled (SpellingTextBufferAdapter *self); SPELLING_AVAILABLE_IN_ALL void spelling_text_buffer_adapter_set_enabled (SpellingTextBufferAdapter *self, gboolean enabled); SPELLING_AVAILABLE_IN_ALL SpellingChecker *spelling_text_buffer_adapter_get_checker (SpellingTextBufferAdapter *self); SPELLING_AVAILABLE_IN_ALL void spelling_text_buffer_adapter_set_checker (SpellingTextBufferAdapter *self, SpellingChecker *checker); SPELLING_AVAILABLE_IN_ALL const char *spelling_text_buffer_adapter_get_language (SpellingTextBufferAdapter *self); SPELLING_AVAILABLE_IN_ALL void spelling_text_buffer_adapter_set_language (SpellingTextBufferAdapter *self, const char *language); SPELLING_AVAILABLE_IN_ALL void spelling_text_buffer_adapter_invalidate_all (SpellingTextBufferAdapter *self); SPELLING_AVAILABLE_IN_ALL GtkTextTag *spelling_text_buffer_adapter_get_tag (SpellingTextBufferAdapter *self); SPELLING_AVAILABLE_IN_ALL GMenuModel *spelling_text_buffer_adapter_get_menu_model (SpellingTextBufferAdapter *self); SPELLING_AVAILABLE_IN_ALL void spelling_text_buffer_adapter_update_corrections (SpellingTextBufferAdapter *self); G_END_DECLS 07070100000039000081A40000000000000000000000016712C6CB0000098B000000000000000000000000000000000000002700000000libspelling-0.4.4/lib/spelling-trace.h/* spelling-trace.h * * Copyright 2024 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <glib.h> #ifndef GETTEXT_PACKAGE # error "config.h was not included before sysprof-trace.h." #endif #ifdef HAVE_SYSPROF # include <sysprof-capture.h> #endif G_BEGIN_DECLS #ifdef HAVE_SYSPROF # define SPELLING_PROFILER_ENABLED 1 # define SPELLING_PROFILER_CURRENT_TIME SYSPROF_CAPTURE_CURRENT_TIME # define SPELLING_PROFILER_ACTIVE (sysprof_collector_is_active()) # define SPELLING_PROFILER_BEGIN_MARK \ G_STMT_START { \ gint64 __begin_time = SYSPROF_CAPTURE_CURRENT_TIME; # define SPELLING_PROFILER_END_MARK(name, message) \ G_STMT_START { \ gint64 __duration = SYSPROF_CAPTURE_CURRENT_TIME - __begin_time; \ sysprof_collector_mark (__begin_time, __duration, "Spelling", name, message); \ } G_STMT_END; \ } G_STMT_END # define SPELLING_PROFILER_MARK(duration, name, message) \ G_STMT_START { \ sysprof_collector_mark (SYSPROF_CAPTURE_CURRENT_TIME - (duration), \ (duration), "Spelling", name, message); \ } G_STMT_END # define SPELLING_PROFILER_LOG(format, ...) \ G_STMT_START { \ if (SPELLING_PROFILER_ACTIVE) \ sysprof_collector_log_printf(G_LOG_LEVEL_DEBUG, G_LOG_DOMAIN, format, __VA_ARGS__); \ } G_STMT_END #else # undef SPELLING_PROFILER_ENABLED # define SPELLING_PROFILER_ACTIVE (0) # define SPELLING_PROFILER_CURRENT_TIME 0 # define SPELLING_PROFILER_MARK(duration, name, message) \ G_STMT_START { } G_STMT_END # define SPELLING_PROFILER_BEGIN_MARK G_STMT_START { # define SPELLING_PROFILER_END_MARK(name, message) (void)0; } G_STMT_END # define SPELLING_PROFILER_LOG(format, ...) G_STMT_START { } G_STMT_END #endif G_END_DECLS 0707010000003A000081A40000000000000000000000016712C6CB0000043B000000000000000000000000000000000000002700000000libspelling-0.4.4/lib/spelling-types.h/* * spelling-types.h * * Copyright 2023 Christian Hergert <chergert@redhat.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <glib.h> G_BEGIN_DECLS typedef struct _SpellingChecker SpellingChecker; typedef struct _SpellingDictionary SpellingDictionary; typedef struct _SpellingLanguage SpellingLanguage; typedef struct _SpellingProvider SpellingProvider; G_END_DECLS 0707010000003B000081A40000000000000000000000016712C6CB00000ED7000000000000000000000000000000000000003000000000libspelling-0.4.4/lib/spelling-version-macros.h/* * spelling-version-macros.h * * Copyright 2023 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #if !defined(LIBSPELLING_INSIDE) && !defined(LIBSPELLING_COMPILATION) # error "Only <libspelling.h> can be included directly." #endif #include <glib.h> #include "spelling-version.h" #ifndef _SPELLING_EXTERN # define _SPELLING_EXTERN extern #endif #define SPELLING_VERSION_CUR_STABLE (G_ENCODE_VERSION (SPELLING_MAJOR_VERSION, 0)) #ifdef SPELLING_DISABLE_DEPRECATION_WARNINGS # define SPELLING_DEPRECATED _SPELLING_EXTERN # define SPELLING_DEPRECATED_FOR(f) _SPELLING_EXTERN # define SPELLING_UNAVAILABLE(maj,min) _SPELLING_EXTERN #else # define SPELLING_DEPRECATED G_DEPRECATED _SPELLING_EXTERN # define SPELLING_DEPRECATED_FOR(f) G_DEPRECATED_FOR (f) _SPELLING_EXTERN # define SPELLING_UNAVAILABLE(maj,min) G_UNAVAILABLE (maj, min) _SPELLING_EXTERN #endif #define SPELLING_VERSION_1_0 (G_ENCODE_VERSION (1, 0)) #if SPELLING_MAJOR_VERSION == SPELLING_VERSION_1_0 # define SPELLING_VERSION_PREV_STABLE (SPELLING_VERSION_1_0) #else # define SPELLING_VERSION_PREV_STABLE (G_ENCODE_VERSION (SPELLING_MAJOR_VERSION - 1, 0)) #endif /** * SPELLING_VERSION_MIN_REQUIRED: * * A macro that should be defined by the user prior to including * the spelling.h header. * * The definition should be one of the predefined SPELLING version * macros: %SPELLING_VERSION_1_0, ... * * This macro defines the lower bound for the Builder API to use. * * If a function has been deprecated in a newer version of Builder, * it is possible to use this symbol to avoid the compiler warnings * without disabling warning for every deprecated function. */ #ifndef SPELLING_VERSION_MIN_REQUIRED # define SPELLING_VERSION_MIN_REQUIRED (SPELLING_VERSION_CUR_STABLE) #endif /** * SPELLING_VERSION_MAX_ALLOWED: * * A macro that should be defined by the user prior to including * the spelling.h header. * The definition should be one of the predefined Builder version * macros: %SPELLING_VERSION_1_0, %SPELLING_VERSION_1_2,... * * This macro defines the upper bound for the SPELLING API to use. * * If a function has been introduced in a newer version of Builder, * it is possible to use this symbol to get compiler warnings when * trying to use that function. */ #ifndef SPELLING_VERSION_MAX_ALLOWED # if SPELLING_VERSION_MIN_REQUIRED > SPELLING_VERSION_PREV_STABLE # define SPELLING_VERSION_MAX_ALLOWED (SPELLING_VERSION_MIN_REQUIRED) # else # define SPELLING_VERSION_MAX_ALLOWED (SPELLING_VERSION_CUR_STABLE) # endif #endif #define SPELLING_AVAILABLE_IN_ALL _SPELLING_EXTERN #if SPELLING_VERSION_MIN_REQUIRED >= SPELLING_VERSION_1_0 # define SPELLING_DEPRECATED_IN_1_0 SPELLING_DEPRECATED # define SPELLING_DEPRECATED_IN_1_0_FOR(f) SPELLING_DEPRECATED_FOR(f) #else # define SPELLING_DEPRECATED_IN_1_0 _SPELLING_EXTERN # define SPELLING_DEPRECATED_IN_1_0_FOR(f) _SPELLING_EXTERN #endif #if SPELLING_VERSION_MAX_ALLOWED < SPELLING_VERSION_1_0 # define SPELLING_AVAILABLE_IN_1_0 SPELLING_UNAVAILABLE(1, 0) #else # define SPELLING_AVAILABLE_IN_1_0 _SPELLING_EXTERN #endif 0707010000003C000081A40000000000000000000000016712C6CB00000ACF000000000000000000000000000000000000002C00000000libspelling-0.4.4/lib/spelling-version.h.in/* libspelling-version.h.in * * Copyright 2023 Christian Hergert * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once /** * SECTION:libspellingversion * @short_description: libspelling version checking * * libspelling provides macros to check the version of the library * at compile-time */ /** * SPELLING_MAJOR_VERSION: * * libspelling major version component (e.g. 1 if %SPELLING_VERSION is 1.2.3) */ #define SPELLING_MAJOR_VERSION (@MAJOR_VERSION@) /** * SPELLING_MINOR_VERSION: * * libspelling minor version component (e.g. 2 if %SPELLING_VERSION is 1.2.3) */ #define SPELLING_MINOR_VERSION (@MINOR_VERSION@) /** * SPELLING_MICRO_VERSION: * * libspelling micro version component (e.g. 3 if %SPELLING_VERSION is 1.2.3) */ #define SPELLING_MICRO_VERSION (@MICRO_VERSION@) /** * SPELLING_VERSION * * libspelling version. */ #define SPELLING_VERSION (@VERSION@) /** * SPELLING_VERSION_S: * * libspelling version, encoded as a string, useful for printing and * concatenation. */ #define SPELLING_VERSION_S "@VERSION@" #define SPELLING_ENCODE_VERSION(major,minor,micro) \ ((major) << 24 | (minor) << 16 | (micro) << 8) /** * SPELLING_VERSION_HEX: * * libspelling version, encoded as an hexadecimal number, useful for * integer comparisons. */ #define SPELLING_VERSION_HEX \ (SPELLING_ENCODE_VERSION (SPELLING_MAJOR_VERSION, SPELLING_MINOR_VERSION, SPELLING_MICRO_VERSION)) /** * SPELLING_CHECK_VERSION: * @major: required major version * @minor: required minor version * @micro: required micro version * * Compile-time version checking. Evaluates to %TRUE if the version * of libspelling is greater than the required one. */ #define SPELLING_CHECK_VERSION(major,minor,micro) \ (SPELLING_MAJOR_VERSION > (major) || \ (SPELLING_MAJOR_VERSION == (major) && SPELLING_MINOR_VERSION > (minor)) || \ (SPELLING_MAJOR_VERSION == (major) && SPELLING_MINOR_VERSION == (minor) && \ SPELLING_MICRO_VERSION >= (micro))) 0707010000003D000081A40000000000000000000000016712C6CB000003CA000000000000000000000000000000000000002300000000libspelling-0.4.4/libspelling.doap<?xml version="1.0" encoding="UTF-8"?> <Project xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" xmlns:foaf="http://xmlns.com/foaf/0.1/" xmlns:gnome="http://api.gnome.org/doap-extensions#" xmlns="http://usefulinc.com/ns/doap#"> <name>libspelling</name> <shortname>libspelling</shortname> <shortdesc>A spellcheck library for GTK 4</shortdesc> <description> libspelling provides spellcheck integration for GTK 4. </description> <homepage rdf:resource="https://gitlab.gnome.org/GNOME/libspelling" /> <license rdf:resource="https://spdx.org/licenses/LGPL-2.1-or-later.html" /> <programming-language>C</programming-language> <maintainer> <foaf:Person> <foaf:name>Christian Hergert</foaf:name> <foaf:mbox rdf:resource="mailto:chergert@gnome.org" /> <gnome:userid>chergert</gnome:userid> </foaf:Person> </maintainer> </Project> 0707010000003E000081A40000000000000000000000016712C6CB000010E0000000000000000000000000000000000000001E00000000libspelling-0.4.4/meson.buildproject('libspelling', 'c', version: '0.4.4', meson_version: '>= 0.62.0', default_options: [ 'warning_level=2', 'werror=false', 'c_std=gnu11', ], ) libgtk_dep = dependency('gtk4', version: '>= 4.15.5') gtksource_dep = dependency('gtksourceview-5', version: '>= 5.10.0') libgio_dep = dependency('gio-2.0') cc = meson.get_compiler('c') prefix = get_option('prefix') datadir = join_paths(prefix, get_option('datadir')) config_h = configuration_data() config_h.set_quoted('PACKAGE_VERSION', meson.project_version()) config_h.set_quoted('GETTEXT_PACKAGE', 'libspelling') config_h.set_quoted('PACKAGE_LOCALE_DIR', join_paths(get_option('prefix'), get_option('datadir'), 'locale')) config_h.set10('HAVE_ENCHANT', get_option('enchant').enabled()) # libsysprof-capture support for profiling if get_option('sysprof') libsysprof_capture_dep = dependency('sysprof-capture-4', required: get_option('sysprof'), default_options: [ 'examples=false', 'gtk=false', 'help=false', 'install-static=false', 'libsysprof=false', 'sysprofd=none', 'tests=false', 'tools=false', ], fallback: ['sysprof', 'libsysprof_capture_dep'], ) config_h.set('HAVE_SYSPROF', libsysprof_capture_dep.found()) profiler_enabled = true else libsysprof_capture_dep = disabler() profiler_enabled = false endif # Detect and set symbol visibility if get_option('default_library') != 'static' if host_machine.system() == 'windows' config_h.set('DLL_EXPORT', true) if cc.get_id() == 'msvc' config_h.set('_SPELLING_EXTERN', '__declspec(dllexport) extern') elif cc.has_argument('-fvisibility=hidden') config_h.set('_SPELLING_EXTERN', '__attribute__((visibility("default"))) __declspec(dllexport) extern') endif elif cc.has_argument('-fvisibility=hidden') config_h.set('_SPELLING_EXTERN', '__attribute__((visibility("default"))) extern') endif endif project_c_args = [] test_c_args = [ '-Wcast-align', '-Wdeclaration-after-statement', '-Werror=address', '-Werror=array-bounds', '-Werror=empty-body', '-Werror=implicit', '-Werror=implicit-function-declaration', '-Werror=incompatible-pointer-types', '-Werror=init-self', '-Werror=int-conversion', '-Werror=int-to-pointer-cast', '-Werror=main', '-Werror=misleading-indentation', '-Werror=missing-braces', '-Werror=missing-include-dirs', '-Werror=nonnull', '-Werror=overflow', '-Werror=parenthesis', '-Werror=pointer-arith', '-Werror=pointer-to-int-cast', '-Werror=redundant-decls', '-Werror=return-type', '-Werror=sequence-point', '-Werror=shadow', '-Werror=strict-prototypes', '-Werror=trigraphs', '-Werror=undef', '-Werror=write-strings', '-Wformat-nonliteral', '-Wignored-qualifiers', '-Wimplicit-function-declaration', '-Wlogical-op', '-Wmissing-declarations', '-Wmissing-format-attribute', '-Wmissing-include-dirs', '-Wmissing-noreturn', '-Wnested-externs', '-Wno-cast-function-type', '-Wno-dangling-pointer', '-Wno-missing-field-initializers', '-Wno-sign-compare', '-Wno-unused-parameter', '-Wold-style-definition', '-Wpointer-arith', '-Wredundant-decls', '-Wstrict-prototypes', '-Wswitch-default', '-Wswitch-enum', '-Wundef', '-Wuninitialized', '-Wunused', '-fno-strict-aliasing', ['-Werror=format-security', '-Werror=format=2'], ] if get_option('buildtype') != 'plain' test_c_args += '-fstack-protector-strong' endif foreach arg: test_c_args if cc.has_multi_arguments(arg) project_c_args += arg endif endforeach add_project_arguments(project_c_args, language: 'c') release_args = [] global_link_args = [] test_link_args = [ '-Wl,-z,relro', '-Wl,-z,now', '-Wl,-z,noexecstack', ] if not get_option('buildtype').startswith('debug') add_global_arguments('-DG_DISABLE_CAST_CHECKS', language: 'c') release_args += [ '-DG_DISABLE_ASSERT' ] test_link_args += [ '-Wl,-Bsymbolic', '-fno-plt', ] endif foreach link_arg: test_link_args if cc.has_link_argument(link_arg) global_link_args += link_arg endif endforeach add_project_link_arguments(global_link_args, language: 'c') configure_file(output: 'config.h', configuration: config_h) subdir('lib') subdir('po') subdir('test') subdir('testsuite') if get_option('docs') subdir('docs') endif 0707010000003F000081A40000000000000000000000016712C6CB00000203000000000000000000000000000000000000002400000000libspelling-0.4.4/meson_options.txtoption('docs', type: 'boolean', value: true, description: 'Generate documentation') option('enchant', type: 'feature', value: 'enabled', description: 'Use enchant for spellchecking') option('introspection', type: 'feature', value: 'enabled', description: 'Generate gir data (requires gobject-introspection)') option('sysprof', type: 'boolean', value: true, description: 'Generate profiler data using Sysprof') option('vapi', type: 'boolean', value: true, description: 'Generate Vala vapi (Requires introspection)') 07070100000040000041ED0000000000000000000000026712C6CB00000000000000000000000000000000000000000000001500000000libspelling-0.4.4/po07070100000041000081A40000000000000000000000016712C6CB0000009C000000000000000000000000000000000000001D00000000libspelling-0.4.4/po/LINGUASab be bg ca cs da de el en_GB eo es eu fa fi fr fur gl he hi hr hu ie is it ja ka kk ko lt lv nb ne nl oc pa pt pt_BR ro ru sk sl sr sv th uk vi zh_CN zh_TW07070100000042000081A40000000000000000000000016712C6CB000000A3000000000000000000000000000000000000002100000000libspelling-0.4.4/po/POTFILES.in# List of source files containing translatable strings. # Please keep this file sorted alphabetically. lib/enchant/spelling-enchant-provider.c lib/spelling-menu.c 07070100000043000081A40000000000000000000000016712C6CB00000000000000000000000000000000000000000000002300000000libspelling-0.4.4/po/POTFILES.skip07070100000044000081A40000000000000000000000016712C6CB000002BA000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/ab.pomsgid "" msgstr "" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "Last-Translator: Нанба Наала <naala-nanba@rambler.ru>\n" "Language-Team: Abkhazian <daniel.abzakh@gmail.com>\n" "Language: ab\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "Аиашаҩыра агәаҭара" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "" 07070100000045000081A40000000000000000000000016712C6CB00000497000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/be.po# Belarusian translation for libspelling. # Copyright (C) 2022 libspelling's COPYRIGHT HOLDER # This file is distributed under the same license as the libspelling package. # Yuras Shumovich <shumovichy@gmail.com>, 2022. # msgid "" msgstr "" "Project-Id-Version: libspelling gnome-43\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-08-23 18:21+0300\n" "Last-Translator: Yuras Shumovich <shumovichy@gmail.com>\n" "Language-Team: Belarusian <i18n-bel-gnome@googlegroups.com>\n" "Language: be\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Generator: Poedit 3.4.4\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "Дадаць у слоўнік" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "Ігнараваць" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "Праверка правапісу" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "Мовы" 07070100000046000081A40000000000000000000000016712C6CB0000048B000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/bg.po# Bulgarian translation of libspelling po-file. # Copyright (C) 2022, 2023, 2024 Alexander Shopov. # Copyright (C) 2024 twlvnn kraftwerk. # This file is distributed under the same license as the libspelling package. # Alexander Shopov <ash@kambanaria.org> 2022, 2023, 2024. # twlvnn kraftwerk <kraft_werk@tutanota.com>, 2024. # msgid "" msgstr "" "Project-Id-Version: libspelling main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-08-31 11:54+0200\n" "Last-Translator: Alexander Shopov <ash@kambanaria.org>\n" "Language-Team: Bulgarian <dict@fsa-bg.org>\n" "Language: bg\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "Добавяне в речника" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "Игнориране" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "Проверка на правописа" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "Езици" 07070100000047000081A40000000000000000000000016712C6CB00000428000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/ca.po# Catalan translation for libspelling. # Copyright (C) 2021 libspelling's COPYRIGHT HOLDER # This file is distributed under the same license as the libspelling package. # Jordi Mas i Hernàndez <jmas@softcatala.org>, 2021, 2023 # msgid "" msgstr "" "Project-Id-Version: libspelling main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-08-22 21:38+0200\n" "Last-Translator: Jordi Mas i Hernàndez <jmas@softcatala.org>\n" "Language-Team: Catalan <gnome@llistes.softcatala.org>\n" "Language: ca\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 2.4.2\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "Afegeix al diccionari" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "Ignora" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "Comprova l'ortografia" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "Idiomes" 07070100000048000081A40000000000000000000000016712C6CB00000428000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/cs.po# Czech translation for libspelling. # Copyright (C) 2021 libspelling's COPYRIGHT HOLDER # This file is distributed under the same license as the libspelling package. # # Marek Černocký <marek@manet.cz>, 2021, 2022, 2023. # msgid "" msgstr "" "Project-Id-Version: libspelling\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-08-26 01:24+0200\n" "Last-Translator: Daniel Rusek <mail@asciiwolf.com>\n" "Language-Team: Czech <gnome-cs-list@gnome.org>\n" "Language: cs\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" "X-Generator: Poedit 3.4.4\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "Přidat do slovníku" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "Ignorovat" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "Kontrolovat pravopis" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "Jazyky" 07070100000049000081A40000000000000000000000016712C6CB00000410000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/da.po# Danish translation for libspelling. # Copyright (C) 2022 libspelling's COPYRIGHT HOLDER # This file is distributed under the same license as the libspelling package. # Alan Mortensen <alanmortensen.am@gmail.com>, 2022-24. # msgid "" msgstr "" "Project-Id-Version: libspelling main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-10-06 23:59+0200\n" "Last-Translator: Alan Mortensen <alanmortensen.am@gmail.com>\n" "Language-Team: Danish <dansk@dansk-gruppen.dk>\n" "Language: da\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 3.4.2\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "Tilføj til ordbog" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "Ignorér" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "Stavekontrol" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "Sprog" 0707010000004A000081A40000000000000000000000016712C6CB000004C1000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/de.po# German translation for libspelling. # Copyright (C) 2021 libspelling's COPYRIGHT HOLDER # This file is distributed under the same license as the libspelling package. # # # Philipp Kiemle <philipp.kiemle@gmail.com>, 2021-2023. # Christian Kirbach <christian.kirbach@gmail.com>, 2021, 2022. # Tim Sabsch <tim@sabsch.com>, 2022. # Jürgen Benvenuti <gastornis@posteo.org>, 2022-2024. # msgid "" msgstr "" "Project-Id-Version: libspelling main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-08-22 22:31+0200\n" "Last-Translator: Jürgen Benvenuti <gastornis@posteo.org>\n" "Language-Team: German <gnome-de@gnome.org>\n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 3.4.4\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "Zum Wörterbuch hinzufügen" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "Ignorieren" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "Rechtschreibung prüfen" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "Sprachen" 0707010000004B000081A40000000000000000000000016712C6CB0000044A000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/el.po# Greek translation for libspelling. # Copyright (C) 2022 libspelling's COPYRIGHT HOLDER # This file is distributed under the same license as the libspelling package. # Efstathios Iosifidis <eiosifidis@gnome.org>, 2022. # msgid "" msgstr "" "Project-Id-Version: libspelling main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-09-01 23:22+0300\n" "Last-Translator: Efstathios Iosifidis <eiosifidis@gnome.org>\n" "Language-Team: Greek <gnome-el-list@gnome.org>\n" "Language: el\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 3.4.4\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "Προσθήκη στο λεξικό" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "Παράβλεψη" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "Έλεγχος ορθογραφίας" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "Γλώσσες" 0707010000004C000081A40000000000000000000000016712C6CB0000043A000000000000000000000000000000000000001E00000000libspelling-0.4.4/po/en_GB.po# British English translation for libspelling. # Copyright (C) 2022 libspelling's COPYRIGHT HOLDER # This file is distributed under the same license as the libspelling package. # Bruce Cowan <bruce@bcowan.me.uk>, 2022-2024. # Andi Chandler <andi@gowling.com>, 2024. # msgid "" msgstr "" "Project-Id-Version: libspelling main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-09-01 13:10+0100\n" "Last-Translator: Bruce Cowan <bruce@bcowan.me.uk>\n" "Language-Team: English - United Kingdom <en@li.org>\n" "Language: en_GB\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 3.4.4\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "Add to Dictionary" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "Ignore" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "Check Spelling" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "Languages" 0707010000004D000081A40000000000000000000000016712C6CB0000042F000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/eo.po# Esperanto translation for libspelling. # Copyright (C) 2023 libspelling's COPYRIGHT HOLDER # This file is distributed under the same license as the libspelling package. # Kristjan SCHMIDT <kristjan.schmidt@googlemail.com>, 2023. # msgid "" msgstr "" "Project-Id-Version: libspelling main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2023-09-14 17:08+0200\n" "Last-Translator: Kristjan SCHMIDT <kristjan.schmidt@googlemail.com>\n" "Language-Team: Esperanto <gnome-eo-list@gnome.org>\n" "Language: eo\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" "X-Generator: Gtranslator 42.0\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "Aldoni al la vortaro" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "Ignori" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "Kontrolu literumadon" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "Lingvoj" 0707010000004E000081A40000000000000000000000016712C6CB000004E6000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/es.po# Spanish translation for libspelling. # Copyright (C) 2021 libspelling's COPYRIGHT HOLDER # This file is distributed under the same license as the libspelling package. # leo <daniel.mustieles@gmail.com>, 2021. # Miguel Ángel Nieto <correo@miguelangelnieto.net>, 2021-2022. # Daniel Mustieles <daniel.mustieles@gmail.com>, 2022-2024. # Daniel Mustieles García <daniel.mustieles@gmail.com>, 2024. # msgid "" msgstr "" "Project-Id-Version: libspelling main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-09-03 10:20+0200\n" "Last-Translator: Daniel Mustieles García <daniel.mustieles@gmail.com>\n" "Language-Team: Spanish - Spain <gnome-es-list@gnome.org>\n" "Language: es_ES\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" "X-Generator: Gtranslator 46.1\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "Añadir al diccionario" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "Ignorar" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "Comprobar ortografía" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "Idiomas" 0707010000004F000081A40000000000000000000000016712C6CB00000414000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/eu.po# Basque translation for libspelling. # Copyright (C) 2021 libspelling's COPYRIGHT HOLDER # This file is distributed under the same license as the libspelling package. # Asier Sarasua Garmendia <asiersarasua@ni.eus>, 2021, 2022, 2023. # msgid "" msgstr "" "Project-Id-Version: libspelling main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-08-25 01:54+0000\n" "Last-Translator: Asier Sarasua Garmendia <asiersarasua@ni.eus>\n" "Language-Team: Basque <librezale@librezale.eus>\n" "Language: eu\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);9\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "Gehitu hiztegiari" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "Ez ikusi egin" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "Ortografia-egiaztapena" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "Hizkuntzak" 07070100000050000081A40000000000000000000000016712C6CB0000044D000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/fa.po# Persian translation for libspelling. # Copyright (C) 2022 libspelling's COPYRIGHT HOLDER # This file is distributed under the same license as the libspelling package. # Danial Behzadi <dani.behzi@ubuntu.com>, 2022-2024. # msgid "" msgstr "" "Project-Id-Version: libspelling main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-08-27 14:39+0330\n" "Last-Translator: Danial Behzadi <dani.behzi@ubuntu.com>\n" "Language-Team: Persian <fa@li.org>\n" "Language: fa\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-DamnedLies-Scope: partial\n" "X-Generator: Poedit 3.4.4\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "افزودن به واژهنامه" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "چشمپوشی" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "بررسی نوشتاری" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "زبانها" 07070100000051000081A40000000000000000000000016712C6CB0000040D000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/fi.po# Finnish translation for libspelling. # Copyright (C) 2021 libspelling's COPYRIGHT HOLDER # This file is distributed under the same license as the libspelling package. # Jiri Groenroos <jiri.gronroos@iki.fi>, 2021. # msgid "" msgstr "" "Project-Id-Version: libspelling main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-08-24 15:31+0300\n" "Last-Translator: Jiri Grönroos <jiri.gronroos+l10n@iki.fi>\n" "Language-Team: Finnish <lokalisointi-lista@googlegroups.com>\n" "Language: fi\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 3.4.4\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "Lisää sanastoon" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "Ohita" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "Oikolue" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "Kielet" 07070100000052000081A40000000000000000000000016712C6CB0000051A000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/fr.po# French translation for libspelling # Copyright (C) 2022 libspelling's COPYRIGHT HOLDER # This file is distributed under the same license as the libspelling package. # Franck Albaret <contact@dansleru.sh>, 2022 # Charles Monzat <charles.monzat@free.fr>, 2022. # Irénée THIRION <irenee.thirion@e.email>, 2022. # Alexandre Franke <afranke@gnome.org>, 2022. # Jean-Marc Tissières <jmetissieres@gmail.com>, 2023. # Vincent Chatelain <vinchatl_gnome@proton.me>, 2024. # msgid "" msgstr "" "Project-Id-Version: libspelling main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-08-29 17:14+0200\n" "Last-Translator: Irénée Thirion <irenee.thirion@e.email>\n" "Language-Team: GNOME French Team <gnomefr@traduc.org>\n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" "X-Generator: Poedit 3.4.4\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "Ajouter au dictionnaire" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "Ignorer" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "Vérifier l’orthographe" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "Langues" 07070100000053000081A40000000000000000000000016712C6CB00000422000000000000000000000000000000000000001C00000000libspelling-0.4.4/po/fur.po# Friulian translation for libspelling. # Copyright (C) 2022 libspelling's COPYRIGHT HOLDER # This file is distributed under the same license as the libspelling package. # Fabio Tomat <f.t.public@gmail.com>, 2022. # msgid "" msgstr "" "Project-Id-Version: libspelling main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-10-14 14:16+0000\n" "Last-Translator: Fabio T. <f.t.public@gmail.com>\n" "Language-Team: Friulian <f.t.public@gmail.com>\n" "Language: fur\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Editor: HaiPO 2.0 beta\n" "X-Generator: Poedit 3.4.2\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "Zonte al dizionari" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "Ignore" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "Controle la ortografie" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "Lenghis" 07070100000054000081A40000000000000000000000016712C6CB000004DB000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/gl.po# Galician translation for libspelling. # Copyright (C) 2021 libspelling's COPYRIGHT HOLDER # This file is distributed under the same license as the libspelling package. # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # Fran Diéguez <frandieguez@gnome.org>, 2022. # Fran Dieguez <frandieguez@gnome.org>, 2021-2022. # msgid "" msgstr "" "Project-Id-Version: libspelling main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-09-02 11:33+0200\n" "Last-Translator: Fran Dieguez <fran.dieguez@gnome.org>\n" "Language-Team: Galician <proxecto@trasno.gal>\n" "Language: gl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-DL-Branch: main\n" "X-DL-Domain: po\n" "X-DL-Module: libspelling\n" "X-DL-State: None\n" "X-DL-Team: gl\n" "X-Generator: Poedit 3.5\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "Engadir ao dicionario" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "Ignorar" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "Verificación ortográfica" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "Linguaxes" 07070100000055000081A40000000000000000000000016712C6CB00000431000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/he.po# Hebrew translation for libspelling. # Copyright (C) 2021 libspelling's COPYRIGHT HOLDER # This file is distributed under the same license as the libspelling package. # Yosef Or Boczko <yoseforb@gmail.com>, 2021-2024. # msgid "" msgstr "" "Project-Id-Version: libspelling gnome-41\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-08-09 14:36+0300\n" "Last-Translator: Yosef Or Boczko <yoseforb@gmail.com>\n" "Language-Team: Hebrew\n" "Language: he\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=(n==1 ? 0 : n==2 ? 1 : n>10 && n%10==0 ? " "2 : 3)\n" "X-Generator: Gtranslator 46.1\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "הוספה למילון" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "התעלמות" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "בדיקת איות" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "שפות" 07070100000056000081A40000000000000000000000016712C6CB00000490000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/hi.po# Hindi translation for libspelling. # Copyright (C) 2024 libspelling's COPYRIGHT HOLDER # This file is distributed under the same license as the libspelling package. # Scrambled777 <weblate.scrambled777@simplelogin.com>, 2024. # msgid "" msgstr "" "Project-Id-Version: libspelling main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-07-05 22:15+0530\n" "Last-Translator: Scrambled777 <weblate.scrambled777@simplelogin.com>\n" "Language-Team: Hindi <indlinux-hindi@lists.sourceforge.net>\n" "Language: hi\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Gtranslator 46.1\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "शब्दकोश में जोड़ें" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "नजरअंदाज करें" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "वर्तनी जांचें" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "भाषाएं" 07070100000057000081A40000000000000000000000016712C6CB00000437000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/hr.po# Croatian translation for libspelling. # Copyright (C) 2022 libspelling's COPYRIGHT HOLDER # This file is distributed under the same license as the libspelling package. # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # msgid "" msgstr "" "Project-Id-Version: libspelling main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2022-10-04 15:57+0200\n" "Last-Translator: gogo <trebelnik2@gmail.com>\n" "Language-Team: Croatian <hr@li.org>\n" "Language: hr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Generator: Poedit 3.1.1\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "Dodaj u rječnik" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "Zanemari" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "Provjera pravopisa" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "Jezici" 07070100000058000081A40000000000000000000000016712C6CB00000477000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/hu.po# Hungarian translation for libspelling. # Copyright (C) 2022, 2023, 2024 Free Software Foundation, Inc. # This file is distributed under the same license as the libspelling package. # # Balázs Meskó <mesko.balazs at fsf dot hu>, 2022, 2024. # Balázs Úr <ur.balazs at fsf dot hu>, 2022, 2023, 2024. msgid "" msgstr "" "Project-Id-Version: libspelling main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-06-28 16:49+0200\n" "Last-Translator: Balázs Úr <ur.balazs at fsf dot hu>\n" "Language-Team: Hungarian <openscope at fsf dot hu>\n" "Language: hu\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Lokalize 23.08.4\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "Hozzáadás a szótárhoz" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "Mellőzés" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "Helyesírás-ellenőrzés" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "Nyelvek" 07070100000059000081A40000000000000000000000016712C6CB000004B1000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/ie.po# Interlingue translation for libspelling. # Copyright (C) 2022 libspelling's COPYRIGHT HOLDER # This file is distributed under the same license as the libspelling package. # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # OIS <mistresssilvara@hotmail.com>, 2022. # msgid "" msgstr "" "Project-Id-Version: libspelling gnome-43\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2022-12-22 16:32+0700\n" "Last-Translator: OIS <mistresssilvara@hotmail.com>\n" "Language-Team: Interlingue; Occidental <None>\n" "Language: ie\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n!=1)\n" "X-DL-Team: ie\n" "X-DL-Module: libspelling\n" "X-DL-Branch: gnome-43\n" "X-DL-Domain: po\n" "X-DL-State: Translating\n" "X-Generator: Gtranslator 42.0\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "Adjuncter al dictionarium" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "Ignorar" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "Controlar ortografie" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "Lingues" 0707010000005A000081A40000000000000000000000016712C6CB000003BE000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/is.po# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Sveinn í Felli <sv1@fellsnet.is>, 2022, 2023. msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2023-06-18 10:19+0000\n" "Last-Translator: Sveinn í Felli <sv1@fellsnet.is>\n" "Language-Team: Icelandic\n" "Language: is\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Lokalize 21.12.3\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "Bæta við orðalista" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "Hunsa" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "Yfirfara stafsetningu" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "Tungumál" 0707010000005B000081A40000000000000000000000016712C6CB0000043F000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/it.po# Italian translation for libspelling. # Copyright (C) 2021 libspelling's COPYRIGHT HOLDER # This file is distributed under the same license as the libspelling # package. # Davide Ferracin <davide.ferracin@protonmail.com>, 2021, 2022, 2023, 2024. # msgid "" msgstr "" "Project-Id-Version: libspelling gnome-41\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-08-31 12:33+0300\n" "Last-Translator: Davide Ferracin <davide.ferracin@protonmail.com>\n" "Language-Team: Italian <gnome-it-list@gnome.org>\n" "Language: it\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "Generated-By: Babel 2.15.0\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "Aggiungi al dizionario" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "Ignora" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "Controllo ortografico" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "Lingue" 0707010000005C000081A40000000000000000000000016712C6CB0000041B000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/ja.po# Japanese translation for libspelling. # Copyright (C) 2022 libspelling's COPYRIGHT HOLDER # This file is distributed under the same license as the libspelling package. # sujiniku <sujinikusityuu@gmail.com>, 2022. # sicklylife <translation@sicklylife.jp>, 2022. # msgid "" msgstr "" "Project-Id-Version: libspelling main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2022-03-15 22:00+0900\n" "Last-Translator: sicklylife <translation@sicklylife.jp>\n" "Language-Team: Japanese <gnome-translation@gnome.gr.jp>\n" "Language: ja\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "辞書に追加" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "無視" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "スペルチェック" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "言語" 0707010000005D000081A40000000000000000000000016712C6CB0000045D000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/ka.po# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # Ekaterine Papava <papava.e@gtu.ge>, 2023-2024. # msgid "" msgstr "" "Project-Id-Version: libspelling\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-08-23 07:34+0200\n" "Last-Translator: Ekaterine Papava <papava.e@gtu.ge>\n" "Language-Team: Georgian <(nothing)>\n" "Language: ka\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 3.3.2\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "სიტყვის ლექსიკონში ჩამატება" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "იგნორი" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "მართლწერის შემოწმება" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "ენები" 0707010000005E000081A40000000000000000000000016712C6CB0000042F000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/kk.po# Kazakh translation for libspelling. # Copyright (C) 2022 libspelling's COPYRIGHT HOLDER # This file is distributed under the same license as the libspelling package. # Baurzhan Muftakhidinov <baurthefirst@gmail.com>, 2022. # msgid "" msgstr "" "Project-Id-Version: libspelling main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-08-30 19:29+0500\n" "Last-Translator: Baurzhan Muftakhidinov <baurthefirst@gmail.com>\n" "Language-Team: Kazakh <kk_KZ@googlegroups.com>\n" "Language: kk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Poedit 3.4.4\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "Сөздікке қосу" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "Елемеу" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "Емлені тексеру" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "Тілдер" 0707010000005F000081A40000000000000000000000016712C6CB00000432000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/ko.po# Korean translation for libspelling. # Copyright (C) 2022 libspelling's COPYRIGHT HOLDER # This file is distributed under the same license as the libspelling package. # Seong-ho Cho <shcho@gnome.org>, 2022, 2023, 2024. # DaeHyun Sung <sungdh86+git@gmail.com> 2023, 2024. # msgid "" msgstr "" "Project-Id-Version: libspelling main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-08-27 15:32+0900\n" "Last-Translator: DaeHyun Sung <sungdh86+git@gmail.com>\n" "Language-Team: Korean <gnome-kr@googlegroups.com>\n" "Language: ko\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Poedit 3.5\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "사전에 추가" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "무시" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "철자 검사" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "언어" 07070100000060000081A40000000000000000000000016712C6CB00000455000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/lt.po# Lithuanian translation for libspelling. # Copyright (C) 2022 libspelling's COPYRIGHT HOLDER # This file is distributed under the same license as the libspelling package. # Aurimas Černius <aurisc4@gmail.com>, 2022-2023. # msgid "" msgstr "" "Project-Id-Version: libspelling main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-09-02 23:04+0300\n" "Last-Translator: Aurimas Černius <aurisc4@gmail.com>\n" "Language-Team: Lietuvių <gnome-lt@lists.akl.lt>\n" "Language: lt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "(n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Generator: Poedit 3.4.4\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "Pridėti į žodyną" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "Nepaisyti" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "Tikrinti rašybą" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "Kalbos" 07070100000061000081A40000000000000000000000016712C6CB00000436000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/lv.po# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # SPDX-FileCopyrightText: 2022, 2023, 2024 Rūdolfs Mazurs <rudolfs.mazurs@gmail.com> msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-09-11 22:53+0300\n" "Last-Translator: Rūdolfs Mazurs <rudolfs.mazurs@gmail.com>\n" "Language-Team: Latvian <lata-l10n@googlegroups.com>\n" "Language: lv\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : " "2);\n" "X-Generator: Lokalize 23.08.5\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "Pievienot vārdnīcai" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "Ignorēt" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "Pārbaudīt pareizrakstību" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "Valodas" 07070100000062000081A40000000000000000000000016712C6CB0000009C000000000000000000000000000000000000002100000000libspelling-0.4.4/po/meson.buildi18n = import('i18n') i18n.gettext('libspelling', preset: 'glib', args: ['--msgid-bugs-address=https://gitlab.gnome.org/GNOME/libspelling/issues'], ) 07070100000063000081A40000000000000000000000016712C6CB0000039F000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/nb.po# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-08-24 22:51+0200\n" "Last-Translator: \n" "Language-Team: \n" "Language: nb\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 3.4.4\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "Legg til i ordliste" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "Ignorer" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "Stavekontroll" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "Språk" 07070100000064000081A40000000000000000000000016712C6CB00000482000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/ne.po# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # msgid "" msgstr "" "Project-Id-Version: Gnome Nepali Translation Project\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-05-28 20:43+0545\n" "Last-Translator: Pawan Chitrakar <chautari@gmail.com>\n" "Language-Team: Nepali Team <chautari@gmail.com>\n" "Language: ne\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 3.4.2\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "शब्दकोशमा थप्नुहोस्" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "वेवास्ता गर्नुहोस" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "र्हिज्जे जाँच गर्नुहोस्" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "भाषा" 07070100000065000081A40000000000000000000000016712C6CB00000477000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/nl.po# Dutch translation for libspelling. # Copyright (C) 2021 libspelling's COPYRIGHT HOLDER # This file is distributed under the same license as the libspelling package. # # Nathan Follens <nfollens@gnome.org>, 2021-2024. # Hannie Dumoleyn <hannie@ubuntu-nl.org>, 2022. # Philip Goto <philip.goto@gmail.com>, 2022. msgid "" msgstr "" "Project-Id-Version: libspelling main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-02-18 13:03+0100\n" "Last-Translator: Nathan Follens <nfollens@gnome.org>\n" "Language-Team: GNOME-NL https://matrix.to/#/#nl:gnome.org\n" "Language: nl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 3.4.2\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "Toevoegen aan woordenboek" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "Negeren" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "Spelling controleren" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "Talen" 07070100000066000081A40000000000000000000000016712C6CB000003F8000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/oc.po# Occitan translation for libspelling. # Copyright (C) 2021 libspelling's COPYRIGHT HOLDER # This file is distributed under the same license as the libspelling package. # Quentin PAGÈS <pages_quentin@hotmail.com>, 2021. # msgid "" msgstr "" "Project-Id-Version: libspelling main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-03-11 20:03+0100\n" "Last-Translator: Quentin PAGÈS\n" "Language-Team: Occitan <totenoc@gmail.com>\n" "Language: oc\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" "X-Generator: Poedit 3.4.2\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "Apondre al diccionari" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "Ignorar" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "Verificar l'ortografia" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "Lengas" 07070100000067000081A40000000000000000000000016712C6CB00000483000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/pa.po# Punjabi translation for libspelling. # Copyright (C) 2022 libspelling's COPYRIGHT HOLDER # This file is distributed under the same license as the libspelling package. # # SPDX-FileCopyrightText: 2022, 2023, 2024 A S Alam <aalam@fedoraproject.org> msgid "" msgstr "" "Project-Id-Version: libspelling main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-03-18 20:58-0500\n" "Last-Translator: A S Alam <aalam@punlinux.org>\n" "Language-Team: Punjabi <punjab-translation@googlegroups.com>\n" "Language: pa\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Lokalize 23.08.5\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "ਡਿਕਸ਼ਨਰੀ ਵਿੱਚ ਜੋੜੋ" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "ਅਣਡਿੱਠਾ ਕਰੋ" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "ਸ਼ਬਦ-ਜੋੜ ਜਾਂਚ" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "ਭਾਸ਼ਾਵਾਂ" 07070100000068000081A40000000000000000000000016712C6CB00000511000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/pt.po# Portuguese translation for libspelling. # Copyright (C) 2022 libspelling's COPYRIGHT HOLDER # This file is distributed under the same license as the libspelling package. # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # Juliano de Souza Camargo <julianosc@protonmail.com>, 2022. # Hugo Carvalho <hugokarvalho@hotmail.com>, 2022, 2023, 2024. # msgid "" msgstr "" "Project-Id-Version: libspelling gnome-41\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-08-30 16:01+0100\n" "Last-Translator: Hugo Carvalho <hugokarvalho@hotmail.com>\n" "Language-Team: Portuguese < https://l10n.gnome.org/teams/pt/>\n" "Language: pt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-DL-Team: pt\n" "X-DL-Module: libspelling\n" "X-DL-Branch: main\n" "X-DL-Domain: po\n" "X-DL-State: Translating\n" "X-Generator: Poedit 3.4.4\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "Adicionar ao dicionário" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "Ignorar" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "Verificar ortografia" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "Idiomas" 07070100000069000081A40000000000000000000000016712C6CB00000583000000000000000000000000000000000000001E00000000libspelling-0.4.4/po/pt_BR.po# Brazilian Portuguese translation for libspelling. # Copyright (C) 2024 libspelling's COPYRIGHT HOLDER # This file is distributed under the same license as the libspelling package. # Enrico Nicoletto <hiko@duck.com>, 2021, 2022. # Matheus Barbosa <mdpb.matheus@gmail.com>, 2022. # Leônidas Araújo <leorusvellt@hotmail.com>, 2022. # Rafael Fontenelle <rafaelff@gnome.org>, 2021-2023. # Jhonata Fernandes <fjhonata14@gmail.com>, 2024. # msgid "" msgstr "" "Project-Id-Version: libspelling main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-03-28 19:22-0300\n" "Last-Translator: Jhonata Fernandes <fjhonata14@gmail.com>\n" "Language-Team: Brazilian Portuguese <https://br.gnome.org/traducao>\n" "Language: pt_BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" "X-Generator: Poedit 3.3.1\n" "X-DL-Team: pt_BR\n" "X-DL-Module: libspelling\n" "X-DL-Branch: main\n" "X-DL-Domain: po\n" "X-DL-State: Translating\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "Adicionar dicionários" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "Ignorar" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "Verificação ortográfica" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "Idiomas" 0707010000006A000081A40000000000000000000000016712C6CB00000481000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/ro.po# Romanian translation for libspelling. # Copyright (C) 2023 libspelling's COPYRIGHT HOLDER # This file is distributed under the same license as the libspelling package. # Florentina Musat <deep.sky.028@gmail.com>, 2023. # msgid "" msgstr "" "Project-Id-Version: libspelling main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-08-23 08:29+0300\n" "Last-Translator: Florentina Mușat <florentina [dot] musat [dot] 28 [at] " "gmail [dot] com>\n" "Language-Team: Romanian <gnomero-list@lists.sourceforge.net>\n" "Language: ro\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < " "20)) ? 1 : 2);\n" "X-Generator: Poedit 3.4.4\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "Adaugă la dicționar" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "Ignoră" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "Verifică ortografia" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "Limbi străine" 0707010000006B000081A40000000000000000000000016712C6CB000004A7000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/ru.po# Russian translation for libspelling. # Copyright (C) 2021 libspelling's COPYRIGHT HOLDER # This file is distributed under the same license as the libspelling package. # Ser82-png <sw@atrus.ru>, 2022. # Ivan Molodetskikh <yalterz@gmail.com>, 2022. # msgid "" msgstr "" "Project-Id-Version: unnamed project\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-06-23 16:35+0300\n" "Last-Translator: Artur So <arturios2005@mail.ru>\n" "Language-Team: Russian <gnome-cyr@gnome.org>\n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Generator: Poedit 3.4.4\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "Добавить в словарь" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "Игнорировать" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "Проверять орфографию" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "Языки" 0707010000006C000081A40000000000000000000000016712C6CB0000044A000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/sk.po# Slovak translation for libspelling. # Copyright (C) 2022 libspelling's COPYRIGHT HOLDER # This file is distributed under the same license as the libspelling package. # Dušan Kazik <prescott66@gmail.com>, 2022. # Jose Riha <jose1711@gmail.com>, 2024. # msgid "" msgstr "" "Project-Id-Version: libspelling main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-08-26 23:57+0200\n" "Last-Translator: Jose Riha <jose1711@gmail.com>\n" "Language-Team: Slovak <gnome-sk-list@gnome.org>\n" "Language: sk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 1 : (n>=2 && n<=4) ? 2 : 0;\n" "X-Generator: Poedit 3.4.2\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "Pridať do slovníka" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "Ignorovať" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "Kontrolovať pravopis" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "Jazyky" 0707010000006D000081A40000000000000000000000016712C6CB00000488000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/sl.po# Slovenian translation for libspelling. # Copyright (C) 2021 libspelling's COPYRIGHT HOLDER # This file is distributed under the same license as the libspelling package. # # Matej Urbančič <mateju@svn.gnome.org>, 2021–2024. # msgid "" msgstr "" "Project-Id-Version: libspelling main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-06-20 00:05+0200\n" "Last-Translator: Martin Srebotnjak <miles@filmsi.net>\n" "Language-Team: Slovenian GNOME Translation Team <gnome-si@googlegroups.com>\n" "Language: sl_SI\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || " "n%100==4 ? 3 : 0);\n" "X-Poedit-SourceCharset: utf-8\n" "X-Generator: Poedit 2.2.1\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "Dodaj v slovar" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "Prezri" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "Preveri črkovanje" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "Jeziki" 0707010000006E000081A40000000000000000000000016712C6CB000004E9000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/sr.po# Serbian translation for libspelling. # Copyright (C) 2022 libspelling's COPYRIGHT HOLDER # This file is distributed under the same license as the libspelling package. # Марко Костић <marko.m.kostic@gmail.com>, 2022. # Марко М. Костић <marko.m.kostic@gmail.com>, 2024. # msgid "" msgstr "" "Project-Id-Version: libspelling main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-06-20 01:11+0200\n" "Last-Translator: Марко М. Костић <marko.m.kostic@gmail.com>\n" "Language-Team: Serbian <gnome-sr@googlegroups.com>\n" "Language: sr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=n==1? 3 : n%10==1 && n%100!=11 ? 0 : " "n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" "X-Generator: Gtranslator 45.3\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "Додај у речник" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "Занемари" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "Провера правописа" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "Језици" 0707010000006F000081A40000000000000000000000016712C6CB0000046B000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/sv.po# Swedish translation for libspelling. # Copyright © 2021-2024 libspelling's COPYRIGHT HOLDER # This file is distributed under the same license as the libspelling package. # Anders Jonsson <anders.jonsson@norsjovallen.se>, 2021, 2022, 2023, 2024. # Luna Jernberg <droidbittin@gmail.com>, 2021, 2022. # msgid "" msgstr "" "Project-Id-Version: libspelling main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-08-31 14:32+0200\n" "Last-Translator: Anders Jonsson <anders.jonsson@norsjovallen.se>\n" "Language-Team: Swedish <tp-sv@listor.tp-sv.se>\n" "Language: sv\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 3.4.4\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "Lägg till i ordbok" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "Ignorera" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "Kontrollera stavning" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "Språk" 07070100000070000081A40000000000000000000000016712C6CB00000417000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/th.po# Thai translation for libspelling. # Copyright (C) 2024 libspelling's COPYRIGHT HOLDER # This file is distributed under the same license as the libspelling package. # Aefgh Threenine <aefgh39622@gmail.com>, 2024. # msgid "" msgstr "" "Project-Id-Version: libspelling main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-01-16 23:36+0700\n" "Last-Translator: \n" "Language-Team: Thai <thai-l10n@googlegroups.com>\n" "Language: th\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Poedit 3.4.2\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "เพิ่มในพจนานุกรม" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "ละเลย" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "ตรวจตัวสะกด" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "ภาษา" 07070100000071000081A40000000000000000000000016712C6CB000004AD000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/uk.po# Ukrainian translation for libspelling. # Copyright (C) 2021 libspelling's COPYRIGHT HOLDER # This file is distributed under the same license as the libspelling package. # # Yuri Chornoivan <yurchor@ukr.net>, 2021, 2022, 2023, 2024. msgid "" msgstr "" "Project-Id-Version: libspelling main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-08-23 15:58+0300\n" "Last-Translator: Yuri Chornoivan <yurchor@ukr.net>\n" "Language-Team: Ukrainian <trans-uk@lists.fedoraproject.org>\n" "Language: uk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=n==1 ? 3 : n%10==1 && n%100!=11 ? 0 : " "n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" "X-Generator: Lokalize 23.04.3\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "Додати до словника" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "Ігнорувати" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "Перевірка правопису" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "Мови" 07070100000072000081A40000000000000000000000016712C6CB000003F2000000000000000000000000000000000000001B00000000libspelling-0.4.4/po/vi.po# Vietnamese translation for libspelling. # This file is distributed under the same license as the libspelling package. # Ngọc Quân Trần <vnwildman@gmail.com>, 2022. # msgid "" msgstr "" "Project-Id-Version: libspelling main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2022-05-29 14:29+0700\n" "Last-Translator: Trần Ngọc Quân <vnwildman@gmail.com>\n" "Language-Team: Vietnamese <gnome-vi-list@gnome.org>\n" "Language: vi\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Gtranslator 3.38.0\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "Thêm vào từ điển" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "Bỏ qua" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "Kiểm tra chính tả" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "Ngôn ngữ" 07070100000073000081A40000000000000000000000016712C6CB0000041F000000000000000000000000000000000000001E00000000libspelling-0.4.4/po/zh_CN.po# Chinese (China) translation for libspelling. # Copyright (C) 2021 libspelling's COPYRIGHT HOLDER # This file is distributed under the same license as the libspelling package. # D <afecr@foxmail.com>, 2021. # lumingzh <lumingzh@qq.com>, 2021-2024. # msgid "" msgstr "" "Project-Id-Version: libspelling main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2024-08-23 15:12+0800\n" "Last-Translator: lumingzh <lumingzh@qq.com>\n" "Language-Team: Chinese (China) <i18n-zh@googlegroups.com>\n" "Language: zh_CN\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Gtranslator 46.1\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "添加到词典" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "忽略" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "检查拼写" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "语言" 07070100000074000081A40000000000000000000000016712C6CB0000047B000000000000000000000000000000000000001E00000000libspelling-0.4.4/po/zh_TW.po# Chinese (Taiwan) translation for libspelling. # Copyright (C) 2022 libspelling's COPYRIGHT HOLDER # This file is distributed under the same license as the libspelling package. # Freddy <freddy4212@gmail.com>, 2022. # Freddy Cheng <freddy4212@gmail.com>, 2022. # msgid "" msgstr "" "Project-Id-Version: libspelling main\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n" "POT-Creation-Date: 2024-10-18 12:30-0700\n" "PO-Revision-Date: 2023-03-07 05:00+0000\n" "Last-Translator: Weblate Admin <pesder@mail.edu.tw>\n" "Language-Team: Chinese (Traditional) <http://darkbear.mercusysddns.com/" "projects/gnome-44/libspelling-main/zh_Hant/>\n" "Language: zh_TW\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 4.16.1\n" #: lib/spelling-menu.c:273 msgid "Add to Dictionary" msgstr "新增至字典" #: lib/spelling-menu.c:274 msgid "Ignore" msgstr "忽略拼寫錯誤" #: lib/spelling-menu.c:275 msgid "Check Spelling" msgstr "檢查拼寫" #: lib/spelling-menu.c:284 msgid "Languages" msgstr "語言" 07070100000075000041ED0000000000000000000000026712C6CB00000000000000000000000000000000000000000000001E00000000libspelling-0.4.4/subprojects07070100000076000081A40000000000000000000000016712C6CB000000C1000000000000000000000000000000000000002700000000libspelling-0.4.4/subprojects/gtk.wrap[wrap-git] directory=gtk url=https://gitlab.gnome.org/GNOME/gtk.git push-url=ssh://git@gitlab.gnome.org:GNOME/gtk.git revision=main depth=1 [provide] gtk4 = libgtk_dep dependency_names = gtk4 07070100000077000081A40000000000000000000000016712C6CB000000FE000000000000000000000000000000000000003100000000libspelling-0.4.4/subprojects/gtksourceview.wrap[wrap-git] directory=gtksourceview url=https://gitlab.gnome.org/GNOME/gtksourceview.git push-url=ssh://git@ssh.gitlab.gnome.org:GNOME/gtksourceview.git revision=master depth=1 [provide] gtksourceview-5 = gtksource_dep dependency_names = gtksourceview-5 07070100000078000041ED0000000000000000000000026712C6CB00000000000000000000000000000000000000000000001700000000libspelling-0.4.4/test07070100000079000081A40000000000000000000000016712C6CB000000E4000000000000000000000000000000000000002300000000libspelling-0.4.4/test/meson.buildtest_spelling = executable('test-spelling', 'test-spelling.c', dependencies: [libspelling_static_dep], install: false, include_directories: [include_directories('..'), include_directories('.')], ) 0707010000007A000081A40000000000000000000000016712C6CB000019C7000000000000000000000000000000000000002700000000libspelling-0.4.4/test/test-spelling.c/* test-spelling.c * * Copyright 2023 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <unistd.h> #include <libspelling.h> static void on_click_pressed_cb (SpellingTextBufferAdapter *adapter, int n_press, double x, double y, GtkGestureClick *click) { GdkEventSequence *sequence; GtkTextBuffer *buffer; GtkTextIter iter, begin, end; GtkWidget *widget; GdkEvent *event; int buf_x, buf_y; widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (click)); sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (click)); event = gtk_gesture_get_last_event (GTK_GESTURE (click), sequence); if (n_press != 1 || !gdk_event_triggers_context_menu (event)) return; buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget)); if (gtk_text_buffer_get_selection_bounds (buffer, &begin, &end)) return; gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (widget), GTK_TEXT_WINDOW_WIDGET, x, y, &buf_x, &buf_y); gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (widget), &iter, buf_x, buf_y); gtk_text_buffer_select_range (buffer, &iter, &iter); spelling_text_buffer_adapter_update_corrections (adapter); } int main (int argc, char *argv[]) { g_autoptr(SpellingTextBufferAdapter) adapter = NULL; g_autoptr(GOptionContext) context = NULL; g_autoptr(GError) error = NULL; GtkSourceLanguage *language = NULL; GtkSourceStyleScheme *scheme = NULL; SpellingChecker *checker = NULL; GtkScrolledWindow *scroller; GtkSourceBuffer *source_buffer; GtkSourceView *source_view; GMenuModel *extra_menu; GtkWindow *window; GMainLoop *main_loop; g_autofree char *contents = NULL; g_autofree char *language_id = NULL; g_autofree char *scheme_id = NULL; GtkGesture *gesture; const GOptionEntry entries[] = { { "language", 'l', 0, G_OPTION_ARG_STRING, &language_id, "The GtkSourceView language ID to use", "c" }, { "scheme", 's', 0, G_OPTION_ARG_STRING, &scheme_id, "The GtkSourceView style scheme ID to use", "Adwaita" }, { 0 } }; gtk_init (); gtk_source_init (); spelling_init (); context = g_option_context_new ("- test spellcheck text-adapter"); g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE); if (!g_option_context_parse (context, &argc, &argv, &error)) { g_printerr ("%s\n", error->message); return EXIT_FAILURE; } if (language_id != NULL) { language = gtk_source_language_manager_get_language (gtk_source_language_manager_get_default (), language_id); if (language == NULL) g_warning ("No such GtkSourceView programming language with id \"%s\"", language_id); } if (scheme_id == NULL) scheme_id = g_strdup ("Adwaita"); scheme = gtk_source_style_scheme_manager_get_scheme (gtk_source_style_scheme_manager_get_default (), scheme_id); if (scheme == NULL) g_warning ("No such GtkSourceView style scheme with id \"%s\"", scheme_id); if (argc == 2) g_file_get_contents (argv[1], &contents, NULL, NULL); main_loop = g_main_loop_new (NULL, FALSE); window = g_object_new (GTK_TYPE_WINDOW, "default-width", 800, "default-height", 600, "title", "Test Spelling", NULL); scroller = g_object_new (GTK_TYPE_SCROLLED_WINDOW, "hscrollbar-policy", GTK_POLICY_NEVER, NULL); source_buffer = g_object_new (GTK_SOURCE_TYPE_BUFFER, "language", language, "style-scheme", scheme, "enable-undo", TRUE, "text", contents ? contents : "", NULL); source_view = g_object_new (GTK_SOURCE_TYPE_VIEW, "buffer", source_buffer, "show-line-numbers", TRUE, "monospace", TRUE, "top-margin", 6, "left-margin", 6, "right-margin", 6, "bottom-margin", 6, NULL); gtk_window_set_child (window, GTK_WIDGET (scroller)); gtk_scrolled_window_set_child (scroller, GTK_WIDGET (source_view)); g_signal_connect_swapped (window, "close-request", G_CALLBACK (g_main_loop_quit), main_loop); /* Setup spellchecking */ checker = spelling_checker_get_default (); adapter = spelling_text_buffer_adapter_new (source_buffer, checker); extra_menu = spelling_text_buffer_adapter_get_menu_model (adapter); gtk_text_view_set_extra_menu (GTK_TEXT_VIEW (source_view), extra_menu); gtk_widget_insert_action_group (GTK_WIDGET (source_view), "spelling", G_ACTION_GROUP (adapter)); spelling_text_buffer_adapter_set_enabled (adapter, TRUE); gesture = gtk_gesture_click_new (); gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), 0); gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (gesture), GTK_PHASE_CAPTURE); g_signal_connect_object (gesture, "pressed", G_CALLBACK (on_click_pressed_cb), adapter, G_CONNECT_SWAPPED); gtk_widget_add_controller (GTK_WIDGET (source_view), GTK_EVENT_CONTROLLER (gesture)); gtk_window_present (window); g_main_loop_run (main_loop); return 0; } 0707010000007B000081ED0000000000000000000000016712C6CB0000035D000000000000000000000000000000000000001F00000000libspelling-0.4.4/test/test.js#!/usr/bin/env gjs const GLib = imports.gi.GLib; const Gtk = imports.gi.Gtk; const GtkSource = imports.gi.GtkSource; const Spelling = imports.gi.Spelling; Gtk.init(); GtkSource.init(); Spelling.init(); let mainLoop = GLib.MainLoop.new(null, false); let win = Gtk.Window.new(); let scroller = Gtk.ScrolledWindow.new(); let view = new GtkSource.View(); let buffer = view.get_buffer(); let checker = Spelling.Checker.get_default(); let adapter = Spelling.TextBufferAdapter.new(buffer, checker); win.set_child(scroller); scroller.set_child(view); buffer.set_style_scheme(GtkSource.StyleSchemeManager.get_default().get_scheme('Adwaita')); view.insert_action_group('spelling', adapter); view.set_extra_menu(adapter.get_menu_model()); adapter.set_enabled(true) win.connect('close-request', function() { mainLoop.quit(); }); win.present(); mainLoop.run(); 0707010000007C000041ED0000000000000000000000026712C6CB00000000000000000000000000000000000000000000001C00000000libspelling-0.4.4/testsuite0707010000007D000081A40000000000000000000000016712C6CB00000337000000000000000000000000000000000000002800000000libspelling-0.4.4/testsuite/meson.buildlibspelling_test_env = [ 'G_DEBUG=gc-friendly', 'GSETTINGS_BACKEND=memory', 'MALLOC_CHECK_=2', ] libspelling_testsuite_c_args = [ '-DG_LOG_DOMAIN="libspelling"', '-DG_ENABLE_DEBUG', '-UG_DISABLE_ASSERT', '-UG_DISABLE_CAST_CHECKS', ] libspelling_testsuite = { 'test-cursor' : {}, 'test-engine' : {}, 'test-job' : {}, 'test-region' : {}, } libspelling_testsuite_deps = [ libspelling_static_dep, ] foreach test, params: libspelling_testsuite test_exe = executable(test, ['@0@.c'.format(test)], c_args: libspelling_testsuite_c_args, dependencies: libspelling_testsuite_deps, include_directories: [include_directories('..'), include_directories('.')], ) if not params.get('skip', false) test(test, test_exe, env: libspelling_test_env) endif endforeach 0707010000007E000081A40000000000000000000000016712C6CB00001B4C000000000000000000000000000000000000002A00000000libspelling-0.4.4/testsuite/test-cursor.c/* test-cursor.c * * Copyright 2024 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <locale.h> #include <glib/gi18n.h> #include <gtksourceview/gtksource.h> #include <libspelling.h> #include "spelling-cursor-private.h" #include "cjhtextregionprivate.h" #undef WANT_DISPLAY_TESTS static const char *test_text = "this is a series of words"; static const char *test_text_2 = "it's possible we're going to have join-words."; #ifdef WANT_DISPLAY_TESTS static const char *test_text_3 = "\ /* ide-buffer.c\ *\ * Copyright 2018-2023 Christian Hergert <chergert@redhat.com>\ *\ * This program is free software: you can redistribute it and/or modify\ * it under the terms of the GNU General Public License as published by\ * the Free Software Foundation, either version 3 of the License, or\ * (at your option) any later version.\ */\ "; #endif static char * next_word (SpellingCursor *cursor) { GtkTextIter begin, end; if (spelling_cursor_next (cursor, &begin, &end)) return gtk_text_iter_get_slice (&begin, &end); return NULL; } static void test_cursor (void) { g_autoptr(GtkTextBuffer) buffer = gtk_text_buffer_new (NULL); CjhTextRegion *region = _cjh_text_region_new (NULL, NULL); g_autoptr(SpellingCursor) cursor = spelling_cursor_new (buffer, region, NULL, NULL); char *word; gtk_text_buffer_set_text (buffer, test_text, -1); _cjh_text_region_insert (region, 0, strlen (test_text), NULL); word = next_word (cursor); g_assert_cmpstr (word, ==, "this"); word = next_word (cursor); g_assert_cmpstr (word, ==, "is"); word = next_word (cursor); g_assert_cmpstr (word, ==, "a"); word = next_word (cursor); g_assert_cmpstr (word, ==, "series"); word = next_word (cursor); g_assert_cmpstr (word, ==, "of"); word = next_word (cursor); g_assert_cmpstr (word, ==, "words"); word = next_word (cursor); g_assert_cmpstr (word, ==, NULL); _cjh_text_region_free (region); } #ifdef WANT_DISPLAY_TESTS static void test_cursor2 (void) { g_autoptr(GtkTextBuffer) buffer = GTK_TEXT_BUFFER (gtk_source_buffer_new (NULL)); GtkWindow *window; GtkSourceView *view; CjhTextRegion *region = _cjh_text_region_new (NULL, NULL); g_autoptr(SpellingCursor) cursor = spelling_cursor_new (buffer, region, NULL, NULL); GtkSourceLanguage *l = gtk_source_language_manager_get_language (gtk_source_language_manager_get_default (), "c"); static const char *words[] = { "ide", "buffer", "c", "Copyright", "2018", "2019", "Christian", "Hergert", "chergert", "redhat", "com", "This", "program", "is", "free", "software", "you", "can", "redistribute", "it", "and", "or", "modify", "it", "under", "the", "terms", "of", "the", "GNU", "General", "Public", "License", "as", "published", "by", "the", "Free", "Software", "Foundation", "either", "version", "3", "of", "the", "License", "or", "at", "your", "option", "any", "later", "version", }; gtk_source_buffer_set_language (GTK_SOURCE_BUFFER (buffer), l); gtk_source_buffer_set_highlight_syntax (GTK_SOURCE_BUFFER (buffer), TRUE); gtk_text_buffer_set_text (buffer, test_text_3, -1); _cjh_text_region_insert (region, 0, strlen (test_text_3), NULL); window = g_object_new (GTK_TYPE_WINDOW, NULL); view = GTK_SOURCE_VIEW (gtk_source_view_new ()); gtk_text_view_set_buffer (GTK_TEXT_VIEW (view), buffer); gtk_window_set_child (window, GTK_WIDGET (view)); gtk_window_present (window); while (g_main_context_pending (NULL)) g_main_context_iteration (NULL, TRUE); for (guint i = 0; i < G_N_ELEMENTS (words); i++) { char *word = next_word (cursor); g_assert_cmpstr (word, ==, words[i]); g_free (word); } _cjh_text_region_free (region); } #endif static void test_cursor_in_word (void) { g_autoptr(GtkTextBuffer) buffer = gtk_text_buffer_new (NULL); CjhTextRegion *region = _cjh_text_region_new (NULL, NULL); g_autoptr(SpellingCursor) cursor = spelling_cursor_new (buffer, region, NULL, NULL); const char *pos = strstr (test_text, "ries "); /* se|ries */ gsize offset = pos - test_text; char *word; gtk_text_buffer_set_text (buffer, test_text, -1); _cjh_text_region_insert (region, 0, strlen (test_text), GINT_TO_POINTER (1)); _cjh_text_region_replace (region, offset, strlen (test_text) - offset, NULL); word = next_word (cursor); g_assert_cmpstr (word, ==, "series"); word = next_word (cursor); g_assert_cmpstr (word, ==, "of"); word = next_word (cursor); g_assert_cmpstr (word, ==, "words"); word = next_word (cursor); g_assert_cmpstr (word, ==, NULL); _cjh_text_region_free (region); } static void test_cursor_join_words (void) { g_autoptr(GtkTextBuffer) buffer = gtk_text_buffer_new (NULL); CjhTextRegion *region = _cjh_text_region_new (NULL, NULL); g_autoptr(SpellingCursor) cursor = spelling_cursor_new (buffer, region, NULL, "-'"); char *word; gtk_text_buffer_set_text (buffer, test_text_2, -1); _cjh_text_region_insert (region, 0, strlen (test_text_2), NULL); word = next_word (cursor); g_assert_cmpstr (word, ==, "it's"); word = next_word (cursor); g_assert_cmpstr (word, ==, "possible"); word = next_word (cursor); g_assert_cmpstr (word, ==, "we're"); word = next_word (cursor); g_assert_cmpstr (word, ==, "going"); word = next_word (cursor); g_assert_cmpstr (word, ==, "to"); word = next_word (cursor); g_assert_cmpstr (word, ==, "have"); word = next_word (cursor); g_assert_cmpstr (word, ==, "join-words"); word = next_word (cursor); g_assert_cmpstr (word, ==, NULL); _cjh_text_region_free (region); } int main (int argc, char *argv[]) { setlocale (LC_ALL, "C"); bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR); bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); textdomain (GETTEXT_PACKAGE); #ifdef WANT_DISPLAY_TESTS gtk_init (); gtk_source_init (); #endif spelling_init (); g_test_init (&argc, &argv, NULL); g_test_add_func ("/Spelling/Cursor/basic", test_cursor); #ifdef WANT_DISPLAY_TESTS g_test_add_func ("/Spelling/Cursor/basic2", test_cursor2); #endif g_test_add_func ("/Spelling/Cursor/in_word", test_cursor_in_word); g_test_add_func ("/Spelling/Cursor/join_words", test_cursor_join_words); return g_test_run (); } 0707010000007F000081A40000000000000000000000016712C6CB00001E58000000000000000000000000000000000000002A00000000libspelling-0.4.4/testsuite/test-engine.c/* test-engine.c * * Copyright 2024 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <libspelling.h> #include "spelling-engine-private.h" static GString *buffer; static GtkBitset *mispelled; static SpellingDictionary *dictionary; static guint cursor; typedef struct { GString *str; const char *pos; guint offset; gunichar ch; } StringIter; static inline gboolean string_iter_is_end (StringIter *iter) { return iter->pos >= iter->str->str + iter->str->len; } static inline void string_iter_init (StringIter *iter, GString *str, guint offset) { iter->str = str; iter->pos = g_utf8_offset_to_pointer (str->str, offset); iter->offset = offset; iter->ch = g_utf8_get_char (iter->pos); } static inline gboolean string_iter_backward (StringIter *iter) { if (iter->pos <= iter->str->str) return FALSE; iter->pos = g_utf8_prev_char (iter->pos); iter->ch = g_utf8_get_char (iter->pos); iter->offset--; return TRUE; } static inline gboolean string_iter_forward (StringIter *iter) { if (iter->pos >= iter->str->str + iter->str->len) return FALSE; iter->pos = g_utf8_next_char (iter->pos); iter->ch = g_utf8_get_char (iter->pos); iter->offset++; return TRUE; } static char * copy_text (gpointer instance, guint position, guint length) { const char *begin = g_utf8_offset_to_pointer (buffer->str, position); const char *end = g_utf8_offset_to_pointer (begin, length); return g_strndup (begin, end - begin); } static void clear_tag (gpointer instance, guint position, guint length) { gtk_bitset_remove_range (mispelled, position, length); } static void apply_tag (gpointer instance, guint position, guint length) { gtk_bitset_add_range (mispelled, position, length); } static gboolean is_word_char (gunichar ch) { const char *extra_word_chars = spelling_dictionary_get_extra_word_chars (dictionary); for (const char *c = extra_word_chars; c && *c; c = g_utf8_next_char (c)) { if (ch == g_utf8_get_char (c)) return TRUE; } return g_unichar_isalnum (ch) || ch == '_'; } static gboolean backward_word_start (gpointer instance, guint *position) { StringIter iter; StringIter peek; string_iter_init (&iter, buffer, *position); /* Move back one char first */ if (!string_iter_backward (&iter)) return FALSE; /* If we have left a word (into space, etc), walk back until * we get to the end of a word. */ if (!is_word_char (iter.ch)) { while (string_iter_backward (&iter)) { if (!is_word_char (iter.ch)) continue; break; } if (iter.offset == 0 && !is_word_char (iter.ch)) return FALSE; } /* Walk backwards saving our last valid position */ peek = iter; while (string_iter_backward (&peek) && is_word_char (peek.ch)) iter = peek; *position = iter.offset; return TRUE; } static gboolean forward_word_end (gpointer instance, guint *position) { StringIter iter; string_iter_init (&iter, buffer, *position); /* Move forward one char first */ if (!string_iter_forward (&iter)) return FALSE; /* If we are no longer on a word character, then walk forward * until we reach a word character. */ if (!is_word_char (iter.ch)) { while (string_iter_forward (&iter)) { if (!is_word_char (iter.ch)) continue; break; } if (string_iter_is_end (&iter)) return FALSE; } /* Walk forward saving our last valid position */ while (string_iter_forward (&iter)) if (!is_word_char (iter.ch)) break; *position = iter.offset; return TRUE; } static void intersect_spellcheck_region (gpointer instance, GtkBitset *bitset) { } static guint get_cursor (gpointer instance) { return cursor; } static PangoLanguage * get_language (gpointer instance) { return pango_language_get_default (); } static SpellingDictionary * get_dictionary (gpointer instance) { return dictionary; } static gboolean check_enabled (gpointer instance) { return TRUE; } static const SpellingAdapter adapter = { .check_enabled = check_enabled, .get_cursor = get_cursor, .copy_text = copy_text, .clear_tag = clear_tag, .apply_tag = apply_tag, .backward_word_start = backward_word_start, .forward_word_end = forward_word_end, .intersect_spellcheck_region = intersect_spellcheck_region, .get_dictionary = get_dictionary, .get_language = get_language, }; static inline void assert_string (const char *str) { g_assert_cmpstr (buffer->str, ==, str); } static void insert (SpellingEngine *engine, const char *text, guint position, const char *expected) { const char *ptr; guint n_chars; if (position == 0) ptr = buffer->str; else ptr = g_utf8_offset_to_pointer (buffer->str, position); n_chars = g_utf8_strlen (text, -1); spelling_engine_before_insert_text (engine, position, n_chars); g_string_insert (buffer, ptr - buffer->str, text); gtk_bitset_splice (mispelled, position, 0, n_chars); cursor = position + n_chars; spelling_engine_after_insert_text (engine, position, n_chars); if (expected) assert_string (expected); spelling_engine_iteration (engine); } static void delete (SpellingEngine *engine, guint position, guint n_chars, const char *expected) { const char *ptr = g_utf8_offset_to_pointer (buffer->str, position); const char *endptr = g_utf8_offset_to_pointer (ptr, n_chars); cursor = position; spelling_engine_before_delete_range (engine, position, n_chars); gtk_bitset_splice (mispelled, position, n_chars, 0); g_string_erase (buffer, ptr - buffer->str, endptr - ptr); spelling_engine_after_delete_range (engine, position); if (expected) assert_string (expected); spelling_engine_iteration (engine); } static void test_engine_basic (void) { g_autoptr(SpellingEngine) engine = NULL; g_autoptr(GObject) instance = g_object_new (G_TYPE_OBJECT, NULL); SpellingProvider *provider = spelling_provider_get_default (); const char *default_code = spelling_provider_get_default_code (provider); dictionary = spelling_provider_load_dictionary (provider, default_code); buffer = g_string_new (NULL); mispelled = gtk_bitset_new_empty (); engine = spelling_engine_new (&adapter, instance); insert (engine, "2", 0, "2"); insert (engine, "1", 0, "12"); insert (engine, "0", 0, "012"); delete (engine, 2, 1, "01"); delete (engine, 1, 1, "0"); delete (engine, 0, 1, ""); g_string_free (buffer, TRUE); gtk_bitset_unref (mispelled); g_object_unref (dictionary); } int main (int argc, char *argv[]) { g_test_init (&argc, &argv, NULL); g_test_add_func ("/Spelling/Engine/basic", test_engine_basic); return g_test_run (); } 07070100000080000081A40000000000000000000000016712C6CB00002B4D000000000000000000000000000000000000002700000000libspelling-0.4.4/testsuite/test-job.c/* test-job.c * * Copyright 2024 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <locale.h> #include <glib/gi18n.h> #include <libspelling.h> #include "spelling-dictionary-internal.h" #include "spelling-provider-internal.h" #include "spelling-job-private.h" static const char *test_dictionary[] = { "Straße", "a", "calle", "caminar", "die", "entlang", "gehen", "has", "it", "it's", "la", "misspelled", "not", "por", "say'", "text", "this", "word", "गच्छन्तु", "वीथिं", "དུ", "འགྲོ", "ལམ", "སྲང", NULL }; #define TEST_TYPE_DICTIONARY (test_dictionary_get_type()) G_DECLARE_FINAL_TYPE (TestDictionary, test_dictionary, TEST, DICTIONARY, SpellingDictionary) struct _TestDictionary { SpellingDictionary parent_instance; }; G_DEFINE_FINAL_TYPE (TestDictionary, test_dictionary, SPELLING_TYPE_DICTIONARY) static gboolean test_dictionary_contains_word (SpellingDictionary *dictionary, const char *word, gssize word_len) { g_autofree char *copy = g_strndup (word, word_len < 0 ? strlen (word) : word_len); return g_strv_contains ((const char * const *)test_dictionary, copy); } static const char * test_dictionary_get_extra_word_chars (SpellingDictionary *dictionary) { return "'"; } static void test_dictionary_class_init (TestDictionaryClass *klass) { SpellingDictionaryClass *dictionary_class = SPELLING_DICTIONARY_CLASS (klass); dictionary_class->contains_word = test_dictionary_contains_word; dictionary_class->get_extra_word_chars = test_dictionary_get_extra_word_chars; } static void test_dictionary_init (TestDictionary *self) { } #define TEST_TYPE_PROVIDER (test_provider_get_type()) G_DECLARE_FINAL_TYPE (TestProvider, test_provider, TEST, PROVIDER, SpellingProvider) struct _TestProvider { SpellingProvider parent_instance; TestDictionary *dictionary; }; G_DEFINE_FINAL_TYPE (TestProvider, test_provider, SPELLING_TYPE_PROVIDER) static SpellingDictionary * test_provider_load_dictionary (SpellingProvider *provider, const char *language) { TestProvider *self = TEST_PROVIDER (provider); return g_object_ref (SPELLING_DICTIONARY (self->dictionary)); } static const char * test_provider_get_default_code (SpellingProvider *provider) { return "C"; } static void test_provider_finalize (GObject *object) { TestProvider *self = (TestProvider *)object; g_clear_object (&self->dictionary); G_OBJECT_CLASS (test_provider_parent_class)->finalize (object); } static void test_provider_class_init (TestProviderClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); SpellingProviderClass *provider_class = SPELLING_PROVIDER_CLASS (klass); object_class->finalize = test_provider_finalize; provider_class->load_dictionary = test_provider_load_dictionary; provider_class->get_default_code = test_provider_get_default_code; } static void test_provider_init (TestProvider *self) { self->dictionary = g_object_new (TEST_TYPE_DICTIONARY, NULL); } static GMainLoop *main_loop; typedef struct _TestJob { const char *text; guint n_mistakes; const SpellingBoundary *mistakes; } TestJob; static void validate (SpellingMistake *mistakes, guint n_mistakes, TestJob *test) { g_assert_cmpint (n_mistakes, ==, test->n_mistakes); g_assert_true (mistakes != NULL || test->n_mistakes == 0); for (guint i = 0; i < n_mistakes; i++) { g_assert_cmpint (test->mistakes[i].offset, ==, mistakes[i].offset); g_assert_cmpint (test->mistakes[i].length, ==, mistakes[i].length); } } static void job_finished (GObject *object, GAsyncResult *result, gpointer user_data) { g_autofree SpellingMistake *mistakes = NULL; g_autofree SpellingBoundary *fragments = NULL; TestJob *test = user_data; GError *error = NULL; guint n_mistakes; guint n_fragments; g_assert (SPELLING_IS_JOB (object)); spelling_job_run_finish (SPELLING_JOB (object), result, &fragments, &n_fragments, &mistakes, &n_mistakes); #if 0 for (guint i = 0; i < n_mistakes; i++) { g_autofree char *word = g_strndup (&test->text[mistakes[i].byte_offset], mistakes[i].byte_length); g_print ("Misspelled: \"%s\"\n", word); } #endif g_assert_no_error (error); validate (mistakes, n_mistakes, test); g_main_loop_quit (main_loop); } static void test_job_basic (void) { g_autoptr(SpellingProvider) provider = g_object_new (TEST_TYPE_PROVIDER, NULL); const char *default_code = spelling_provider_get_default_code (provider); g_autoptr(SpellingDictionary) dictionary = spelling_provider_load_dictionary (provider, default_code); TestJob tests[] = { { "it's not a misspelled word", 0, NULL }, { "not", 0, NULL }, { "it's", 0, NULL }, { "it's ", 0, NULL }, { "\t\nsay' \t\n", 0, NULL }, { "say'", 0, NULL }, { "die Straße entlang gehen", 0, NULL }, { "वीथिं गच्छन्तु", 0, NULL }, { "སྲང་ལམ་དུ་འགྲོ།", 0, NULL }, { "caminar por la calle", 0, NULL }, { "it'", 1, (const SpellingBoundary[]) { { .offset = 0, .length = 3 }, }, }, { "it' ", 1, (const SpellingBoundary[]) { { .offset = 0, .length = 3 }, }, }, { "this text has a misplled word", 1, (const SpellingBoundary[]) { { .offset = 16, .length = 8 }, }, }, }; for (guint i = 0; i < G_N_ELEMENTS (tests); i++) { g_autoptr(GBytes) bytes = g_bytes_new (tests[i].text, strlen (tests[i].text)); g_autoptr(SpellingJob) job = spelling_job_new (dictionary, pango_language_get_default ()); g_autofree SpellingMistake *mistakes = NULL; g_autofree SpellingBoundary *fragments = NULL; guint n_mistakes; guint n_fragments; spelling_job_add_fragment (job, bytes, 0, g_utf8_strlen (tests[i].text, -1)); /* Test async version */ spelling_job_run (job, job_finished, &tests[i]); g_main_loop_run (main_loop); /* Now test sync version */ spelling_job_run_sync (job, &fragments, &n_fragments, &mistakes, &n_mistakes); validate (mistakes, n_mistakes, &tests[i]); } } static void test_job_discard (void) { g_autoptr(SpellingProvider) provider = g_object_new (TEST_TYPE_PROVIDER, NULL); const char *default_code = spelling_provider_get_default_code (provider); g_autoptr(SpellingDictionary) dictionary = spelling_provider_load_dictionary (provider, default_code); g_autoptr(GBytes) bytes = g_bytes_new ("misplled word", 13); g_autoptr(SpellingJob) job = NULL; g_autofree SpellingMistake *mistakes = NULL; guint n_mistakes = 0; /* First make sure things work */ job = spelling_job_new (dictionary, pango_language_get_default ()); spelling_job_add_fragment (job, bytes, 1, 13); spelling_job_run_sync (job, NULL, NULL, &mistakes, &n_mistakes); g_assert_cmpint (n_mistakes, ==, 1); g_assert_cmpint (mistakes[0].offset, ==, 1); g_assert_cmpint (mistakes[0].length, ==, 8); g_clear_pointer (&mistakes, g_free); g_clear_object (&job); /* Now try to do an INSERT that SHOULD NOT collide */ job = spelling_job_new (dictionary, pango_language_get_default ()); spelling_job_add_fragment (job, bytes, 1, 13); spelling_job_notify_insert (job, 0, 3); spelling_job_run_sync (job, NULL, NULL, &mistakes, &n_mistakes); g_assert_cmpint (n_mistakes, ==, 1); g_assert_cmpint (mistakes[0].offset, ==, 1 + 3); g_assert_cmpint (mistakes[0].length, ==, 8); g_clear_pointer (&mistakes, g_free); g_clear_object (&job); /* Now try to do an INSERT that SHOULD collide */ job = spelling_job_new (dictionary, pango_language_get_default ()); spelling_job_add_fragment (job, bytes, 1, 13); spelling_job_notify_insert (job, 1, 3); spelling_job_run_sync (job, NULL, NULL, &mistakes, &n_mistakes); g_assert_cmpint (n_mistakes, ==, 0); g_assert_null (mistakes); g_clear_pointer (&mistakes, g_free); g_clear_object (&job); /* Now try to do a DELETE that SHOULD NOT collide */ job = spelling_job_new (dictionary, pango_language_get_default ()); spelling_job_add_fragment (job, bytes, 2, 13); spelling_job_notify_delete (job, 0, 1); spelling_job_run_sync (job, NULL, NULL, &mistakes, &n_mistakes); g_assert_cmpint (n_mistakes, ==, 1); g_assert_cmpint (mistakes[0].offset, ==, 1); g_assert_cmpint (mistakes[0].length, ==, 8); g_clear_pointer (&mistakes, g_free); g_clear_object (&job); /* Now try to do a DELETE that SHOULD collide */ job = spelling_job_new (dictionary, pango_language_get_default ()); spelling_job_add_fragment (job, bytes, 1, 13); spelling_job_notify_delete (job, 0, 1); spelling_job_run_sync (job, NULL, NULL, &mistakes, &n_mistakes); g_assert_cmpint (n_mistakes, ==, 0); g_assert_null (mistakes); g_clear_pointer (&mistakes, g_free); g_clear_object (&job); /* Now try to do a DELETE that SHOULD collide */ job = spelling_job_new (dictionary, pango_language_get_default ()); spelling_job_add_fragment (job, bytes, 0, 13); spelling_job_notify_delete (job, 13, 1); spelling_job_run_sync (job, NULL, NULL, &mistakes, &n_mistakes); g_assert_cmpint (n_mistakes, ==, 0); g_assert_null (mistakes); g_clear_pointer (&mistakes, g_free); g_clear_object (&job); /* Now try to do a DELETE that SHOULD NOT collide */ job = spelling_job_new (dictionary, pango_language_get_default ()); spelling_job_add_fragment (job, bytes, 0, 13); spelling_job_notify_delete (job, 14, 1); spelling_job_run_sync (job, NULL, NULL, &mistakes, &n_mistakes); g_assert_cmpint (n_mistakes, ==, 1); g_assert_cmpint (mistakes[0].offset, ==, 0); g_assert_cmpint (mistakes[0].length, ==, 8); g_clear_pointer (&mistakes, g_free); g_clear_object (&job); } int main (int argc, char *argv[]) { main_loop = g_main_loop_new (NULL, FALSE); setlocale (LC_ALL, ""); bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); textdomain (GETTEXT_PACKAGE); g_test_init (&argc, &argv, NULL); g_test_add_func ("/Spelling/Job/basic", test_job_basic); g_test_add_func ("/Spelling/Job/discard", test_job_discard); return g_test_run (); } 07070100000081000081A40000000000000000000000016712C6CB00004C13000000000000000000000000000000000000002A00000000libspelling-0.4.4/testsuite/test-region.c/* test-region.c * * Copyright 2024 Christian Hergert <chergert@redhat.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include "cjhtextregionprivate.h" #include "cjhtextregionbtree.h" static void get_run_at_offset (void) { CjhTextRegion *region = _cjh_text_region_new (NULL, NULL); const CjhTextRegionRun *run; gsize offset; _cjh_text_region_insert (region, 0, 10, GUINT_TO_POINTER (1)); _cjh_text_region_insert (region, 10, 10, GUINT_TO_POINTER (2)); _cjh_text_region_replace (region, 15, 1, GUINT_TO_POINTER (3)); run = _cjh_text_region_get_run_at_offset (region, 15, &offset); g_assert_true (run != NULL); g_assert_cmpint (offset, ==, 15); g_assert_cmpint (run->length, ==, 1); g_assert_true (run->data == GUINT_TO_POINTER (3)); _cjh_text_region_free (region); } static void assert_leaves_empty (CjhTextRegion *region) { CjhTextRegionNode *leaf = _cjh_text_region_get_first_leaf (region); guint count = 0; for (; leaf; leaf = leaf->leaf.next, count++) { CjhTextRegionNode *parent = cjh_text_region_node_get_parent (leaf); guint length = cjh_text_region_node_length (leaf); guint length_in_parent = 0; SORTED_ARRAY_FOREACH (&parent->branch.children, CjhTextRegionChild, child, { if (child->node == leaf) { length_in_parent = child->length; break; } }); if (length || length_in_parent) g_error ("leaf %p %u has length of %u in %u runs. Parent thinks it has length of %u.", leaf, count, length, SORTED_ARRAY_LENGTH (&leaf->leaf.runs), length_in_parent); } } static guint count_leaves (CjhTextRegion *region) { CjhTextRegionNode *leaf = _cjh_text_region_get_first_leaf (region); guint count = 0; for (; leaf; leaf = leaf->leaf.next) count++; return count; } static guint count_internal_recuse (CjhTextRegionNode *node) { guint count = 1; g_assert (!cjh_text_region_node_is_leaf (node)); SORTED_ARRAY_FOREACH (&node->branch.children, CjhTextRegionChild, child, { g_assert (child->node != NULL); if (!cjh_text_region_node_is_leaf (child->node)) count += count_internal_recuse (child->node); }); return count; } static guint count_internal (CjhTextRegion *region) { return count_internal_recuse (®ion->root); } G_GNUC_UNUSED static inline void print_tree (CjhTextRegionNode *node, guint depth) { for (guint i = 0; i < depth; i++) g_print (" "); g_print ("%p %s Length=%"G_GSIZE_MODIFIER"u Items=%u Prev<%p> Next<%p>\n", node, cjh_text_region_node_is_leaf (node) ? "Leaf" : "Branch", cjh_text_region_node_length (node), cjh_text_region_node_is_leaf (node) ? SORTED_ARRAY_LENGTH (&node->leaf.runs) : SORTED_ARRAY_LENGTH (&node->branch.children), cjh_text_region_node_is_leaf (node) ? node->leaf.prev : node->branch.prev, cjh_text_region_node_is_leaf (node) ? node->leaf.next : node->branch.next); if (!cjh_text_region_node_is_leaf (node)) { SORTED_ARRAY_FOREACH (&node->branch.children, CjhTextRegionChild, child, { print_tree (child->node, depth+1); }); } } static void assert_empty (CjhTextRegion *region) { #if 0 print_tree (®ion->root, 0); #endif g_assert_cmpint (_cjh_text_region_get_length (region), ==, 0); assert_leaves_empty (region); g_assert_cmpint (1, ==, count_internal (region)); g_assert_cmpint (1, ==, count_leaves (region)); } static gboolean non_overlapping_insert_remove_cb (gsize offset, const CjhTextRegionRun *run, gpointer user_data) { g_assert_cmpint (offset, ==, GPOINTER_TO_UINT (run->data)); return FALSE; } static void non_overlapping_insert_remove (void) { CjhTextRegion *region = _cjh_text_region_new (NULL, NULL); assert_empty (region); for (guint i = 0; i < 100000; i++) { _cjh_text_region_insert (region, i, 1, GUINT_TO_POINTER (i)); g_assert_cmpint (_cjh_text_region_get_length (region), ==, i + 1); } g_assert_cmpint (_cjh_text_region_get_length (region), ==, 100000); _cjh_text_region_foreach (region, non_overlapping_insert_remove_cb, NULL); for (guint i = 0; i < 100000; i++) _cjh_text_region_remove (region, 100000-1-i, 1); g_assert_cmpint (_cjh_text_region_get_length (region), ==, 0); assert_empty (region); _cjh_text_region_free (region); } typedef struct { gsize offset; gsize length; gpointer data; } SplitRunCheck; typedef struct { gsize index; gsize count; const SplitRunCheck *checks; } SplitRun; static gboolean split_run_cb (gsize offset, const CjhTextRegionRun *run, gpointer user_data) { SplitRun *state = user_data; g_assert_cmpint (offset, ==, state->checks[state->index].offset); g_assert_cmpint (run->length, ==, state->checks[state->index].length); g_assert_true (run->data == state->checks[state->index].data); state->index++; return FALSE; } static void split_run (void) { static const SplitRunCheck checks[] = { { 0, 1, NULL }, { 1, 1, GSIZE_TO_POINTER (1) }, { 2, 1, NULL }, }; SplitRun state = { 0, 3, checks }; CjhTextRegion *region = _cjh_text_region_new (NULL, NULL); _cjh_text_region_insert (region, 0, 2, NULL); g_assert_cmpint (_cjh_text_region_get_length (region), ==, 2); _cjh_text_region_insert (region, 1, 1, GSIZE_TO_POINTER (1)); g_assert_cmpint (_cjh_text_region_get_length (region), ==, 3); _cjh_text_region_foreach (region, split_run_cb, &state); _cjh_text_region_free (region); } static gboolean can_join_cb (gsize offset, const CjhTextRegionRun *left, const CjhTextRegionRun *right) { return left->data == right->data; } static void no_split_run (void) { static const SplitRunCheck checks[] = { { 0, 3, NULL }, }; SplitRun state = { 0, 1, checks }; CjhTextRegion *region = _cjh_text_region_new (can_join_cb, NULL); _cjh_text_region_insert (region, 0, 2, NULL); g_assert_cmpint (_cjh_text_region_get_length (region), ==, 2); _cjh_text_region_insert (region, 1, 1, NULL); g_assert_cmpint (_cjh_text_region_get_length (region), ==, 3); _cjh_text_region_foreach (region, split_run_cb, &state); _cjh_text_region_free (region); } static void random_insertion (void) { CjhTextRegion *region = _cjh_text_region_new (NULL, NULL); gsize expected = 0; for (guint i = 0; i < 10000; i++) { guint pos = g_random_int_range (0, region->length + 1); guint len = g_random_int_range (1, 20); _cjh_text_region_insert (region, pos, len, GUINT_TO_POINTER (i)); expected += len; } g_assert_cmpint (expected, ==, region->length); _cjh_text_region_replace (region, 0, region->length, NULL); g_assert_cmpint (count_leaves (region), ==, 1); g_assert_cmpint (count_internal (region), ==, 1); _cjh_text_region_free (region); } static void random_deletion (void) { CjhTextRegion *region = _cjh_text_region_new (NULL, NULL); _cjh_text_region_insert (region, 0, 10000, NULL); while (region->length > 0) { guint pos = region->length > 1 ? g_random_int_range (0, region->length-1) : 0; guint len = region->length - pos > 1 ? g_random_int_range (1, region->length - pos) : 1; _cjh_text_region_remove (region, pos, len); } _cjh_text_region_free (region); } static void random_insert_deletion (void) { CjhTextRegion *region = _cjh_text_region_new (NULL, NULL); guint expected = 0; guint i = 0; while (region->length < 10000) { guint pos = g_random_int_range (0, region->length + 1); guint len = g_random_int_range (1, 20); _cjh_text_region_insert (region, pos, len, GUINT_TO_POINTER (i)); expected += len; i++; } g_assert_cmpint (expected, ==, region->length); while (region->length > 0) { guint pos = region->length > 1 ? g_random_int_range (0, region->length-1) : 0; guint len = region->length - pos > 1 ? g_random_int_range (1, region->length - pos) : 1; g_assert (pos + len <= region->length); _cjh_text_region_remove (region, pos, len); } _cjh_text_region_free (region); } static void test_val_queue (void) { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wtype-limits" VAL_QUEUE_NODE(guint8, 32) field; guint8 pos; VAL_QUEUE_INIT (&field); for (guint i = 0; i < 32; i++) VAL_QUEUE_PUSH_TAIL (&field, i); g_assert_cmpint (VAL_QUEUE_LENGTH (&field), ==, 32); for (guint i = 0; i < 32; i++) { VAL_QUEUE_NTH (&field, i, pos); g_assert_cmpint (pos, ==, i); } for (guint i = 0; i < 32; i++) { VAL_QUEUE_POP_HEAD (&field, pos); g_assert_cmpint (pos, ==, i); } g_assert_cmpint (VAL_QUEUE_LENGTH (&field), ==, 0); for (guint i = 0; i < 32; i++) VAL_QUEUE_PUSH_TAIL (&field, i); g_assert_cmpint (VAL_QUEUE_LENGTH (&field), ==, 32); for (guint i = 0; i < 32; i++) { VAL_QUEUE_POP_TAIL (&field, pos); g_assert_cmpint (pos, ==, 31-i); } g_assert_cmpint (VAL_QUEUE_LENGTH (&field), ==, 0); for (guint i = 0; i < 32; i++) VAL_QUEUE_PUSH_TAIL (&field, i); while (VAL_QUEUE_LENGTH (&field)) VAL_QUEUE_POP_NTH (&field, VAL_QUEUE_LENGTH (&field)/2, pos); #pragma GCC diagnostic pop } typedef struct { int v; } Dummy; static void sorted_array (void) { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wtype-limits" SORTED_ARRAY_FIELD (Dummy, 32) field; Dummy d; guint i; SORTED_ARRAY_INIT (&field); d.v = 0; SORTED_ARRAY_INSERT_VAL (&field, 0, d); d.v = 2; SORTED_ARRAY_INSERT_VAL (&field, 1, d); d.v = 1; SORTED_ARRAY_INSERT_VAL (&field, 1, d); i = 0; g_assert_cmpint (SORTED_ARRAY_LENGTH (&field), ==, 3); SORTED_ARRAY_FOREACH (&field, Dummy, dummy, { g_assert_cmpint (dummy->v, ==, i++); }); g_assert_cmpint (i, ==, 3); SORTED_ARRAY_POP_HEAD (&field, d); g_assert_cmpint (d.v, ==, 0); SORTED_ARRAY_POP_HEAD (&field, d); g_assert_cmpint (d.v, ==, 1); SORTED_ARRAY_POP_HEAD (&field, d); g_assert_cmpint (d.v, ==, 2); for (i = 0; i < 10; i++) { d.v = i * 2; SORTED_ARRAY_INSERT_VAL (&field, i, d); } for (i = 0; i < 10; i++) { d.v = i * 2 + 1; SORTED_ARRAY_INSERT_VAL (&field, i*2+1, d); } i = 0; g_assert_cmpint (SORTED_ARRAY_LENGTH (&field), ==, 20); SORTED_ARRAY_FOREACH (&field, Dummy, dummy, { g_assert_cmpint (dummy->v, ==, i++); }); g_assert_cmpint (i, ==, 20); SORTED_ARRAY_FOREACH (&field, Dummy, dummy, { (void)dummy; SORTED_ARRAY_FOREACH_REMOVE (&field); }); g_assert_cmpint (SORTED_ARRAY_LENGTH (&field), ==, 0); for (i = 0; i < 32; i++) { d.v = i; SORTED_ARRAY_PUSH_TAIL (&field, d); } g_assert_cmpint (32, ==, SORTED_ARRAY_LENGTH (&field)); i = 0; SORTED_ARRAY_FOREACH (&field, Dummy, dummy, { g_assert_cmpint (dummy->v, ==, i); g_assert_cmpint (SORTED_ARRAY_LENGTH (&field), ==, 32-i); SORTED_ARRAY_FOREACH_REMOVE (&field); i++; }); g_assert_cmpint (0, ==, SORTED_ARRAY_LENGTH (&field)); for (i = 0; i < 32; i++) { d.v = i; SORTED_ARRAY_PUSH_TAIL (&field, d); } g_assert_cmpint (32, ==, SORTED_ARRAY_LENGTH (&field)); i = 31; SORTED_ARRAY_FOREACH_REVERSE (&field, Dummy, dummy, { g_assert_cmpint (dummy->v, ==, i); SORTED_ARRAY_REMOVE_INDEX (&field, i, d); i--; }); #pragma GCC diagnostic pop } static gboolean replace_part_of_long_run_join (gsize offset, const CjhTextRegionRun *left, const CjhTextRegionRun *right) { return FALSE; } static void replace_part_of_long_run_split (gsize offset, const CjhTextRegionRun *run, CjhTextRegionRun *left, CjhTextRegionRun *right) { left->data = run->data; right->data = GSIZE_TO_POINTER (GPOINTER_TO_SIZE (run->data) + left->length); } static void replace_part_of_long_run (void) { CjhTextRegion *region = _cjh_text_region_new (replace_part_of_long_run_join, replace_part_of_long_run_split); static const SplitRunCheck checks0[] = { { 0, 5, NULL }, }; static const SplitRunCheck checks1[] = { { 0, 1, NULL }, { 1, 3, GSIZE_TO_POINTER (2) }, }; static const SplitRunCheck checks2[] = { { 0, 1, GSIZE_TO_POINTER (0) }, { 1, 1, GSIZE_TO_POINTER ((1L<<31)|1) }, { 2, 3, GSIZE_TO_POINTER (2) }, }; static const SplitRunCheck checks3[] = { { 0, 1, GSIZE_TO_POINTER (0) }, { 1, 1, GSIZE_TO_POINTER ((1L<<31)|1) }, { 2, 1, GSIZE_TO_POINTER (2) }, { 3, 1, GSIZE_TO_POINTER (4) }, }; static const SplitRunCheck checks4[] = { { 0, 1, GSIZE_TO_POINTER (0) }, { 1, 1, GSIZE_TO_POINTER ((1L<<31)|1) }, { 2, 1, GSIZE_TO_POINTER (2) }, { 3, 1, GSIZE_TO_POINTER ((1L<<31)|2) }, { 4, 1, GSIZE_TO_POINTER (4) }, }; SplitRun state0 = { 0, 1, checks0 }; SplitRun state1 = { 0, 2, checks1 }; SplitRun state2 = { 0, 3, checks2 }; SplitRun state3 = { 0, 4, checks3 }; SplitRun state4 = { 0, 5, checks4 }; _cjh_text_region_insert (region, 0, 5, NULL); _cjh_text_region_foreach (region, split_run_cb, &state0); _cjh_text_region_remove (region, 1, 1); _cjh_text_region_foreach (region, split_run_cb, &state1); _cjh_text_region_insert (region, 1, 1, GSIZE_TO_POINTER ((1L<<31)|1)); _cjh_text_region_foreach (region, split_run_cb, &state2); _cjh_text_region_remove (region, 3, 1); _cjh_text_region_foreach (region, split_run_cb, &state3); _cjh_text_region_insert (region, 3, 1, GSIZE_TO_POINTER ((1L<<31)|2)); _cjh_text_region_foreach (region, split_run_cb, &state4); _cjh_text_region_free (region); } typedef struct { char *original; char *changes; GString *res; } wordstate; static gboolean word_foreach_cb (gsize offset, const CjhTextRegionRun *run, gpointer data) { wordstate *state = data; gsize sdata = GPOINTER_TO_SIZE (run->data); gsize soff = sdata & ~(1L<<31); char *src; if (sdata == soff) src = state->original; else src = state->changes; #if 0 g_print ("%lu len %lu (%s at %lu) %s\n", offset, run->length, sdata == soff ? "original" : "changes", soff, sdata == soff && src[sdata] == '\n' ? "is-newline" : ""); #endif g_string_append_len (state->res, src + soff, run->length); return FALSE; } static gboolean join_word_cb (gsize offset, const CjhTextRegionRun *left, const CjhTextRegionRun *right) { return FALSE; } static void split_word_cb (gsize offset, const CjhTextRegionRun *run, CjhTextRegionRun *left, CjhTextRegionRun *right) { gsize sdata = GPOINTER_TO_SIZE (run->data); left->data = run->data; right->data = GSIZE_TO_POINTER (sdata + left->length); } static void test_words_database (void) { CjhTextRegion *region = _cjh_text_region_new (join_word_cb, split_word_cb); g_autofree char *contents = NULL; g_autoptr(GString) str = g_string_new (NULL); g_autoptr(GString) res = g_string_new (NULL); const char *word; const char *iter; gsize len; wordstate state; if (!g_file_get_contents ("/usr/share/dict/words", &contents, &len, NULL)) { g_test_skip ("Words database not available"); return; } /* 0 offset of base buffer */ _cjh_text_region_insert (region, 0, len, NULL); /* For each each word, remove it and replace it with a word added to str. * At the end we'll create the buffer and make sure we get the same. */ word = contents; iter = contents; for (;;) { if (*iter == 0) break; if (g_unichar_isspace (g_utf8_get_char (iter))) { gsize pos = str->len; g_string_append_len (str, word, iter - word); _cjh_text_region_replace (region, word - contents, iter - word, GSIZE_TO_POINTER ((1L<<31)|pos)); while (*iter && g_unichar_isspace (g_utf8_get_char (iter))) iter = g_utf8_next_char (iter); word = iter; } else iter = g_utf8_next_char (iter); } state.original = contents; state.changes = str->str; state.res = res; _cjh_text_region_foreach (region, word_foreach_cb, &state); g_assert_true (g_str_equal (contents, res->str)); _cjh_text_region_free (region); } static gboolean foreach_cb (gsize offset, const CjhTextRegionRun *run, gpointer user_data) { guint *count = user_data; g_assert_cmpint (GPOINTER_TO_SIZE (run->data), ==, offset); (*count)++; return FALSE; } static void foreach_in_range (void) { CjhTextRegion *region = _cjh_text_region_new (NULL, NULL); guint count; for (guint i = 0; i < 100000; i++) { _cjh_text_region_insert (region, i, 1, GUINT_TO_POINTER (i)); g_assert_cmpint (_cjh_text_region_get_length (region), ==, i + 1); } count = 0; _cjh_text_region_foreach_in_range (region, 0, 100000, foreach_cb, &count); g_assert_cmpint (count, ==, 100000); count = 0; _cjh_text_region_foreach_in_range (region, 1000, 5000, foreach_cb, &count); g_assert_cmpint (count, ==, 4000); _cjh_text_region_replace (region, 0, 10000, NULL); count = 0; _cjh_text_region_foreach_in_range (region, 1000, 5000, foreach_cb, &count); g_assert_cmpint (count, ==, 1); _cjh_text_region_free (region); } int main (int argc, char *argv[]) { g_test_init (&argc, &argv, NULL); g_test_add_func ("/Cjh/TextRegion/val_queue", test_val_queue); g_test_add_func ("/Cjh/TextRegion/sorted_array", sorted_array); g_test_add_func ("/Cjh/TextRegion/non_overlapping_insert_remove", non_overlapping_insert_remove); g_test_add_func ("/Cjh/TextRegion/foreach_in_range", foreach_in_range); g_test_add_func ("/Cjh/TextRegion/split_run", split_run); g_test_add_func ("/Cjh/TextRegion/no_split_run", no_split_run); g_test_add_func ("/Cjh/TextRegion/random_insertion", random_insertion); g_test_add_func ("/Cjh/TextRegion/random_deletion", random_deletion); g_test_add_func ("/Cjh/TextRegion/random_insert_deletion", random_insert_deletion); g_test_add_func ("/Cjh/TextRegion/replace_part_of_long_run", replace_part_of_long_run); g_test_add_func ("/Cjh/TextRegion/words_database", test_words_database); g_test_add_func ("/Cjh/TextRegion/get_run_at_offset", get_run_at_offset); return g_test_run (); } 07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!939 blocks
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor