Compare commits
73
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c3de1334a | ||
|
|
41f3501e1f | ||
|
|
12b5fe7304 | ||
|
|
d8e0185d1c | ||
|
|
fd1f5b8d64 | ||
|
|
69e766dc28 | ||
|
|
5911f37449 | ||
|
|
6e46279da4 | ||
|
|
d90415bffc | ||
|
|
62a866d9bb | ||
|
|
dc2997ec16 | ||
|
|
70877a7ee1 | ||
|
|
cb73f6b3b0 | ||
|
|
e6eaa1d83c | ||
|
|
62731ae766 | ||
|
|
b5cec0d049 | ||
|
|
c821f30880 | ||
|
|
13908d7ba7 | ||
|
|
ec69d4db73 | ||
|
|
511138316a | ||
|
|
d6e0823e21 | ||
|
|
54e7b58c24 | ||
|
|
77a3b71d31 | ||
|
|
21dd86ad3d | ||
|
|
fea303c898 | ||
|
|
e8653cd116 | ||
|
|
5e28fb1e5c | ||
|
|
609eaa8246 | ||
|
|
71bab687d7 | ||
|
|
060f68237b | ||
|
|
ee5f66fbbb | ||
|
|
7ee484f0c0 | ||
|
|
99dc917de3 | ||
|
|
a52d5243ab | ||
|
|
72fcccc8a9 | ||
|
|
0627ca5fd5 | ||
|
|
6225915fda | ||
|
|
e9c0565c04 | ||
|
|
bc3fe1eee1 | ||
|
|
b7a1cb9049 | ||
|
|
91df1df63a | ||
|
|
1a195f75b9 | ||
|
|
6946dde0ad | ||
|
|
608699c431 | ||
|
|
c233fdbac3 | ||
|
|
bd91eea411 | ||
|
|
13f2750a7e | ||
|
|
88a6451fa8 | ||
|
|
3ab71d6441 | ||
|
|
aaadba3812 | ||
|
|
3e920291a0 | ||
|
|
61b8bbc5fa | ||
|
|
2819598ae2 | ||
|
|
73d9ae0517 | ||
|
|
f5c506e210 | ||
|
|
bcd87801e6 | ||
|
|
80a4620724 | ||
|
|
5df23abb10 | ||
|
|
b54e0589d2 | ||
|
|
7a369886de | ||
|
|
dac0fa3fb1 | ||
|
|
d55dd4e5da | ||
|
|
813706265a | ||
|
|
228837bdb5 | ||
|
|
63454aef9d | ||
|
|
b6c4b94bbe | ||
|
|
a4be7a2117 | ||
|
|
1ae33eeeba | ||
|
|
0e3f3513e6 | ||
|
|
424bb34e06 | ||
|
|
7196dc5ec8 | ||
|
|
6c9136bdb2 | ||
|
|
b79220c1ae |
+131
@@ -0,0 +1,131 @@
|
||||
database.db
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
pip-wheel-metadata/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
@@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
@@ -1,12 +1,13 @@
|
||||
# Newton Editor
|
||||
# Newton
|
||||
A Python + Gtk 3 based quasi-IDE.
|
||||
|
||||
### Note
|
||||
This repository is comprised of two branches with the first using gtk and GtkSource.View widgets to handle text editing.
|
||||
The second bramch uses Angular + Electron with Ace Editor component to handle text editing.
|
||||
Both branches have similar design patterns to Vim with 'buffers' loading to views than the traditionally understood one view per one one tab pattern.
|
||||
[TODO](TODO.md)
|
||||
|
||||
* [release/newton_gtk](https://code.itdominator.com/itdominator/Newton-Editor/src/branch/release/newton_gtk/)
|
||||
* [release/newton_electron](https://code.itdominator.com/itdominator/Newton-Editor/src/branch/release/newton_electron/)
|
||||
|
||||
# LSP
|
||||
* [Newton-LSP](https://code.itdominator.com/itdominator/Newton-LSP)
|
||||
### Images
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
___
|
||||
### Add
|
||||
1. Add <Ctrl\>i to **lsp_manager** to list who implements xyz
|
||||
|
||||
___
|
||||
### Change
|
||||
1. Make **telescope** plugin a generic base to allow query mode additions through plugins
|
||||
|
||||
___
|
||||
### Fix
|
||||
- Fix LSP WS Server to Godot LSP Server communication
|
||||
- Fix <Ctrl\>z in multi-insert mode being funky. Insure updates happen on block level.
|
||||
I.E, maybe push updates to queue to insure block undo/redo?
|
||||
|
||||
___
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.0 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 951 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 901 KiB |
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Plugin Module
|
||||
"""
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Plugin Package
|
||||
"""
|
||||
@@ -0,0 +1,97 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
class Autopairs:
|
||||
def __init__(self):
|
||||
...
|
||||
|
||||
def handle_word_wrap(self, buffer, char_str: str):
|
||||
wrap_block = self.get_wrap_block(char_str)
|
||||
if not wrap_block: return
|
||||
|
||||
selection = buffer.get_selection_bounds()
|
||||
if not selection:
|
||||
self.insert_pair(buffer, char_str, wrap_block)
|
||||
return True
|
||||
|
||||
self.wrap_selection(buffer, char_str, wrap_block, selection)
|
||||
|
||||
return True
|
||||
|
||||
def insert_pair(
|
||||
self, buffer, char_str: str, wrap_block: tuple
|
||||
):
|
||||
buffer.begin_user_action()
|
||||
|
||||
left_block, right_block = wrap_block
|
||||
insert_mark = buffer.get_insert()
|
||||
|
||||
insert_itr = buffer.get_iter_at_mark(insert_mark)
|
||||
buffer.insert(insert_itr, f"{left_block}{right_block}")
|
||||
insert_itr = buffer.get_iter_at_mark( insert_mark )
|
||||
insert_itr.backward_char()
|
||||
|
||||
buffer.place_cursor(insert_itr)
|
||||
|
||||
buffer.end_user_action()
|
||||
|
||||
def wrap_selection(
|
||||
self, buffer, char_str: str, wrap_block: tuple, selection
|
||||
):
|
||||
left_block, \
|
||||
right_block = wrap_block
|
||||
start_itr, \
|
||||
end_itr = selection
|
||||
data = buffer.get_text(
|
||||
start_itr, end_itr, include_hidden_chars = False
|
||||
)
|
||||
start_mark = buffer.create_mark("startclose", start_itr, False)
|
||||
end_mark = buffer.create_mark("endclose", end_itr, True)
|
||||
|
||||
buffer.begin_user_action()
|
||||
|
||||
buffer.insert(start_itr, left_block)
|
||||
end_itr = buffer.get_iter_at_mark(end_mark)
|
||||
buffer.insert(end_itr, right_block)
|
||||
|
||||
start = buffer.get_iter_at_mark(start_mark)
|
||||
end = buffer.get_iter_at_mark(end_mark)
|
||||
|
||||
buffer.select_range(start, end)
|
||||
buffer.delete_mark_by_name("startclose")
|
||||
buffer.delete_mark_by_name("endclose")
|
||||
|
||||
buffer.end_user_action()
|
||||
|
||||
def get_wrap_block(self, char_str) -> tuple:
|
||||
left_block = ""
|
||||
right_block = ""
|
||||
|
||||
match char_str:
|
||||
case "(" | ")":
|
||||
left_block = "("
|
||||
right_block = ")"
|
||||
case "[" | "]":
|
||||
left_block = "["
|
||||
right_block = "]"
|
||||
case "{" | "}":
|
||||
left_block = "{"
|
||||
right_block = "}"
|
||||
case '"':
|
||||
left_block = '"'
|
||||
right_block = '"'
|
||||
case "'":
|
||||
left_block = "'"
|
||||
right_block = "'"
|
||||
case "`":
|
||||
left_block = "`"
|
||||
right_block = "`"
|
||||
case _:
|
||||
return ()
|
||||
|
||||
return left_block, right_block
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "Autopairs",
|
||||
"author": "ITDominator",
|
||||
"version": "0.0.1",
|
||||
"support": "",
|
||||
"requests": {}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
from libs.event_factory import Event_Factory, Code_Event_Types
|
||||
|
||||
from plugins.plugin_types import PluginCode
|
||||
|
||||
from .autopairs import Autopairs
|
||||
|
||||
|
||||
|
||||
autopairs = Autopairs()
|
||||
|
||||
|
||||
|
||||
class Plugin(PluginCode):
|
||||
def __init__(self):
|
||||
super(Plugin, self).__init__()
|
||||
|
||||
def _controller_message(self, event: Code_Event_Types.CodeEvent):
|
||||
...
|
||||
|
||||
def load(self):
|
||||
event = Event_Factory.create_event("register_command",
|
||||
command_name = "autopairs",
|
||||
command = Handler,
|
||||
binding_mode = "held",
|
||||
binding = [
|
||||
"'", "`", "[", "]",
|
||||
'<Shift>"',
|
||||
'<Shift>(',
|
||||
'<Shift>)',
|
||||
'<Shift>{',
|
||||
'<Shift>}'
|
||||
]
|
||||
)
|
||||
|
||||
self.emit_to("source_views", event)
|
||||
|
||||
def unload(self):
|
||||
event = Event_Factory.create_event("unregister_command",
|
||||
command_name = "autopairs",
|
||||
command = Handler,
|
||||
binding_mode = "held",
|
||||
binding = [
|
||||
"'", "`", "[", "]",
|
||||
'<Shift>"',
|
||||
'<Shift>(',
|
||||
'<Shift>)',
|
||||
'<Shift>{',
|
||||
'<Shift>}'
|
||||
]
|
||||
)
|
||||
|
||||
self.emit_to("source_views", event)
|
||||
autopairs = None
|
||||
|
||||
def run(self):
|
||||
...
|
||||
|
||||
|
||||
class Handler:
|
||||
@staticmethod
|
||||
def execute(
|
||||
view: any,
|
||||
char_str: str,
|
||||
*args,
|
||||
**kwargs
|
||||
):
|
||||
logger.debug("Command: Autopairs")
|
||||
autopairs.handle_word_wrap(view.get_buffer(), char_str)
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Pligin Module
|
||||
"""
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Pligin Package
|
||||
"""
|
||||
@@ -0,0 +1,111 @@
|
||||
# Python imports
|
||||
import colorsys
|
||||
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
class ColorConverterMixinException(Exception):
|
||||
...
|
||||
|
||||
|
||||
|
||||
class ColorConverterMixin:
|
||||
# NOTE: HSV HSL, and Hex Alpha parsing are available in Gtk 4.0- not lower.
|
||||
# So, for compatability we're gunna convert to rgba string ourselves...
|
||||
def get_color_text(self, buffer, start_itr, end_itr):
|
||||
text = buffer.get_text(start_itr, end_itr, include_hidden_chars = False)
|
||||
|
||||
try:
|
||||
if "hsl" in text:
|
||||
text = self.hsl_to_rgb(text)
|
||||
|
||||
if "hsv" in text:
|
||||
text = self.hsv_to_rgb(text)
|
||||
|
||||
if "#" == text[0]:
|
||||
hex = text[1:]
|
||||
size = len(hex)
|
||||
if size in [4, 8, 16]:
|
||||
rgba = self.hex_to_rgba(hex, size)
|
||||
logger.debug(f"Colorize Plugin: RGBA = {rgba}")
|
||||
|
||||
except ColorConverterMixinException as e:
|
||||
...
|
||||
|
||||
return text
|
||||
|
||||
def hex_to_rgba(self, hex: str, size: int) -> str:
|
||||
rgba = []
|
||||
slots = None
|
||||
step = 2
|
||||
bytes = 16
|
||||
|
||||
if size == 4: # NOTE: RGBA
|
||||
step = 1
|
||||
slots = (0, 1, 2, 3)
|
||||
|
||||
if size == 6: # NOTE: RR GG BB
|
||||
slots = (0, 2, 4)
|
||||
|
||||
if size == 8: # NOTE: RR GG BB AA
|
||||
step = 2
|
||||
slots = (0, 2, 4, 6)
|
||||
|
||||
if size == 16: # NOTE: RRRR GGGG BBBB AAAA
|
||||
step = 4
|
||||
slots = (0, 4, 8, 12)
|
||||
|
||||
for i in slots:
|
||||
v = int(hex[i : i + step], bytes)
|
||||
rgba.append(v)
|
||||
|
||||
|
||||
rgb_sub = ','.join(map(str, tuple(rgba)))
|
||||
|
||||
return f"rgba({rgb_sub})"
|
||||
# return tuple(rgba)
|
||||
|
||||
|
||||
def hsl_to_rgb(self, text: str) -> str:
|
||||
_h, _s , _l = text.replace("hsl", "") \
|
||||
.replace("deg", "") \
|
||||
.replace("(", "") \
|
||||
.replace(")", "") \
|
||||
.replace("%", "") \
|
||||
.replace(" ", "") \
|
||||
.split(",")
|
||||
|
||||
h = None
|
||||
s = None
|
||||
l = None
|
||||
|
||||
h, s , l = int(_h) / 360, float(_s) / 100, float(_l) / 100
|
||||
|
||||
rgb = tuple(round(i * 255) for i in colorsys.hls_to_rgb(h, l, s))
|
||||
rgb_sub = ','.join(map(str, rgb))
|
||||
|
||||
return f"rgb({rgb_sub})"
|
||||
|
||||
|
||||
def hsv_to_rgb(self, text: str) -> str:
|
||||
_h, _s , _v = text.replace("hsv", "") \
|
||||
.replace("deg", "") \
|
||||
.replace("(", "") \
|
||||
.replace(")", "") \
|
||||
.replace("%", "") \
|
||||
.replace(" ", "") \
|
||||
.split(",")
|
||||
|
||||
h = None
|
||||
s = None
|
||||
v = None
|
||||
|
||||
h, s , v = int(_h) / 360, float(_s) / 100, float(_v) / 100
|
||||
|
||||
rgb = tuple(round(i * 255) for i in colorsys.hsv_to_rgb(h,s,v))
|
||||
rgb_sub = ','.join(map(str, rgb))
|
||||
|
||||
return f"rgb({rgb_sub})"
|
||||
@@ -0,0 +1,240 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
gi.require_version('Gdk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import Gdk
|
||||
|
||||
# Application imports
|
||||
from .color_converter_mixin import ColorConverterMixin
|
||||
|
||||
|
||||
|
||||
class Colorize(ColorConverterMixin):
|
||||
def __init__(self):
|
||||
super(Colorize, self).__init__()
|
||||
|
||||
self.tag_stub_name: str = "colorize-tag"
|
||||
self.is_colorize_paused: bool = True
|
||||
|
||||
|
||||
def handle_colorize(self, buffer):
|
||||
if self.is_colorize_paused: return
|
||||
|
||||
tag_table = buffer.get_tag_table()
|
||||
start_itr = None
|
||||
end_itr = buffer.get_iter_at_mark( buffer.get_insert() )
|
||||
|
||||
i = 0
|
||||
walker_iter = end_itr.copy()
|
||||
working_tag = self.find_working_tag(walker_iter, i)
|
||||
if working_tag:
|
||||
start_itr = self.find_start_range(walker_iter, working_tag)
|
||||
|
||||
self.find_end_range(end_itr, working_tag)
|
||||
buffer.remove_tag(working_tag, start_itr, end_itr)
|
||||
else:
|
||||
start_itr = self.traverse_backward_25_or_less(walker_iter)
|
||||
self.traverse_forward_25_or_less(end_itr)
|
||||
|
||||
self.do_colorize(buffer, start_itr, end_itr)
|
||||
|
||||
def do_colorize(self, buffer = None, start_itr = None, end_itr = None):
|
||||
if not start_itr or not end_itr:
|
||||
start_itr = buffer.get_start_iter()
|
||||
end_itr = buffer.get_end_iter()
|
||||
|
||||
# rgb(a), hsl, hsv
|
||||
results = self.finalize_non_hex_matches(
|
||||
self.collect_preliminary_results(
|
||||
buffer, start_itr, end_itr
|
||||
)
|
||||
)
|
||||
self.process_results(buffer, results)
|
||||
|
||||
# hex color search
|
||||
results = self.finalize_hex_matches(
|
||||
self.collect_preliminary_hex_results(
|
||||
buffer, start_itr, end_itr
|
||||
)
|
||||
)
|
||||
self.process_results(buffer, results)
|
||||
|
||||
|
||||
|
||||
|
||||
def collect_preliminary_results(self, buffer = None, start_itr = None, end_itr = None):
|
||||
if not buffer: return []
|
||||
|
||||
if not start_itr:
|
||||
start_itr = buffer.get_start_iter()
|
||||
|
||||
results1 = self.search(start_itr, end_itr, "rgb")
|
||||
results2 = self.search(start_itr, end_itr, "hsl")
|
||||
results3 = self.search(start_itr, end_itr, "hsv")
|
||||
|
||||
return results1 + results2 + results3
|
||||
|
||||
def find_working_tag(self, walker_iter, i):
|
||||
tags = walker_iter.get_tags()
|
||||
for tag in tags:
|
||||
if not tag.props.name or not self.tag_stub_name in tag.props.name: continue
|
||||
return tag
|
||||
|
||||
result = walker_iter.backward_char()
|
||||
|
||||
if not result: return
|
||||
if i > 25: return
|
||||
return self.find_working_tag(walker_iter, i + 1)
|
||||
|
||||
def find_start_range(self, walker_iter, working_tag):
|
||||
tags = walker_iter.get_tags()
|
||||
for tag in tags:
|
||||
if not tag.props.name or not working_tag.props.name in tag.props.name: continue
|
||||
if not walker_iter.backward_char(): continue
|
||||
|
||||
self.find_start_range(walker_iter, working_tag)
|
||||
|
||||
return walker_iter
|
||||
|
||||
def find_end_range(self, end_itr, working_tag):
|
||||
tags = end_itr.get_tags()
|
||||
for tag in tags:
|
||||
if not tag.props.name or not working_tag.props.name in tag.props.name: continue
|
||||
if not end_itr.forward_char(): continue
|
||||
|
||||
self.find_end_range(end_itr, working_tag)
|
||||
|
||||
def traverse_backward_25_or_less(self, walker_itr):
|
||||
i = 1
|
||||
while i <= 25:
|
||||
res = walker_itr.backward_char()
|
||||
if not res: break
|
||||
i += 1
|
||||
|
||||
def traverse_forward_25_or_less(self, end_itr):
|
||||
i = 1
|
||||
while i <= 25:
|
||||
res = end_itr.forward_char()
|
||||
if not res: break
|
||||
i += 1
|
||||
|
||||
def collect_preliminary_hex_results(
|
||||
self,
|
||||
buffer = None,
|
||||
start_itr = None,
|
||||
end_itr = None
|
||||
) -> list:
|
||||
if not buffer: return []
|
||||
|
||||
if not start_itr:
|
||||
start_itr = buffer.get_start_iter()
|
||||
|
||||
return self.search(start_itr, end_itr, "#")
|
||||
|
||||
def search(self, start_itr = None, end_itr = None, query: str = None) -> list:
|
||||
if not start_itr or not query: return []
|
||||
|
||||
results: list = []
|
||||
flags = Gtk.TextSearchFlags.VISIBLE_ONLY | Gtk.TextSearchFlags.TEXT_ONLY
|
||||
while True:
|
||||
result = start_itr.forward_search(query, flags, end_itr)
|
||||
if not result: break
|
||||
|
||||
results.append(result)
|
||||
start_itr = result[1]
|
||||
|
||||
return results
|
||||
|
||||
def finalize_non_hex_matches(self, result_hits: list = []) -> list:
|
||||
results: list = []
|
||||
|
||||
for start_itr, end_itr in result_hits:
|
||||
# If one of end chars of rgb/rgba/hsv/hsl
|
||||
if end_itr.get_char() in ["a", "b", "l", "v"]:
|
||||
end_itr.forward_char()
|
||||
|
||||
# If afterwards no paren
|
||||
if end_itr.get_char() != "(":
|
||||
continue
|
||||
|
||||
end_itr.forward_chars(21) # Check if best case (255, 255, 255, 0.64)
|
||||
if end_itr.get_char() == ")":
|
||||
end_itr.forward_char()
|
||||
results.append([start_itr, end_itr])
|
||||
continue
|
||||
|
||||
# Break loop if we get back to rgb/rgba/hsl/hsv -> (
|
||||
while end_itr.get_char() != "(":
|
||||
if end_itr.get_char() == ")":
|
||||
end_itr.forward_char()
|
||||
results.append([start_itr, end_itr])
|
||||
break
|
||||
|
||||
if not end_itr.backward_char(): break
|
||||
|
||||
return results
|
||||
|
||||
def finalize_hex_matches(self, result_hits: list = []) -> list:
|
||||
results: list = []
|
||||
|
||||
for start_itr, end_itr in result_hits:
|
||||
i = 0
|
||||
_ch = end_itr.get_char()
|
||||
ch = ord(end_itr.get_char()) if _ch else -1
|
||||
|
||||
while (
|
||||
(ch >= 48 and ch <= 57) or \
|
||||
(ch >= 65 and ch <= 70) or \
|
||||
(ch >= 97 and ch <= 102)
|
||||
):
|
||||
if i > 16: break
|
||||
|
||||
i += 1
|
||||
end_itr.forward_char()
|
||||
_ch = end_itr.get_char()
|
||||
ch = ord(end_itr.get_char()) if _ch else -1
|
||||
|
||||
if i in [3, 4, 6, 8, 9, 12, 16]:
|
||||
results.append([start_itr, end_itr])
|
||||
|
||||
return results
|
||||
|
||||
def process_results(self, buffer, results):
|
||||
for start_itr, end_itr in results:
|
||||
text = self.get_color_text(buffer, start_itr, end_itr)
|
||||
color = Gdk.RGBA()
|
||||
|
||||
if not color.parse(text): continue
|
||||
|
||||
tag = self.get_colorized_tag(buffer, text, color)
|
||||
buffer.apply_tag(tag, start_itr, end_itr)
|
||||
|
||||
def get_colorized_tag(self, buffer, tag, color: Gdk.RGBA):
|
||||
tag_table = buffer.get_tag_table()
|
||||
colorize_tag = f"{self.tag_stub_name}_{tag}"
|
||||
search_tag = tag_table.lookup(colorize_tag)
|
||||
|
||||
if not search_tag:
|
||||
search_tag = buffer.create_tag(
|
||||
colorize_tag, background_rgba = color
|
||||
)
|
||||
|
||||
return search_tag
|
||||
|
||||
def clear_color_tags(self, buffer):
|
||||
tag_table = buffer.get_tag_table()
|
||||
|
||||
def traverse_tags(tag, user_data):
|
||||
name = tag.get_property("name")
|
||||
|
||||
if not name: return
|
||||
if name.startswith(self.tag_stub_name):
|
||||
user_data.append(tag)
|
||||
|
||||
tags = []
|
||||
tag_table.foreach(traverse_tags, tags)
|
||||
for tag in tags:
|
||||
tag_table.remove(tag)
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "Colorize",
|
||||
"author": "ITDominator",
|
||||
"version": "0.0.1",
|
||||
"support": "",
|
||||
"requests": {}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
from libs.event_factory import Event_Factory, Code_Event_Types
|
||||
|
||||
from plugins.plugin_types import PluginCode
|
||||
|
||||
from .colorize import Colorize
|
||||
|
||||
|
||||
|
||||
colorize = Colorize()
|
||||
|
||||
|
||||
|
||||
class Plugin(PluginCode):
|
||||
def __init__(self):
|
||||
super(Plugin, self).__init__()
|
||||
|
||||
|
||||
def _controller_message(self, event: Code_Event_Types.CodeEvent):
|
||||
if isinstance(event, Code_Event_Types.AddedNewFileEvent):
|
||||
colorize.handle_colorize(event.file.buffer)
|
||||
elif isinstance(event, Code_Event_Types.TextChangedEvent):
|
||||
colorize.handle_colorize(event.buffer)
|
||||
|
||||
def load(self):
|
||||
self._manage_signals("register_command")
|
||||
|
||||
def unload(self):
|
||||
self._manage_signals("unregister_command")
|
||||
event = Event_Factory.create_event("get_source_views")
|
||||
|
||||
self.emit_to("source_views", event)
|
||||
for view in event.response:
|
||||
buffer = view.get_buffer()
|
||||
colorize.clear_color_tags(buffer)
|
||||
|
||||
def _manage_signals(self, action: str):
|
||||
event = Event_Factory.create_event(action,
|
||||
command_name = "tggle_colorize",
|
||||
command = Handler,
|
||||
binding_mode = "released",
|
||||
binding = "<Shift><Control>c"
|
||||
)
|
||||
|
||||
self.emit_to("source_views", event)
|
||||
|
||||
|
||||
def run(self):
|
||||
...
|
||||
|
||||
|
||||
class Handler:
|
||||
@staticmethod
|
||||
def execute(
|
||||
view: any,
|
||||
*args,
|
||||
**kwargs
|
||||
):
|
||||
logger.debug("Command: Toggle Colorize")
|
||||
|
||||
colorize.is_colorize_paused = not colorize.is_colorize_paused
|
||||
if colorize.is_colorize_paused:
|
||||
colorize.clear_color_tags( view.get_buffer() )
|
||||
return
|
||||
|
||||
colorize.handle_colorize( view.get_buffer() )
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Pligin Module
|
||||
"""
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Pligin Package
|
||||
"""
|
||||
@@ -0,0 +1,107 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
from .mixins.code_comment_tags_mixin import CodeCommentTagsMixin
|
||||
|
||||
|
||||
|
||||
class Commenter(CodeCommentTagsMixin):
|
||||
def __init__(self):
|
||||
...
|
||||
|
||||
|
||||
def keyboard_tggl_comment(self, buffer):
|
||||
language = buffer.get_language()
|
||||
if not language: return
|
||||
|
||||
start_tag, end_tag = self.get_comment_tags(language)
|
||||
if not (start_tag or end_tag): return
|
||||
|
||||
start_tag += " "
|
||||
end_tag = end_tag or ""
|
||||
bounds = buffer.get_selection_bounds()
|
||||
|
||||
(self._bounds_comment if bounds else self._line_comment)(
|
||||
buffer, start_tag, end_tag, bounds
|
||||
)
|
||||
|
||||
def _line_comment(self, buffer, start_tag: str, end_tag: str, bounds):
|
||||
start = buffer.get_iter_at_mark(buffer.get_insert()).copy()
|
||||
end = start.copy()
|
||||
line, col = start.get_line() + 1, start.get_line_offset()
|
||||
|
||||
if not start.starts_line():
|
||||
start.set_line_offset(0)
|
||||
if not end.ends_line():
|
||||
end.forward_to_line_end()
|
||||
|
||||
text = buffer.get_text(start, end, True)
|
||||
stripped = text.lstrip()
|
||||
indent = text[:-len(stripped)] if stripped else text
|
||||
|
||||
if stripped.startswith(start_tag):
|
||||
stripped = stripped[len(start_tag):].lstrip().replace(end_tag, "", 1)
|
||||
else:
|
||||
stripped = f"{start_tag}{stripped}{end_tag}"
|
||||
|
||||
buffer.begin_user_action()
|
||||
buffer.delete(start, end)
|
||||
buffer.insert(start, indent + stripped)
|
||||
buffer.end_user_action()
|
||||
|
||||
buffer.place_cursor(buffer.get_iter_at_line_offset(line, col))
|
||||
|
||||
def _bounds_comment(self, buffer, start_tag: str, end_tag: str, bounds):
|
||||
def indent_len(s): return len(s) - len(s.lstrip())
|
||||
|
||||
def insert(line, idx):
|
||||
return f"{line[:idx]}{start_tag}{line[idx:]}{end_tag}"
|
||||
|
||||
def process(lines):
|
||||
base_indent = min(
|
||||
(indent_len(l) for l in lines if l.strip()),
|
||||
default = 0
|
||||
)
|
||||
|
||||
is_commented = all(
|
||||
l.lstrip().startswith(start_tag)
|
||||
for l in lines if l.strip()
|
||||
)
|
||||
|
||||
if is_commented:
|
||||
return [
|
||||
l.replace(start_tag, "", 1).replace(end_tag, "", 1)
|
||||
if l.lstrip().startswith(start_tag.lstrip())
|
||||
else l
|
||||
for l in lines
|
||||
]
|
||||
|
||||
return [
|
||||
l if not l.strip()
|
||||
else insert(l, base_indent)
|
||||
for l in lines
|
||||
]
|
||||
|
||||
start, end = bounds
|
||||
sline, scol = start.get_line(), start.get_line_offset()
|
||||
eline, ecol = end.get_line(), end.get_line_offset()
|
||||
|
||||
if not start.starts_line():
|
||||
start.set_line_offset(0)
|
||||
if not end.ends_line():
|
||||
end.forward_to_line_end()
|
||||
|
||||
lines = buffer.get_text(start, end, True).splitlines()
|
||||
new_text = "\n".join(process(lines))
|
||||
|
||||
buffer.begin_user_action()
|
||||
buffer.delete(start, end)
|
||||
buffer.insert(start, new_text)
|
||||
buffer.end_user_action()
|
||||
|
||||
buffer.select_range(
|
||||
buffer.get_iter_at_line_offset(sline, scol),
|
||||
buffer.get_iter_at_line_offset(eline, ecol),
|
||||
)
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "Commentzar",
|
||||
"author": "ITDominator",
|
||||
"version": "0.0.1",
|
||||
"support": "",
|
||||
"requests": {}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Pligin Module Mixin
|
||||
"""
|
||||
@@ -0,0 +1,30 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
class CodeCommentTagsMixin:
|
||||
def get_comment_tags(self, language):
|
||||
start_tag, end_tag = self.get_line_comment_tags(language)
|
||||
if (start_tag, end_tag) == (None, None):
|
||||
start_tag, end_tag = self.get_block_comment_tags(language)
|
||||
|
||||
return start_tag, end_tag
|
||||
|
||||
def get_block_comment_tags(self, language):
|
||||
start_tag = language.get_metadata('block-comment-start')
|
||||
end_tag = language.get_metadata('block-comment-end')
|
||||
|
||||
if start_tag and end_tag: return (start_tag, end_tag)
|
||||
|
||||
return (None, None)
|
||||
|
||||
def get_line_comment_tags(self, language):
|
||||
start_tag = language.get_metadata('line-comment-start')
|
||||
|
||||
if start_tag: return (start_tag, None)
|
||||
|
||||
return (None, None)
|
||||
@@ -0,0 +1,61 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
|
||||
# Application imports
|
||||
from libs.event_factory import Event_Factory, Code_Event_Types
|
||||
|
||||
from plugins.plugin_types import PluginCode
|
||||
|
||||
from .commenter import Commenter
|
||||
|
||||
|
||||
|
||||
commenter = Commenter()
|
||||
|
||||
|
||||
|
||||
class Plugin(PluginCode):
|
||||
def __init__(self):
|
||||
super(Plugin, self).__init__()
|
||||
|
||||
|
||||
def _controller_message(self, event: Code_Event_Types.CodeEvent):
|
||||
...
|
||||
|
||||
def load(self):
|
||||
event = Event_Factory.create_event("register_command",
|
||||
command_name = "keyboard_tggl_comment",
|
||||
command = Handler,
|
||||
binding_mode = "released",
|
||||
binding = "<Control>slash"
|
||||
)
|
||||
|
||||
self.emit_to("source_views", event)
|
||||
|
||||
def unload(self):
|
||||
event = Event_Factory.create_event("unregister_command",
|
||||
command_name = "keyboard_tggl_comment",
|
||||
command = Handler,
|
||||
binding_mode = "released",
|
||||
binding = "<Control>slash"
|
||||
)
|
||||
|
||||
self.emit_to("source_views", event)
|
||||
|
||||
def run(self):
|
||||
...
|
||||
|
||||
|
||||
class Handler:
|
||||
@staticmethod
|
||||
def execute(
|
||||
view: any,
|
||||
*args,
|
||||
**kwargs
|
||||
):
|
||||
logger.debug("Command: Toggle Comment")
|
||||
commenter.keyboard_tggl_comment( view.get_buffer() )
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Plugin Module
|
||||
"""
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Plugin Package
|
||||
"""
|
||||
@@ -0,0 +1,97 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
class Autopairs:
|
||||
def __init__(self):
|
||||
...
|
||||
|
||||
def handle_word_wrap(self, buffer, char_str: str):
|
||||
wrap_block = self.get_wrap_block(char_str)
|
||||
if not wrap_block: return
|
||||
|
||||
selection = buffer.get_selection_bounds()
|
||||
if not selection:
|
||||
self.insert_pair(buffer, char_str, wrap_block)
|
||||
return True
|
||||
|
||||
self.wrap_selection(buffer, char_str, wrap_block, selection)
|
||||
|
||||
return True
|
||||
|
||||
def insert_pair(
|
||||
self, buffer, char_str: str, wrap_block: tuple
|
||||
):
|
||||
buffer.begin_user_action()
|
||||
|
||||
left_block, right_block = wrap_block
|
||||
insert_mark = buffer.get_insert()
|
||||
|
||||
insert_itr = buffer.get_iter_at_mark(insert_mark)
|
||||
buffer.insert(insert_itr, f"{left_block}{right_block}")
|
||||
insert_itr = buffer.get_iter_at_mark( insert_mark )
|
||||
insert_itr.backward_char()
|
||||
|
||||
buffer.place_cursor(insert_itr)
|
||||
|
||||
buffer.end_user_action()
|
||||
|
||||
def wrap_selection(
|
||||
self, buffer, char_str: str, wrap_block: tuple, selection
|
||||
):
|
||||
left_block, \
|
||||
right_block = wrap_block
|
||||
start_itr, \
|
||||
end_itr = selection
|
||||
data = buffer.get_text(
|
||||
start_itr, end_itr, include_hidden_chars = False
|
||||
)
|
||||
start_mark = buffer.create_mark("startclose", start_itr, False)
|
||||
end_mark = buffer.create_mark("endclose", end_itr, True)
|
||||
|
||||
buffer.begin_user_action()
|
||||
|
||||
buffer.insert(start_itr, left_block)
|
||||
end_itr = buffer.get_iter_at_mark(end_mark)
|
||||
buffer.insert(end_itr, right_block)
|
||||
|
||||
start = buffer.get_iter_at_mark(start_mark)
|
||||
end = buffer.get_iter_at_mark(end_mark)
|
||||
|
||||
buffer.select_range(start, end)
|
||||
buffer.delete_mark_by_name("startclose")
|
||||
buffer.delete_mark_by_name("endclose")
|
||||
|
||||
buffer.end_user_action()
|
||||
|
||||
def get_wrap_block(self, char_str) -> tuple:
|
||||
left_block = ""
|
||||
right_block = ""
|
||||
|
||||
match char_str:
|
||||
case "(" | ")":
|
||||
left_block = "("
|
||||
right_block = ")"
|
||||
case "[" | "]":
|
||||
left_block = "["
|
||||
right_block = "]"
|
||||
case "{" | "}":
|
||||
left_block = "{"
|
||||
right_block = "}"
|
||||
case '"':
|
||||
left_block = '"'
|
||||
right_block = '"'
|
||||
case "'":
|
||||
left_block = "'"
|
||||
right_block = "'"
|
||||
case "`":
|
||||
left_block = "`"
|
||||
right_block = "`"
|
||||
case _:
|
||||
return ()
|
||||
|
||||
return left_block, right_block
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "File History",
|
||||
"author": "ITDominator",
|
||||
"version": "0.0.1",
|
||||
"support": "",
|
||||
"requests": {}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
from libs.event_factory import Event_Factory, Code_Event_Types
|
||||
|
||||
from plugins.plugin_types import PluginCode
|
||||
|
||||
|
||||
|
||||
history: list = []
|
||||
history_size: int = 30
|
||||
|
||||
|
||||
class Plugin(PluginCode):
|
||||
def __init__(self):
|
||||
super(Plugin, self).__init__()
|
||||
|
||||
|
||||
def _controller_message(self, event: Code_Event_Types.CodeEvent):
|
||||
if isinstance(event, Code_Event_Types.RemovedFileEvent):
|
||||
if event.file.ftype == "buffer": return
|
||||
|
||||
if len(history) == history_size:
|
||||
history.pop(0)
|
||||
|
||||
history.append(event.file.fpath)
|
||||
|
||||
def load(self):
|
||||
self._manage_signals("register_command")
|
||||
|
||||
def unload(self):
|
||||
self._manage_signals("unregister_command")
|
||||
|
||||
def _manage_signals(self, action: str):
|
||||
event = Event_Factory.create_event(action,
|
||||
command_name = "file_history_pop",
|
||||
command = Handler,
|
||||
binding_mode = "released",
|
||||
binding = "<Shift><Control>t"
|
||||
)
|
||||
|
||||
self.emit_to("source_views", event)
|
||||
|
||||
def run(self):
|
||||
...
|
||||
|
||||
|
||||
class Handler:
|
||||
@staticmethod
|
||||
def execute(
|
||||
view: any,
|
||||
char_str: str,
|
||||
*args,
|
||||
**kwargs
|
||||
):
|
||||
logger.debug("Command: File History")
|
||||
if len(history) == 0: return
|
||||
|
||||
view._on_uri_data_received(
|
||||
[
|
||||
f"file://{history.pop()}"
|
||||
]
|
||||
)
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Pligin Module
|
||||
"""
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Pligin Package
|
||||
"""
|
||||
@@ -0,0 +1,57 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
|
||||
gi.require_version('GtkSource', '4')
|
||||
|
||||
from gi.repository import GtkSource
|
||||
|
||||
# Application imports
|
||||
from .helpers import clear_temp_cut_buffer_delayed, set_temp_cut_buffer_delayed
|
||||
|
||||
|
||||
|
||||
class Handler:
|
||||
@staticmethod
|
||||
def execute(view: GtkSource.View, *args, **kwargs):
|
||||
logger.debug("Command: Cut to Temp Buffer")
|
||||
|
||||
clear_temp_cut_buffer_delayed(view)
|
||||
|
||||
buffer = view.get_buffer()
|
||||
|
||||
if buffer.get_has_selection():
|
||||
start_itr, end_itr = buffer.get_selection_bounds()
|
||||
|
||||
start_itr.set_line_offset(0)
|
||||
|
||||
if not end_itr.ends_line():
|
||||
end_itr.forward_to_line_end()
|
||||
|
||||
if not end_itr.is_end():
|
||||
end_itr.forward_char()
|
||||
else:
|
||||
itr = buffer.get_iter_at_mark(buffer.get_insert())
|
||||
|
||||
start_itr = itr.copy()
|
||||
start_itr.set_line_offset(0)
|
||||
|
||||
end_itr = start_itr.copy()
|
||||
if not end_itr.forward_line():
|
||||
end_itr = buffer.get_end_iter()
|
||||
|
||||
if not hasattr(view, "_cut_buffer"):
|
||||
view._cut_buffer = ""
|
||||
|
||||
text = buffer.get_text(start_itr, end_itr, True)
|
||||
|
||||
if not text.endswith("\n"):
|
||||
text += "\n"
|
||||
|
||||
view._cut_buffer += text
|
||||
|
||||
buffer.delete(start_itr, end_itr)
|
||||
buffer.place_cursor(start_itr)
|
||||
|
||||
set_temp_cut_buffer_delayed(view)
|
||||
@@ -0,0 +1,24 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
|
||||
from gi.repository import GLib
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
def clear_temp_cut_buffer_delayed(view: any):
|
||||
if not hasattr(view, "_cut_temp_timeout_id"): return
|
||||
if not view._cut_temp_timeout_id: return
|
||||
|
||||
GLib.source_remove(view._cut_temp_timeout_id)
|
||||
|
||||
def set_temp_cut_buffer_delayed(view: any):
|
||||
def clear_temp_buffer(view: any):
|
||||
view._cut_buffer = ""
|
||||
view._cut_temp_timeout_id = None
|
||||
return False
|
||||
|
||||
view._cut_temp_timeout_id = GLib.timeout_add(15000, clear_temp_buffer, view)
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "Nanoesq Temp Buffer",
|
||||
"author": "ITDominator",
|
||||
"version": "0.0.1",
|
||||
"support": "",
|
||||
"requests": {}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
|
||||
gi.require_version('GtkSource', '4')
|
||||
|
||||
from gi.repository import GLib
|
||||
from gi.repository import GtkSource
|
||||
|
||||
# Application imports
|
||||
from .helpers import clear_temp_cut_buffer_delayed, set_temp_cut_buffer_delayed
|
||||
|
||||
|
||||
class Handler2:
|
||||
@staticmethod
|
||||
def execute(
|
||||
view: GtkSource.View,
|
||||
*args,
|
||||
**kwargs
|
||||
):
|
||||
logger.debug("Command: Paste Temp Buffer")
|
||||
if not hasattr(view, "_cut_temp_timeout_id"): return
|
||||
if not hasattr(view, "_cut_buffer"): return
|
||||
if not view._cut_buffer: return
|
||||
|
||||
clear_temp_cut_buffer_delayed(view)
|
||||
|
||||
buffer = view.get_buffer()
|
||||
itr = buffer.get_iter_at_mark( buffer.get_insert() )
|
||||
insert_itr = itr.copy()
|
||||
|
||||
buffer.insert(insert_itr, view._cut_buffer, -1)
|
||||
|
||||
set_temp_cut_buffer_delayed(view)
|
||||
@@ -0,0 +1,49 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
from libs.event_factory import Event_Factory, Code_Event_Types
|
||||
|
||||
from plugins.plugin_types import PluginCode
|
||||
|
||||
from .cut_to_temp_buffer import Handler
|
||||
from .paste_temp_buffer import Handler2
|
||||
|
||||
|
||||
|
||||
class Plugin(PluginCode):
|
||||
def __init__(self):
|
||||
super(Plugin, self).__init__()
|
||||
|
||||
|
||||
def _controller_message(self, event: Code_Event_Types.CodeEvent):
|
||||
...
|
||||
|
||||
def load(self):
|
||||
self._manage_signals("register_command")
|
||||
|
||||
def unload(self):
|
||||
self._manage_signals("unregister_command")
|
||||
|
||||
def _manage_signals(self, action: str):
|
||||
event = Event_Factory.create_event(action,
|
||||
command_name = "cut_to_temp_buffer",
|
||||
command = Handler,
|
||||
binding_mode = "held",
|
||||
binding = "<Control>k"
|
||||
)
|
||||
|
||||
self.emit_to("source_views", event)
|
||||
|
||||
event = Event_Factory.create_event(action,
|
||||
command_name = "paste_temp_buffer",
|
||||
command = Handler2,
|
||||
binding_mode = "held",
|
||||
binding = "<Control>u"
|
||||
)
|
||||
|
||||
self.emit_to("source_views", event)
|
||||
|
||||
def run(self):
|
||||
...
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Pligin Module
|
||||
"""
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Pligin Package
|
||||
"""
|
||||
@@ -0,0 +1,77 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
|
||||
gi.require_version('Gtk', '3.0')
|
||||
gi.require_version('GtkSource', '4')
|
||||
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import GtkSource
|
||||
|
||||
# Application imports
|
||||
from libs.event_factory import Event_Factory, Code_Event_Types
|
||||
|
||||
|
||||
|
||||
emit_to: callable = None
|
||||
|
||||
def get_source_view(widget):
|
||||
if isinstance(widget, GtkSource.View):
|
||||
return widget
|
||||
|
||||
if isinstance(widget, Gtk.ScrolledWindow):
|
||||
return widget.get_child()
|
||||
|
||||
if isinstance(widget, Gtk.Paned):
|
||||
return get_source_view(widget.get_child1())
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def execute(
|
||||
source_view,
|
||||
char_str,
|
||||
modkeys_states
|
||||
):
|
||||
logger.debug("Command: Close Split Pane")
|
||||
|
||||
scrolled_win = source_view.get_parent()
|
||||
pane = scrolled_win.get_parent()
|
||||
|
||||
if not isinstance(pane, Gtk.Paned): return
|
||||
|
||||
container = pane.get_parent()
|
||||
source_view1 = pane.get_child1()
|
||||
source_view2 = pane.get_child2()
|
||||
|
||||
if scrolled_win == source_view1:
|
||||
remaining = source_view2
|
||||
closing_view = source_view
|
||||
else:
|
||||
remaining = source_view1
|
||||
closing_view = source_view
|
||||
|
||||
remaining_view = get_source_view(remaining)
|
||||
left = closing_view.sibling_left
|
||||
right = closing_view.sibling_right
|
||||
|
||||
if left:
|
||||
left.sibling_right = right
|
||||
|
||||
if right:
|
||||
right.sibling_left = left
|
||||
|
||||
pane.remove(source_view1)
|
||||
pane.remove(source_view2)
|
||||
|
||||
container.remove(pane)
|
||||
container.add(remaining)
|
||||
|
||||
event = Event_Factory.create_event(
|
||||
"remove_source_view",
|
||||
view = closing_view
|
||||
)
|
||||
emit_to("source_views", event)
|
||||
|
||||
remaining_view.grab_focus()
|
||||
@@ -0,0 +1,77 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
|
||||
gi.require_version("Gtk", "3.0")
|
||||
|
||||
from gi.repository import Gtk
|
||||
|
||||
# Application imports
|
||||
from libs.event_factory import Event_Factory, Code_Event_Types
|
||||
from libs.dto.states import SourceViewStates
|
||||
|
||||
|
||||
|
||||
emit_to: callable = None
|
||||
|
||||
def execute(
|
||||
source_view1,
|
||||
char_str,
|
||||
modkeys_states
|
||||
):
|
||||
logger.debug("Command: Split Pane")
|
||||
|
||||
scrolled_win1 = source_view1.get_parent()
|
||||
container = scrolled_win1.get_parent()
|
||||
pane = Gtk.Paned()
|
||||
event = Event_Factory.create_event(
|
||||
"create_source_view",
|
||||
state = SourceViewStates.INSERT
|
||||
)
|
||||
emit_to("source_views", event)
|
||||
|
||||
scrolled_win2, \
|
||||
source_view2 = event.response
|
||||
old_sibling_right = None
|
||||
|
||||
if source_view1.sibling_right:
|
||||
old_sibling_right = source_view1.sibling_right
|
||||
|
||||
source_view1.sibling_right = source_view2
|
||||
if old_sibling_right:
|
||||
old_sibling_right.sibling_left = source_view2
|
||||
source_view2.sibling_right = old_sibling_right
|
||||
|
||||
source_view2.sibling_left = source_view1
|
||||
|
||||
pane.set_hexpand(True)
|
||||
pane.set_vexpand(True)
|
||||
pane.set_wide_handle(True)
|
||||
|
||||
container.remove(scrolled_win1)
|
||||
pane.pack1( scrolled_win1, True, True )
|
||||
pane.pack2( scrolled_win2, True, True )
|
||||
container.add(pane)
|
||||
|
||||
def _show(pane, alloc, is_vertical: bool):
|
||||
if is_vertical:
|
||||
pane.set_position(alloc.width / 2)
|
||||
else:
|
||||
pane.set_position(alloc.height / 2)
|
||||
|
||||
pane.disconnect(pane.show_id)
|
||||
|
||||
is_control, is_shift, is_alt = modkeys_states
|
||||
alloc = container.get_allocation()
|
||||
if char_str == "|":
|
||||
pane.show_id = pane.connect("show", _show, alloc, True)
|
||||
pane.set_orientation(Gtk.Orientation.VERTICAL)
|
||||
elif char_str == "\\":
|
||||
pane.show_id = pane.connect("show", _show, alloc, False)
|
||||
pane.set_orientation(Gtk.Orientation.HORIZONTAL)
|
||||
|
||||
pane.show_all()
|
||||
|
||||
source_view2.command.exec("new_file")
|
||||
source_view2.grab_focus()
|
||||
@@ -0,0 +1,22 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
|
||||
gi.require_version('GtkSource', '4')
|
||||
|
||||
from gi.repository import GtkSource
|
||||
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
def execute(
|
||||
view: GtkSource.View,
|
||||
*args,
|
||||
**kwargs
|
||||
):
|
||||
logger.debug("Command: Focus Left Sibling")
|
||||
if not view.sibling_left: return
|
||||
view.sibling_left.grab_focus()
|
||||
@@ -0,0 +1,21 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
|
||||
gi.require_version('GtkSource', '4')
|
||||
|
||||
from gi.repository import GtkSource
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
def execute(
|
||||
view: GtkSource.View,
|
||||
*args,
|
||||
**kwargs
|
||||
):
|
||||
logger.debug("Command: Focus Right Sibling")
|
||||
if not view.sibling_right: return
|
||||
view.sibling_right.grab_focus()
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "Split Pane",
|
||||
"author": "ITDominator",
|
||||
"version": "0.0.1",
|
||||
"support": "",
|
||||
"requests": {}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
|
||||
gi.require_version('GtkSource', '4')
|
||||
|
||||
from gi.repository import GtkSource
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
def execute(
|
||||
view: GtkSource.View,
|
||||
*args,
|
||||
**kwargs
|
||||
):
|
||||
logger.debug("Command: Move To Left Sibling")
|
||||
if not view.sibling_left: return
|
||||
|
||||
buffer = view.get_buffer()
|
||||
popped_file, next_file = view.command.get_swap_file(view)
|
||||
|
||||
view.sibling_left.set_buffer(buffer)
|
||||
view.sibling_left.get_parent().show()
|
||||
view.sibling_left.grab_focus()
|
||||
|
||||
if next_file:
|
||||
view.set_buffer(next_file.buffer)
|
||||
else:
|
||||
view.command.exec("new_file")
|
||||
@@ -0,0 +1,32 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
|
||||
gi.require_version('GtkSource', '4')
|
||||
|
||||
from gi.repository import GtkSource
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
def execute(
|
||||
view: GtkSource.View,
|
||||
*args,
|
||||
**kwargs
|
||||
):
|
||||
logger.debug("Command: Move To Right Sibling")
|
||||
if not view.sibling_right: return
|
||||
|
||||
buffer = view.get_buffer()
|
||||
popped_file, next_file = view.command.get_swap_file(view)
|
||||
|
||||
view.sibling_right.set_buffer(buffer)
|
||||
view.sibling_right.get_parent().show()
|
||||
view.sibling_right.grab_focus()
|
||||
|
||||
if next_file:
|
||||
view.set_buffer(next_file.buffer)
|
||||
else:
|
||||
view.command.exec("new_file")
|
||||
@@ -0,0 +1,100 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
|
||||
gi.require_version("Gtk", "3.0")
|
||||
|
||||
from gi.repository import Gtk
|
||||
|
||||
# Application imports
|
||||
from plugins.plugin_types import PluginCode
|
||||
|
||||
from libs.event_factory import Event_Factory, Code_Event_Types
|
||||
|
||||
from . import create_split_view, \
|
||||
close_split_view, \
|
||||
focus_left_sibling, \
|
||||
focus_right_sibling, \
|
||||
move_to_left_sibling, \
|
||||
move_to_right_sibling
|
||||
|
||||
|
||||
|
||||
class Plugin(PluginCode):
|
||||
def __init__(self):
|
||||
super(Plugin, self).__init__()
|
||||
|
||||
|
||||
def _controller_message(self, event: Code_Event_Types.CodeEvent):
|
||||
...
|
||||
|
||||
def load(self):
|
||||
gemit_to = self.emit_to
|
||||
self._manage_signals("register_command")
|
||||
|
||||
def unload(self):
|
||||
self._manage_signals("unregister_command")
|
||||
|
||||
def _manage_signals(self, action: str):
|
||||
_create_split_view = create_split_view
|
||||
_close_split_view = close_split_view
|
||||
_create_split_view.emit_to = self.emit_to
|
||||
_close_split_view.emit_to = self.emit_to
|
||||
|
||||
event = Event_Factory.create_event(action,
|
||||
command_name = "create_split_view",
|
||||
command = _create_split_view,
|
||||
binding_mode = "released",
|
||||
binding = ["<Control>\\", "<Shift><Control>|"]
|
||||
)
|
||||
|
||||
self.emit_to("source_views", event)
|
||||
|
||||
event = Event_Factory.create_event(action,
|
||||
command_name = "close_split_view",
|
||||
command = _close_split_view,
|
||||
binding_mode = "released",
|
||||
binding = "<Alt>\\"
|
||||
)
|
||||
|
||||
self.emit_to("source_views", event)
|
||||
|
||||
event = Event_Factory.create_event(action,
|
||||
command_name = "focus_left_sibling",
|
||||
command = focus_left_sibling,
|
||||
binding_mode = "released",
|
||||
binding = "<Control>Page_Up"
|
||||
)
|
||||
|
||||
self.emit_to("source_views", event)
|
||||
|
||||
event = Event_Factory.create_event(action,
|
||||
command_name = "focus_right_sibling",
|
||||
command = focus_right_sibling,
|
||||
binding_mode = "released",
|
||||
binding = "<Control>Page_Down"
|
||||
)
|
||||
|
||||
self.emit_to("source_views", event)
|
||||
|
||||
event = Event_Factory.create_event(action,
|
||||
command_name = "move_to_left_sibling",
|
||||
command = move_to_left_sibling,
|
||||
binding_mode = "released",
|
||||
binding = "<Control><Shift>Up"
|
||||
)
|
||||
|
||||
self.emit_to("source_views", event)
|
||||
|
||||
event = Event_Factory.create_event(action,
|
||||
command_name = "move_to_right_sibling",
|
||||
command = move_to_right_sibling,
|
||||
binding_mode = "released",
|
||||
binding = "<Control><Shift>Down"
|
||||
)
|
||||
|
||||
self.emit_to("source_views", event)
|
||||
|
||||
def run(self):
|
||||
...
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Pligin Module
|
||||
"""
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Pligin Package
|
||||
"""
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "Example Completer",
|
||||
"author": "John Doe",
|
||||
"version": "0.0.1",
|
||||
"support": "",
|
||||
"requests": {}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
|
||||
# Application imports
|
||||
from libs.event_factory import Event_Factory, Code_Event_Types
|
||||
|
||||
from plugins.plugin_types import PluginCode
|
||||
|
||||
from .provider import Provider
|
||||
|
||||
|
||||
|
||||
class Plugin(PluginCode):
|
||||
def __init__(self):
|
||||
super(Plugin, self).__init__()
|
||||
|
||||
self.provider: Provider = None
|
||||
|
||||
|
||||
def _controller_message(self, event: Code_Event_Types.CodeEvent):
|
||||
...
|
||||
|
||||
def load(self):
|
||||
self.provider = Provider()
|
||||
|
||||
event = Event_Factory.create_event(
|
||||
"register_provider",
|
||||
provider_name = "Example Completer",
|
||||
provider = self.provider,
|
||||
language_ids = []
|
||||
)
|
||||
self.emit_to("completion", event)
|
||||
|
||||
def unload(self):
|
||||
event = Event_Factory.create_event(
|
||||
"unregister_provider",
|
||||
provider_name = "Example Completer"
|
||||
)
|
||||
self.emit_to("completion", event)
|
||||
|
||||
self.provider = None
|
||||
del self.provider
|
||||
|
||||
def run(self):
|
||||
...
|
||||
@@ -0,0 +1,76 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('GtkSource', '4')
|
||||
|
||||
from gi.repository import GObject
|
||||
from gi.repository import GtkSource
|
||||
|
||||
# Application imports
|
||||
from .provider_response_cache import ProviderResponseCache
|
||||
|
||||
|
||||
|
||||
class Provider(GObject.GObject, GtkSource.CompletionProvider):
|
||||
"""
|
||||
This is a custom Completion Example Provider.
|
||||
# NOTE: used information from here --> https://warroom.rsmus.com/do-that-auto-complete/
|
||||
"""
|
||||
__gtype_name__ = 'ExampleCompletionProvider'
|
||||
|
||||
def __init__(self):
|
||||
super(Provider, self).__init__()
|
||||
|
||||
self.response_cache: ProviderResponseCache = ProviderResponseCache()
|
||||
|
||||
|
||||
def do_get_name(self):
|
||||
""" Returns: a new string containing the name of the provider. """
|
||||
return 'Example Code Completion'
|
||||
|
||||
def do_match(self, context):
|
||||
# Note: If provider is in interactive activation then need to check
|
||||
# view focus as otherwise non focus views start trying to grab it.
|
||||
completion = context.get_property("completion")
|
||||
if not completion.get_view().has_focus(): return
|
||||
|
||||
word = self.response_cache.get_word(context)
|
||||
if not word or len(word) < 2: return False
|
||||
return True
|
||||
|
||||
def do_get_priority(self):
|
||||
""" Determin position in result list along other providor results. """
|
||||
return 5
|
||||
|
||||
def do_activate_proposal(self, proposal, iter_):
|
||||
""" Manually handle actual completion insert or set flags and handle normally. """
|
||||
|
||||
buffer = iter_.get_buffer()
|
||||
# Note: Flag mostly intended for SourceViewsMultiInsertState
|
||||
# to insure marker processes inserted text correctly.
|
||||
buffer.is_processing_completion = True
|
||||
return False
|
||||
|
||||
def do_get_activation(self):
|
||||
""" The context for when a provider will show results """
|
||||
# return GtkSource.CompletionActivation.NONE
|
||||
# return GtkSource.CompletionActivation.USER_REQUESTED
|
||||
# return GtkSource.CompletionActivation.USER_REQUESTED | GtkSource.CompletionActivation.INTERACTIVE
|
||||
return GtkSource.CompletionActivation.INTERACTIVE
|
||||
|
||||
def do_populate(self, context):
|
||||
results = self.response_cache.filter_with_context(context)
|
||||
proposals = []
|
||||
|
||||
for entry in results:
|
||||
proposals.append(
|
||||
self.response_cache.create_completion_item(
|
||||
entry["label"],
|
||||
entry["text"],
|
||||
entry["info"]
|
||||
)
|
||||
)
|
||||
|
||||
context.add_proposals(self, proposals, True)
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
# Python imports
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
import re
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
gi.require_version('GtkSource', '4')
|
||||
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import GLib
|
||||
from gi.repository import GtkSource
|
||||
|
||||
# Application imports
|
||||
from libs.event_factory import Code_Event_Types
|
||||
|
||||
from core.widgets.code.completion_providers.provider_response_cache_base import ProviderResponseCacheBase
|
||||
|
||||
|
||||
|
||||
class ProviderResponseCache(ProviderResponseCacheBase):
|
||||
def __init__(self):
|
||||
super(ProviderResponseCache, self).__init__()
|
||||
|
||||
# Note: Using asyncio.run causes a keyboard trap that prevents app
|
||||
# closure from terminal. ThreadPoolExecutor seems to not have such issues...
|
||||
self.executor = ThreadPoolExecutor(max_workers = 1)
|
||||
self.matchers: dict = {
|
||||
"hello": {
|
||||
"label": "Hello, World!",
|
||||
"text": "Hello, World!",
|
||||
"info": GLib.markup_escape_text( "<b>Says the first ever program developers write...</b>" )
|
||||
},
|
||||
"foo": {
|
||||
"label": "foo",
|
||||
"text": "foo }}"
|
||||
},
|
||||
"bar": {
|
||||
"label": "bar",
|
||||
"text": "bar }}"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def process_file_load(self, event: Code_Event_Types.AddedNewFileEvent):
|
||||
...
|
||||
|
||||
def process_file_close(self, event: Code_Event_Types.RemovedFileEvent):
|
||||
...
|
||||
|
||||
def process_file_save(self, event: Code_Event_Types.SavedFileEvent):
|
||||
...
|
||||
|
||||
def process_file_text_inserted(self, event: Code_Event_Types.TextInsertedEvent):
|
||||
...
|
||||
|
||||
def process_file_delete_range(self, event: Code_Event_Types.DeleteRangeEvent):
|
||||
...
|
||||
|
||||
def filter(self, word: str) -> list[dict]:
|
||||
...
|
||||
|
||||
def filter_with_context(self, context) -> list[dict]:
|
||||
"""
|
||||
In this instance, it will do 2 things:
|
||||
1) always provide Hello World! (Not ideal but an option so its in the example)
|
||||
2) Utilizes the Gtk.TextIter from the TextBuffer to determine if there is a jinja
|
||||
example of '{{ custom.' if so it will provide you with the options of foo and bar.
|
||||
If selected it will insert foo }} or bar }}, completing your syntax...
|
||||
|
||||
PLEASE NOTE the GtkTextIter Logic and regex are really rough and should be adjusted and tuned
|
||||
"""
|
||||
|
||||
proposals: list[dict] = [
|
||||
{
|
||||
"label": self.matchers[ "hello" ]["label"],
|
||||
"text": self.matchers[ "hello" ]["text"],
|
||||
"info": self.matchers[ "hello" ]["info"]
|
||||
}
|
||||
]
|
||||
|
||||
# Gtk Versions differ on get_iter responses...
|
||||
end_iter = context.get_iter()
|
||||
if not isinstance(end_iter, Gtk.TextIter):
|
||||
_, end_iter = context.get_iter()
|
||||
|
||||
if not end_iter: return
|
||||
|
||||
buf = end_iter.get_buffer()
|
||||
mov_iter = end_iter.copy()
|
||||
if mov_iter.backward_search('{{', Gtk.TextSearchFlags.VISIBLE_ONLY):
|
||||
mov_iter, _ = mov_iter.backward_search('{{', Gtk.TextSearchFlags.VISIBLE_ONLY)
|
||||
left_text = buf.get_text(mov_iter, end_iter, True)
|
||||
else:
|
||||
left_text = ''
|
||||
|
||||
if re.match(r'.*\{\{\s*custom\.$', left_text):
|
||||
# optionally proposed based on left search via regex
|
||||
proposals.append(
|
||||
{
|
||||
"label": self.matchers[ "foo" ]["label"],
|
||||
"text": self.matchers[ "foo" ]["text"],
|
||||
"info": ""
|
||||
}
|
||||
)
|
||||
|
||||
# optionally proposed based on left search via regex
|
||||
proposals.append(
|
||||
{
|
||||
"label": self.matchers[ "bar" ]["label"],
|
||||
"text": self.matchers[ "bar" ]["text"],
|
||||
"info": ""
|
||||
}
|
||||
)
|
||||
|
||||
return proposals
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Pligin Module
|
||||
"""
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Pligin Package
|
||||
"""
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "Python Completer",
|
||||
"author": "ITDominator",
|
||||
"version": "0.0.1",
|
||||
"support": "",
|
||||
"requests": {}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
|
||||
# Application imports
|
||||
from libs.event_factory import Event_Factory, Code_Event_Types
|
||||
|
||||
from plugins.plugin_types import PluginCode
|
||||
|
||||
from .provider import Provider
|
||||
|
||||
|
||||
|
||||
class Plugin(PluginCode):
|
||||
def __init__(self):
|
||||
super(Plugin, self).__init__()
|
||||
|
||||
self.provider: Provider = None
|
||||
|
||||
|
||||
def _controller_message(self, event: Code_Event_Types.CodeEvent):
|
||||
...
|
||||
|
||||
def load(self):
|
||||
self.provider = Provider()
|
||||
|
||||
event = Event_Factory.create_event(
|
||||
"register_provider",
|
||||
provider_name = "Python Completer",
|
||||
provider = self.provider,
|
||||
language_ids = []
|
||||
)
|
||||
self.emit_to("completion", event)
|
||||
|
||||
def unload(self):
|
||||
event = Event_Factory.create_event(
|
||||
"register_provider",
|
||||
provider_name = "Python Completer"
|
||||
)
|
||||
self.emit_to("completion", event)
|
||||
|
||||
self.provider = None
|
||||
del self.provider
|
||||
|
||||
def run(self):
|
||||
...
|
||||
@@ -0,0 +1,80 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('GtkSource', '4')
|
||||
|
||||
from gi.repository import GObject
|
||||
from gi.repository import GtkSource
|
||||
|
||||
# Application imports
|
||||
from .provider_response_cache import ProviderResponseCache
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class Provider(GObject.GObject, GtkSource.CompletionProvider):
|
||||
"""
|
||||
This code is A python code completion plugin for Newton.
|
||||
# NOTE: Some code pulled/referenced from here --> https://github.com/isamert/gedi
|
||||
"""
|
||||
__gtype_name__ = 'PythonCompletionProvider'
|
||||
|
||||
def __init__(self):
|
||||
super(Provider, self).__init__()
|
||||
|
||||
self.response_cache: ProviderResponseCache = ProviderResponseCache()
|
||||
|
||||
|
||||
def do_get_name(self):
|
||||
return "Python Code Completion"
|
||||
|
||||
def do_match(self, context):
|
||||
word = self.response_cache.get_word(context)
|
||||
if not word or len(word) < 2: return False
|
||||
|
||||
iter = self.response_cache.get_iter_correctly(context)
|
||||
iter.backward_char()
|
||||
|
||||
ch = iter.get_char()
|
||||
# NOTE: Look to re-add or apply supporting logic to use spaces
|
||||
# As is it slows down the editor in certain contexts...
|
||||
# if not (ch in ('_', '.', ' ') or ch.isalnum()):
|
||||
if not (ch in ('_', '.') or ch.isalnum()):
|
||||
return False
|
||||
|
||||
buffer = iter.get_buffer()
|
||||
if buffer.get_context_classes_at_iter(iter) != ['no-spell-check']:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def do_get_priority(self):
|
||||
return 5
|
||||
|
||||
def do_activate_proposal(self, proposal, iter_):
|
||||
buffer = iter_.get_buffer()
|
||||
# Note: Flag mostly intended for SourceViewsMultiInsertState
|
||||
# to insure marker processes inserted text correctly.
|
||||
buffer.is_processing_completion = True
|
||||
return False
|
||||
|
||||
def do_get_activation(self):
|
||||
""" The context for when a provider will show results """
|
||||
return GtkSource.CompletionActivation.INTERACTIVE
|
||||
|
||||
def do_populate(self, context):
|
||||
results = self.response_cache.filter_with_context(context)
|
||||
|
||||
proposals = []
|
||||
for entry in results:
|
||||
proposals.append(
|
||||
self.response_cache.create_completion_item(
|
||||
entry["label"],
|
||||
entry["text"],
|
||||
entry["info"]
|
||||
)
|
||||
)
|
||||
|
||||
context.add_proposals(self, proposals, True)
|
||||
@@ -0,0 +1,109 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('GtkSource', '4')
|
||||
|
||||
from gi.repository import GObject
|
||||
from gi.repository import GtkSource
|
||||
|
||||
import jedi
|
||||
from jedi.api import Script
|
||||
|
||||
# Application imports
|
||||
from libs.event_factory import Code_Event_Types
|
||||
|
||||
from core.widgets.code.completion_providers.provider_response_cache_base import ProviderResponseCacheBase
|
||||
|
||||
|
||||
|
||||
# FIXME: Find real icon names...
|
||||
icon_names = {
|
||||
'import': '',
|
||||
'module': '',
|
||||
'class': '',
|
||||
'function': '',
|
||||
'statement': '',
|
||||
'param': ''
|
||||
}
|
||||
|
||||
|
||||
class Jedi:
|
||||
def get_script(file, doc_text):
|
||||
return Script(code = doc_text, path = file)
|
||||
|
||||
|
||||
|
||||
class ProviderResponseCache(ProviderResponseCacheBase):
|
||||
def __init__(self):
|
||||
super(ProviderResponseCache, self).__init__()
|
||||
|
||||
self._file = None
|
||||
|
||||
|
||||
def process_file_load(self, event: Code_Event_Types.AddedNewFileEvent):
|
||||
...
|
||||
|
||||
def process_file_close(self, event: Code_Event_Types.RemovedFileEvent):
|
||||
...
|
||||
|
||||
def process_file_save(self, event: Code_Event_Types.SavedFileEvent):
|
||||
...
|
||||
|
||||
def process_file_text_inserted(self, event: Code_Event_Types.TextInsertedEvent):
|
||||
...
|
||||
|
||||
def process_file_delete_range(self, event: Code_Event_Types.DeleteRangeEvent):
|
||||
...
|
||||
|
||||
def filter(self, word: str) -> list[dict]:
|
||||
response: list[dict] = []
|
||||
|
||||
return response
|
||||
|
||||
def filter_with_context(self, context: GtkSource.CompletionContext) -> list[dict]:
|
||||
response: list[dict] = []
|
||||
|
||||
if not self._file: return response
|
||||
|
||||
itr = self.get_iter_correctly(context)
|
||||
buffer = itr.get_buffer()
|
||||
|
||||
doc_text = buffer.get_text(
|
||||
buffer.get_start_iter(),
|
||||
buffer.get_end_iter(),
|
||||
False
|
||||
)
|
||||
iter_cursor = buffer.get_iter_at_mark( buffer.get_insert() )
|
||||
linenum = iter_cursor.get_line() + 1
|
||||
charnum = iter_cursor.get_line_index()
|
||||
|
||||
def create_generator():
|
||||
if not self._file: return []
|
||||
|
||||
for completion in Jedi.get_script(self._file, doc_text).complete(
|
||||
line = linenum,
|
||||
column = None,
|
||||
fuzzy = False
|
||||
):
|
||||
{
|
||||
"label": completion.name,
|
||||
"text": completion.name,
|
||||
"info": completion.docstring()
|
||||
"icon" self.get_icon_for_type(completion.type)
|
||||
}
|
||||
|
||||
yield comp_item
|
||||
|
||||
for item in create_generator():
|
||||
response.append(item)
|
||||
|
||||
return response
|
||||
|
||||
def get_icon_for_type(self, _type):
|
||||
try:
|
||||
return self._theme.load_icon(icon_names[_type.lower()], 16, 0)
|
||||
except (KeyError, AttributeError, GObject.GError) as e:
|
||||
return self._theme.load_icon(Gtk.STOCK_ADD, 16, 0)
|
||||
except (GObject.GError, AttributeError) as e:
|
||||
return None
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Pligin Module
|
||||
"""
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Pligin Package
|
||||
"""
|
||||
@@ -0,0 +1,3 @@
|
||||
from .parser import load, loads
|
||||
from .writer import dump, dumps
|
||||
from .speg import ParseError
|
||||
@@ -0,0 +1,295 @@
|
||||
from .speg import peg
|
||||
import re, sys
|
||||
|
||||
if sys.version_info[0] == 2:
|
||||
_chr = unichr
|
||||
else:
|
||||
_chr = chr
|
||||
|
||||
def load(fin):
|
||||
return loads(fin.read())
|
||||
|
||||
def loads(s):
|
||||
if isinstance(s, bytes):
|
||||
s = s.decode('utf-8')
|
||||
if s.startswith(u'\ufeff'):
|
||||
s = s[1:]
|
||||
return peg(s.replace('\r\n', '\n'), _p_root)
|
||||
|
||||
def _p_ws(p):
|
||||
p('[ \t]*')
|
||||
|
||||
def _p_nl(p):
|
||||
p(r'([ \t]*(?:#[^\n]*)?\r?\n)+')
|
||||
|
||||
def _p_ews(p):
|
||||
with p:
|
||||
p(_p_nl)
|
||||
p(_p_ws)
|
||||
|
||||
def _p_id(p):
|
||||
return p(r'[$a-zA-Z_][$0-9a-zA-Z_]*')
|
||||
|
||||
_escape_table = {
|
||||
'r': '\r',
|
||||
'n': '\n',
|
||||
't': '\t',
|
||||
'f': '\f',
|
||||
'b': '\b',
|
||||
}
|
||||
def _p_unescape(p):
|
||||
esc = p('\\\\(?:u[0-9a-fA-F]{4}|[^\n])')
|
||||
if esc[1] == 'u':
|
||||
return _chr(int(esc[2:], 16))
|
||||
return _escape_table.get(esc[1:], esc[1:])
|
||||
|
||||
_re_indent = re.compile(r'[ \t]*')
|
||||
def _p_block_str(p, c):
|
||||
p(r'{c}{c}{c}'.format(c=c))
|
||||
lines = [['']]
|
||||
with p:
|
||||
while True:
|
||||
s = p(r'(?:{c}(?!{c}{c})|[^{c}\\])*'.format(c=c))
|
||||
l = s.split('\n')
|
||||
lines[-1].append(l[0])
|
||||
lines.extend([x] for x in l[1:])
|
||||
if p(r'(?:\\\n[ \t]*)*'):
|
||||
continue
|
||||
p.commit()
|
||||
lines[-1].append(p(_p_unescape))
|
||||
p(r'{c}{c}{c}'.format(c=c))
|
||||
|
||||
lines = [''.join(l) for l in lines]
|
||||
strip_ws = len(lines) > 1
|
||||
if strip_ws and all(c in ' \t' for c in lines[-1]):
|
||||
lines.pop()
|
||||
|
||||
indent = None
|
||||
for line in lines[1:]:
|
||||
if not line:
|
||||
continue
|
||||
if indent is None:
|
||||
indent = _re_indent.match(line).group(0)
|
||||
continue
|
||||
for i, (c1, c2) in enumerate(zip(indent, line)):
|
||||
if c1 != c2:
|
||||
indent = indent[:i]
|
||||
break
|
||||
|
||||
ind_len = len(indent or '')
|
||||
if strip_ws and all(c in ' \t' for c in lines[0]):
|
||||
lines = [line[ind_len:] for line in lines[1:]]
|
||||
else:
|
||||
lines[1:] = [line[ind_len:] for line in lines[1:]]
|
||||
|
||||
return '\n'.join(lines)
|
||||
|
||||
_re_mstr_nl = re.compile(r'(?:[ \t]*\n)+[ \t]*')
|
||||
_re_mstr_trailing_nl = re.compile(_re_mstr_nl.pattern + r'\Z')
|
||||
def _p_multiline_str(p, c):
|
||||
p('{c}(?!{c}{c})(?:[ \t]*\n[ \t]*)?'.format(c=c))
|
||||
string_parts = []
|
||||
with p:
|
||||
while True:
|
||||
string_parts.append(p(r'[^{c}\\]*'.format(c=c)))
|
||||
if p(r'(?:\\\n[ \t]*)*'):
|
||||
string_parts.append('')
|
||||
continue
|
||||
p.commit()
|
||||
string_parts.append(p(_p_unescape))
|
||||
p(c)
|
||||
string_parts[-1] = _re_mstr_trailing_nl.sub('', string_parts[-1])
|
||||
string_parts[::2] = [_re_mstr_nl.sub(' ', part) for part in string_parts[::2]]
|
||||
return ''.join(string_parts)
|
||||
|
||||
def _p_string(p):
|
||||
with p:
|
||||
return p(_p_block_str, '"')
|
||||
with p:
|
||||
return p(_p_block_str, "'")
|
||||
with p:
|
||||
return p(_p_multiline_str, '"')
|
||||
return p(_p_multiline_str, "'")
|
||||
|
||||
def _p_array_value(p):
|
||||
with p:
|
||||
p(_p_nl)
|
||||
return p(_p_object)
|
||||
with p:
|
||||
p(_p_ws)
|
||||
return p(_p_line_object)
|
||||
p(_p_ews)
|
||||
return p(_p_simple_value)
|
||||
|
||||
def _p_key(p):
|
||||
with p:
|
||||
return p(_p_id)
|
||||
return p(_p_string)
|
||||
|
||||
def _p_flow_kv(p):
|
||||
k = p(_p_key)
|
||||
p(_p_ews)
|
||||
p(':')
|
||||
with p:
|
||||
p(_p_nl)
|
||||
return k, p(_p_object)
|
||||
with p:
|
||||
p(_p_ws)
|
||||
return k, p(_p_line_object)
|
||||
p(_p_ews)
|
||||
return k, p(_p_simple_value)
|
||||
|
||||
def _p_flow_obj_sep(p):
|
||||
with p:
|
||||
p(_p_ews)
|
||||
p(',')
|
||||
p(_p_ews)
|
||||
return
|
||||
|
||||
p(_p_nl)
|
||||
p(_p_ws)
|
||||
|
||||
def _p_simple_value(p):
|
||||
with p:
|
||||
p('null')
|
||||
return None
|
||||
|
||||
with p:
|
||||
p('false')
|
||||
return False
|
||||
with p:
|
||||
p('true')
|
||||
return True
|
||||
|
||||
with p:
|
||||
return int(p('0b[01]+')[2:], 2)
|
||||
with p:
|
||||
return int(p('0o[0-7]+')[2:], 8)
|
||||
with p:
|
||||
return int(p('0x[0-9a-fA-F]+')[2:], 16)
|
||||
with p:
|
||||
return float(p(r'-?(?:[1-9][0-9]*|0)?\.[0-9]+(?:[Ee][\+-]?[0-9]+)?|(?:[1-9][0-9]*|0)(?:\.[0-9]+)?[Ee][\+-]?[0-9]+'))
|
||||
with p:
|
||||
return int(p('-?[1-9][0-9]*|0'), 10)
|
||||
|
||||
with p:
|
||||
return p(_p_string)
|
||||
|
||||
with p:
|
||||
p(r'\[')
|
||||
r = []
|
||||
with p:
|
||||
p.set('I', '')
|
||||
r.append(p(_p_array_value))
|
||||
with p:
|
||||
while True:
|
||||
with p:
|
||||
p(_p_ews)
|
||||
p(',')
|
||||
rr = p(_p_array_value)
|
||||
if not p:
|
||||
p(_p_nl)
|
||||
with p:
|
||||
rr = p(_p_object)
|
||||
if not p:
|
||||
p(_p_ews)
|
||||
rr = p(_p_simple_value)
|
||||
r.append(rr)
|
||||
p.commit()
|
||||
with p:
|
||||
p(_p_ews)
|
||||
p(',')
|
||||
p(_p_ews)
|
||||
p(r'\]')
|
||||
return r
|
||||
|
||||
p(r'\{')
|
||||
|
||||
r = {}
|
||||
p(_p_ews)
|
||||
with p:
|
||||
p.set('I', '')
|
||||
k, v = p(_p_flow_kv)
|
||||
r[k] = v
|
||||
with p:
|
||||
while True:
|
||||
p(_p_flow_obj_sep)
|
||||
k, v = p(_p_flow_kv)
|
||||
r[k] = v
|
||||
p.commit()
|
||||
p(_p_ews)
|
||||
with p:
|
||||
p(',')
|
||||
p(_p_ews)
|
||||
p(r'\}')
|
||||
return r
|
||||
|
||||
def _p_line_kv(p):
|
||||
k = p(_p_key)
|
||||
p(_p_ws)
|
||||
p(':')
|
||||
p(_p_ws)
|
||||
with p:
|
||||
p(_p_nl)
|
||||
p(p.get('I'))
|
||||
return k, p(_p_indented_object)
|
||||
with p:
|
||||
return k, p(_p_line_object)
|
||||
with p:
|
||||
return k, p(_p_simple_value)
|
||||
p(_p_nl)
|
||||
p(p.get('I'))
|
||||
p('[ \t]')
|
||||
p(_p_ws)
|
||||
return k, p(_p_simple_value)
|
||||
|
||||
def _p_line_object(p):
|
||||
k, v = p(_p_line_kv)
|
||||
r = { k: v }
|
||||
with p:
|
||||
while True:
|
||||
p(_p_ws)
|
||||
p(',')
|
||||
p(_p_ws)
|
||||
k, v = p(_p_line_kv)
|
||||
r[k] = v # uniqueness
|
||||
p.commit()
|
||||
return r
|
||||
|
||||
def _p_object(p):
|
||||
p.set('I', p.get('I') + p('[ \t]*'))
|
||||
r = p(_p_line_object)
|
||||
with p:
|
||||
while True:
|
||||
p(_p_ws)
|
||||
with p:
|
||||
p(',')
|
||||
p(_p_nl)
|
||||
p(p.get('I'))
|
||||
rr = p(_p_line_object)
|
||||
r.update(rr) # unqueness
|
||||
p.commit()
|
||||
return r
|
||||
|
||||
def _p_indented_object(p):
|
||||
p.set('I', p.get('I') + p('[ \t]'))
|
||||
return p(_p_object)
|
||||
|
||||
def _p_root(p):
|
||||
with p:
|
||||
p(_p_nl)
|
||||
|
||||
with p:
|
||||
p.set('I', '')
|
||||
r = p(_p_object)
|
||||
p(_p_ws)
|
||||
with p:
|
||||
p(',')
|
||||
|
||||
if not p:
|
||||
p(_p_ws)
|
||||
r = p(_p_simple_value)
|
||||
|
||||
p(_p_ews)
|
||||
p(p.eof)
|
||||
return r
|
||||
@@ -0,0 +1 @@
|
||||
from .peg import peg, ParseError
|
||||
@@ -0,0 +1,147 @@
|
||||
import sys, re
|
||||
|
||||
class ParseError(Exception):
|
||||
def __init__(self, msg, text, offset, line, col):
|
||||
self.msg = msg
|
||||
self.text = text
|
||||
self.offset = offset
|
||||
self.line = line
|
||||
self.col = col
|
||||
super(ParseError, self).__init__(msg, offset, line, col)
|
||||
|
||||
if sys.version_info[0] == 2:
|
||||
_basestr = basestring
|
||||
else:
|
||||
_basestr = str
|
||||
|
||||
def peg(s, r):
|
||||
p = _Peg(s)
|
||||
try:
|
||||
return p(r)
|
||||
except _UnexpectedError as e:
|
||||
offset = max(p._errors)
|
||||
err = p._errors[offset]
|
||||
raise ParseError(err.msg, s, offset, err.line, err.col)
|
||||
|
||||
class _UnexpectedError(RuntimeError):
|
||||
def __init__(self, state, expr):
|
||||
self.state = state
|
||||
self.expr = expr
|
||||
|
||||
class _PegState:
|
||||
def __init__(self, pos, line, col):
|
||||
self.pos = pos
|
||||
self.line = line
|
||||
self.col = col
|
||||
self.vars = {}
|
||||
self.commited = False
|
||||
|
||||
class _PegError:
|
||||
def __init__(self, msg, line, col):
|
||||
self.msg = msg
|
||||
self.line = line
|
||||
self.col = col
|
||||
|
||||
class _Peg:
|
||||
def __init__(self, s):
|
||||
self._s = s
|
||||
self._states = [_PegState(0, 1, 1)]
|
||||
self._errors = {}
|
||||
self._re_cache = {}
|
||||
|
||||
def __call__(self, r, *args, **kw):
|
||||
if isinstance(r, _basestr):
|
||||
compiled = self._re_cache.get(r)
|
||||
if not compiled:
|
||||
compiled = re.compile(r)
|
||||
self._re_cache[r] = compiled
|
||||
st = self._states[-1]
|
||||
m = compiled.match(self._s[st.pos:])
|
||||
if not m:
|
||||
self.error(expr=r, err=kw.get('err'))
|
||||
|
||||
ms = m.group(0)
|
||||
st.pos += len(ms)
|
||||
nl_pos = ms.rfind('\n')
|
||||
if nl_pos < 0:
|
||||
st.col += len(ms)
|
||||
else:
|
||||
st.col = len(ms) - nl_pos
|
||||
st.line += ms[:nl_pos].count('\n') + 1
|
||||
return ms
|
||||
else:
|
||||
kw.pop('err', None)
|
||||
return r(self, *args, **kw)
|
||||
|
||||
def __repr__(self):
|
||||
pos = self._states[-1].pos
|
||||
vars = {}
|
||||
for st in self._states:
|
||||
vars.update(st.vars)
|
||||
return '_Peg(%r, %r)' % (self._s[:pos] + '*' + self._s[pos:], vars)
|
||||
|
||||
@staticmethod
|
||||
def eof(p):
|
||||
if p._states[-1].pos != len(p._s):
|
||||
p.error()
|
||||
|
||||
def error(self, err=None, expr=None):
|
||||
st = self._states[-1]
|
||||
if err is None:
|
||||
err = 'expected {!r}, found {!r}'.format(expr, self._s[st.pos:st.pos+4])
|
||||
self._errors[st.pos] = _PegError(err, st.line, st.col)
|
||||
raise _UnexpectedError(st, expr)
|
||||
|
||||
def get(self, key, default=None):
|
||||
for state in self._states[::-1]:
|
||||
if key in state.vars:
|
||||
return state.vars[key][0]
|
||||
return default
|
||||
|
||||
def set(self, key, value):
|
||||
self._states[-1].vars[key] = value, False
|
||||
|
||||
def set_global(self, key, value):
|
||||
self._states[-1].vars[key] = value, True
|
||||
|
||||
def opt(self, *args, **kw):
|
||||
with self:
|
||||
return self(*args, **kw)
|
||||
|
||||
def not_(self, s, *args, **kw):
|
||||
with self:
|
||||
self(s)
|
||||
self.error()
|
||||
|
||||
def __enter__(self):
|
||||
self._states[-1].committed = False
|
||||
self._states.append(_PegState(self._states[-1].pos, self._states[-1].line, self._states[-1].col))
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
if type is None:
|
||||
self.commit()
|
||||
self._states.pop()
|
||||
return type == _UnexpectedError
|
||||
|
||||
def commit(self):
|
||||
cur = self._states[-1]
|
||||
prev = self._states[-2]
|
||||
|
||||
for key in cur.vars:
|
||||
val, g = cur.vars[key]
|
||||
if not g:
|
||||
continue
|
||||
if key in prev.vars:
|
||||
prev.vars[key] = val, prev.vars[key][1]
|
||||
else:
|
||||
prev.vars[key] = val, True
|
||||
|
||||
prev.pos = cur.pos
|
||||
prev.line = cur.line
|
||||
prev.col = cur.col
|
||||
prev.committed = True
|
||||
|
||||
def __nonzero__(self):
|
||||
return self._states[-1].committed
|
||||
|
||||
__bool__ = __nonzero__
|
||||
@@ -0,0 +1,191 @@
|
||||
import re, json, sys
|
||||
|
||||
if sys.version_info[0] == 2:
|
||||
def _is_num(o):
|
||||
return isinstance(o, int) or isinstance(o, long) or isinstance(o, float)
|
||||
def _stringify(o):
|
||||
if isinstance(o, str):
|
||||
return unicode(o)
|
||||
if isinstance(o, unicode):
|
||||
return o
|
||||
return None
|
||||
else:
|
||||
def _is_num(o):
|
||||
return isinstance(o, int) or isinstance(o, float)
|
||||
def _stringify(o):
|
||||
if isinstance(o, bytes):
|
||||
return o.decode()
|
||||
if isinstance(o, str):
|
||||
return o
|
||||
return None
|
||||
|
||||
_id_re = re.compile(r'[$a-zA-Z_][$0-9a-zA-Z_]*\Z')
|
||||
|
||||
class CSONEncoder:
|
||||
def __init__(self, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, sort_keys=False,
|
||||
indent=None, default=None):
|
||||
self._skipkeys = skipkeys
|
||||
self._ensure_ascii = ensure_ascii
|
||||
self._allow_nan = allow_nan
|
||||
self._sort_keys = sort_keys
|
||||
self._indent = ' ' * (indent or 4)
|
||||
self._default = default
|
||||
if check_circular:
|
||||
self._obj_stack = set()
|
||||
else:
|
||||
self._obj_stack = None
|
||||
|
||||
def _format_simple_val(self, o):
|
||||
if o is None:
|
||||
return 'null'
|
||||
if isinstance(o, bool):
|
||||
return 'true' if o else 'false'
|
||||
if _is_num(o):
|
||||
return str(o)
|
||||
s = _stringify(o)
|
||||
if s is not None:
|
||||
return self._escape_string(s)
|
||||
return None
|
||||
|
||||
def _escape_string(self, s):
|
||||
r = json.dumps(s, ensure_ascii=self._ensure_ascii)
|
||||
return u"'{}'".format(r[1:-1].replace("'", r"\'"))
|
||||
|
||||
def _escape_key(self, s):
|
||||
if s is None or isinstance(s, bool) or _is_num(s):
|
||||
s = str(s)
|
||||
s = _stringify(s)
|
||||
if s is None:
|
||||
if self._skipkeys:
|
||||
return None
|
||||
raise TypeError('keys must be a string')
|
||||
if not _id_re.match(s):
|
||||
return self._escape_string(s)
|
||||
return s
|
||||
|
||||
def _push_obj(self, o):
|
||||
if self._obj_stack is not None:
|
||||
if id(o) in self._obj_stack:
|
||||
raise ValueError('Circular reference detected')
|
||||
self._obj_stack.add(id(o))
|
||||
|
||||
def _pop_obj(self, o):
|
||||
if self._obj_stack is not None:
|
||||
self._obj_stack.remove(id(o))
|
||||
|
||||
def _encode(self, o, obj_val=False, indent='', force_flow=False):
|
||||
if isinstance(o, list):
|
||||
if not o:
|
||||
if obj_val:
|
||||
yield ' []\n'
|
||||
else:
|
||||
yield indent
|
||||
yield '[]\n'
|
||||
else:
|
||||
if obj_val:
|
||||
yield ' [\n'
|
||||
else:
|
||||
yield indent
|
||||
yield '[\n'
|
||||
indent = indent + self._indent
|
||||
self._push_obj(o)
|
||||
for v in o:
|
||||
for chunk in self._encode(v, obj_val=False, indent=indent, force_flow=True):
|
||||
yield chunk
|
||||
self._pop_obj(o)
|
||||
yield indent[:-len(self._indent)]
|
||||
yield ']\n'
|
||||
elif isinstance(o, dict):
|
||||
items = [(self._escape_key(k), v) for k, v in o.items()]
|
||||
if self._skipkeys:
|
||||
items = [(k, v) for k, v in items if k is not None]
|
||||
if self._sort_keys:
|
||||
items.sort()
|
||||
if force_flow or not items:
|
||||
if not items:
|
||||
if obj_val:
|
||||
yield ' {}\n'
|
||||
else:
|
||||
yield indent
|
||||
yield '{}\n'
|
||||
else:
|
||||
if obj_val:
|
||||
yield ' {\n'
|
||||
else:
|
||||
yield indent
|
||||
yield '{\n'
|
||||
indent = indent + self._indent
|
||||
self._push_obj(o)
|
||||
for k, v in items:
|
||||
yield indent
|
||||
yield k
|
||||
yield ':'
|
||||
for chunk in self._encode(v, obj_val=True, indent=indent + self._indent, force_flow=False):
|
||||
yield chunk
|
||||
self._pop_obj(o)
|
||||
yield indent[:-len(self._indent)]
|
||||
yield '}\n'
|
||||
else:
|
||||
if obj_val:
|
||||
yield '\n'
|
||||
self._push_obj(o)
|
||||
for k, v in items:
|
||||
yield indent
|
||||
yield k
|
||||
yield ':'
|
||||
for chunk in self._encode(v, obj_val=True, indent=indent + self._indent, force_flow=False):
|
||||
yield chunk
|
||||
self._pop_obj(o)
|
||||
else:
|
||||
v = self._format_simple_val(o)
|
||||
if v is None:
|
||||
self._push_obj(o)
|
||||
v = self.default(o)
|
||||
for chunk in self._encode(v, obj_val=obj_val, indent=indent, force_flow=force_flow):
|
||||
yield chunk
|
||||
self._pop_obj(o)
|
||||
else:
|
||||
if obj_val:
|
||||
yield ' '
|
||||
else:
|
||||
yield indent
|
||||
yield v
|
||||
yield '\n'
|
||||
|
||||
def iterencode(self, o):
|
||||
return self._encode(o)
|
||||
|
||||
def encode(self, o):
|
||||
return ''.join(self.iterencode(o))
|
||||
|
||||
def default(self, o):
|
||||
if self._default is None:
|
||||
raise TypeError('Cannot serialize an object of type {}'.format(type(o).__name__))
|
||||
return self._default(o)
|
||||
|
||||
def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None,
|
||||
indent=None, default=None, sort_keys=False, **kw):
|
||||
if indent is None and cls is None:
|
||||
return json.dump(obj, fp, skipkeys=skipkeys, ensure_ascii=ensure_ascii, check_circular=check_circular,
|
||||
allow_nan=allow_nan, default=default, sort_keys=sort_keys, separators=(',', ':'))
|
||||
|
||||
if cls is None:
|
||||
cls = CSONEncoder
|
||||
encoder = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii, check_circular=check_circular,
|
||||
allow_nan=allow_nan, sort_keys=sort_keys, indent=indent, default=default, **kw)
|
||||
|
||||
for chunk in encoder.iterencode(obj):
|
||||
fp.write(chunk)
|
||||
|
||||
def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None,
|
||||
default=None, sort_keys=False, **kw):
|
||||
if indent is None and cls is None:
|
||||
return json.dumps(obj, skipkeys=skipkeys, ensure_ascii=ensure_ascii, check_circular=check_circular,
|
||||
allow_nan=allow_nan, default=default, sort_keys=sort_keys, separators=(',', ':'))
|
||||
|
||||
if cls is None:
|
||||
cls = CSONEncoder
|
||||
encoder = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii, check_circular=check_circular,
|
||||
allow_nan=allow_nan, sort_keys=sort_keys, indent=indent, default=default, **kw)
|
||||
|
||||
return encoder.encode(obj)
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "Snippets Completer",
|
||||
"author": "ITDominator",
|
||||
"version": "0.0.1",
|
||||
"support": "",
|
||||
"requests": {}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
|
||||
# Application imports
|
||||
from libs.event_factory import Event_Factory, Code_Event_Types
|
||||
|
||||
from plugins.plugin_types import PluginCode
|
||||
|
||||
from .provider import Provider
|
||||
|
||||
|
||||
|
||||
class Plugin(PluginCode):
|
||||
def __init__(self):
|
||||
super(Plugin, self).__init__()
|
||||
|
||||
self.provider: Provider = None
|
||||
|
||||
|
||||
def _controller_message(self, event: Code_Event_Types.CodeEvent):
|
||||
...
|
||||
|
||||
def load(self):
|
||||
self.provider = Provider()
|
||||
|
||||
event = Event_Factory.create_event(
|
||||
"register_provider",
|
||||
provider_name = "Snippets Completer",
|
||||
provider = self.provider,
|
||||
language_ids = []
|
||||
)
|
||||
self.emit_to("completion", event)
|
||||
|
||||
def unload(self):
|
||||
event = Event_Factory.create_event(
|
||||
"unregister_provider",
|
||||
provider_name = "Snippets Completer"
|
||||
)
|
||||
self.emit_to("completion", event)
|
||||
|
||||
self.provider = None
|
||||
del self.provider
|
||||
|
||||
def run(self):
|
||||
...
|
||||
@@ -0,0 +1,68 @@
|
||||
# Python imports
|
||||
import re
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('GtkSource', '4')
|
||||
|
||||
from gi.repository import GObject
|
||||
from gi.repository import GtkSource
|
||||
|
||||
# Application imports
|
||||
from .provider_response_cache import ProviderResponseCache
|
||||
|
||||
|
||||
|
||||
class Provider(GObject.GObject, GtkSource.CompletionProvider):
|
||||
"""
|
||||
This is a Snippits Completion Provider.
|
||||
# NOTE: used information from here --> https://warroom.rsmus.com/do-that-auto-complete/
|
||||
"""
|
||||
__gtype_name__ = 'SnippetsCompletionProvider'
|
||||
|
||||
def __init__(self):
|
||||
super(Provider, self).__init__()
|
||||
|
||||
self.response_cache: ProviderResponseCache = ProviderResponseCache()
|
||||
|
||||
|
||||
def do_get_name(self):
|
||||
return 'Snippits Completion'
|
||||
|
||||
def do_match(self, context):
|
||||
word = self.response_cache.get_word(context)
|
||||
if not word or len(word) < 2: return False
|
||||
|
||||
return True
|
||||
|
||||
def do_get_priority(self):
|
||||
return 2
|
||||
|
||||
def do_activate_proposal(self, proposal, iter_):
|
||||
buffer = iter_.get_buffer()
|
||||
# Note: Flag mostly intended for SourceViewsMultiInsertState
|
||||
# to insure marker processes inserted text correctly.
|
||||
buffer.is_processing_completion = True
|
||||
return False
|
||||
|
||||
def do_get_activation(self):
|
||||
""" The context for when a provider will show results """
|
||||
# return GtkSource.CompletionActivation.NONE
|
||||
return GtkSource.CompletionActivation.USER_REQUESTED
|
||||
# return GtkSource.CompletionActivation.INTERACTIVE
|
||||
|
||||
def do_populate(self, context):
|
||||
word = self.response_cache.get_word(context)
|
||||
results = self.response_cache.filter(word)
|
||||
proposals = []
|
||||
|
||||
for entry in results:
|
||||
proposals.append(
|
||||
self.response_cache.create_completion_item(
|
||||
entry["label"],
|
||||
entry["text"],
|
||||
entry["info"]
|
||||
)
|
||||
)
|
||||
|
||||
context.add_proposals(self, proposals, True)
|
||||
@@ -0,0 +1,71 @@
|
||||
# Python imports
|
||||
from os import path
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('GtkSource', '4')
|
||||
|
||||
from gi.repository import GLib
|
||||
from gi.repository import GtkSource
|
||||
|
||||
# Application imports
|
||||
from libs.event_factory import Code_Event_Types
|
||||
|
||||
from core.widgets.code.completion_providers.provider_response_cache_base import ProviderResponseCacheBase
|
||||
|
||||
from . import cson
|
||||
|
||||
|
||||
|
||||
class ProviderResponseCache(ProviderResponseCacheBase):
|
||||
def __init__(self):
|
||||
super(ProviderResponseCache, self).__init__()
|
||||
|
||||
self.matchers: dict = {}
|
||||
|
||||
self.load_snippets()
|
||||
|
||||
|
||||
def load_snippets(self):
|
||||
fpath = path.join(path.dirname(path.realpath(__file__)), "snippets.cson")
|
||||
with open(fpath, 'rb') as f:
|
||||
self.snippets = cson.load(f)
|
||||
for group in self.snippets:
|
||||
self.snippets[group]
|
||||
for entry in self.snippets[group]:
|
||||
data = self.snippets[group][entry]
|
||||
self.matchers[ data["prefix"] ] = {
|
||||
"label": entry,
|
||||
"text": data["body"],
|
||||
"info": GLib.markup_escape_text( data["body"] )
|
||||
}
|
||||
|
||||
def process_file_load(self, event: Code_Event_Types.AddedNewFileEvent):
|
||||
...
|
||||
|
||||
def process_file_close(self, event: Code_Event_Types.RemovedFileEvent):
|
||||
...
|
||||
|
||||
def process_file_save(self, event: Code_Event_Types.SavedFileEvent):
|
||||
...
|
||||
|
||||
def process_file_text_inserted(self, event: Code_Event_Types.TextInsertedEvent):
|
||||
...
|
||||
|
||||
def process_file_delete_range(self, event: Code_Event_Types.DeleteRangeEvent):
|
||||
...
|
||||
|
||||
def filter(self, word: str) -> list[dict]:
|
||||
response: list[dict] = []
|
||||
|
||||
for entry in self.matchers:
|
||||
if not word in entry: continue
|
||||
data = self.matchers[entry]
|
||||
response.append(data)
|
||||
|
||||
return response
|
||||
|
||||
def filter_with_context(self, context: GtkSource.CompletionContext) -> list[dict]:
|
||||
response: list[dict] = []
|
||||
|
||||
return response
|
||||
@@ -0,0 +1,614 @@
|
||||
# Your snippets
|
||||
#
|
||||
# Atom snippets allow you to enter a simple prefix in the editor and hit tab to
|
||||
# expand the prefix into a larger code block with templated values.
|
||||
#
|
||||
# You can create a new snippet in this file by typing "snip" and then hitting
|
||||
# tab.
|
||||
#
|
||||
# An example CoffeeScript snippet to expand log to console.log:
|
||||
#
|
||||
# '.source.coffee':
|
||||
# 'Console log':
|
||||
# 'prefix': 'log'
|
||||
# 'body': 'console.log $1'
|
||||
#
|
||||
# Each scope (e.g. '.source.coffee' above) can only be declared once.
|
||||
#
|
||||
# This file uses CoffeeScript Object Notation (CSON).
|
||||
# If you are unfamiliar with CSON, you can read more about it in the
|
||||
# Atom Flight Manual:
|
||||
# http://flight-manual.atom.io/using-atom/sections/basic-customization/#_cson
|
||||
|
||||
|
||||
### HTML SNIPPETS ###
|
||||
'.text.html.basic':
|
||||
|
||||
'HTML Template':
|
||||
'prefix': 'html'
|
||||
'body': """<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title></title>
|
||||
<link rel="shortcut icon" href="fave_icon.png">
|
||||
<link rel="stylesheet" href="resources/css/base.css">
|
||||
<link rel="stylesheet" href="resources/css/main.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script src="resources/js/.js" charset="utf-8"></script>
|
||||
<script src="resources/js/.js" charset="utf-8"></script>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
'Canvas Tag':
|
||||
'prefix': 'canvas'
|
||||
'body': """<canvas id="canvas" width="800" height="600" style="border:1px solid #c3c3c3;"></canvas>"""
|
||||
|
||||
'Img Tag':
|
||||
'prefix': 'img'
|
||||
'body': """<img class="" src="" alt="" />"""
|
||||
|
||||
'Br Tag':
|
||||
'prefix': 'br'
|
||||
'body': """<br/>"""
|
||||
|
||||
'Hr Tag':
|
||||
'prefix': 'hr'
|
||||
'body': """<hr/>"""
|
||||
|
||||
'Server Side Events':
|
||||
'prefix': 'sse'
|
||||
'body': """// SSE events if supported
|
||||
if(typeof(EventSource) !== "undefined") {
|
||||
let source = new EventSource("resources/php/sse.php");
|
||||
source.onmessage = (event) => {
|
||||
if (event.data === "<yourDataStringToLookFor>") {
|
||||
// code here
|
||||
}
|
||||
};
|
||||
} else {
|
||||
console.log("SSE Not Supported In Browser...");
|
||||
}
|
||||
"""
|
||||
|
||||
'AJAX Template Function':
|
||||
'prefix': 'ajax template'
|
||||
'body': """const doAjax = async (actionPath, data) => {
|
||||
let xhttp = new XMLHttpRequest();
|
||||
|
||||
xhttp.onreadystatechange = function() {
|
||||
if (this.readyState === 4 && this.status === 200) {
|
||||
if (this.responseText != null) { // this.responseXML if getting XML fata
|
||||
handleReturnData(JSON.parse(this.responseText));
|
||||
} else {
|
||||
console.log("No content returned. Check the file path.");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
xhttp.open("POST", actionPath, true);
|
||||
xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
|
||||
// Force return to be JSON NOTE: Use application/xml to force XML
|
||||
xhttp.overrideMimeType('application/json');
|
||||
xhttp.send(data);
|
||||
}
|
||||
"""
|
||||
|
||||
'CSS Message Colors':
|
||||
'prefix': 'css colors'
|
||||
'body': """.error { color: rgb(255, 0, 0); }
|
||||
.warning { color: rgb(255, 168, 0); }
|
||||
.success { color: rgb(136, 204, 39); }
|
||||
"""
|
||||
|
||||
|
||||
### JS SNIPPETS ###
|
||||
'.source.js':
|
||||
|
||||
'Server Side Events':
|
||||
'prefix': 'sse'
|
||||
'body': """// SSE events if supported
|
||||
if(typeof(EventSource) !== "undefined") {
|
||||
let source = new EventSource("resources/php/sse.php");
|
||||
source.onmessage = (event) => {
|
||||
if (event.data === "<yourDataStringToLookFor>") {
|
||||
// code here
|
||||
}
|
||||
};
|
||||
} else {
|
||||
console.log("SSE Not Supported In Browser...");
|
||||
}
|
||||
"""
|
||||
|
||||
'AJAX Template Function':
|
||||
'prefix': 'ajax template'
|
||||
'body': """const doAjax = async (actionPath, data) => {
|
||||
let xhttp = new XMLHttpRequest();
|
||||
|
||||
xhttp.onreadystatechange = function() {
|
||||
if (this.readyState === 4 && this.status === 200) {
|
||||
if (this.responseText != null) { // this.responseXML if getting XML fata
|
||||
handleReturnData(JSON.parse(this.responseText));
|
||||
} else {
|
||||
console.log("No content returned. Check the file path.");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
xhttp.open("POST", actionPath, true);
|
||||
xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
|
||||
// Force return to be JSON NOTE: Use application/xml to force XML
|
||||
xhttp.overrideMimeType('application/json');
|
||||
xhttp.send(data);
|
||||
}
|
||||
"""
|
||||
|
||||
'SE6 Function':
|
||||
'prefix': 'function se6'
|
||||
'body': """const funcName = (arg = "") => {
|
||||
|
||||
}
|
||||
"""
|
||||
|
||||
### CSS SNIPPETS ###
|
||||
'.source.css':
|
||||
|
||||
'CSS Message Colors':
|
||||
'prefix': 'css colors'
|
||||
'body': """.error { color: rgb(255, 0, 0); }
|
||||
.warning { color: rgb(255, 168, 0); }
|
||||
.success { color: rgb(136, 204, 39); }
|
||||
"""
|
||||
|
||||
### PHP SNIPPETS ###
|
||||
'.text.html.php':
|
||||
|
||||
'SSE PHP':
|
||||
'prefix': 'sse php'
|
||||
'body': """<?php
|
||||
// Start the session
|
||||
session_start();
|
||||
|
||||
header('Content-Type: text/event-stream');
|
||||
header('Cache-Control: no-cache');
|
||||
|
||||
echo "data:dataToReturn\\\\n\\\\n";
|
||||
|
||||
flush();
|
||||
?>
|
||||
"""
|
||||
|
||||
'PHP Template':
|
||||
'prefix': 'php'
|
||||
'body': """<?php
|
||||
// Start the session
|
||||
session_start();
|
||||
|
||||
|
||||
// Determin action
|
||||
chdir("../../"); // Note: If in resources/php/
|
||||
if (isset($_POST['yourPostID'])) {
|
||||
// code here
|
||||
} else {
|
||||
$message = "Server: [Error] --> Illegal Access Method!";
|
||||
serverMessage("error", $message);
|
||||
}
|
||||
?>
|
||||
"""
|
||||
'HTML Template':
|
||||
'prefix': 'html'
|
||||
'body': """<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title></title>
|
||||
<link rel="shortcut icon" href="fave_icon.png">
|
||||
<link rel="stylesheet" href="resources/css/base.css">
|
||||
<link rel="stylesheet" href="resources/css/main.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script src="resources/js/.js" charset="utf-8"></script>
|
||||
<script src="resources/js/.js" charset="utf-8"></script>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
|
||||
### BASH SNIPPETS ###
|
||||
'.source.shell':
|
||||
|
||||
'Bash or Shell Template':
|
||||
'prefix': 'bash template'
|
||||
'body': """#!/bin/bash
|
||||
|
||||
# . CONFIG.sh
|
||||
|
||||
# set -o xtrace ## To debug scripts
|
||||
# set -o errexit ## To exit on error
|
||||
# set -o errunset ## To exit if a variable is referenced but not set
|
||||
|
||||
|
||||
function main() {
|
||||
cd "$(dirname "$0")"
|
||||
echo "Working Dir: " $(pwd)
|
||||
|
||||
file="$1"
|
||||
if [ -z "${file}" ]; then
|
||||
echo "ERROR: No file argument supplied..."
|
||||
exit
|
||||
fi
|
||||
|
||||
if [[ -f "${file}" ]]; then
|
||||
echo "SUCCESS: The path and file exists!"
|
||||
else
|
||||
echo "ERROR: The path or file '${file}' does NOT exist..."
|
||||
fi
|
||||
}
|
||||
main "$@";
|
||||
"""
|
||||
|
||||
|
||||
'Bash or Shell Config':
|
||||
'prefix': 'bash config'
|
||||
'body': """#!/bin/bash
|
||||
|
||||
shopt -s expand_aliases
|
||||
|
||||
alias echo="echo -e"
|
||||
"""
|
||||
|
||||
|
||||
### PYTHON SNIPPETS ###
|
||||
'.source.python':
|
||||
|
||||
'Glade __main__ Class Template':
|
||||
'prefix': 'glade __main__ class'
|
||||
'body': """#!/usr/bin/python3
|
||||
|
||||
|
||||
# Python imports
|
||||
import argparse
|
||||
import faulthandler
|
||||
import traceback
|
||||
import signal
|
||||
from setproctitle import setproctitle
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import GLib
|
||||
|
||||
# Application imports
|
||||
from app import Application
|
||||
|
||||
|
||||
def run():
|
||||
try:
|
||||
setproctitle('<replace this>')
|
||||
faulthandler.enable() # For better debug info
|
||||
|
||||
GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, Gtk.main_quit)
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
# Add long and short arguments
|
||||
parser.add_argument("--debug", "-d", default="false", help="Do extra console messaging.")
|
||||
parser.add_argument("--trace-debug", "-td", default="false", help="Disable saves, ignore IPC lock, do extra console messaging.")
|
||||
parser.add_argument("--no-plugins", "-np", default="false", help="Do not load plugins.")
|
||||
|
||||
parser.add_argument("--new-tab", "-t", default="", help="Open a file into new tab.")
|
||||
parser.add_argument("--new-window", "-w", default="", help="Open a file into a new window.")
|
||||
|
||||
# Read arguments (If any...)
|
||||
args, unknownargs = parser.parse_known_args()
|
||||
|
||||
main = Application(args, unknownargs)
|
||||
Gtk.main()
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
quit()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
''' Set process title, get arguments, and create GTK main thread. '''
|
||||
run()
|
||||
|
||||
|
||||
"""
|
||||
|
||||
|
||||
'Glade __main__ Testing Template':
|
||||
'prefix': 'glade testing class'
|
||||
'body': """#!/usr/bin/python3
|
||||
|
||||
|
||||
# Python imports
|
||||
import traceback
|
||||
import faulthandler
|
||||
import signal
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import GLib
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
app_name = "Gtk Quick Test"
|
||||
|
||||
|
||||
class Application(Gtk.ApplicationWindow):
|
||||
def __init__(self):
|
||||
super(Application, self).__init__()
|
||||
self._setup_styling()
|
||||
self._setup_signals()
|
||||
self._load_widgets()
|
||||
|
||||
self.add(Gtk.Box())
|
||||
|
||||
self.show_all()
|
||||
|
||||
|
||||
def _setup_styling(self):
|
||||
self.set_default_size(1670, 830)
|
||||
self.set_title(f"{app_name}")
|
||||
# self.set_icon_from_file( settings.get_window_icon() )
|
||||
self.set_gravity(5) # 5 = CENTER
|
||||
self.set_position(1) # 1 = CENTER, 4 = CENTER_ALWAYS
|
||||
|
||||
def _setup_signals(self):
|
||||
self.connect("delete-event", Gtk.main_quit)
|
||||
|
||||
|
||||
def _load_widgets(self):
|
||||
...
|
||||
|
||||
|
||||
|
||||
|
||||
def run():
|
||||
try:
|
||||
faulthandler.enable() # For better debug info
|
||||
GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, Gtk.main_quit)
|
||||
|
||||
main = Application()
|
||||
Gtk.main()
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
quit()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
''' Set process title, get arguments, and create GTK main thread. '''
|
||||
run()
|
||||
|
||||
|
||||
"""
|
||||
|
||||
|
||||
'Glade _init_ Class Template':
|
||||
'prefix': 'glade __init__ class'
|
||||
'body': """# Python imports
|
||||
import inspect
|
||||
|
||||
|
||||
# Lib imports
|
||||
|
||||
|
||||
# Application imports
|
||||
from utils import Settings
|
||||
from signal_classes import CrossClassSignals
|
||||
|
||||
|
||||
class Main:
|
||||
def __init__(self, args):
|
||||
settings = Settings()
|
||||
builder = settings.returnBuilder()
|
||||
|
||||
# Gets the methods from the classes and sets to handler.
|
||||
# Then, builder connects to any signals it needs.
|
||||
classes = [CrossClassSignals(settings)]
|
||||
|
||||
handlers = {}
|
||||
for c in classes:
|
||||
methods = inspect.getmembers(c, predicate=inspect.ismethod)
|
||||
handlers.update(methods)
|
||||
|
||||
builder.connect_signals(handlers)
|
||||
window = settings.createWindow()
|
||||
window.show()
|
||||
|
||||
"""
|
||||
|
||||
'Class Method':
|
||||
'prefix': 'def1'
|
||||
'body': """
|
||||
def fname(self):
|
||||
...
|
||||
"""
|
||||
|
||||
'Gtk Class Method':
|
||||
'prefix': 'def2'
|
||||
'body': """
|
||||
def fname(self, widget, eve):
|
||||
...
|
||||
"""
|
||||
|
||||
|
||||
'Python Glade Settings Template':
|
||||
'prefix': 'glade settings class'
|
||||
'body': """# Python imports
|
||||
import os
|
||||
|
||||
# Lib imports
|
||||
import gi, cairo
|
||||
gi.require_version('Gtk', '3.0')
|
||||
gi.require_version('Gdk', '3.0')
|
||||
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import Gdk
|
||||
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
class Settings:
|
||||
def __init__(self):
|
||||
self.SCRIPT_PTH = os.path.dirname(os.path.realpath(__file__)) + "/"
|
||||
self.builder = Gtk.Builder()
|
||||
self.builder.add_from_file(self.SCRIPT_PTH + "../resources/Main_Window.glade")
|
||||
|
||||
# 'Filters'
|
||||
self.office = ('.doc', '.docx', '.xls', '.xlsx', '.xlt', '.xltx', '.xlm',
|
||||
'.ppt', 'pptx', '.pps', '.ppsx', '.odt', '.rtf')
|
||||
self.vids = ('.mkv', '.avi', '.flv', '.mov', '.m4v', '.mpg', '.wmv',
|
||||
'.mpeg', '.mp4', '.webm')
|
||||
self.txt = ('.txt', '.text', '.sh', '.cfg', '.conf')
|
||||
self.music = ('.psf', '.mp3', '.ogg' , '.flac')
|
||||
self.images = ('.png', '.jpg', '.jpeg', '.gif')
|
||||
self.pdf = ('.pdf')
|
||||
|
||||
|
||||
def createWindow(self):
|
||||
# Get window and connect signals
|
||||
window = self.builder.get_object("Main_Window")
|
||||
window.connect("delete-event", gtk.main_quit)
|
||||
self.setWindowData(window, False)
|
||||
return window
|
||||
|
||||
def setWindowData(self, window, paintable):
|
||||
screen = window.get_screen()
|
||||
visual = screen.get_rgba_visual()
|
||||
|
||||
if visual != None and screen.is_composited():
|
||||
window.set_visual(visual)
|
||||
|
||||
# bind css file
|
||||
cssProvider = gtk.CssProvider()
|
||||
cssProvider.load_from_path(self.SCRIPT_PTH + '../resources/stylesheet.css')
|
||||
screen = Gdk.Screen.get_default()
|
||||
styleContext = Gtk.StyleContext()
|
||||
styleContext.add_provider_for_screen(screen, cssProvider, gtk.STYLE_PROVIDER_PRIORITY_USER)
|
||||
|
||||
window.set_app_paintable(paintable)
|
||||
if paintable:
|
||||
window.connect("draw", self.area_draw)
|
||||
|
||||
def getMonitorData(self):
|
||||
screen = self.builder.get_object("Main_Window").get_screen()
|
||||
monitors = []
|
||||
for m in range(screen.get_n_monitors()):
|
||||
monitors.append(screen.get_monitor_geometry(m))
|
||||
|
||||
for monitor in monitors:
|
||||
print(str(monitor.width) + "x" + str(monitor.height) + "+" + str(monitor.x) + "+" + str(monitor.y))
|
||||
|
||||
return monitors
|
||||
|
||||
def area_draw(self, widget, cr):
|
||||
cr.set_source_rgba(0, 0, 0, 0.54)
|
||||
cr.set_operator(cairo.OPERATOR_SOURCE)
|
||||
cr.paint()
|
||||
cr.set_operator(cairo.OPERATOR_OVER)
|
||||
|
||||
|
||||
def returnBuilder(self): return self.builder
|
||||
|
||||
# Filter returns
|
||||
def returnOfficeFilter(self): return self.office
|
||||
def returnVidsFilter(self): return self.vids
|
||||
def returnTextFilter(self): return self.txt
|
||||
def returnMusicFilter(self): return self.music
|
||||
def returnImagesFilter(self): return self.images
|
||||
def returnPdfFilter(self): return self.pdf
|
||||
|
||||
"""
|
||||
|
||||
'Python Glade CrossClassSignals Template':
|
||||
'prefix': 'glade crossClassSignals class'
|
||||
'body': """# Python imports
|
||||
import threading
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
def threaded(fn):
|
||||
def wrapper(*args, **kwargs):
|
||||
threading.Thread(target=fn, args=args, kwargs=kwargs).start()
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
class CrossClassSignals:
|
||||
def __init__(self, settings):
|
||||
self.settings = settings
|
||||
self.builder = self.settings.returnBuilder()
|
||||
|
||||
|
||||
def getClipboardData(self):
|
||||
proc = subprocess.Popen(['xclip','-selection', 'clipboard', '-o'], stdout=subprocess.PIPE)
|
||||
retcode = proc.wait()
|
||||
data = proc.stdout.read()
|
||||
return data.decode("utf-8").strip()
|
||||
|
||||
def setClipboardData(self, data):
|
||||
proc = subprocess.Popen(['xclip','-selection','clipboard'], stdin=subprocess.PIPE)
|
||||
proc.stdin.write(data)
|
||||
proc.stdin.close()
|
||||
retcode = proc.wait()
|
||||
|
||||
"""
|
||||
|
||||
|
||||
'Python Glade Generic Template':
|
||||
'prefix': 'glade generic class'
|
||||
'body': """# Python imports
|
||||
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
class GenericClass:
|
||||
def __init__(self):
|
||||
super(GenericClass, self).__init__()
|
||||
|
||||
self._setup_styling()
|
||||
self._setup_signals()
|
||||
self._subscribe_to_events()
|
||||
self._load_widgets()
|
||||
|
||||
|
||||
def _setup_styling(self):
|
||||
...
|
||||
|
||||
def _setup_signals(self):
|
||||
...
|
||||
|
||||
def _subscribe_to_events(self):
|
||||
event_system.subscribe("handle_file_from_ipc", self.handle_file_from_ipc)
|
||||
|
||||
def _load_widgets(self):
|
||||
...
|
||||
|
||||
"""
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Pligin Module
|
||||
"""
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Pligin Package
|
||||
"""
|
||||
@@ -0,0 +1,68 @@
|
||||
# Python imports
|
||||
import re
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
gi.require_version('GtkSource', '4')
|
||||
|
||||
from gi.repository import GObject
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import GtkSource
|
||||
|
||||
# Application imports
|
||||
from .provider_response_cache import ProviderResponseCache
|
||||
|
||||
|
||||
|
||||
class Provider(GtkSource.CompletionWords):
|
||||
"""
|
||||
This is a Words Completion Provider.
|
||||
# NOTE: used information from here --> https://warroom.rsmus.com/do-that-auto-complete/
|
||||
"""
|
||||
__gtype_name__ = 'WordsCompletionProvider'
|
||||
|
||||
def __init__(self):
|
||||
super(Provider, self).__init__()
|
||||
|
||||
self.response_cache: ProviderResponseCache = ProviderResponseCache()
|
||||
|
||||
|
||||
def do_get_name(self):
|
||||
return 'Words Completion'
|
||||
|
||||
def do_match(self, context):
|
||||
# Note: If provider is in interactive activation then need to check
|
||||
# view focus as otherwise non focus views start trying to grab it.
|
||||
completion = context.get_property("completion")
|
||||
if not completion.get_view().has_focus(): return
|
||||
|
||||
word = self.response_cache.get_word(context)
|
||||
if not word or len(word) < 2: return False
|
||||
|
||||
iter = self.response_cache.get_iter_correctly(context)
|
||||
iter.backward_char()
|
||||
ch = iter.get_char()
|
||||
# NOTE: Look to re-add or apply supprting logic to use spaces
|
||||
# As is it slows down the editor in certain contexts...
|
||||
# if not (ch in ('_', '.', ' ') or ch.isalnum()):
|
||||
if not (ch in ('_', '.') or ch.isalnum()):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def do_get_priority(self):
|
||||
return 0
|
||||
|
||||
def do_activate_proposal(self, proposal, iter_):
|
||||
buffer = iter_.get_buffer()
|
||||
# Note: Flag mostly intended for SourceViewsMultiInsertState
|
||||
# to insure marker processes inserted text correctly.
|
||||
buffer.is_processing_completion = True
|
||||
return False
|
||||
|
||||
def do_get_activation(self):
|
||||
""" The context for when a provider will show results """
|
||||
# return GtkSource.CompletionActivation.NONE
|
||||
# return GtkSource.CompletionActivation.USER_REQUESTED
|
||||
return GtkSource.CompletionActivation.INTERACTIVE
|
||||
@@ -0,0 +1,29 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
from libs.event_factory import Code_Event_Types
|
||||
|
||||
from core.widgets.code.completion_providers.provider_response_cache_base import ProviderResponseCacheBase
|
||||
|
||||
|
||||
|
||||
class ProviderResponseCache(ProviderResponseCacheBase):
|
||||
def __init__(self):
|
||||
super(ProviderResponseCache, self).__init__()
|
||||
|
||||
self.matchers: dict = {}
|
||||
|
||||
|
||||
def process_file_load(self, event: Code_Event_Types.AddedNewFileEvent):
|
||||
...
|
||||
|
||||
def process_file_close(self, event: Code_Event_Types.RemovedFileEvent):
|
||||
...
|
||||
|
||||
def process_file_save(self, event: Code_Event_Types.SavedFileEvent):
|
||||
...
|
||||
|
||||
def process_file_change(self, event: Code_Event_Types.TextChangedEvent):
|
||||
...
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "Words Completer",
|
||||
"author": "ITDominator",
|
||||
"version": "0.0.1",
|
||||
"support": "",
|
||||
"requests": {}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
|
||||
# Application imports
|
||||
from libs.event_factory import Event_Factory, Code_Event_Types
|
||||
|
||||
from plugins.plugin_types import PluginCode
|
||||
|
||||
from .provider import Provider
|
||||
|
||||
|
||||
|
||||
class Plugin(PluginCode):
|
||||
def __init__(self):
|
||||
super(Plugin, self).__init__()
|
||||
|
||||
self.provider: Provider = None
|
||||
|
||||
|
||||
def _controller_message(self, event: Code_Event_Types.CodeEvent):
|
||||
...
|
||||
|
||||
def load(self):
|
||||
self.provider = Provider()
|
||||
|
||||
event = Event_Factory.create_event(
|
||||
"register_provider",
|
||||
provider_name = "Words Completer",
|
||||
provider = self.provider,
|
||||
language_ids = []
|
||||
)
|
||||
self.emit_to("completion", event)
|
||||
|
||||
def unload(self):
|
||||
event = Event_Factory.create_event(
|
||||
"unregister_provider",
|
||||
provider_name = "Words Completer"
|
||||
)
|
||||
self.emit_to("completion", event)
|
||||
|
||||
self.provider = None
|
||||
del self.provider
|
||||
|
||||
def run(self):
|
||||
...
|
||||
@@ -0,0 +1,86 @@
|
||||
# Python imports
|
||||
import re
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('GtkSource', '4')
|
||||
|
||||
from gi.repository import GObject
|
||||
from gi.repository import GtkSource
|
||||
|
||||
# Application imports
|
||||
from .provider_response_cache import ProviderResponseCache
|
||||
|
||||
|
||||
|
||||
class Provider(GObject.GObject, GtkSource.CompletionProvider):
|
||||
"""
|
||||
This is a Words Completion Provider.
|
||||
# NOTE: used information from here --> https://warroom.rsmus.com/do-that-auto-complete/
|
||||
"""
|
||||
__gtype_name__ = 'WordsCompletionProvider'
|
||||
|
||||
def __init__(self):
|
||||
super(Provider, self).__init__()
|
||||
|
||||
self.response_cache: ProviderResponseCache = ProviderResponseCache()
|
||||
|
||||
|
||||
def do_get_name(self):
|
||||
return 'Words Completion'
|
||||
|
||||
def do_match(self, context):
|
||||
# Note: If provider is in interactive activation then need to check
|
||||
# view focus as otherwise non focus views start trying to grab it.
|
||||
completion = context.get_property("completion")
|
||||
if not completion.get_view().has_focus(): return
|
||||
|
||||
word = self.response_cache.get_word(context)
|
||||
if not word or len(word) < 2: return False
|
||||
|
||||
iter = self.response_cache.get_iter_correctly(context)
|
||||
iter.backward_char()
|
||||
ch = iter.get_char()
|
||||
# NOTE: Look to re-add or apply supprting logic to use spaces
|
||||
# As is it slows down the editor in certain contexts...
|
||||
# if not (ch in ('_', '.', ' ') or ch.isalnum()):
|
||||
if not (ch in ('_', '.') or ch.isalnum()):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def do_get_priority(self):
|
||||
return 0
|
||||
|
||||
def do_activate_proposal(self, proposal, iter_):
|
||||
buffer = iter_.get_buffer()
|
||||
# Note: Flag mostly intended for SourceViewsMultiInsertState
|
||||
# to insure marker processes inserted text correctly.
|
||||
buffer.is_processing_completion = True
|
||||
return False
|
||||
|
||||
def do_get_activation(self):
|
||||
""" The context for when a provider will show results """
|
||||
# return GtkSource.CompletionActivation.NONE
|
||||
# return GtkSource.CompletionActivation.USER_REQUESTED
|
||||
return GtkSource.CompletionActivation.INTERACTIVE
|
||||
|
||||
def do_populate(self, context):
|
||||
word = self.response_cache.get_word(context)
|
||||
results = self.response_cache.filter_with_context(context)
|
||||
# results = self.response_cache.filter(word)
|
||||
|
||||
# if not results:
|
||||
# results = self.response_cache.filter_with_context(context)
|
||||
|
||||
proposals = []
|
||||
for entry in results:
|
||||
proposals.append(
|
||||
self.response_cache.create_completion_item(
|
||||
entry["label"],
|
||||
entry["text"],
|
||||
entry["info"]
|
||||
)
|
||||
)
|
||||
|
||||
context.add_proposals(self, proposals, True)
|
||||
@@ -0,0 +1,139 @@
|
||||
# Python imports
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('GtkSource', '4')
|
||||
|
||||
from gi.repository import GLib
|
||||
from gi.repository import GtkSource
|
||||
|
||||
# Application imports
|
||||
from libs.event_factory import Code_Event_Types
|
||||
|
||||
from core.widgets.code.completion_providers.provider_response_cache_base import ProviderResponseCacheBase
|
||||
|
||||
|
||||
|
||||
class ProviderResponseCache(ProviderResponseCacheBase):
|
||||
def __init__(self):
|
||||
super(ProviderResponseCache, self).__init__()
|
||||
|
||||
self.matchers: dict = {}
|
||||
self._temp_timeout_id: int = None
|
||||
|
||||
|
||||
def process_file_load(self, event: Code_Event_Types.AddedNewFileEvent):
|
||||
buffer = event.file.buffer
|
||||
with ThreadPoolExecutor(max_workers = 1) as executor:
|
||||
executor.submit(self._handle_change, buffer)
|
||||
|
||||
def process_file_close(self, event: Code_Event_Types.RemovedFileEvent):
|
||||
self.matchers[event.file.buffer] = set()
|
||||
del self.matchers[event.file.buffer]
|
||||
|
||||
def process_file_save(self, event: Code_Event_Types.SavedFileEvent):
|
||||
...
|
||||
|
||||
def process_file_text_inserted(self, event: Code_Event_Types.TextInsertedEvent):
|
||||
buffer = event.buffer
|
||||
self._clear_temp_delay()
|
||||
self._set_temp_delay(buffer)
|
||||
|
||||
def process_file_delete_range(self, event: Code_Event_Types.DeleteRangeEvent):
|
||||
...
|
||||
|
||||
def _clear_temp_delay(self):
|
||||
if self._temp_timeout_id:
|
||||
GLib.source_remove(self._temp_timeout_id)
|
||||
|
||||
def _set_temp_delay(self, buffer):
|
||||
def run_refresh_update(buffer):
|
||||
with ThreadPoolExecutor(max_workers = 1) as executor:
|
||||
executor.submit(self._handle_change, buffer)
|
||||
|
||||
return False
|
||||
|
||||
self._temp_timeout_id = GLib.timeout_add(1500, run_refresh_update, buffer)
|
||||
|
||||
def _handle_change(self, buffer):
|
||||
start_itr = buffer.get_start_iter()
|
||||
end_itr = buffer.get_end_iter()
|
||||
data = buffer.get_text(start_itr, end_itr, False)
|
||||
|
||||
if not data:
|
||||
GLib.idle_add(self.load_empty_set, buffer)
|
||||
return
|
||||
|
||||
if not buffer in self.matchers:
|
||||
GLib.idle_add(self.load_as_new_set, buffer, data)
|
||||
return
|
||||
|
||||
new_words = self.get_all_words(data)
|
||||
GLib.idle_add(self.load_into_set, buffer, new_words)
|
||||
|
||||
def filter(self, word: str) -> list[dict]:
|
||||
response: list[dict] = []
|
||||
|
||||
for entry in self.matchers:
|
||||
if not word in entry: continue
|
||||
data = self.matchers[entry]
|
||||
response.append(data)
|
||||
|
||||
return response
|
||||
|
||||
def filter_with_context(self, context: GtkSource.CompletionContext) -> list[dict]:
|
||||
buffer = self.get_iter_correctly(context).get_buffer()
|
||||
word = self.get_word(context).rstrip()
|
||||
|
||||
if not buffer in self.matchers:
|
||||
self.matchers[buffer] = set()
|
||||
|
||||
response: list[dict] = []
|
||||
for entry in self.matchers[buffer]:
|
||||
if not entry.rstrip().startswith(word): continue
|
||||
|
||||
data = {
|
||||
"label": entry,
|
||||
"text": entry,
|
||||
"info": ""
|
||||
}
|
||||
|
||||
response.append(data)
|
||||
|
||||
return response
|
||||
|
||||
def load_empty_set(self, buffer):
|
||||
self.matchers[buffer] = set()
|
||||
|
||||
def load_into_set(self, buffer, new_words):
|
||||
# self.matchers[buffer].update(new_words)
|
||||
self.matchers[buffer] = new_words
|
||||
|
||||
def load_as_new_set(self, buffer, data):
|
||||
self.matchers[buffer] = self.get_all_words(data)
|
||||
|
||||
def get_all_words(self, data: str):
|
||||
words = set()
|
||||
|
||||
def is_word_char(c):
|
||||
return c.isalnum() or c == '_'
|
||||
|
||||
size = len(data)
|
||||
i = 0
|
||||
|
||||
while i < size:
|
||||
# Skip non-word characters
|
||||
while i < size and not is_word_char(data[i]):
|
||||
i += 1
|
||||
|
||||
start = i
|
||||
# Consume word characters
|
||||
while i < size and is_word_char(data[i]):
|
||||
i += 1
|
||||
|
||||
word = data[start:i]
|
||||
if not word: continue
|
||||
words.add(word)
|
||||
|
||||
return words
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Plugin Module
|
||||
"""
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Plugin Package
|
||||
"""
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "Extend Source View Menu",
|
||||
"author": "ITDominator",
|
||||
"version": "0.0.1",
|
||||
"support": "",
|
||||
"requests": {}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
from libs.event_factory import Event_Factory, Code_Event_Types
|
||||
|
||||
from plugins.plugin_types import PluginCode
|
||||
|
||||
from .source_view_menu import extend_source_view_menu
|
||||
|
||||
|
||||
|
||||
class Plugin(PluginCode):
|
||||
def __init__(self):
|
||||
super(Plugin, self).__init__()
|
||||
|
||||
def _controller_message(self, event: Code_Event_Types.CodeEvent):
|
||||
if isinstance(event, Code_Event_Types.PopulateSourceViewPopupEvent):
|
||||
extend_source_view_menu(event.buffer, event.menu)
|
||||
|
||||
def load(self):
|
||||
...
|
||||
|
||||
def unload(self):
|
||||
...
|
||||
|
||||
def run(self):
|
||||
...
|
||||
@@ -0,0 +1,68 @@
|
||||
# Python imports
|
||||
import json
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
|
||||
gi.require_version('Gtk', '3.0')
|
||||
|
||||
from gi.repository import Gtk
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
def on_case_handle(menuitem, buffer, action):
|
||||
start_itr, \
|
||||
end_itr = buffer.get_selection_bounds()
|
||||
data = buffer.get_text(start_itr, end_itr, False)
|
||||
text = data
|
||||
|
||||
if action == "on_all_upper":
|
||||
text = data.upper()
|
||||
elif action == "on_all_lower":
|
||||
text = data.lower()
|
||||
elif action == "on_invert":
|
||||
text = data.swapcase()
|
||||
elif action == "on_title":
|
||||
text = data.title()
|
||||
elif action == "on_title_strip":
|
||||
text = data.title().replace("-", "").replace("_", "").replace(" ", "")
|
||||
|
||||
buffer.begin_user_action()
|
||||
buffer.delete(start_itr, end_itr)
|
||||
buffer.insert(start_itr, text)
|
||||
buffer.end_user_action()
|
||||
|
||||
|
||||
|
||||
def extend_source_view_menu(buffer, menu):
|
||||
if not buffer.get_selection_bounds(): return
|
||||
|
||||
for child in menu.get_children():
|
||||
if not child.get_label() == "C_hange Case": continue
|
||||
menu.remove(child)
|
||||
|
||||
change_case_item = Gtk.MenuItem(label = "Change Case")
|
||||
|
||||
case_menu = Gtk.Menu()
|
||||
au_case_item = Gtk.MenuItem(label = "All Upper Case")
|
||||
al_case_item = Gtk.MenuItem(label = "All Lower Case")
|
||||
inver_case_item = Gtk.MenuItem(label = "Invert Case")
|
||||
title_case_item = Gtk.MenuItem(label = "Title Case")
|
||||
title_strip_case_item = Gtk.MenuItem(label = "Title Strip Case")
|
||||
|
||||
au_case_item.connect("activate", on_case_handle, buffer, "on_all_upper")
|
||||
al_case_item.connect("activate", on_case_handle, buffer, "on_all_lower")
|
||||
inver_case_item.connect("activate", on_case_handle, buffer, "on_invert")
|
||||
title_case_item.connect("activate", on_case_handle, buffer, "on_title")
|
||||
title_strip_case_item.connect("activate", on_case_handle, buffer, "on_title_strip")
|
||||
|
||||
case_menu.append(au_case_item)
|
||||
case_menu.append(al_case_item)
|
||||
case_menu.append(inver_case_item)
|
||||
case_menu.append(title_case_item)
|
||||
case_menu.append(title_strip_case_item)
|
||||
change_case_item.set_submenu(case_menu)
|
||||
|
||||
menu.append(change_case_item)
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Pligin Module
|
||||
"""
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Pligin Package
|
||||
"""
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "File State Watcher",
|
||||
"author": "ITDominator",
|
||||
"version": "0.0.1",
|
||||
"support": "",
|
||||
"requests": {}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
from libs.event_factory import Event_Factory, Code_Event_Types
|
||||
|
||||
from plugins.plugin_types import PluginCode
|
||||
|
||||
from .watcher_checks import *
|
||||
|
||||
|
||||
|
||||
class Plugin(PluginCode):
|
||||
def __init__(self):
|
||||
super(Plugin, self).__init__()
|
||||
|
||||
|
||||
def _controller_message(self, event: Code_Event_Types.CodeEvent):
|
||||
if not isinstance(event, Code_Event_Types.FocusedViewEvent): return
|
||||
event = Event_Factory.create_event(
|
||||
"get_file", buffer = event.view.get_buffer()
|
||||
)
|
||||
self.emit_to("files", event)
|
||||
|
||||
file = event.response
|
||||
if not file: return
|
||||
if file.ftype == "buffer": return
|
||||
|
||||
file.check_file_on_disk()
|
||||
|
||||
if file.is_deleted():
|
||||
file_is_deleted(file, self.emit)
|
||||
elif file.is_externally_modified():
|
||||
file_is_externally_modified(file, self.emit)
|
||||
|
||||
def load(self):
|
||||
...
|
||||
|
||||
def unload(self):
|
||||
...
|
||||
|
||||
def run(self):
|
||||
...
|
||||
@@ -0,0 +1,54 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
|
||||
# Application imports
|
||||
from libs.event_factory import Event_Factory, Code_Event_Types
|
||||
|
||||
|
||||
|
||||
def ask_yes_no(message):
|
||||
dialog = Gtk.MessageDialog(
|
||||
parent = None,
|
||||
flags = 0,
|
||||
message_type = Gtk.MessageType.QUESTION,
|
||||
buttons = Gtk.ButtonsType.YES_NO,
|
||||
text = message,
|
||||
)
|
||||
dialog.set_title("Confirm")
|
||||
|
||||
response = dialog.run()
|
||||
dialog.destroy()
|
||||
|
||||
return response == Gtk.ResponseType.YES
|
||||
|
||||
|
||||
def file_is_deleted(file, emit):
|
||||
file.was_deleted = True
|
||||
event = Event_Factory.create_event(
|
||||
"file_externally_deleted",
|
||||
file = file,
|
||||
buffer = file.buffer
|
||||
)
|
||||
emit(event)
|
||||
|
||||
|
||||
def file_is_externally_modified(file, emit):
|
||||
event = Event_Factory.create_event(
|
||||
"file_externally_modified",
|
||||
file = file,
|
||||
buffer = file.buffer
|
||||
)
|
||||
emit(event)
|
||||
|
||||
if not file.buffer.get_modified():
|
||||
file.reload()
|
||||
return
|
||||
|
||||
result = ask_yes_no("File has been externally modified. Reload?")
|
||||
if not result: return
|
||||
|
||||
file.reload()
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Pligin Module
|
||||
"""
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Pligin Package
|
||||
"""
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "Prettify JSON",
|
||||
"author": "ITDominator",
|
||||
"version": "0.0.1",
|
||||
"support": "",
|
||||
"requests": {}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
|
||||
gi.require_version('Gtk', '3.0')
|
||||
|
||||
from gi.repository import Gtk
|
||||
|
||||
# Application imports
|
||||
from libs.event_factory import Event_Factory, Code_Event_Types
|
||||
|
||||
from plugins.plugin_types import PluginCode
|
||||
|
||||
from .prettify_json import add_prettify_json
|
||||
|
||||
|
||||
|
||||
class Plugin(PluginCode):
|
||||
def __init__(self):
|
||||
super(Plugin, self).__init__()
|
||||
|
||||
|
||||
def _controller_message(self, event: Code_Event_Types.CodeEvent):
|
||||
if isinstance(event, Code_Event_Types.PopulateSourceViewPopupEvent):
|
||||
language = event.buffer.get_language()
|
||||
if not language: return
|
||||
|
||||
if "json" == language.get_id():
|
||||
add_prettify_json(event.buffer, event.menu)
|
||||
|
||||
def load(self):
|
||||
...
|
||||
|
||||
def unload(self):
|
||||
...
|
||||
|
||||
def run(self):
|
||||
...
|
||||
@@ -0,0 +1,29 @@
|
||||
# Python imports
|
||||
import json
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
|
||||
gi.require_version('Gtk', '3.0')
|
||||
|
||||
from gi.repository import Gtk
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
def add_prettify_json(buffer, menu):
|
||||
def on_prettify_json(menuitem, buffer):
|
||||
start_itr, \
|
||||
end_itr = buffer.get_start_iter(), buffer.get_end_iter()
|
||||
data = buffer.get_text(start_itr, end_itr, False)
|
||||
text = json.dumps(json.loads(data), separators = (',', ':'), indent = 4)
|
||||
|
||||
buffer.begin_user_action()
|
||||
buffer.delete(start_itr, end_itr)
|
||||
buffer.insert(start_itr, text)
|
||||
buffer.end_user_action()
|
||||
|
||||
item = Gtk.MenuItem(label = "Prettify JSON")
|
||||
item.connect("activate", on_prettify_json, buffer)
|
||||
menu.append(item)
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Pligin Module
|
||||
"""
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Pligin Package
|
||||
"""
|
||||
@@ -0,0 +1,134 @@
|
||||
#!/bin/bash
|
||||
|
||||
# . CONFIG.sh
|
||||
|
||||
# set -o xtrace ## To debug scripts
|
||||
# set -o errexit ## To exit on error
|
||||
# set -o errunset ## To exit if a variable is referenced but not set
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
TOOLS="$ROOT/.tools"
|
||||
NODE_DIR="$TOOLS/node"
|
||||
GRAMMARS_DIR="$ROOT/grammars"
|
||||
BUILD_DIR="$ROOT/build"
|
||||
OUTPUT="$ROOT/compiled"
|
||||
NODE_VERSION="v24.14.1"
|
||||
NODE_DIST="node-$NODE_VERSION-linux-x64"
|
||||
NODE_ARCHIVE="$TOOLS/node.tar.xz"
|
||||
NODE_URL="https://nodejs.org/dist/$NODE_VERSION/$NODE_DIST.tar.xz"
|
||||
TS_CLI_VERSION="0.22.6"
|
||||
|
||||
|
||||
LANGS=(
|
||||
tree-sitter-python
|
||||
tree-sitter-javascript
|
||||
tree-sitter-html
|
||||
tree-sitter-css
|
||||
tree-sitter-json
|
||||
tree-sitter-java
|
||||
tree-sitter-c
|
||||
tree-sitter-cpp
|
||||
tree-sitter-go
|
||||
)
|
||||
|
||||
REPOS=(
|
||||
https://github.com/tree-sitter/tree-sitter-python
|
||||
https://github.com/tree-sitter/tree-sitter-javascript
|
||||
https://github.com/tree-sitter/tree-sitter-html
|
||||
https://github.com/tree-sitter/tree-sitter-css
|
||||
https://github.com/tree-sitter/tree-sitter-json
|
||||
https://github.com/tree-sitter/tree-sitter-java
|
||||
https://github.com/tree-sitter/tree-sitter-c
|
||||
https://github.com/tree-sitter/tree-sitter-cpp
|
||||
https://github.com/tree-sitter/tree-sitter-go
|
||||
)
|
||||
|
||||
mkdir -p "$TOOLS" "$GRAMMARS_DIR" "$BUILD_DIR" "$OUTPUT"
|
||||
|
||||
ensure_node() {
|
||||
if [ -x "$NODE_DIR/bin/node" ]; then
|
||||
echo "==> Using cached Node.js"
|
||||
return
|
||||
fi
|
||||
|
||||
echo "==> Downloading Node.js $NODE_VERSION"
|
||||
wget -O "$NODE_ARCHIVE" "$NODE_URL"
|
||||
|
||||
echo "==> Extracting Node.js"
|
||||
mkdir -p "$NODE_DIR"
|
||||
tar -xf "$NODE_ARCHIVE" -C "$NODE_DIR" --strip-components=1
|
||||
rm "$NODE_ARCHIVE"
|
||||
|
||||
echo "==> Node installed at $NODE_DIR"
|
||||
}
|
||||
|
||||
ensure_tree_sitter() {
|
||||
export PATH="$NODE_DIR/bin:$PATH"
|
||||
TS="$TOOLS/node_modules/.bin/tree-sitter"
|
||||
|
||||
if [ -x "$TS" ]; then
|
||||
echo "==> Using cached tree-sitter-cli"
|
||||
return
|
||||
fi
|
||||
|
||||
echo "==> Installing tree-sitter-cli"
|
||||
cd "$TOOLS"
|
||||
npm init -y >/dev/null 2>&1 || true
|
||||
npm install tree-sitter-cli@$TS_CLI_VERSION
|
||||
}
|
||||
|
||||
sync_grammars() {
|
||||
echo "==> Syncing grammars"
|
||||
|
||||
for i in "${!LANGS[@]}"; do
|
||||
NAME="${LANGS[$i]}"
|
||||
REPO="${REPOS[$i]}"
|
||||
TARGET="$GRAMMARS_DIR/$NAME"
|
||||
|
||||
if [ -d "$TARGET/.git" ]; then
|
||||
echo "Updating $NAME"
|
||||
git -C "$TARGET" pull --depth 1
|
||||
else
|
||||
echo "Cloning $NAME"
|
||||
git clone --depth 1 "$REPO" "$TARGET"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
build_lib() {
|
||||
echo "==> Building Tree-sitter library"
|
||||
|
||||
mkdir -p "$OUTPUT"
|
||||
|
||||
PARSER_SRC=()
|
||||
INCLUDE_PATHS=()
|
||||
|
||||
for GRAMMAR in "${LANGS[@]/#/$GRAMMARS_DIR/}"; do
|
||||
echo "==> Processing grammar $GRAMMAR"
|
||||
|
||||
PARSER_SRC+=("$GRAMMAR/src/parser.c")
|
||||
|
||||
if [[ -f "$GRAMMAR/src/scanner.c" ]]; then
|
||||
PARSER_SRC+=("$GRAMMAR/src/scanner.c")
|
||||
fi
|
||||
|
||||
INCLUDE_PATHS+=("-I$GRAMMAR/src")
|
||||
done
|
||||
|
||||
gcc -shared -o "$OUTPUT/languages.so" "${PARSER_SRC[@]}" "${INCLUDE_PATHS[@]}" -fPIC
|
||||
|
||||
echo "==> Output: $OUTPUT/languages.so"
|
||||
}
|
||||
|
||||
main() {
|
||||
ensure_node
|
||||
ensure_tree_sitter
|
||||
sync_grammars
|
||||
build_lib
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user