fix: use Recommends instead of Depends for ueberzugpp in deb, add Ubuntu install guide
This commit is contained in:
parent
915b695e88
commit
1577ee5525
1302 changed files with 402483 additions and 2 deletions
|
|
@ -56,7 +56,11 @@ curl -sL https://github.com/vrubelroman/vcom/releases/download/v0.2.5/vcom_0.2.5
|
|||
sudo apt install /tmp/vcom_0.2.5_amd64.deb
|
||||
```
|
||||
|
||||
The Debian package declares `ueberzug` (or `ueberzugpp` where available) as a dependency for image preview outside `kitty`.
|
||||
The Debian package recommends `ueberzugpp` for image preview outside `kitty` (optional). To install it:
|
||||
|
||||
```bash
|
||||
sudo apt install pipx && pipx ensurepath && pipx install ueberzugpp
|
||||
```
|
||||
|
||||
### Arch Linux
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ Section: utils
|
|||
Priority: optional
|
||||
Architecture: amd64
|
||||
Maintainer: Roman Vrubel <roman@vrubel.dev>
|
||||
Depends: ueberzug | ueberzugpp
|
||||
Recommends: ueberzugpp
|
||||
Description: Terminal file manager inspired by Midnight Commander
|
||||
A two-pane terminal file manager with inspect mode and text previews.
|
||||
EOF
|
||||
|
|
|
|||
1
src/vcom-0.2.5.tar.gz
Symbolic link
1
src/vcom-0.2.5.tar.gz
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
/home/vrubel/projects/vcom/vcom-0.2.5.tar.gz
|
||||
0
src/vcom-0.2.5/.codex
Normal file
0
src/vcom-0.2.5/.codex
Normal file
86
src/vcom-0.2.5/.github/workflows/release.yml
vendored
Normal file
86
src/vcom-0.2.5/.github/workflows/release.yml
vendored
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
env:
|
||||
BIN_NAME: vcom
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
cache: true
|
||||
|
||||
- name: Run tests
|
||||
run: GOFLAGS=-mod=vendor go test ./...
|
||||
|
||||
release:
|
||||
needs: test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
cache: true
|
||||
|
||||
- name: Install system dependencies
|
||||
run: sudo apt-get update && sudo apt-get install -y dpkg-dev
|
||||
|
||||
- name: Derive release version
|
||||
shell: bash
|
||||
run: echo "VERSION=${GITHUB_REF_NAME#v}" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Vendor Go modules
|
||||
run: go mod vendor
|
||||
|
||||
- name: Build release binary
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p target/release
|
||||
GOFLAGS=-mod=vendor CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o "target/release/${BIN_NAME}" ./cmd/vcom
|
||||
|
||||
- name: Build deb package
|
||||
shell: bash
|
||||
run: ./scripts/build-deb.sh "${VERSION}"
|
||||
|
||||
- name: Bundle tarball
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
tar -C target/release -czf "${BIN_NAME}-${GITHUB_REF_NAME}-x86_64-unknown-linux-gnu.tar.gz" "${BIN_NAME}"
|
||||
|
||||
- name: Generate checksums
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
sha256sum \
|
||||
"${BIN_NAME}-${GITHUB_REF_NAME}-x86_64-unknown-linux-gnu.tar.gz" \
|
||||
"target/debian/${BIN_NAME}_${VERSION}_amd64.deb" \
|
||||
> "${BIN_NAME}-${GITHUB_REF_NAME}-checksums.txt"
|
||||
|
||||
- name: Publish release assets
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
generate_release_notes: true
|
||||
files: |
|
||||
vcom-${{ github.ref_name }}-x86_64-unknown-linux-gnu.tar.gz
|
||||
vcom-${{ github.ref_name }}-checksums.txt
|
||||
target/debian/vcom_${{ env.VERSION }}_amd64.deb
|
||||
2
src/vcom-0.2.5/.gitignore
vendored
Normal file
2
src/vcom-0.2.5/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
target/
|
||||
vcom
|
||||
674
src/vcom-0.2.5/LICENSE
Normal file
674
src/vcom-0.2.5/LICENSE
Normal file
|
|
@ -0,0 +1,674 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
34
src/vcom-0.2.5/PKGBUILD
Normal file
34
src/vcom-0.2.5/PKGBUILD
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
pkgname=vcom
|
||||
pkgver=0.2.5
|
||||
pkgrel=1
|
||||
pkgdesc="Terminal file manager inspired by Midnight Commander"
|
||||
arch=("x86_64" "aarch64")
|
||||
url="https://github.com/vrubelroman/vcom"
|
||||
license=("MIT")
|
||||
depends=("glibc" "ueberzugpp")
|
||||
makedepends=("go")
|
||||
source=("$pkgname-$pkgver.tar.gz::$url/archive/refs/tags/v$pkgver.tar.gz")
|
||||
sha256sums=("SKIP")
|
||||
|
||||
build() {
|
||||
cd "$srcdir/$pkgname-$pkgver"
|
||||
export CGO_ENABLED=0
|
||||
export GOFLAGS="-mod=vendor -trimpath"
|
||||
go build -ldflags="-s -w" -o "target/release/vcom" ./cmd/vcom
|
||||
}
|
||||
|
||||
check() {
|
||||
cd "$srcdir/$pkgname-$pkgver"
|
||||
export CGO_ENABLED=0
|
||||
export GOFLAGS="-mod=vendor"
|
||||
go test ./...
|
||||
}
|
||||
|
||||
package() {
|
||||
cd "$srcdir/$pkgname-$pkgver"
|
||||
|
||||
install -Dm755 "target/release/vcom" "$pkgdir/usr/bin/vcom"
|
||||
install -Dm644 "README.md" "$pkgdir/usr/share/doc/vcom/README.md"
|
||||
install -Dm644 "vcom.toml" "$pkgdir/usr/share/doc/vcom/vcom.toml"
|
||||
install -Dm644 "LICENSE" "$pkgdir/usr/share/licenses/vcom/LICENSE"
|
||||
}
|
||||
204
src/vcom-0.2.5/README.md
Normal file
204
src/vcom-0.2.5/README.md
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
# vcom
|
||||
|
||||
`vcom` is a two-pane terminal file manager with a fast built-in info/preview panel for the active selection.
|
||||
|
||||
## Why vcom
|
||||
|
||||
- Two-pane workflow focused on keyboard speed
|
||||
- Built-in preview/info pane for the active selection
|
||||
- Asynchronous copy/move with progress and background mode
|
||||
- Theme support and configurable layout/columns
|
||||
|
||||
## Screenshots
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
## Build and run
|
||||
|
||||
Run directly:
|
||||
|
||||
```bash
|
||||
go run ./cmd/vcom
|
||||
```
|
||||
|
||||
Build local binary:
|
||||
|
||||
```bash
|
||||
go build -o vcom ./cmd/vcom
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
### NixOS / Nix
|
||||
|
||||
Run directly from the flake:
|
||||
|
||||
```bash
|
||||
nix run github:vrubelroman/vcom?ref=v0.2.5
|
||||
```
|
||||
|
||||
Install into user profile:
|
||||
|
||||
```bash
|
||||
nix profile add github:vrubelroman/vcom?ref=v0.2.5
|
||||
```
|
||||
|
||||
The Nix package wraps `vcom` with `ueberzugpp` in `PATH`, so image preview works in non-`kitty` terminals out of the box.
|
||||
|
||||
### Debian / Ubuntu
|
||||
|
||||
Download and install the latest release:
|
||||
|
||||
```bash
|
||||
curl -sL https://github.com/vrubelroman/vcom/releases/download/v0.2.5/vcom_0.2.4_amd64.deb -o /tmp/vcom_0.2.4_amd64.deb
|
||||
sudo apt install /tmp/vcom_0.2.4_amd64.deb
|
||||
```
|
||||
|
||||
The Debian package declares `ueberzug` (or `ueberzugpp` where available) as a dependency for image preview outside `kitty`.
|
||||
|
||||
### Arch Linux
|
||||
|
||||
A `PKGBUILD` is included in the repository:
|
||||
|
||||
```bash
|
||||
makepkg -si
|
||||
```
|
||||
|
||||
The Arch package depends on `ueberzugpp`, so non-`kitty` image preview is installed together with `vcom`.
|
||||
|
||||
## Font requirement (icons)
|
||||
|
||||
For file icons, `vcom` expects a Nerd Font in your terminal profile.
|
||||
|
||||
Default behavior is `ui.icon_mode = "auto"`:
|
||||
|
||||
- if a Nerd Font is detected, `vcom` uses Nerd icons
|
||||
- if not, `vcom` falls back to ASCII icons automatically
|
||||
|
||||
You can force behavior in config:
|
||||
|
||||
- `ui.icon_mode = "nerd"`: always use Nerd icons
|
||||
- `ui.icon_mode = "ascii"`: always use ASCII icons
|
||||
|
||||
### Installing a Nerd Font
|
||||
|
||||
**Ubuntu / Debian:**
|
||||
|
||||
```bash
|
||||
wget -qO /tmp/JetBrainsMono.zip https://github.com/ryanoasis/nerd-fonts/releases/latest/download/JetBrainsMono.zip
|
||||
mkdir -p ~/.local/share/fonts/JetBrainsMonoNerd
|
||||
unzip -o /tmp/JetBrainsMono.zip -d ~/.local/share/fonts/JetBrainsMonoNerd
|
||||
fc-cache -fv
|
||||
```
|
||||
|
||||
**Arch Linux:**
|
||||
|
||||
```bash
|
||||
sudo pacman -S ttf-jetbrains-mono-nerd
|
||||
```
|
||||
|
||||
Or via AUR helper:
|
||||
|
||||
```bash
|
||||
yay -S nerd-fonts-jetbrains-mono
|
||||
```
|
||||
|
||||
**NixOS / Nix:**
|
||||
|
||||
Add to your `/etc/nixos/configuration.nix`:
|
||||
|
||||
```nix
|
||||
fonts.packages = with pkgs; [ nerd-fonts.jetbrains-mono ];
|
||||
```
|
||||
|
||||
Or install imperatively:
|
||||
|
||||
```bash
|
||||
nix profile install nixpkgs#nerd-fonts.jetbrains-mono
|
||||
```
|
||||
|
||||
### Configuring terminal to use the installed Nerd Font
|
||||
|
||||
After installing, set `JetBrainsMono Nerd Font` (or another Nerd Font) as the terminal font:
|
||||
|
||||
- **GNOME Terminal:** `Preferences → Profile → Text → Custom font` → choose `JetBrainsMono Nerd Font`
|
||||
- **Konsole:** `Settings → Edit Current Profile → Appearance` → choose a Nerd Font profile
|
||||
- **Alacritty:** set `font.normal.family: "JetBrainsMono Nerd Font"` in `~/.config/alacritty/alacritty.yml`
|
||||
- **Kitty:** set `font_family JetBrainsMono Nerd Font` in `~/.config/kitty/kitty.conf`
|
||||
- **Foot:** set `font=JetBrainsMono Nerd Font:size=11` in `~/.config/foot/foot.ini`
|
||||
- **WezTerm:** set `font = wezterm.font("JetBrainsMono Nerd Font")` in `~/.config/wezterm/wezterm.lua`
|
||||
- **Windows Terminal:** `Settings → Profiles → Defaults → Appearance → Font face` → choose `JetBrainsMono Nerd Font`
|
||||
|
||||
Preview mode (`F9` / `i`) temporarily replaces the inactive pane and shows:
|
||||
|
||||
- directory listing preview
|
||||
- text file preview with syntax highlighting
|
||||
- image metadata (format + dimensions)
|
||||
- safe fallback for binary files
|
||||
|
||||
## Configuration
|
||||
|
||||
Optional config lookup order:
|
||||
|
||||
1. `-config /path/to/vcom.toml`
|
||||
2. `./vcom.toml`
|
||||
3. `./config/vcom.toml`
|
||||
4. `$XDG_CONFIG_HOME/vcom/vcom.toml`
|
||||
5. `~/.config/vcom/vcom.toml`
|
||||
|
||||
Reference config: [vcom.toml](/home/vrubel/projects/vcom/vcom.toml)
|
||||
|
||||
Icon mode example:
|
||||
|
||||
```toml
|
||||
[ui]
|
||||
icon_mode = "auto" # auto | nerd | ascii
|
||||
```
|
||||
|
||||
## Themes
|
||||
|
||||
Built-in themes (press `t` to open theme selector or set `ui.theme` in config):
|
||||
|
||||
- `catppuccin-mocha` (default)
|
||||
- `catppuccin-macchiato`
|
||||
- `catppuccin-lavender`
|
||||
- `tokyo-night`
|
||||
- `gruvbox-dark`
|
||||
- `nord`
|
||||
- `one-dark`
|
||||
- `everforest`
|
||||
- `github-dark`
|
||||
- `ayu-dark`
|
||||
- `breeze`
|
||||
- `cyberpunk`
|
||||
- `dracula`
|
||||
- `eldritch`
|
||||
- `kanagawa`
|
||||
- `kanagawa-paper`
|
||||
- `rose-pine`
|
||||
- `solarized-dark`
|
||||
- `vesper`
|
||||
|
||||
## Releases
|
||||
|
||||
Pushing a tag like `v0.2.5` triggers GitHub Actions release workflow (`.github/workflows/release.yml`) which:
|
||||
|
||||
- runs tests
|
||||
- vendors Go modules
|
||||
- builds release binary
|
||||
- builds Debian package
|
||||
- publishes release assets
|
||||
|
||||
Release artifacts:
|
||||
|
||||
- `vcom-v0.2.5-x86_64-unknown-linux-gnu.tar.gz`
|
||||
- `vcom_0.2.4_amd64.deb`
|
||||
- `vcom-v0.2.5-checksums.txt`
|
||||
|
||||
## Notes
|
||||
|
||||
- File creation time depends on filesystem/OS support; unavailable values are shown as `n/a`.
|
||||
|
||||
Architecture notes: [docs/architecture.md](/home/vrubel/projects/vcom/docs/architecture.md)
|
||||
196
src/vcom-0.2.5/docs/architecture.md
Normal file
196
src/vcom-0.2.5/docs/architecture.md
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
# Architecture
|
||||
|
||||
## Why this shape
|
||||
|
||||
The project should not become a giant `main.go` that mixes rendering, key handling, filesystem I/O and business rules. The architecture is split so the UI remains reactive and file operations stay isolated.
|
||||
|
||||
## High-level modules
|
||||
|
||||
- `cmd/vcom`
|
||||
Entry point, config loading, program startup.
|
||||
- `internal/config`
|
||||
Config schema, defaults, search paths and TOML parsing.
|
||||
- `internal/fs`
|
||||
Filesystem model, directory scanning, previews and file operations.
|
||||
- `internal/theme`
|
||||
Theme presets and style tokens.
|
||||
- `internal/ui`
|
||||
Bubble Tea model, key map, pane rendering, modal flow and layout.
|
||||
|
||||
## State model
|
||||
|
||||
The Bubble Tea root model owns:
|
||||
|
||||
- terminal dimensions
|
||||
- full config
|
||||
- active browser pane
|
||||
- left and right pane state
|
||||
- center preview state
|
||||
- transient modal state
|
||||
- busy/status state for async work
|
||||
|
||||
This keeps the Elm-style update loop simple:
|
||||
|
||||
1. key or resize event arrives
|
||||
2. model decides whether to mutate local state or start async work
|
||||
3. async work returns a typed message
|
||||
4. view renders from plain state
|
||||
|
||||
## Pane model
|
||||
|
||||
Each side pane stores:
|
||||
|
||||
- current path
|
||||
- scanned entries
|
||||
- cursor index
|
||||
- scroll offset
|
||||
- cached directory sizes for entries calculated on demand
|
||||
|
||||
This is intentionally independent from rendering details, so the pane can be unit-tested later without Lip Gloss.
|
||||
|
||||
## Preview pipeline
|
||||
|
||||
The center pane is driven only by the current selection from the active side pane.
|
||||
|
||||
Preview strategies:
|
||||
|
||||
- directory preview
|
||||
Shows child entries and summary data.
|
||||
- text preview
|
||||
Reads up to a configured byte limit and displays text safely.
|
||||
- image preview
|
||||
Detects dimensions and format through standard Go image decoders.
|
||||
- binary fallback
|
||||
Avoids dumping junk bytes into the terminal.
|
||||
|
||||
Metadata shown above the preview:
|
||||
|
||||
- kind
|
||||
- full path
|
||||
- size
|
||||
- created time
|
||||
- modified time
|
||||
- permissions
|
||||
|
||||
Directory size is expensive, so it is not calculated eagerly. Pressing `Space` starts an async scan and updates both:
|
||||
|
||||
- side-pane size column
|
||||
- preview metadata widget
|
||||
|
||||
## Configuration design
|
||||
|
||||
TOML is used because:
|
||||
|
||||
- it is readable in terminal workflows
|
||||
- comments are first-class
|
||||
- optional settings can stay commented out for manual enable/disable
|
||||
|
||||
The example config enables only MC-like columns by default:
|
||||
|
||||
- `name`
|
||||
- `size`
|
||||
- `modified`
|
||||
|
||||
Additional columns are left commented out so users can literally uncomment them:
|
||||
|
||||
- `created`
|
||||
- `permissions`
|
||||
- `extension`
|
||||
|
||||
Other useful config areas:
|
||||
|
||||
- startup paths for left and right panes
|
||||
- theme preset
|
||||
- hidden file visibility
|
||||
- sort field and reverse mode
|
||||
- center pane width ratio
|
||||
- confirmation behavior
|
||||
- preview byte limits
|
||||
- text wrapping in preview
|
||||
- compact path display
|
||||
|
||||
## Rendering strategy
|
||||
|
||||
The side panes are custom-rendered rather than built on top of the generic table bubble.
|
||||
|
||||
Reasoning:
|
||||
|
||||
- MC-like directory panes are not just tables
|
||||
- we need tight control over selection styling, truncation and empty states
|
||||
- the center pane uses a different rendering model than the side panes
|
||||
|
||||
Bubble usage:
|
||||
|
||||
- `bubbletea`
|
||||
event loop and async commands
|
||||
- `bubbles/key`
|
||||
declarative key bindings
|
||||
- `bubbles/textinput`
|
||||
mkdir modal
|
||||
- `bubbles/viewport`
|
||||
preview scrolling surface
|
||||
- `lipgloss`
|
||||
all layout and styling
|
||||
|
||||
## Operations
|
||||
|
||||
The first operational slice is intentionally MC-core:
|
||||
|
||||
- copy selected entry to passive pane directory
|
||||
- move selected entry to passive pane directory
|
||||
- create directory in active pane
|
||||
- delete selected entry
|
||||
- refresh active pane
|
||||
|
||||
These operations are dispatched asynchronously to avoid freezing the TUI on large directories.
|
||||
|
||||
Overwrite handling is decided before the async operation begins:
|
||||
|
||||
- if the target does not exist, the operation runs directly
|
||||
- if the target exists and overwrite confirmation is enabled, the UI opens a modal
|
||||
- if overwrite confirmation is disabled, the operation replaces the target immediately
|
||||
|
||||
## Theme system
|
||||
|
||||
Themes are token-based rather than style-object based.
|
||||
|
||||
Each preset defines semantic colors:
|
||||
|
||||
- background
|
||||
- panel
|
||||
- panel inactive
|
||||
- border
|
||||
- border active
|
||||
- text
|
||||
- muted text
|
||||
- accent
|
||||
- selection
|
||||
- warning
|
||||
- danger
|
||||
- footer key
|
||||
|
||||
This allows future user-defined themes without touching UI logic.
|
||||
|
||||
The current UI also supports runtime theme cycling, but config remains the source of truth for default startup appearance.
|
||||
|
||||
## Extension points
|
||||
|
||||
The architecture is designed to accept later additions without a rewrite:
|
||||
|
||||
- multi-select and batch operations
|
||||
- file viewer/editor integration
|
||||
- terminal image protocols
|
||||
- tab history per pane
|
||||
- bookmarks and quick-jump
|
||||
- sort modes
|
||||
- archive browsing
|
||||
- search/filter overlay
|
||||
- pluggable preview providers
|
||||
|
||||
## Constraints worth keeping
|
||||
|
||||
- never calculate directory sizes during normal navigation
|
||||
- never read full large files into memory for preview
|
||||
- keep filesystem code out of `View()`
|
||||
- keep styling decisions out of `internal/fs`
|
||||
- prefer typed Tea messages over stringly-typed status events
|
||||
128
src/vcom-0.2.5/docs/project-description.md
Normal file
128
src/vcom-0.2.5/docs/project-description.md
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
# vcom: описание проекта и реализованного функционала
|
||||
|
||||
`vcom` — терминальный файловый менеджер в стиле Midnight Commander, написанный на Go на базе Bubble Tea.
|
||||
|
||||
## 1. Основная концепция
|
||||
|
||||
Приложение работает в двухпанельном режиме:
|
||||
- левая файловая панель,
|
||||
- правая файловая панель.
|
||||
|
||||
Активная панель управляется с клавиатуры и мыши, неактивная сохраняет своё состояние (путь, позицию курсора).
|
||||
|
||||
## 2. Реализованные режимы интерфейса
|
||||
|
||||
- Двухпанельный браузер директорий.
|
||||
- Режим Info/Preview (`i`): неактивная панель временно заменяется превью выбранного элемента из активной панели.
|
||||
- Режим выделения текста в превью (`Ctrl+t`) для текстовых файлов.
|
||||
- Модальные окна (подтверждения, прогресс операций, help, уведомления).
|
||||
|
||||
## 3. Навигация и просмотр
|
||||
|
||||
- Перемещение по списку: `j/k`, `Up/Down`, `PgUp/PgDn`.
|
||||
- Переключение активной панели: `Tab`, `h`, `l`.
|
||||
- Вход в директорию: `Enter` / `Right`.
|
||||
- Переход в родительскую директорию: `Backspace` / `Left`.
|
||||
- Обновление панелей: `r`.
|
||||
- Внешний просмотр файла: `F3` (`$PAGER` при наличии).
|
||||
- Внешнее редактирование файла: `F4` (`$VISUAL/$EDITOR` или fallback-редакторы).
|
||||
|
||||
## 4. Операции с файлами и директориями
|
||||
|
||||
- `F5` — копирование.
|
||||
- `F6` — перемещение.
|
||||
- `F7` — создание директории.
|
||||
- `F8` — удаление.
|
||||
|
||||
Операции copy/move реализованы с:
|
||||
- предварительным диалогом подтверждения,
|
||||
- подсчётом объёма и количества файлов,
|
||||
- прогрессом по байтам и по количеству файлов,
|
||||
- возможностью отправить операцию в фон (`b`),
|
||||
- уведомлением о завершении фоновой операции.
|
||||
|
||||
Подтверждение overwrite учитывается для существующих целей.
|
||||
|
||||
## 5. Мультивыделение
|
||||
|
||||
Реализовано выделение элементов с клавиатуры:
|
||||
- `Shift+Up/Shift+Down` (а также `Shift+K/Shift+J`) добавляют/снимают выделение на текущем проходе.
|
||||
- Повторный проход по уже выделенному элементу снимает его выделение (toggle).
|
||||
- `Esc` очищает выделение активной панели.
|
||||
|
||||
Если есть выделенные элементы, `F5/F6/F8` применяются ко всему выделенному набору.
|
||||
Если выделения нет — операция применяется к текущему элементу под курсором.
|
||||
|
||||
## 6. Работа мыши
|
||||
|
||||
- ЛКМ: выбор элемента и активация панели.
|
||||
- Двойной ЛКМ: открытие элемента.
|
||||
- ПКМ: переключение режима Info/Preview для выбранного элемента.
|
||||
- Колесо мыши: прокрутка списка; в preview-области — прокрутка содержимого превью.
|
||||
|
||||
## 7. Help-окно
|
||||
|
||||
`F1` или `?` открывает справку по управлению.
|
||||
|
||||
Особенности help:
|
||||
- логические блоки (Navigation, View and Panels, Dialogs and Transfers, Mouse),
|
||||
- цветовое оформление заголовков и элементов на основе активной темы,
|
||||
- закрытие по `F1`, `?`, `Esc`, `Enter`, `q`.
|
||||
|
||||
## 8. Модальные окна и закрытие
|
||||
|
||||
Во всех модальных окнах поддержано закрытие по `q`.
|
||||
|
||||
Поведение в прогрессе copy/move:
|
||||
- `q` не прерывает операцию,
|
||||
- окно закрывается,
|
||||
- операция продолжается в фоне.
|
||||
|
||||
## 9. Визуальные доработки
|
||||
|
||||
- Убрана верхняя title-строка приложения.
|
||||
- Убраны текстовые лейблы `LEFT/RIGHT` в заголовках панелей.
|
||||
- Убрана строка `CONTENT` в preview-панели.
|
||||
- Путь активной панели сделан жирным и в цвете `TextFile` текущей темы.
|
||||
- Подсветка курсора отображается только в активной панели.
|
||||
- Выделенные (marked) элементы подсвечиваются цветом `Danger` темы по всей строке.
|
||||
|
||||
## 10. Конфигурация
|
||||
|
||||
Поддерживается TOML-конфиг (`vcom.toml`), включая:
|
||||
- стартовые директории,
|
||||
- визуальные параметры UI,
|
||||
- набор и видимость колонок,
|
||||
- сортировку,
|
||||
- поведение превью,
|
||||
- поведение подтверждений и операций.
|
||||
|
||||
## 11. Технологический стек
|
||||
|
||||
- Go
|
||||
- Bubble Tea (`github.com/charmbracelet/bubbletea`)
|
||||
- Bubbles
|
||||
- Lip Gloss
|
||||
- TOML (`pelletier/go-toml/v2`)
|
||||
|
||||
## 12. Сборка и запуск
|
||||
|
||||
Локально:
|
||||
|
||||
```bash
|
||||
go run ./cmd/vcom
|
||||
```
|
||||
|
||||
Сборка бинаря:
|
||||
|
||||
```bash
|
||||
go build -o vcom ./cmd/vcom
|
||||
```
|
||||
|
||||
Nix:
|
||||
|
||||
```bash
|
||||
nix run .
|
||||
```
|
||||
|
||||
Для запуска тегов из GitHub-репозитория рекомендуется использовать версии с `v0.1.1` и выше.
|
||||
BIN
src/vcom-0.2.5/docs/screen.png
Normal file
BIN
src/vcom-0.2.5/docs/screen.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 209 KiB |
BIN
src/vcom-0.2.5/docs/screen2.png
Normal file
BIN
src/vcom-0.2.5/docs/screen2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 358 KiB |
BIN
src/vcom-0.2.5/docs/screen3.png
Normal file
BIN
src/vcom-0.2.5/docs/screen3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 327 KiB |
61
src/vcom-0.2.5/flake.lock
generated
Normal file
61
src/vcom-0.2.5/flake.lock
generated
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1776548001,
|
||||
"narHash": "sha256-ZSK0NL4a1BwVbbTBoSnWgbJy9HeZFXLYQizjb2DPF24=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "b12141ef619e0a9c1c84dc8c684040326f27cdcc",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
59
src/vcom-0.2.5/flake.nix
Normal file
59
src/vcom-0.2.5/flake.nix
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
{
|
||||
description = "vcom terminal file manager";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, flake-utils }:
|
||||
flake-utils.lib.eachDefaultSystem (system:
|
||||
let
|
||||
pkgs = import nixpkgs { inherit system; };
|
||||
lib = pkgs.lib;
|
||||
packageBase = pkgs.buildGoModule {
|
||||
pname = "vcom";
|
||||
version = "0.2.5";
|
||||
src = ./.;
|
||||
vendorHash = null;
|
||||
|
||||
subPackages = [ "cmd/vcom" ];
|
||||
|
||||
ldflags = [
|
||||
"-s"
|
||||
"-w"
|
||||
];
|
||||
|
||||
meta = with lib; {
|
||||
description = "Terminal file manager inspired by Midnight Commander";
|
||||
homepage = "https://github.com/vrubelroman/vcom";
|
||||
license = licenses.mit;
|
||||
mainProgram = "vcom";
|
||||
platforms = platforms.linux;
|
||||
};
|
||||
};
|
||||
package = pkgs.symlinkJoin {
|
||||
name = "vcom";
|
||||
paths = [ packageBase ];
|
||||
nativeBuildInputs = [ pkgs.makeWrapper ];
|
||||
postBuild = ''
|
||||
wrapProgram "$out/bin/vcom" \
|
||||
--prefix PATH : "${lib.makeBinPath [ pkgs.ueberzugpp ]}"
|
||||
'';
|
||||
};
|
||||
in {
|
||||
packages.default = package;
|
||||
|
||||
apps.default = {
|
||||
type = "app";
|
||||
program = "${package}/bin/vcom";
|
||||
};
|
||||
|
||||
devShells.default = pkgs.mkShell {
|
||||
packages = with pkgs; [
|
||||
go
|
||||
gopls
|
||||
];
|
||||
};
|
||||
});
|
||||
}
|
||||
36
src/vcom-0.2.5/go.mod
Normal file
36
src/vcom-0.2.5/go.mod
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
module vcom
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
github.com/alecthomas/chroma/v2 v2.23.1
|
||||
github.com/charmbracelet/bubbles v0.21.0
|
||||
github.com/charmbracelet/bubbletea v1.3.10
|
||||
github.com/charmbracelet/lipgloss v1.1.0
|
||||
github.com/pelletier/go-toml/v2 v2.2.4
|
||||
golang.org/x/sys v0.43.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/atotto/clipboard v0.1.4 // indirect
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
|
||||
github.com/charmbracelet/x/ansi v0.10.1 // indirect
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
|
||||
github.com/charmbracelet/x/term v0.2.1 // indirect
|
||||
github.com/dlclark/regexp2 v1.11.5 // indirect
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
|
||||
github.com/kr/fs v0.1.0 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-localereader v0.0.1 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
|
||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||
github.com/muesli/termenv v0.16.0 // indirect
|
||||
github.com/pkg/sftp v1.13.10 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
golang.org/x/crypto v0.50.0 // indirect
|
||||
golang.org/x/text v0.36.0 // indirect
|
||||
)
|
||||
63
src/vcom-0.2.5/go.sum
Normal file
63
src/vcom-0.2.5/go.sum
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
github.com/alecthomas/chroma/v2 v2.23.1 h1:nv2AVZdTyClGbVQkIzlDm/rnhk1E9bU9nXwmZ/Vk/iY=
|
||||
github.com/alecthomas/chroma/v2 v2.23.1/go.mod h1:NqVhfBR0lte5Ouh3DcthuUCTUpDC9cxBOfyMbMQPs3o=
|
||||
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
||||
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||
github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs=
|
||||
github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg=
|
||||
github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw=
|
||||
github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
|
||||
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
|
||||
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
|
||||
github.com/charmbracelet/x/ansi v0.10.1 h1:rL3Koar5XvX0pHGfovN03f5cxLbCF2YvLeyz7D2jVDQ=
|
||||
github.com/charmbracelet/x/ansi v0.10.1/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
|
||||
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
|
||||
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
|
||||
github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
|
||||
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
|
||||
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
|
||||
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
|
||||
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
||||
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
|
||||
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/pkg/sftp v1.13.10 h1:+5FbKNTe5Z9aspU88DPIKJ9z2KZoaGCu6Sr6kKR/5mU=
|
||||
github.com/pkg/sftp v1.13.10/go.mod h1:bJ1a7uDhrX/4OII+agvy28lzRvQrmIQuaHrcI1HbeGA=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
|
||||
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
|
||||
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E=
|
||||
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
|
||||
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
|
||||
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
|
||||
273
src/vcom-0.2.5/internal/config/config.go
Normal file
273
src/vcom-0.2.5/internal/config/config.go
Normal file
|
|
@ -0,0 +1,273 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
toml "github.com/pelletier/go-toml/v2"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Startup StartupConfig `toml:"startup"`
|
||||
UI UIConfig `toml:"ui"`
|
||||
Browser BrowserConfig `toml:"browser"`
|
||||
Preview PreviewConfig `toml:"preview"`
|
||||
Behavior BehaviorConfig `toml:"behavior"`
|
||||
}
|
||||
|
||||
type StartupConfig struct {
|
||||
LeftPath string `toml:"left_path"`
|
||||
RightPath string `toml:"right_path"`
|
||||
}
|
||||
|
||||
type UIConfig struct {
|
||||
AppTitle string `toml:"app_title"`
|
||||
Theme string `toml:"theme"`
|
||||
IconMode string `toml:"icon_mode"`
|
||||
ShowTitleBar bool `toml:"show_title_bar"`
|
||||
ShowFooter bool `toml:"show_footer"`
|
||||
Border string `toml:"border"`
|
||||
PathDisplay string `toml:"path_display"`
|
||||
PaneGap int `toml:"pane_gap"`
|
||||
CenterWidthPercent int `toml:"center_width_percent"`
|
||||
}
|
||||
|
||||
type BrowserConfig struct {
|
||||
ShowHidden bool `toml:"show_hidden"`
|
||||
DirsFirst bool `toml:"dirs_first"`
|
||||
HumanReadableSize bool `toml:"human_readable_size"`
|
||||
Sort SortConfig `toml:"sort"`
|
||||
Columns BrowserColumnsConfig `toml:"columns"`
|
||||
}
|
||||
|
||||
type SortConfig struct {
|
||||
By string `toml:"by"`
|
||||
Reverse bool `toml:"reverse"`
|
||||
}
|
||||
|
||||
type BrowserColumnsConfig struct {
|
||||
Name bool `toml:"name"`
|
||||
Size bool `toml:"size"`
|
||||
Modified bool `toml:"modified"`
|
||||
Created bool `toml:"created"`
|
||||
Permissions bool `toml:"permissions"`
|
||||
Extension bool `toml:"extension"`
|
||||
}
|
||||
|
||||
type PreviewConfig struct {
|
||||
ShowMetadata bool `toml:"show_metadata"`
|
||||
WrapText bool `toml:"wrap_text"`
|
||||
MaxPreviewBytes int64 `toml:"max_preview_bytes"`
|
||||
DirectoryPreviewLimit int `toml:"directory_preview_limit"`
|
||||
}
|
||||
|
||||
type BehaviorConfig struct {
|
||||
ConfirmDelete bool `toml:"confirm_delete"`
|
||||
ConfirmOverwrite bool `toml:"confirm_overwrite"`
|
||||
CalculateDirSizeOnSpace bool `toml:"calculate_dir_size_on_space"`
|
||||
FollowSymlinks bool `toml:"follow_symlinks"`
|
||||
}
|
||||
|
||||
func Default() Config {
|
||||
return Config{
|
||||
Startup: StartupConfig{},
|
||||
UI: UIConfig{
|
||||
AppTitle: "vcom",
|
||||
Theme: "catppuccin-mocha",
|
||||
IconMode: "auto",
|
||||
ShowTitleBar: true,
|
||||
ShowFooter: true,
|
||||
Border: "rounded",
|
||||
PathDisplay: "short",
|
||||
PaneGap: 1,
|
||||
CenterWidthPercent: 30,
|
||||
},
|
||||
Browser: BrowserConfig{
|
||||
ShowHidden: true,
|
||||
DirsFirst: true,
|
||||
HumanReadableSize: true,
|
||||
Sort: SortConfig{
|
||||
By: "name",
|
||||
Reverse: false,
|
||||
},
|
||||
Columns: BrowserColumnsConfig{
|
||||
Name: true,
|
||||
Size: true,
|
||||
Modified: true,
|
||||
},
|
||||
},
|
||||
Preview: PreviewConfig{
|
||||
ShowMetadata: true,
|
||||
WrapText: false,
|
||||
MaxPreviewBytes: 64 * 1024,
|
||||
DirectoryPreviewLimit: 80,
|
||||
},
|
||||
Behavior: BehaviorConfig{
|
||||
ConfirmDelete: true,
|
||||
ConfirmOverwrite: true,
|
||||
CalculateDirSizeOnSpace: true,
|
||||
FollowSymlinks: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func Load(explicitPath string) (Config, string, error) {
|
||||
cfg := Default()
|
||||
|
||||
path, found, err := resolvePath(explicitPath)
|
||||
if err != nil {
|
||||
return Config{}, "", err
|
||||
}
|
||||
if !found {
|
||||
return cfg, "", nil
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return Config{}, "", fmt.Errorf("read %s: %w", path, err)
|
||||
}
|
||||
if err := toml.Unmarshal(data, &cfg); err != nil {
|
||||
return Config{}, "", fmt.Errorf("parse %s: %w", path, err)
|
||||
}
|
||||
|
||||
if err := cfg.Validate(); err != nil {
|
||||
return Config{}, "", fmt.Errorf("validate %s: %w", path, err)
|
||||
}
|
||||
|
||||
return cfg, path, nil
|
||||
}
|
||||
|
||||
func Save(cfg Config, path string) (string, error) {
|
||||
if err := cfg.Validate(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
targetPath := strings.TrimSpace(path)
|
||||
if targetPath == "" {
|
||||
var err error
|
||||
targetPath, err = DefaultUserPath()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
absPath, err := filepath.Abs(targetPath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("resolve %s: %w", targetPath, err)
|
||||
}
|
||||
if err := os.MkdirAll(filepath.Dir(absPath), 0o755); err != nil {
|
||||
return "", fmt.Errorf("mkdir %s: %w", filepath.Dir(absPath), err)
|
||||
}
|
||||
|
||||
data, err := toml.Marshal(cfg)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("marshal config: %w", err)
|
||||
}
|
||||
if err := os.WriteFile(absPath, data, 0o644); err != nil {
|
||||
return "", fmt.Errorf("write %s: %w", absPath, err)
|
||||
}
|
||||
return absPath, nil
|
||||
}
|
||||
|
||||
func (c *Config) Validate() error {
|
||||
if c.UI.Theme == "" {
|
||||
return errors.New("ui.theme must not be empty")
|
||||
}
|
||||
switch strings.ToLower(strings.TrimSpace(c.UI.IconMode)) {
|
||||
case "", "auto":
|
||||
c.UI.IconMode = "auto"
|
||||
case "nerd", "ascii":
|
||||
default:
|
||||
return errors.New("ui.icon_mode must be one of: auto, nerd, ascii")
|
||||
}
|
||||
if strings.TrimSpace(c.UI.AppTitle) == "" {
|
||||
c.UI.AppTitle = "vcom"
|
||||
}
|
||||
if c.Preview.MaxPreviewBytes <= 0 {
|
||||
return errors.New("preview.max_preview_bytes must be > 0")
|
||||
}
|
||||
if c.Preview.DirectoryPreviewLimit <= 0 {
|
||||
return errors.New("preview.directory_preview_limit must be > 0")
|
||||
}
|
||||
if !c.Browser.Columns.Name {
|
||||
return errors.New("browser.columns.name must stay enabled")
|
||||
}
|
||||
if strings.TrimSpace(c.UI.PathDisplay) == "" {
|
||||
c.UI.PathDisplay = "short"
|
||||
}
|
||||
if c.UI.PaneGap < 0 || c.UI.PaneGap > 4 {
|
||||
return errors.New("ui.pane_gap must be between 0 and 4")
|
||||
}
|
||||
if c.UI.CenterWidthPercent < 20 || c.UI.CenterWidthPercent > 60 {
|
||||
return errors.New("ui.center_width_percent must be between 20 and 60")
|
||||
}
|
||||
switch strings.ToLower(strings.TrimSpace(c.Browser.Sort.By)) {
|
||||
case "", "name":
|
||||
c.Browser.Sort.By = "name"
|
||||
case "modified", "size", "created", "extension":
|
||||
default:
|
||||
return fmt.Errorf("browser.sort.by must be one of: name, modified, size, created, extension")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func resolvePath(explicitPath string) (string, bool, error) {
|
||||
var candidates []string
|
||||
|
||||
if explicitPath != "" {
|
||||
candidates = append(candidates, explicitPath)
|
||||
} else {
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return "", false, fmt.Errorf("resolve home dir: %w", err)
|
||||
}
|
||||
xdgDir := os.Getenv("XDG_CONFIG_HOME")
|
||||
if xdgDir == "" {
|
||||
xdgDir = filepath.Join(homeDir, ".config")
|
||||
}
|
||||
|
||||
candidates = append(candidates,
|
||||
"vcom.toml",
|
||||
filepath.Join("config", "vcom.toml"),
|
||||
filepath.Join(xdgDir, "vcom", "vcom.toml"),
|
||||
filepath.Join(homeDir, ".config", "vcom", "vcom.toml"),
|
||||
)
|
||||
}
|
||||
|
||||
for _, candidate := range candidates {
|
||||
if candidate == "" {
|
||||
continue
|
||||
}
|
||||
absPath, err := filepath.Abs(candidate)
|
||||
if err != nil {
|
||||
return "", false, fmt.Errorf("resolve %s: %w", candidate, err)
|
||||
}
|
||||
if _, err := os.Stat(absPath); err == nil {
|
||||
return absPath, true, nil
|
||||
} else if !isMissingPathError(err) {
|
||||
return "", false, fmt.Errorf("stat %s: %w", absPath, err)
|
||||
}
|
||||
}
|
||||
|
||||
return "", false, nil
|
||||
}
|
||||
|
||||
func isMissingPathError(err error) bool {
|
||||
return errors.Is(err, os.ErrNotExist) || errors.Is(err, syscall.ENOTDIR)
|
||||
}
|
||||
|
||||
func DefaultUserPath() (string, error) {
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("resolve home dir: %w", err)
|
||||
}
|
||||
xdgDir := os.Getenv("XDG_CONFIG_HOME")
|
||||
if xdgDir == "" {
|
||||
xdgDir = filepath.Join(homeDir, ".config")
|
||||
}
|
||||
return filepath.Join(xdgDir, "vcom", "vcom.toml"), nil
|
||||
}
|
||||
84
src/vcom-0.2.5/internal/config/session.go
Normal file
84
src/vcom-0.2.5/internal/config/session.go
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
toml "github.com/pelletier/go-toml/v2"
|
||||
)
|
||||
|
||||
// SessionState stores the UI state that should be restored on next launch.
|
||||
type SessionState struct {
|
||||
ActivePane string `toml:"active_pane"`
|
||||
Left PaneSession `toml:"left"`
|
||||
Right PaneSession `toml:"right"`
|
||||
}
|
||||
|
||||
// PaneSession stores per-pane session state (path and selected entry name).
|
||||
type PaneSession struct {
|
||||
Path string `toml:"path"`
|
||||
EntryName string `toml:"entry_name"`
|
||||
CursorMemory map[string]string `toml:"cursor_memory"`
|
||||
}
|
||||
|
||||
// DefaultSessionPath returns the path to the session file.
|
||||
func DefaultSessionPath() (string, error) {
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("resolve home dir: %w", err)
|
||||
}
|
||||
xdgDir := os.Getenv("XDG_CONFIG_HOME")
|
||||
if xdgDir == "" {
|
||||
xdgDir = filepath.Join(homeDir, ".config")
|
||||
}
|
||||
return filepath.Join(xdgDir, "vcom", "session.toml"), nil
|
||||
}
|
||||
|
||||
// LoadSession reads the session state from disk.
|
||||
// Returns (SessionState, nil) on success, (empty, error) if file doesn't exist.
|
||||
func LoadSession() (SessionState, error) {
|
||||
path, err := DefaultSessionPath()
|
||||
if err != nil {
|
||||
return SessionState{}, err
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return SessionState{}, nil
|
||||
}
|
||||
return SessionState{}, fmt.Errorf("read %s: %w", path, err)
|
||||
}
|
||||
|
||||
var s SessionState
|
||||
if err := toml.Unmarshal(data, &s); err != nil {
|
||||
return SessionState{}, fmt.Errorf("parse %s: %w", path, err)
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// SaveSession writes the session state to disk.
|
||||
func SaveSession(s SessionState) error {
|
||||
path, err := DefaultSessionPath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
absPath, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("resolve %s: %w", path, err)
|
||||
}
|
||||
if err := os.MkdirAll(filepath.Dir(absPath), 0o755); err != nil {
|
||||
return fmt.Errorf("mkdir %s: %w", filepath.Dir(absPath), err)
|
||||
}
|
||||
|
||||
data, err := toml.Marshal(s)
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshal session: %w", err)
|
||||
}
|
||||
if err := os.WriteFile(absPath, data, 0o644); err != nil {
|
||||
return fmt.Errorf("write %s: %w", path, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
656
src/vcom-0.2.5/internal/fs/archive.go
Normal file
656
src/vcom-0.2.5/internal/fs/archive.go
Normal file
|
|
@ -0,0 +1,656 @@
|
|||
package vfs
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"archive/zip"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func ExtractArchiveToTemp(sourcePath string) (string, error) {
|
||||
// Count total files for progress reporting
|
||||
totalFiles, totalBytes := countArchiveEntries(sourcePath)
|
||||
|
||||
tempDir, err := os.MkdirTemp("", "vcom-archive-")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
cleanupOnErr := func(extractErr error) (string, error) {
|
||||
_ = os.RemoveAll(tempDir)
|
||||
return "", extractErr
|
||||
}
|
||||
|
||||
// Use background context for temp extraction (no cancellation needed)
|
||||
ctx := context.Background()
|
||||
|
||||
sourceLower := strings.ToLower(sourcePath)
|
||||
switch {
|
||||
case strings.HasSuffix(sourceLower, ".zip"):
|
||||
if err := extractZipArchive(ctx, sourcePath, tempDir, nil, totalFiles, totalBytes); err != nil {
|
||||
return cleanupOnErr(err)
|
||||
}
|
||||
case strings.HasSuffix(sourceLower, ".tar"):
|
||||
if err := extractTarArchive(ctx, sourcePath, tempDir, false, nil, totalFiles, totalBytes); err != nil {
|
||||
return cleanupOnErr(err)
|
||||
}
|
||||
case strings.HasSuffix(sourceLower, ".tar.gz"), strings.HasSuffix(sourceLower, ".tgz"):
|
||||
if err := extractTarArchive(ctx, sourcePath, tempDir, true, nil, totalFiles, totalBytes); err != nil {
|
||||
return cleanupOnErr(err)
|
||||
}
|
||||
default:
|
||||
return cleanupOnErr(fmt.Errorf("archive format is not supported: %s", filepath.Ext(sourcePath)))
|
||||
}
|
||||
|
||||
return tempDir, nil
|
||||
}
|
||||
|
||||
// ExtractArchiveToDir extracts an archive to the specified target directory.
|
||||
// Unlike ExtractArchiveToTemp, it extracts directly to targetDir without
|
||||
// creating a temporary directory. The progress callback is called after each
|
||||
// file is extracted; it may be nil. Cancellation is supported via ctx.
|
||||
func ExtractArchiveToDir(ctx context.Context, sourcePath, targetDir string, progress func(CopyProgress)) error {
|
||||
totalFiles, totalBytes := countArchiveEntries(sourcePath)
|
||||
sourceLower := strings.ToLower(sourcePath)
|
||||
switch {
|
||||
case strings.HasSuffix(sourceLower, ".zip"):
|
||||
return extractZipArchive(ctx, sourcePath, targetDir, progress, totalFiles, totalBytes)
|
||||
case strings.HasSuffix(sourceLower, ".tar"):
|
||||
return extractTarArchive(ctx, sourcePath, targetDir, false, progress, totalFiles, totalBytes)
|
||||
case strings.HasSuffix(sourceLower, ".tar.gz"), strings.HasSuffix(sourceLower, ".tgz"):
|
||||
return extractTarArchive(ctx, sourcePath, targetDir, true, progress, totalFiles, totalBytes)
|
||||
default:
|
||||
return fmt.Errorf("archive format is not supported: %s", filepath.Ext(sourcePath))
|
||||
}
|
||||
}
|
||||
|
||||
// countArchiveEntries counts the total number of files and total uncompressed
|
||||
// bytes in an archive without extracting. Used for progress reporting.
|
||||
func countArchiveEntries(sourcePath string) (int64, int64) {
|
||||
sourceLower := strings.ToLower(sourcePath)
|
||||
switch {
|
||||
case strings.HasSuffix(sourceLower, ".zip"):
|
||||
return countZipEntries(sourcePath)
|
||||
case strings.HasSuffix(sourceLower, ".tar"), strings.HasSuffix(sourceLower, ".tar.gz"), strings.HasSuffix(sourceLower, ".tgz"):
|
||||
return countTarEntries(sourcePath)
|
||||
default:
|
||||
return 0, 0
|
||||
}
|
||||
}
|
||||
|
||||
func countZipEntries(sourcePath string) (int64, int64) {
|
||||
r, err := zip.OpenReader(sourcePath)
|
||||
if err != nil {
|
||||
return 0, 0
|
||||
}
|
||||
defer r.Close()
|
||||
var files, bytes int64
|
||||
for _, f := range r.File {
|
||||
if !f.FileInfo().IsDir() {
|
||||
files++
|
||||
bytes += int64(f.UncompressedSize64)
|
||||
}
|
||||
}
|
||||
return files, bytes
|
||||
}
|
||||
|
||||
func countTarEntries(sourcePath string) (int64, int64) {
|
||||
f, err := os.Open(sourcePath)
|
||||
if err != nil {
|
||||
return 0, 0
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var reader io.Reader = f
|
||||
if strings.HasSuffix(strings.ToLower(sourcePath), ".tar.gz") || strings.HasSuffix(strings.ToLower(sourcePath), ".tgz") {
|
||||
gr, err := gzip.NewReader(f)
|
||||
if err != nil {
|
||||
return 0, 0
|
||||
}
|
||||
defer gr.Close()
|
||||
reader = gr
|
||||
}
|
||||
|
||||
tarReader := tar.NewReader(reader)
|
||||
var files, bytes int64
|
||||
for {
|
||||
hdr, err := tarReader.Next()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
if hdr.Typeflag == tar.TypeReg || hdr.Typeflag == tar.TypeRegA {
|
||||
files++
|
||||
bytes += hdr.Size
|
||||
}
|
||||
}
|
||||
return files, bytes
|
||||
}
|
||||
|
||||
func extractZipArchive(ctx context.Context, sourcePath string, targetDir string, progress func(CopyProgress), totalFiles, totalBytes int64) error {
|
||||
reader, err := zip.OpenReader(sourcePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
var filesDone int64
|
||||
for _, file := range reader.File {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
relPath, ok := safeArchivePath(file.Name)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
fullPath := filepath.Join(targetDir, relPath)
|
||||
|
||||
if file.FileInfo().IsDir() {
|
||||
if err := os.MkdirAll(fullPath, 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(fullPath), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
src, err := file.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := writeArchiveFile(fullPath, src, file.Mode()); err != nil {
|
||||
src.Close()
|
||||
return err
|
||||
}
|
||||
src.Close()
|
||||
|
||||
filesDone++
|
||||
if progress != nil {
|
||||
progress(CopyProgress{
|
||||
FilesDone: int(filesDone),
|
||||
FilesTotal: int(totalFiles),
|
||||
BytesDone: 0,
|
||||
BytesTotal: totalBytes,
|
||||
Stage: "Extracting data",
|
||||
})
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func extractTarArchive(ctx context.Context, sourcePath string, targetDir string, gzipped bool, progress func(CopyProgress), totalFiles, totalBytes int64) error {
|
||||
file, err := os.Open(sourcePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var reader io.Reader = file
|
||||
if gzipped {
|
||||
gzipReader, err := gzip.NewReader(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer gzipReader.Close()
|
||||
reader = gzipReader
|
||||
}
|
||||
|
||||
tarReader := tar.NewReader(reader)
|
||||
var filesDone int64
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
header, err := tarReader.Next()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
relPath, ok := safeArchivePath(header.Name)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
fullPath := filepath.Join(targetDir, relPath)
|
||||
|
||||
switch header.Typeflag {
|
||||
case tar.TypeDir:
|
||||
if err := os.MkdirAll(fullPath, 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
case tar.TypeReg, tar.TypeRegA:
|
||||
if err := os.MkdirAll(filepath.Dir(fullPath), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := writeArchiveFile(fullPath, tarReader, os.FileMode(header.Mode)); err != nil {
|
||||
return err
|
||||
}
|
||||
filesDone++
|
||||
if progress != nil {
|
||||
progress(CopyProgress{
|
||||
FilesDone: int(filesDone),
|
||||
FilesTotal: int(totalFiles),
|
||||
BytesDone: 0,
|
||||
BytesTotal: totalBytes,
|
||||
Stage: "Extracting data",
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeArchiveFile(path string, source io.Reader, mode os.FileMode) error {
|
||||
if mode == 0 {
|
||||
mode = 0o644
|
||||
}
|
||||
output, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, mode.Perm())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer output.Close()
|
||||
|
||||
_, err = io.Copy(output, source)
|
||||
return err
|
||||
}
|
||||
|
||||
func safeArchivePath(name string) (string, bool) {
|
||||
clean := filepath.Clean(name)
|
||||
if clean == "." || clean == string(filepath.Separator) {
|
||||
return "", false
|
||||
}
|
||||
if filepath.IsAbs(clean) {
|
||||
return "", false
|
||||
}
|
||||
if clean == ".." || strings.HasPrefix(clean, ".."+string(filepath.Separator)) {
|
||||
return "", false
|
||||
}
|
||||
return clean, true
|
||||
}
|
||||
|
||||
// ArchiveFormat returns the file extension for a given archive format name.
|
||||
func ArchiveFormat(format string) string {
|
||||
switch strings.ToLower(strings.TrimSpace(format)) {
|
||||
case "zip":
|
||||
return ".zip"
|
||||
case "tar":
|
||||
return ".tar"
|
||||
case "targz", "tar.gz", "tgz":
|
||||
return ".tar.gz"
|
||||
default:
|
||||
return ".zip"
|
||||
}
|
||||
}
|
||||
|
||||
// ArchiveName generates an archive filename from source paths.
|
||||
func ArchiveName(sources []string, format string) string {
|
||||
ext := ArchiveFormat(format)
|
||||
if len(sources) == 1 {
|
||||
base := strings.TrimSuffix(filepath.Base(sources[0]), filepath.Ext(sources[0]))
|
||||
return base + ext
|
||||
}
|
||||
base := filepath.Base(filepath.Dir(sources[0]))
|
||||
if base == "." || base == "" || base == string(filepath.Separator) {
|
||||
base = "archive"
|
||||
}
|
||||
return base + ext
|
||||
}
|
||||
|
||||
// CreateArchive creates an archive from source paths using the given format.
|
||||
// Supported formats: "zip", "tar", "tar.gz" (or "targz", "tgz").
|
||||
// Progress is reported via the callback function.
|
||||
func CreateArchive(ctx context.Context, sources []string, archivePath string, progress func(CopyProgress)) error {
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
|
||||
lower := strings.ToLower(archivePath)
|
||||
switch {
|
||||
case strings.HasSuffix(lower, ".zip"):
|
||||
return createZipArchive(ctx, sources, archivePath, progress)
|
||||
case strings.HasSuffix(lower, ".tar.gz"), strings.HasSuffix(lower, ".tgz"):
|
||||
return createTarGzArchive(ctx, sources, archivePath, progress)
|
||||
case strings.HasSuffix(lower, ".tar"):
|
||||
return createTarArchive(ctx, sources, archivePath, progress)
|
||||
default:
|
||||
return fmt.Errorf("unsupported archive format: %s", filepath.Ext(archivePath))
|
||||
}
|
||||
}
|
||||
|
||||
func createZipArchive(ctx context.Context, sources []string, archivePath string, progress func(CopyProgress)) error {
|
||||
file, err := os.Create(archivePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create %s: %w", archivePath, err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
zipWriter := zip.NewWriter(file)
|
||||
defer zipWriter.Close()
|
||||
|
||||
var totalFiles int
|
||||
var totalBytes int64
|
||||
for _, source := range sources {
|
||||
info, err := os.Lstat(source)
|
||||
if err != nil {
|
||||
return fmt.Errorf("stat %s: %w", source, err)
|
||||
}
|
||||
if info.IsDir() {
|
||||
err = filepath.Walk(source, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
totalFiles++
|
||||
if !info.IsDir() {
|
||||
totalBytes += info.Size()
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
totalFiles++
|
||||
totalBytes += info.Size()
|
||||
}
|
||||
}
|
||||
|
||||
state := ©ProgressState{
|
||||
ctx: ctx,
|
||||
stats: TransferStats{FilesTotal: totalFiles, BytesTotal: totalBytes},
|
||||
callback: progress,
|
||||
lastEmit: time.Now(),
|
||||
}
|
||||
|
||||
baseDir := commonBaseDir(sources)
|
||||
for _, source := range sources {
|
||||
info, err := os.Lstat(source)
|
||||
if err != nil {
|
||||
return fmt.Errorf("stat %s: %w", source, err)
|
||||
}
|
||||
relRoot := source
|
||||
if baseDir != "" {
|
||||
relRoot, _ = filepath.Rel(baseDir, source)
|
||||
}
|
||||
if info.IsDir() {
|
||||
err = filepath.Walk(source, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
}
|
||||
relPath, _ := filepath.Rel(baseDir, path)
|
||||
relPath = filepath.ToSlash(relPath)
|
||||
header, zipErr := zip.FileInfoHeader(info)
|
||||
if zipErr != nil {
|
||||
return zipErr
|
||||
}
|
||||
header.Name = relPath
|
||||
if info.IsDir() {
|
||||
header.Name += "/"
|
||||
} else {
|
||||
header.Method = zip.Deflate
|
||||
}
|
||||
writer, zipErr := zipWriter.CreateHeader(header)
|
||||
if zipErr != nil {
|
||||
return zipErr
|
||||
}
|
||||
if !info.IsDir() {
|
||||
f, openErr := os.Open(path)
|
||||
if openErr != nil {
|
||||
return openErr
|
||||
}
|
||||
written, copyErr := io.Copy(writer, f)
|
||||
f.Close()
|
||||
if copyErr != nil {
|
||||
return copyErr
|
||||
}
|
||||
state.filesDone++
|
||||
state.bytesDone += written
|
||||
} else {
|
||||
state.filesDone++
|
||||
}
|
||||
emitArchiveProgress(state, path)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
}
|
||||
relPath := filepath.ToSlash(relRoot)
|
||||
header, zipErr := zip.FileInfoHeader(info)
|
||||
if zipErr != nil {
|
||||
return zipErr
|
||||
}
|
||||
header.Name = relPath
|
||||
header.Method = zip.Deflate
|
||||
writer, zipErr := zipWriter.CreateHeader(header)
|
||||
if zipErr != nil {
|
||||
return zipErr
|
||||
}
|
||||
f, openErr := os.Open(source)
|
||||
if openErr != nil {
|
||||
return openErr
|
||||
}
|
||||
written, copyErr := io.Copy(writer, f)
|
||||
f.Close()
|
||||
if copyErr != nil {
|
||||
return copyErr
|
||||
}
|
||||
state.filesDone++
|
||||
state.bytesDone += written
|
||||
emitArchiveProgress(state, source)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func createTarArchive(ctx context.Context, sources []string, archivePath string, progress func(CopyProgress)) error {
|
||||
return createTarArchiveWithGzip(ctx, sources, archivePath, false, progress)
|
||||
}
|
||||
|
||||
func createTarGzArchive(ctx context.Context, sources []string, archivePath string, progress func(CopyProgress)) error {
|
||||
return createTarArchiveWithGzip(ctx, sources, archivePath, true, progress)
|
||||
}
|
||||
|
||||
func createTarArchiveWithGzip(ctx context.Context, sources []string, archivePath string, gzipped bool, progress func(CopyProgress)) error {
|
||||
file, err := os.Create(archivePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create %s: %w", archivePath, err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var writer io.WriteCloser = file
|
||||
if gzipped {
|
||||
gzipWriter := gzip.NewWriter(file)
|
||||
defer gzipWriter.Close()
|
||||
writer = gzipWriter
|
||||
}
|
||||
|
||||
tarWriter := tar.NewWriter(writer)
|
||||
defer tarWriter.Close()
|
||||
|
||||
var totalFiles int
|
||||
var totalBytes int64
|
||||
for _, source := range sources {
|
||||
info, err := os.Lstat(source)
|
||||
if err != nil {
|
||||
return fmt.Errorf("stat %s: %w", source, err)
|
||||
}
|
||||
if info.IsDir() {
|
||||
err = filepath.Walk(source, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
totalFiles++
|
||||
if !info.IsDir() {
|
||||
totalBytes += info.Size()
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
totalFiles++
|
||||
totalBytes += info.Size()
|
||||
}
|
||||
}
|
||||
|
||||
state := ©ProgressState{
|
||||
ctx: ctx,
|
||||
stats: TransferStats{FilesTotal: totalFiles, BytesTotal: totalBytes},
|
||||
callback: progress,
|
||||
lastEmit: time.Now(),
|
||||
}
|
||||
|
||||
baseDir := commonBaseDir(sources)
|
||||
for _, source := range sources {
|
||||
info, err := os.Lstat(source)
|
||||
if err != nil {
|
||||
return fmt.Errorf("stat %s: %w", source, err)
|
||||
}
|
||||
if info.IsDir() {
|
||||
err = filepath.Walk(source, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
}
|
||||
relPath, _ := filepath.Rel(baseDir, path)
|
||||
relPath = filepath.ToSlash(relPath)
|
||||
header, tarErr := tar.FileInfoHeader(info, path)
|
||||
if tarErr != nil {
|
||||
return tarErr
|
||||
}
|
||||
header.Name = relPath
|
||||
if info.IsDir() {
|
||||
header.Name += "/"
|
||||
}
|
||||
if err := tarWriter.WriteHeader(header); err != nil {
|
||||
return err
|
||||
}
|
||||
if !info.IsDir() {
|
||||
f, openErr := os.Open(path)
|
||||
if openErr != nil {
|
||||
return openErr
|
||||
}
|
||||
written, copyErr := io.Copy(tarWriter, f)
|
||||
f.Close()
|
||||
if copyErr != nil {
|
||||
return copyErr
|
||||
}
|
||||
state.filesDone++
|
||||
state.bytesDone += written
|
||||
} else {
|
||||
state.filesDone++
|
||||
}
|
||||
emitArchiveProgress(state, path)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
}
|
||||
relPath, _ := filepath.Rel(baseDir, source)
|
||||
relPath = filepath.ToSlash(relPath)
|
||||
header, tarErr := tar.FileInfoHeader(info, source)
|
||||
if tarErr != nil {
|
||||
return tarErr
|
||||
}
|
||||
header.Name = relPath
|
||||
if err := tarWriter.WriteHeader(header); err != nil {
|
||||
return err
|
||||
}
|
||||
f, openErr := os.Open(source)
|
||||
if openErr != nil {
|
||||
return openErr
|
||||
}
|
||||
written, copyErr := io.Copy(tarWriter, f)
|
||||
f.Close()
|
||||
if copyErr != nil {
|
||||
return copyErr
|
||||
}
|
||||
state.filesDone++
|
||||
state.bytesDone += written
|
||||
emitArchiveProgress(state, source)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func emitArchiveProgress(state *copyProgressState, currentPath string) {
|
||||
if state.callback == nil {
|
||||
return
|
||||
}
|
||||
now := time.Now()
|
||||
if now.Sub(state.lastEmit) < 50*time.Millisecond {
|
||||
return
|
||||
}
|
||||
state.lastEmit = now
|
||||
state.callback(CopyProgress{
|
||||
FilesDone: state.filesDone,
|
||||
FilesTotal: state.stats.FilesTotal,
|
||||
BytesDone: state.bytesDone,
|
||||
BytesTotal: state.stats.BytesTotal,
|
||||
CurrentPath: currentPath,
|
||||
Stage: "Archiving",
|
||||
})
|
||||
}
|
||||
|
||||
// commonBaseDir returns the longest common directory prefix for the given paths.
|
||||
func commonBaseDir(paths []string) string {
|
||||
if len(paths) == 0 {
|
||||
return ""
|
||||
}
|
||||
if len(paths) == 1 {
|
||||
if info, err := os.Lstat(paths[0]); err == nil && info.IsDir() {
|
||||
return filepath.Dir(paths[0])
|
||||
}
|
||||
return filepath.Dir(paths[0])
|
||||
}
|
||||
|
||||
base := filepath.Dir(paths[0])
|
||||
for _, p := range paths[1:] {
|
||||
dir := filepath.Dir(p)
|
||||
for !strings.HasPrefix(dir, base) && base != "" {
|
||||
parent := filepath.Dir(base)
|
||||
if parent == base {
|
||||
return ""
|
||||
}
|
||||
base = parent
|
||||
}
|
||||
}
|
||||
return base
|
||||
}
|
||||
156
src/vcom-0.2.5/internal/fs/entry.go
Normal file
156
src/vcom-0.2.5/internal/fs/entry.go
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
package vfs
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
configExtensions = map[string]struct{}{
|
||||
"toml": {}, "yaml": {}, "yml": {}, "json": {}, "jsonc": {}, "ini": {}, "conf": {},
|
||||
"config": {}, "env": {}, "properties": {}, "xml": {}, "mod": {}, "sum": {}, "lock": {},
|
||||
}
|
||||
textExtensions = map[string]struct{}{
|
||||
"txt": {}, "md": {}, "rst": {}, "go": {}, "rs": {}, "c": {}, "h": {}, "cpp": {}, "hpp": {},
|
||||
"py": {}, "js": {}, "ts": {}, "tsx": {}, "jsx": {}, "java": {}, "kt": {}, "kts": {}, "swift": {},
|
||||
"html": {}, "css": {}, "scss": {}, "sass": {}, "less": {}, "styl": {},
|
||||
"sh": {}, "bash": {}, "zsh": {}, "fish": {}, "sql": {},
|
||||
"log": {}, "csv": {}, "tsv": {},
|
||||
"lua": {}, "rb": {}, "pl": {}, "pm": {}, "t": {}, "ps1": {}, "bat": {}, "cmd": {},
|
||||
"vue": {}, "svelte": {}, "astro": {}, "ejs": {}, "hbs": {}, "pug": {}, "haml": {}, "php": {}, "twig": {},
|
||||
"scala": {}, "groovy": {}, "clj": {}, "ex": {}, "exs": {}, "elm": {}, "hs": {}, "lisp": {}, "cl": {}, "rkt": {}, "scm": {}, "dart": {},
|
||||
"tex": {}, "bib": {}, "sty": {}, "cls": {},
|
||||
"gradle": {}, "cmake": {}, "mk": {}, "mak": {},
|
||||
"asm": {}, "s": {}, "inc": {},
|
||||
"patch": {}, "diff": {},
|
||||
"proto": {}, "graphql": {}, "gql": {},
|
||||
"tf": {}, "hcl": {},
|
||||
"r": {}, "m": {}, "mm": {},
|
||||
"nim": {}, "zig": {}, "odin": {}, "v": {}, "nix": {},
|
||||
"cr": {}, "jl": {},
|
||||
"erl": {}, "hrl": {},
|
||||
}
|
||||
// textFilenames lists common text files without a meaningful extension
|
||||
// (like Makefile, Dockerfile, etc.) so they open in the editor.
|
||||
textFilenames = map[string]struct{}{
|
||||
"makefile": {}, "dockerfile": {}, "containerfile": {},
|
||||
"readme": {}, "license": {}, "licence": {}, "copying": {}, "changelog": {}, "changes": {},
|
||||
"todo": {}, "notes": {}, "authors": {}, "contributors": {}, "maintainers": {},
|
||||
"procfile": {}, "gemfile": {}, "rakefile": {}, "snapfile": {}, "fastfile": {},
|
||||
"cmakelists": {}, "justfile": {}, "taskfile": {},
|
||||
"gitignore": {}, "gitattributes": {}, "gitmodules": {}, "gitkeep": {},
|
||||
"gitconfig": {}, "git-blame-ignore-revs": {},
|
||||
"editorconfig": {}, "envrc": {}, "hushlogin": {},
|
||||
"xsession": {}, "xresources": {}, "xinitrc": {},
|
||||
"bashrc": {}, "bash_profile": {}, "bash_logout": {},
|
||||
"zshrc": {}, "zprofile": {}, "zlogin": {}, "zlogout": {},
|
||||
"profile": {}, "inputrc": {}, "tmux.conf": {},
|
||||
"npmrc": {}, "yarnrc": {}, "pnpmrc": {},
|
||||
"eslintrc": {}, "prettierrc": {}, "babelrc": {},
|
||||
"stylelintrc": {}, "commitlintrc": {},
|
||||
"htaccess": {}, "htpasswd": {},
|
||||
}
|
||||
imageExtensions = map[string]struct{}{
|
||||
"png": {}, "jpg": {}, "jpeg": {}, "gif": {}, "webp": {}, "bmp": {}, "svg": {}, "ico": {},
|
||||
"avif": {}, "heic": {}, "heif": {}, "tiff": {}, "tif": {},
|
||||
}
|
||||
pdfExtensions = map[string]struct{}{
|
||||
"pdf": {},
|
||||
}
|
||||
audioExtensions = map[string]struct{}{
|
||||
"mp3": {}, "flac": {}, "ogg": {}, "opus": {}, "wav": {},
|
||||
"aac": {}, "m4a": {}, "wma": {}, "dsf": {}, "ape": {},
|
||||
}
|
||||
videoExtensions = map[string]struct{}{
|
||||
"mp4": {}, "mkv": {}, "mov": {}, "avi": {}, "webm": {},
|
||||
"m4v": {}, "wmv": {}, "flv": {}, "ts": {}, "mts": {},
|
||||
}
|
||||
archiveExtensions = map[string]struct{}{
|
||||
"zip": {}, "tar": {}, "gz": {}, "tgz": {}, "xz": {}, "bz2": {}, "7z": {}, "rar": {},
|
||||
"zst": {}, "lz": {}, "lz4": {}, "lzma": {},
|
||||
"iso": {}, "img": {}, "dmg": {},
|
||||
}
|
||||
)
|
||||
|
||||
type Entry struct {
|
||||
Name string
|
||||
Path string
|
||||
Extension string
|
||||
Mode fs.FileMode
|
||||
Size int64
|
||||
ModifiedAt time.Time
|
||||
CreatedAt time.Time
|
||||
CreatedKnown bool
|
||||
IsDir bool
|
||||
IsParent bool
|
||||
IsHidden bool
|
||||
IsRemote bool
|
||||
Connected bool
|
||||
DirSizeKnown bool
|
||||
RemoteHostName string
|
||||
}
|
||||
|
||||
func (e Entry) DisplayName() string {
|
||||
if e.IsParent {
|
||||
return ".."
|
||||
}
|
||||
if e.IsDir {
|
||||
return e.Name + "/"
|
||||
}
|
||||
return e.Name
|
||||
}
|
||||
|
||||
func (e Entry) IsFile() bool {
|
||||
return !e.IsDir && !e.IsParent
|
||||
}
|
||||
|
||||
func (e Entry) MatchKey() string {
|
||||
return strings.ToLower(e.Name)
|
||||
}
|
||||
|
||||
func (e Entry) IsExecutable() bool {
|
||||
return !e.IsDir && !e.IsParent && e.Mode&0o111 != 0
|
||||
}
|
||||
|
||||
func (e Entry) Category() string {
|
||||
switch {
|
||||
case e.IsParent:
|
||||
return "parent"
|
||||
case e.IsRemote:
|
||||
return "remote"
|
||||
case e.IsDir:
|
||||
return "directory"
|
||||
case hasExt(configExtensions, e.Extension):
|
||||
return "config"
|
||||
case hasExt(textExtensions, e.Extension):
|
||||
return "text"
|
||||
case hasExt(textFilenames, strings.ToLower(e.Name)):
|
||||
return "text"
|
||||
case hasExt(imageExtensions, e.Extension):
|
||||
return "image"
|
||||
case hasExt(pdfExtensions, e.Extension):
|
||||
return "pdf"
|
||||
case hasExt(audioExtensions, e.Extension):
|
||||
return "audio"
|
||||
case hasExt(videoExtensions, e.Extension):
|
||||
return "video"
|
||||
case hasExt(archiveExtensions, e.Extension):
|
||||
return "archive"
|
||||
case e.IsExecutable():
|
||||
return "executable"
|
||||
default:
|
||||
return "binary"
|
||||
}
|
||||
}
|
||||
|
||||
func ext(name string) string {
|
||||
value := strings.TrimPrefix(filepath.Ext(name), ".")
|
||||
return strings.ToLower(value)
|
||||
}
|
||||
|
||||
func hasExt(set map[string]struct{}, ext string) bool {
|
||||
_, ok := set[strings.ToLower(ext)]
|
||||
return ok
|
||||
}
|
||||
527
src/vcom-0.2.5/internal/fs/ops.go
Normal file
527
src/vcom-0.2.5/internal/fs/ops.go
Normal file
|
|
@ -0,0 +1,527 @@
|
|||
package vfs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TransferStats struct {
|
||||
FilesTotal int
|
||||
BytesTotal int64
|
||||
}
|
||||
|
||||
type CopyProgress struct {
|
||||
FilesDone int
|
||||
FilesTotal int
|
||||
BytesDone int64
|
||||
BytesTotal int64
|
||||
CurrentPath string
|
||||
Stage string
|
||||
}
|
||||
|
||||
type copyProgressState struct {
|
||||
ctx context.Context
|
||||
filesDone int
|
||||
bytesDone int64
|
||||
stats TransferStats
|
||||
callback func(CopyProgress)
|
||||
lastEmit time.Time
|
||||
stage string
|
||||
discover bool // if true, count files during copy for progress total
|
||||
}
|
||||
|
||||
func (s *copyProgressState) discoverFiles(count int, dirPath string) {
|
||||
if count == 0 {
|
||||
return
|
||||
}
|
||||
s.stats.FilesTotal += count
|
||||
s.emit(dirPath, false)
|
||||
}
|
||||
|
||||
func CopyPath(srcPath string, dstDir string, overwrite bool) (string, error) {
|
||||
return CopyPathWithProgress(srcPath, dstDir, overwrite, TransferStats{}, nil)
|
||||
}
|
||||
|
||||
func CopyPathWithProgressContext(ctx context.Context, srcPath string, dstDir string, overwrite bool, stats TransferStats, progress func(CopyProgress)) (string, error) {
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
|
||||
srcInfo, err := os.Lstat(srcPath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("stat %s: %w", srcPath, err)
|
||||
}
|
||||
|
||||
targetPath := filepath.Join(dstDir, filepath.Base(srcPath))
|
||||
if same, err := samePath(srcPath, targetPath); err != nil {
|
||||
return "", err
|
||||
} else if same {
|
||||
return "", fmt.Errorf("source and target are the same: %s", targetPath)
|
||||
}
|
||||
|
||||
if exists, err := PathExists(targetPath); err != nil {
|
||||
return "", err
|
||||
} else if exists {
|
||||
if !overwrite {
|
||||
return "", ErrOverwrite(targetPath)
|
||||
}
|
||||
if err := os.RemoveAll(targetPath); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
if err := ctx.Err(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if progress == nil {
|
||||
progress = func(CopyProgress) {}
|
||||
}
|
||||
tracker := copyProgressState{
|
||||
ctx: ctx,
|
||||
stats: stats,
|
||||
callback: progress,
|
||||
stage: "Scanning files...",
|
||||
discover: stats.FilesTotal == 0,
|
||||
}
|
||||
tracker.emit(srcPath, true)
|
||||
tracker.stage = "Copying files..."
|
||||
|
||||
cleanupOnErr := func(copyErr error) (string, error) {
|
||||
if copyErr != nil {
|
||||
_ = os.RemoveAll(targetPath)
|
||||
}
|
||||
return "", copyErr
|
||||
}
|
||||
|
||||
if srcInfo.Mode()&os.ModeSymlink != 0 {
|
||||
target, err := os.Readlink(srcPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := ctx.Err(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := os.Symlink(target, targetPath); err != nil {
|
||||
return "", err
|
||||
}
|
||||
tracker.finishFile(srcPath)
|
||||
return targetPath, nil
|
||||
}
|
||||
|
||||
if srcInfo.IsDir() {
|
||||
if err := copyDir(srcPath, targetPath, &tracker); err != nil {
|
||||
return cleanupOnErr(err)
|
||||
}
|
||||
if err := ctx.Err(); err != nil {
|
||||
return cleanupOnErr(err)
|
||||
}
|
||||
tracker.emit(srcPath, true)
|
||||
return targetPath, nil
|
||||
}
|
||||
|
||||
if err := copyFile(srcPath, targetPath, srcInfo.Mode(), &tracker); err != nil {
|
||||
return cleanupOnErr(err)
|
||||
}
|
||||
tracker.emit(srcPath, true)
|
||||
return targetPath, nil
|
||||
}
|
||||
|
||||
func CopyStats(srcPath string) (TransferStats, error) {
|
||||
srcInfo, err := os.Lstat(srcPath)
|
||||
if err != nil {
|
||||
return TransferStats{}, fmt.Errorf("stat %s: %w", srcPath, err)
|
||||
}
|
||||
|
||||
if srcInfo.Mode()&os.ModeSymlink != 0 {
|
||||
return TransferStats{FilesTotal: 1, BytesTotal: 0}, nil
|
||||
}
|
||||
if !srcInfo.IsDir() {
|
||||
return TransferStats{FilesTotal: 1, BytesTotal: srcInfo.Size()}, nil
|
||||
}
|
||||
|
||||
stats := TransferStats{}
|
||||
err = filepath.WalkDir(srcPath, func(current string, d fs.DirEntry, walkErr error) error {
|
||||
if walkErr != nil {
|
||||
return walkErr
|
||||
}
|
||||
if d.IsDir() {
|
||||
return nil
|
||||
}
|
||||
stats.FilesTotal++
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return TransferStats{}, err
|
||||
}
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
func CopyPathWithProgress(srcPath string, dstDir string, overwrite bool, stats TransferStats, progress func(CopyProgress)) (string, error) {
|
||||
return CopyPathWithProgressContext(context.Background(), srcPath, dstDir, overwrite, stats, progress)
|
||||
}
|
||||
|
||||
func MovePath(srcPath string, dstDir string, overwrite bool) (string, error) {
|
||||
return MovePathWithProgress(srcPath, dstDir, overwrite, TransferStats{}, nil)
|
||||
}
|
||||
|
||||
func MovePathWithProgress(srcPath string, dstDir string, overwrite bool, stats TransferStats, progress func(CopyProgress)) (string, error) {
|
||||
return MovePathWithProgressContext(context.Background(), srcPath, dstDir, overwrite, stats, progress)
|
||||
}
|
||||
|
||||
func MovePathWithProgressContext(ctx context.Context, srcPath string, dstDir string, overwrite bool, stats TransferStats, progress func(CopyProgress)) (string, error) {
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
|
||||
targetPath := filepath.Join(dstDir, filepath.Base(srcPath))
|
||||
if same, err := samePath(srcPath, targetPath); err != nil {
|
||||
return "", err
|
||||
} else if same {
|
||||
return "", fmt.Errorf("source and target are the same: %s", targetPath)
|
||||
}
|
||||
|
||||
if exists, err := PathExists(targetPath); err != nil {
|
||||
return "", err
|
||||
} else if exists {
|
||||
if !overwrite {
|
||||
return "", ErrOverwrite(targetPath)
|
||||
}
|
||||
if err := os.RemoveAll(targetPath); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
if progress == nil {
|
||||
progress = func(CopyProgress) {}
|
||||
}
|
||||
if err := ctx.Err(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := os.Rename(srcPath, targetPath); err == nil {
|
||||
progress(CopyProgress{
|
||||
FilesDone: stats.FilesTotal,
|
||||
FilesTotal: stats.FilesTotal,
|
||||
BytesDone: stats.BytesTotal,
|
||||
BytesTotal: stats.BytesTotal,
|
||||
CurrentPath: srcPath,
|
||||
Stage: "Move completed",
|
||||
})
|
||||
return targetPath, nil
|
||||
} else if !errors.Is(err, syscall.EXDEV) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
targetPath, err := CopyPathWithProgressContext(ctx, srcPath, dstDir, overwrite, stats, progress)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := ctx.Err(); err != nil {
|
||||
_ = os.RemoveAll(targetPath)
|
||||
return "", err
|
||||
}
|
||||
progress(CopyProgress{
|
||||
FilesDone: stats.FilesTotal,
|
||||
FilesTotal: stats.FilesTotal,
|
||||
BytesDone: stats.BytesTotal,
|
||||
BytesTotal: stats.BytesTotal,
|
||||
CurrentPath: srcPath,
|
||||
Stage: "Finalizing move",
|
||||
})
|
||||
if err := DeletePath(srcPath); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return targetPath, nil
|
||||
}
|
||||
|
||||
func PathExists(path string) (bool, error) {
|
||||
if _, err := os.Lstat(path); err == nil {
|
||||
return true, nil
|
||||
} else if errors.Is(err, os.ErrNotExist) {
|
||||
return false, nil
|
||||
} else {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
func DeletePath(path string) error {
|
||||
return os.RemoveAll(path)
|
||||
}
|
||||
|
||||
// MoveToTrash moves a file or directory to the FreeDesktop Trash directory
|
||||
// (~/.local/share/Trash). Follows the FreeDesktop Trash specification:
|
||||
// - The original item is moved to Trash/files/<basename>
|
||||
// - A .trashinfo file is written to Trash/info/<basename>.trashinfo
|
||||
// - If <basename> already exists in Trash/files, a numeric suffix is appended.
|
||||
func MoveToTrash(path string) error {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot determine home directory: %w", err)
|
||||
}
|
||||
|
||||
trashDir := filepath.Join(home, ".local", "share", "Trash")
|
||||
filesDir := filepath.Join(trashDir, "files")
|
||||
infoDir := filepath.Join(trashDir, "info")
|
||||
|
||||
if err := os.MkdirAll(filesDir, 0o700); err != nil {
|
||||
return fmt.Errorf("cannot create trash files directory: %w", err)
|
||||
}
|
||||
if err := os.MkdirAll(infoDir, 0o700); err != nil {
|
||||
return fmt.Errorf("cannot create trash info directory: %w", err)
|
||||
}
|
||||
|
||||
baseName := filepath.Base(path)
|
||||
|
||||
// Generate a unique name in the trash directory
|
||||
destName := baseName
|
||||
for counter := 1; ; counter++ {
|
||||
destPath := filepath.Join(filesDir, destName)
|
||||
if _, err := os.Stat(destPath); os.IsNotExist(err) {
|
||||
break
|
||||
} else if err != nil {
|
||||
return fmt.Errorf("cannot stat trash path: %w", err)
|
||||
}
|
||||
destName = fmt.Sprintf("%s.%d", baseName, counter)
|
||||
}
|
||||
|
||||
destPath := filepath.Join(filesDir, destName)
|
||||
if err := os.Rename(path, destPath); err != nil {
|
||||
// Cross-filesystem move: fall back to copy+delete
|
||||
return fmt.Errorf("cannot move to trash: %w", err)
|
||||
}
|
||||
|
||||
// Write .trashinfo file
|
||||
absPath, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
absPath = path
|
||||
}
|
||||
now := time.Now().Format("2006-01-02T15:04:05")
|
||||
infoContent := fmt.Sprintf("[Trash Info]\nPath=%s\nDeletionDate=%s\n", absPath, now)
|
||||
infoPath := filepath.Join(infoDir, destName+".trashinfo")
|
||||
if err := os.WriteFile(infoPath, []byte(infoContent), 0o600); err != nil {
|
||||
// Best-effort: if info file fails, try to move the file back
|
||||
_ = os.Rename(destPath, path)
|
||||
return fmt.Errorf("cannot write trash info: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func MakeDir(parent string, name string) (string, error) {
|
||||
target := filepath.Join(parent, name)
|
||||
if err := os.MkdirAll(target, 0o755); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return target, nil
|
||||
}
|
||||
|
||||
func RenamePath(sourcePath string, newName string) (string, error) {
|
||||
newName = filepath.Base(filepath.Clean(newName))
|
||||
if newName == "." || newName == "" {
|
||||
return "", fmt.Errorf("invalid target name")
|
||||
}
|
||||
|
||||
targetPath := filepath.Join(filepath.Dir(sourcePath), newName)
|
||||
if same, err := samePath(sourcePath, targetPath); err != nil {
|
||||
return "", err
|
||||
} else if same {
|
||||
return "", fmt.Errorf("source and target are the same: %s", targetPath)
|
||||
}
|
||||
|
||||
if exists, err := PathExists(targetPath); err != nil {
|
||||
return "", err
|
||||
} else if exists {
|
||||
return "", ErrOverwrite(targetPath)
|
||||
}
|
||||
|
||||
if err := os.Rename(sourcePath, targetPath); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return targetPath, nil
|
||||
}
|
||||
|
||||
func copyDir(srcDir string, dstDir string, tracker *copyProgressState) error {
|
||||
if tracker != nil && tracker.ctx != nil {
|
||||
if err := tracker.ctx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
info, err := os.Lstat(srcDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.MkdirAll(dstDir, info.Mode().Perm()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
entries, err := os.ReadDir(srcDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Count files in this directory so progress total converges
|
||||
if tracker != nil && tracker.discover {
|
||||
fileCount := 0
|
||||
for _, entry := range entries {
|
||||
if !entry.IsDir() {
|
||||
fileCount++
|
||||
}
|
||||
}
|
||||
tracker.discoverFiles(fileCount, srcDir)
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
if tracker != nil && tracker.ctx != nil {
|
||||
if err := tracker.ctx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
srcPath := filepath.Join(srcDir, entry.Name())
|
||||
dstPath := filepath.Join(dstDir, entry.Name())
|
||||
|
||||
info, err := os.Lstat(srcPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch {
|
||||
case info.Mode()&os.ModeSymlink != 0:
|
||||
target, err := os.Readlink(srcPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.Symlink(target, dstPath); err != nil {
|
||||
return err
|
||||
}
|
||||
if tracker != nil {
|
||||
tracker.finishFile(srcPath)
|
||||
}
|
||||
case info.IsDir():
|
||||
if err := copyDir(srcPath, dstPath, tracker); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
if err := copyFile(srcPath, dstPath, info.Mode(), tracker); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if tracker != nil && tracker.ctx != nil {
|
||||
if err := tracker.ctx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func copyFile(srcPath string, dstPath string, mode os.FileMode, tracker *copyProgressState) error {
|
||||
if tracker != nil && tracker.ctx != nil {
|
||||
if err := tracker.ctx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
srcFile, err := os.Open(srcPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer srcFile.Close()
|
||||
|
||||
dstFile, err := os.OpenFile(dstPath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, mode.Perm())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer dstFile.Close()
|
||||
|
||||
writer := io.Writer(dstFile)
|
||||
if tracker != nil {
|
||||
writer = &progressWriter{base: dstFile, tracker: tracker, path: srcPath}
|
||||
}
|
||||
if _, err := io.Copy(writer, srcFile); err != nil {
|
||||
_ = dstFile.Close()
|
||||
_ = os.Remove(dstPath)
|
||||
return err
|
||||
}
|
||||
if tracker != nil && tracker.ctx != nil {
|
||||
if err := tracker.ctx.Err(); err != nil {
|
||||
_ = dstFile.Close()
|
||||
_ = os.Remove(dstPath)
|
||||
return err
|
||||
}
|
||||
}
|
||||
if tracker != nil {
|
||||
tracker.finishFile(srcPath)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type progressWriter struct {
|
||||
base io.Writer
|
||||
tracker *copyProgressState
|
||||
path string
|
||||
}
|
||||
|
||||
func (w *progressWriter) Write(data []byte) (int, error) {
|
||||
if w.tracker != nil && w.tracker.ctx != nil {
|
||||
if err := w.tracker.ctx.Err(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
n, err := w.base.Write(data)
|
||||
if n > 0 {
|
||||
w.tracker.addBytes(int64(n), w.path)
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (s *copyProgressState) addBytes(delta int64, currentPath string) {
|
||||
s.bytesDone += delta
|
||||
s.emit(currentPath, false)
|
||||
}
|
||||
|
||||
func (s *copyProgressState) finishFile(currentPath string) {
|
||||
s.filesDone++
|
||||
s.emit(currentPath, true)
|
||||
}
|
||||
|
||||
func (s *copyProgressState) emit(currentPath string, force bool) {
|
||||
if s.callback == nil {
|
||||
return
|
||||
}
|
||||
if !force && time.Since(s.lastEmit) < 75*time.Millisecond {
|
||||
return
|
||||
}
|
||||
s.lastEmit = time.Now()
|
||||
stage := s.stage
|
||||
if stage == "" {
|
||||
stage = "Transferring data"
|
||||
}
|
||||
s.callback(CopyProgress{
|
||||
FilesDone: s.filesDone,
|
||||
FilesTotal: s.stats.FilesTotal,
|
||||
BytesDone: s.bytesDone,
|
||||
BytesTotal: s.stats.BytesTotal,
|
||||
CurrentPath: currentPath,
|
||||
Stage: stage,
|
||||
})
|
||||
}
|
||||
|
||||
func samePath(left string, right string) (bool, error) {
|
||||
leftAbs, err := filepath.Abs(left)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
rightAbs, err := filepath.Abs(right)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return leftAbs == rightAbs, nil
|
||||
}
|
||||
135
src/vcom-0.2.5/internal/fs/ops_test.go
Normal file
135
src/vcom-0.2.5/internal/fs/ops_test.go
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
package vfs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCopyPathWithProgressContextRemovesPartialTargetOnCancel(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
root := t.TempDir()
|
||||
srcDir := filepath.Join(root, "src")
|
||||
dstDir := filepath.Join(root, "dst")
|
||||
if err := os.MkdirAll(srcDir, 0o755); err != nil {
|
||||
t.Fatalf("mkdir src: %v", err)
|
||||
}
|
||||
if err := os.MkdirAll(dstDir, 0o755); err != nil {
|
||||
t.Fatalf("mkdir dst: %v", err)
|
||||
}
|
||||
|
||||
for idx := 0; idx < 64; idx++ {
|
||||
path := filepath.Join(srcDir, "file-"+strconv.Itoa(idx)+".txt")
|
||||
if err := os.WriteFile(path, []byte("payload-"+strconv.Itoa(idx)), 0o644); err != nil {
|
||||
t.Fatalf("write source file %d: %v", idx, err)
|
||||
}
|
||||
}
|
||||
|
||||
stats, err := CopyStats(srcDir)
|
||||
if err != nil {
|
||||
t.Fatalf("copy stats: %v", err)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
_, err = CopyPathWithProgressContext(ctx, srcDir, dstDir, false, stats, func(progress CopyProgress) {
|
||||
if progress.FilesDone >= 1 {
|
||||
cancel()
|
||||
}
|
||||
})
|
||||
if !errors.Is(err, context.Canceled) {
|
||||
t.Fatalf("expected context cancellation, got %v", err)
|
||||
}
|
||||
|
||||
targetPath := filepath.Join(dstDir, filepath.Base(srcDir))
|
||||
if _, statErr := os.Stat(targetPath); !errors.Is(statErr, os.ErrNotExist) {
|
||||
t.Fatalf("expected partial target to be removed, stat err=%v", statErr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMovePathWithProgressContextCancelledBeforeStartKeepsSource(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
root := t.TempDir()
|
||||
srcFile := filepath.Join(root, "source.txt")
|
||||
dstDir := filepath.Join(root, "dst")
|
||||
if err := os.WriteFile(srcFile, []byte("payload"), 0o644); err != nil {
|
||||
t.Fatalf("write source: %v", err)
|
||||
}
|
||||
if err := os.MkdirAll(dstDir, 0o755); err != nil {
|
||||
t.Fatalf("mkdir dst: %v", err)
|
||||
}
|
||||
|
||||
stats, err := CopyStats(srcFile)
|
||||
if err != nil {
|
||||
t.Fatalf("copy stats: %v", err)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
|
||||
_, err = MovePathWithProgressContext(ctx, srcFile, dstDir, false, stats, nil)
|
||||
if !errors.Is(err, context.Canceled) {
|
||||
t.Fatalf("expected context cancellation, got %v", err)
|
||||
}
|
||||
|
||||
if _, statErr := os.Stat(srcFile); statErr != nil {
|
||||
t.Fatalf("expected source to remain in place, stat err=%v", statErr)
|
||||
}
|
||||
targetPath := filepath.Join(dstDir, filepath.Base(srcFile))
|
||||
if _, statErr := os.Stat(targetPath); !errors.Is(statErr, os.ErrNotExist) {
|
||||
t.Fatalf("expected destination file to be absent, stat err=%v", statErr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenamePath(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
root := t.TempDir()
|
||||
source := filepath.Join(root, "old.txt")
|
||||
if err := os.WriteFile(source, []byte("payload"), 0o644); err != nil {
|
||||
t.Fatalf("write source: %v", err)
|
||||
}
|
||||
|
||||
target, err := RenamePath(source, "new.txt")
|
||||
if err != nil {
|
||||
t.Fatalf("rename: %v", err)
|
||||
}
|
||||
|
||||
if filepath.Base(target) != "new.txt" {
|
||||
t.Fatalf("unexpected target path: %s", target)
|
||||
}
|
||||
if _, statErr := os.Stat(target); statErr != nil {
|
||||
t.Fatalf("expected renamed file to exist, stat err=%v", statErr)
|
||||
}
|
||||
if _, statErr := os.Stat(source); !errors.Is(statErr, os.ErrNotExist) {
|
||||
t.Fatalf("expected source to be absent, stat err=%v", statErr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenamePathRejectsExistingTarget(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
root := t.TempDir()
|
||||
source := filepath.Join(root, "old.txt")
|
||||
target := filepath.Join(root, "new.txt")
|
||||
if err := os.WriteFile(source, []byte("payload"), 0o644); err != nil {
|
||||
t.Fatalf("write source: %v", err)
|
||||
}
|
||||
if err := os.WriteFile(target, []byte("payload"), 0o644); err != nil {
|
||||
t.Fatalf("write target: %v", err)
|
||||
}
|
||||
|
||||
_, err := RenamePath(source, "new.txt")
|
||||
if err == nil {
|
||||
t.Fatalf("expected overwrite error, got nil")
|
||||
}
|
||||
if got, want := err.Error(), ErrOverwrite(target).Error(); got != want {
|
||||
t.Fatalf("expected overwrite error %q, got %q", want, got)
|
||||
}
|
||||
}
|
||||
693
src/vcom-0.2.5/internal/fs/preview.go
Normal file
693
src/vcom-0.2.5/internal/fs/preview.go
Normal file
|
|
@ -0,0 +1,693 @@
|
|||
package vfs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"image"
|
||||
_ "image/gif"
|
||||
_ "image/jpeg"
|
||||
_ "image/png"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/chroma/v2"
|
||||
"github.com/alecthomas/chroma/v2/formatters"
|
||||
"github.com/alecthomas/chroma/v2/lexers"
|
||||
"github.com/alecthomas/chroma/v2/styles"
|
||||
)
|
||||
|
||||
var sgrRegexp = regexp.MustCompile(`\x1b\[([0-9;:]*)m`)
|
||||
var sgrNumberRegexp = regexp.MustCompile(`\d+`)
|
||||
|
||||
type PreviewKind string
|
||||
|
||||
const (
|
||||
PreviewKindEmpty PreviewKind = "empty"
|
||||
PreviewKindDirectory PreviewKind = "directory"
|
||||
PreviewKindText PreviewKind = "text"
|
||||
PreviewKindImage PreviewKind = "image"
|
||||
PreviewKindPDF PreviewKind = "pdf"
|
||||
PreviewKindAudio PreviewKind = "audio"
|
||||
PreviewKindVideo PreviewKind = "video"
|
||||
PreviewKindBinary PreviewKind = "binary"
|
||||
PreviewKindError PreviewKind = "error"
|
||||
)
|
||||
|
||||
type Metadata struct {
|
||||
Path string
|
||||
Kind string
|
||||
Size int64
|
||||
SizeKnown bool
|
||||
ModifiedAt string
|
||||
CreatedAt string
|
||||
Permissions string
|
||||
ImageFormat string
|
||||
ImageSize string
|
||||
Extension string
|
||||
|
||||
// Extended preview metadata
|
||||
Duration string
|
||||
Bitrate string
|
||||
AudioCodec string
|
||||
VideoCodec string
|
||||
SampleRate string
|
||||
Channels string
|
||||
PageCount string
|
||||
Dimensions string
|
||||
}
|
||||
|
||||
type Preview struct {
|
||||
Kind PreviewKind
|
||||
Title string
|
||||
Body string
|
||||
PlainBody string
|
||||
Metadata Metadata
|
||||
Entries []Entry
|
||||
}
|
||||
|
||||
type PreviewOptions struct {
|
||||
ShowHidden bool
|
||||
DirsFirst bool
|
||||
SortBy string
|
||||
SortReverse bool
|
||||
MaxPreviewBytes int64
|
||||
DirectoryPreviewLimit int
|
||||
HumanReadableSize bool
|
||||
ThemeName string
|
||||
UseNerdIcons bool
|
||||
ImagePreviewWidth int
|
||||
ImagePreviewHeight int
|
||||
}
|
||||
|
||||
func BuildPreview(entry Entry, options PreviewOptions) Preview {
|
||||
preview := Preview{
|
||||
Kind: PreviewKindEmpty,
|
||||
Title: entry.DisplayName(),
|
||||
Metadata: Metadata{
|
||||
Path: entry.Path,
|
||||
Kind: kindLabel(entry),
|
||||
Permissions: Permissions(entry.Mode),
|
||||
ModifiedAt: ShortTime(entry.ModifiedAt),
|
||||
CreatedAt: "n/a",
|
||||
Extension: entry.Extension,
|
||||
},
|
||||
}
|
||||
|
||||
if entry.CreatedKnown {
|
||||
preview.Metadata.CreatedAt = ShortTime(entry.CreatedAt)
|
||||
}
|
||||
|
||||
if entry.IsDir {
|
||||
preview.Kind = PreviewKindDirectory
|
||||
preview.Metadata.Size = entry.Size
|
||||
preview.Metadata.SizeKnown = entry.DirSizeKnown
|
||||
preview.Body, preview.Entries = buildDirectoryPreview(entry.Path, options)
|
||||
preview.PlainBody = preview.Body
|
||||
return preview
|
||||
}
|
||||
|
||||
preview.Metadata.Size = entry.Size
|
||||
preview.Metadata.SizeKnown = true
|
||||
|
||||
file, err := os.Open(entry.Path)
|
||||
if err != nil {
|
||||
preview.Kind = PreviewKindError
|
||||
preview.Body = fmt.Sprintf("Could not open file:\n\n%s", err)
|
||||
preview.PlainBody = preview.Body
|
||||
return preview
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
buffer := new(bytes.Buffer)
|
||||
if _, err := io.CopyN(buffer, file, options.MaxPreviewBytes); err != nil && err != io.EOF {
|
||||
preview.Kind = PreviewKindError
|
||||
preview.Body = fmt.Sprintf("Could not read preview:\n\n%s", err)
|
||||
preview.PlainBody = preview.Body
|
||||
return preview
|
||||
}
|
||||
|
||||
data := buffer.Bytes()
|
||||
if format, dimensions, ok := DetectImage(data); ok {
|
||||
preview.Kind = PreviewKindImage
|
||||
preview.Metadata.ImageFormat = format
|
||||
preview.Metadata.ImageSize = dimensions
|
||||
inline := renderImageInlinePreview(entry.Path, options.ImagePreviewWidth, options.ImagePreviewHeight)
|
||||
if inline != "" {
|
||||
preview.Body = inline
|
||||
}
|
||||
preview.PlainBody = preview.Body
|
||||
return preview
|
||||
}
|
||||
|
||||
// Extended preview for PDF, audio, video via external utilities
|
||||
if hasExt(pdfExtensions, entry.Extension) {
|
||||
return buildPDFPreview(entry, options, preview)
|
||||
}
|
||||
if hasExt(audioExtensions, entry.Extension) {
|
||||
return buildAudioPreview(entry, options, preview)
|
||||
}
|
||||
if hasExt(videoExtensions, entry.Extension) {
|
||||
return buildVideoPreview(entry, options, preview)
|
||||
}
|
||||
|
||||
if IsBinarySample(data) {
|
||||
preview.Kind = PreviewKindBinary
|
||||
preview.Body = "Binary file detected.\n\nSafe inline preview is disabled for this file type."
|
||||
preview.PlainBody = preview.Body
|
||||
return preview
|
||||
}
|
||||
|
||||
preview.Kind = PreviewKindText
|
||||
preview.PlainBody = strings.ReplaceAll(string(data), "\t", " ")
|
||||
preview.Body = highlightText(entry.Path, preview.PlainBody, options.ThemeName)
|
||||
return preview
|
||||
}
|
||||
|
||||
func highlightText(path string, source string, themeName string) string {
|
||||
lexer := lexers.Match(path)
|
||||
if lexer == nil {
|
||||
lexer = lexers.Analyse(source)
|
||||
}
|
||||
if lexer == nil {
|
||||
return source
|
||||
}
|
||||
|
||||
iterator, err := chroma.Coalesce(lexer).Tokenise(nil, source)
|
||||
if err != nil {
|
||||
return source
|
||||
}
|
||||
|
||||
style := styles.Get(chromaStyleName(themeName))
|
||||
if style == nil {
|
||||
return source
|
||||
}
|
||||
style = styleWithoutBackground(style)
|
||||
if style == nil {
|
||||
return source
|
||||
}
|
||||
|
||||
var output bytes.Buffer
|
||||
if err := formatters.TTY16m.Format(&output, style, iterator); err != nil {
|
||||
return source
|
||||
}
|
||||
return stripBackgroundSGR(output.String())
|
||||
}
|
||||
|
||||
func styleWithoutBackground(base *chroma.Style) *chroma.Style {
|
||||
if base == nil {
|
||||
return nil
|
||||
}
|
||||
builder := base.Builder().Transform(func(entry chroma.StyleEntry) chroma.StyleEntry {
|
||||
entry.Background = 0
|
||||
return entry
|
||||
})
|
||||
stripped, err := builder.Build()
|
||||
if err != nil {
|
||||
return base
|
||||
}
|
||||
return stripped
|
||||
}
|
||||
|
||||
func stripBackgroundSGR(text string) string {
|
||||
return sgrRegexp.ReplaceAllStringFunc(text, func(seq string) string {
|
||||
matches := sgrRegexp.FindStringSubmatch(seq)
|
||||
if len(matches) != 2 {
|
||||
return seq
|
||||
}
|
||||
filtered := filterSGRParams(matches[1])
|
||||
if filtered == "" {
|
||||
return ""
|
||||
}
|
||||
return "\x1b[" + filtered + "m"
|
||||
})
|
||||
}
|
||||
|
||||
func filterSGRParams(paramString string) string {
|
||||
if paramString == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
raw := sgrNumberRegexp.FindAllString(paramString, -1)
|
||||
if len(raw) == 0 {
|
||||
return ""
|
||||
}
|
||||
codes := make([]int, 0, len(raw))
|
||||
for _, token := range raw {
|
||||
value, err := strconv.Atoi(token)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
codes = append(codes, value)
|
||||
}
|
||||
|
||||
kept := make([]string, 0, len(codes))
|
||||
for i := 0; i < len(codes); i++ {
|
||||
code := codes[i]
|
||||
|
||||
if code == 0 {
|
||||
// Do not hard-reset background to terminal default.
|
||||
// Reset common text attributes + foreground only.
|
||||
kept = append(kept, "39", "22", "23", "24", "59")
|
||||
continue
|
||||
}
|
||||
|
||||
if code == 49 || code == 7 || code == 27 || (code >= 40 && code <= 47) || (code >= 100 && code <= 107) {
|
||||
continue
|
||||
}
|
||||
|
||||
switch code {
|
||||
case 48:
|
||||
// Background color payloads:
|
||||
// 48;5;n or 48;2;r;g;b (also appears in ':' form; parsed as the same int stream).
|
||||
if i+1 < len(codes) {
|
||||
mode := codes[i+1]
|
||||
switch mode {
|
||||
case 5:
|
||||
i += 2
|
||||
case 2:
|
||||
i += 4
|
||||
default:
|
||||
i++
|
||||
}
|
||||
}
|
||||
continue
|
||||
case 38, 58:
|
||||
// Preserve foreground (38) and underline color (58) payloads.
|
||||
kept = append(kept, strconv.Itoa(code))
|
||||
if i+1 < len(codes) {
|
||||
mode := codes[i+1]
|
||||
kept = append(kept, strconv.Itoa(mode))
|
||||
switch mode {
|
||||
case 5:
|
||||
if i+2 < len(codes) {
|
||||
kept = append(kept, strconv.Itoa(codes[i+2]))
|
||||
}
|
||||
i += 2
|
||||
case 2:
|
||||
if i+4 < len(codes) {
|
||||
kept = append(kept,
|
||||
strconv.Itoa(codes[i+2]),
|
||||
strconv.Itoa(codes[i+3]),
|
||||
strconv.Itoa(codes[i+4]),
|
||||
)
|
||||
}
|
||||
i += 4
|
||||
default:
|
||||
i++
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
kept = append(kept, strconv.Itoa(code))
|
||||
}
|
||||
|
||||
return strings.Join(kept, ";")
|
||||
}
|
||||
|
||||
func chromaStyleName(themeName string) string {
|
||||
switch strings.ToLower(strings.TrimSpace(themeName)) {
|
||||
case "catppuccin-mocha", "catppuccin-lavender":
|
||||
return "catppuccin-mocha"
|
||||
case "tokyo-night":
|
||||
return "tokyonight-night"
|
||||
case "gruvbox-dark", "gruvbox":
|
||||
return "gruvbox"
|
||||
case "nord-frost", "nord":
|
||||
return "nord"
|
||||
case "dracula":
|
||||
return "dracula"
|
||||
case "rose-pine":
|
||||
return "rose-pine"
|
||||
case "solarized-dark":
|
||||
return "solarized-dark"
|
||||
default:
|
||||
return "catppuccin-mocha"
|
||||
}
|
||||
}
|
||||
|
||||
func buildDirectoryPreview(path string, options PreviewOptions) (string, []Entry) {
|
||||
entries, err := ListDir(path, ListOptions{
|
||||
ShowHidden: options.ShowHidden,
|
||||
DirsFirst: options.DirsFirst,
|
||||
SortBy: options.SortBy,
|
||||
SortReverse: options.SortReverse,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Could not list directory:\n\n%s", err), nil
|
||||
}
|
||||
if len(entries) == 0 {
|
||||
return "Directory is empty.", nil
|
||||
}
|
||||
|
||||
// Return all entries as-is for column-based rendering.
|
||||
// The text body is still generated for terminals that don't support
|
||||
// the rich rendering, and as a fallback.
|
||||
var lines []string
|
||||
for _, entry := range entries {
|
||||
if entry.IsParent {
|
||||
continue
|
||||
}
|
||||
icon := previewIcon(entry, options.UseNerdIcons)
|
||||
|
||||
size := ""
|
||||
if !entry.IsDir {
|
||||
if options.HumanReadableSize {
|
||||
size = HumanSize(entry.Size)
|
||||
} else {
|
||||
size = fmt.Sprintf("%d", entry.Size)
|
||||
}
|
||||
}
|
||||
|
||||
lines = append(lines, fmt.Sprintf("%s %-36s %12s %s", icon, entry.DisplayName(), size, ShortTime(entry.ModifiedAt)))
|
||||
if len(lines) >= options.DirectoryPreviewLimit {
|
||||
lines = append(lines, "…")
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Join(lines, "\n"), entries
|
||||
}
|
||||
|
||||
func previewIcon(entry Entry, useNerdIcons bool) string {
|
||||
if !useNerdIcons {
|
||||
switch entry.Category() {
|
||||
case "directory":
|
||||
return "[D]"
|
||||
case "config":
|
||||
return "[C]"
|
||||
case "text":
|
||||
return "[T]"
|
||||
case "image":
|
||||
return "[I]"
|
||||
case "executable":
|
||||
return "[X]"
|
||||
case "archive":
|
||||
return "[A]"
|
||||
default:
|
||||
return "[F]"
|
||||
}
|
||||
}
|
||||
|
||||
switch entry.Category() {
|
||||
case "directory":
|
||||
return ""
|
||||
case "config":
|
||||
return ""
|
||||
case "text":
|
||||
return ""
|
||||
case "image":
|
||||
return ""
|
||||
case "executable":
|
||||
return ""
|
||||
case "archive":
|
||||
return ""
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func renderImageInlinePreview(path string, width int, height int) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func findTool(name string) string {
|
||||
path, err := exec.LookPath(name)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
func buildPDFPreview(entry Entry, options PreviewOptions, base Preview) Preview {
|
||||
pdftotext := findTool("pdftotext")
|
||||
if pdftotext == "" {
|
||||
base.Kind = PreviewKindBinary
|
||||
base.Body = "PDF file detected.\nInstall poppler-utils (pdftotext) for text preview."
|
||||
base.PlainBody = base.Body
|
||||
return base
|
||||
}
|
||||
|
||||
cmd := exec.Command(pdftotext, "-layout", "-nopgbrk", entry.Path, "-")
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
base.Kind = PreviewKindError
|
||||
base.Body = fmt.Sprintf("pdftotext error:\n\n%s", err)
|
||||
base.PlainBody = base.Body
|
||||
return base
|
||||
}
|
||||
|
||||
text := string(out)
|
||||
if int64(len(text)) > options.MaxPreviewBytes {
|
||||
text = text[:options.MaxPreviewBytes]
|
||||
}
|
||||
|
||||
// Get page count via pdfinfo if available
|
||||
pdfinfo := findTool("pdfinfo")
|
||||
if pdfinfo != "" {
|
||||
infoCmd := exec.Command(pdfinfo, entry.Path)
|
||||
if infoOut, err := infoCmd.Output(); err == nil {
|
||||
for _, line := range strings.Split(string(infoOut), "\n") {
|
||||
if strings.HasPrefix(strings.ToLower(line), "pages:") {
|
||||
base.Metadata.PageCount = strings.TrimSpace(strings.TrimPrefix(line[5:], ":"))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
base.Kind = PreviewKindPDF
|
||||
base.PlainBody = text
|
||||
base.Body = highlightText(entry.Path, text, options.ThemeName)
|
||||
return base
|
||||
}
|
||||
|
||||
func buildAudioPreview(entry Entry, options PreviewOptions, base Preview) Preview {
|
||||
ffprobe := findTool("ffprobe")
|
||||
if ffprobe == "" {
|
||||
base.Kind = PreviewKindBinary
|
||||
base.Body = "Audio file detected.\nInstall ffmpeg (ffprobe) for metadata preview."
|
||||
base.PlainBody = base.Body
|
||||
return base
|
||||
}
|
||||
|
||||
cmd := exec.Command(ffprobe,
|
||||
"-v", "quiet",
|
||||
"-print_format", "json",
|
||||
"-show_format",
|
||||
"-show_streams",
|
||||
entry.Path,
|
||||
)
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
base.Kind = PreviewKindError
|
||||
base.Body = fmt.Sprintf("ffprobe error:\n\n%s", err)
|
||||
base.PlainBody = base.Body
|
||||
return base
|
||||
}
|
||||
|
||||
var info struct {
|
||||
Format struct {
|
||||
Duration string `json:"duration"`
|
||||
Bitrate string `json:"bit_rate"`
|
||||
} `json:"format"`
|
||||
Streams []struct {
|
||||
CodecType string `json:"codec_type"`
|
||||
CodecName string `json:"codec_name"`
|
||||
SampleRate string `json:"sample_rate"`
|
||||
Channels int `json:"channels"`
|
||||
} `json:"streams"`
|
||||
}
|
||||
if err := json.Unmarshal(out, &info); err != nil {
|
||||
base.Kind = PreviewKindError
|
||||
base.Body = fmt.Sprintf("Could not parse ffprobe output:\n\n%s", err)
|
||||
base.PlainBody = base.Body
|
||||
return base
|
||||
}
|
||||
|
||||
// Format duration
|
||||
if info.Format.Duration != "" {
|
||||
if secs, err := strconv.ParseFloat(info.Format.Duration, 64); err == nil {
|
||||
mins := int(secs) / 60
|
||||
secsRem := int(secs) % 60
|
||||
base.Metadata.Duration = fmt.Sprintf("%d:%02d", mins, secsRem)
|
||||
}
|
||||
}
|
||||
|
||||
if info.Format.Bitrate != "" {
|
||||
if bps, err := strconv.ParseInt(info.Format.Bitrate, 10, 64); err == nil {
|
||||
base.Metadata.Bitrate = fmt.Sprintf("%d kbps", bps/1000)
|
||||
}
|
||||
}
|
||||
|
||||
for _, stream := range info.Streams {
|
||||
if stream.CodecType == "audio" {
|
||||
base.Metadata.AudioCodec = stream.CodecName
|
||||
if stream.SampleRate != "" {
|
||||
base.Metadata.SampleRate = stream.SampleRate + " Hz"
|
||||
}
|
||||
switch stream.Channels {
|
||||
case 1:
|
||||
base.Metadata.Channels = "mono"
|
||||
case 2:
|
||||
base.Metadata.Channels = "stereo"
|
||||
case 6:
|
||||
base.Metadata.Channels = "5.1"
|
||||
case 8:
|
||||
base.Metadata.Channels = "7.1"
|
||||
default:
|
||||
base.Metadata.Channels = fmt.Sprintf("%d ch", stream.Channels)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var lines []string
|
||||
lines = append(lines, fmt.Sprintf(" Duration: %s", base.Metadata.Duration))
|
||||
if base.Metadata.Bitrate != "" {
|
||||
lines = append(lines, fmt.Sprintf(" Bitrate: %s", base.Metadata.Bitrate))
|
||||
}
|
||||
if base.Metadata.AudioCodec != "" {
|
||||
lines = append(lines, fmt.Sprintf(" Codec: %s", base.Metadata.AudioCodec))
|
||||
}
|
||||
if base.Metadata.SampleRate != "" {
|
||||
lines = append(lines, fmt.Sprintf(" Rate: %s", base.Metadata.SampleRate))
|
||||
}
|
||||
if base.Metadata.Channels != "" {
|
||||
lines = append(lines, fmt.Sprintf(" Channels: %s", base.Metadata.Channels))
|
||||
}
|
||||
|
||||
base.Kind = PreviewKindAudio
|
||||
base.Body = fmt.Sprintf("🎵 Audio File\n\n%s", strings.Join(lines, "\n"))
|
||||
base.PlainBody = fmt.Sprintf("Audio File\n\nDuration: %s\nBitrate: %s\nCodec: %s\nRate: %s\nChannels: %s",
|
||||
base.Metadata.Duration, base.Metadata.Bitrate, base.Metadata.AudioCodec,
|
||||
base.Metadata.SampleRate, base.Metadata.Channels)
|
||||
return base
|
||||
}
|
||||
|
||||
func buildVideoPreview(entry Entry, options PreviewOptions, base Preview) Preview {
|
||||
ffprobe := findTool("ffprobe")
|
||||
if ffprobe == "" {
|
||||
base.Kind = PreviewKindBinary
|
||||
base.Body = "Video file detected.\nInstall ffmpeg (ffprobe) for metadata preview."
|
||||
base.PlainBody = base.Body
|
||||
return base
|
||||
}
|
||||
|
||||
cmd := exec.Command(ffprobe,
|
||||
"-v", "quiet",
|
||||
"-print_format", "json",
|
||||
"-show_format",
|
||||
"-show_streams",
|
||||
entry.Path,
|
||||
)
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
base.Kind = PreviewKindError
|
||||
base.Body = fmt.Sprintf("ffprobe error:\n\n%s", err)
|
||||
base.PlainBody = base.Body
|
||||
return base
|
||||
}
|
||||
|
||||
var info struct {
|
||||
Format struct {
|
||||
Duration string `json:"duration"`
|
||||
Bitrate string `json:"bit_rate"`
|
||||
} `json:"format"`
|
||||
Streams []struct {
|
||||
CodecType string `json:"codec_type"`
|
||||
CodecName string `json:"codec_name"`
|
||||
Width int `json:"width"`
|
||||
Height int `json:"height"`
|
||||
} `json:"streams"`
|
||||
}
|
||||
if err := json.Unmarshal(out, &info); err != nil {
|
||||
base.Kind = PreviewKindError
|
||||
base.Body = fmt.Sprintf("Could not parse ffprobe output:\n\n%s", err)
|
||||
base.PlainBody = base.Body
|
||||
return base
|
||||
}
|
||||
|
||||
// Format duration
|
||||
if info.Format.Duration != "" {
|
||||
if secs, err := strconv.ParseFloat(info.Format.Duration, 64); err == nil {
|
||||
hrs := int(secs) / 3600
|
||||
mins := (int(secs) % 3600) / 60
|
||||
secsRem := int(secs) % 60
|
||||
if hrs > 0 {
|
||||
base.Metadata.Duration = fmt.Sprintf("%d:%02d:%02d", hrs, mins, secsRem)
|
||||
} else {
|
||||
base.Metadata.Duration = fmt.Sprintf("%d:%02d", mins, secsRem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if info.Format.Bitrate != "" {
|
||||
if bps, err := strconv.ParseInt(info.Format.Bitrate, 10, 64); err == nil {
|
||||
base.Metadata.Bitrate = fmt.Sprintf("%d kbps", bps/1000)
|
||||
}
|
||||
}
|
||||
|
||||
for _, stream := range info.Streams {
|
||||
switch stream.CodecType {
|
||||
case "video":
|
||||
base.Metadata.VideoCodec = stream.CodecName
|
||||
if stream.Width > 0 && stream.Height > 0 {
|
||||
base.Metadata.Dimensions = fmt.Sprintf("%dx%d", stream.Width, stream.Height)
|
||||
}
|
||||
case "audio":
|
||||
if base.Metadata.AudioCodec == "" {
|
||||
base.Metadata.AudioCodec = stream.CodecName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var lines []string
|
||||
lines = append(lines, fmt.Sprintf(" Duration: %s", base.Metadata.Duration))
|
||||
if base.Metadata.Bitrate != "" {
|
||||
lines = append(lines, fmt.Sprintf(" Bitrate: %s", base.Metadata.Bitrate))
|
||||
}
|
||||
if base.Metadata.VideoCodec != "" {
|
||||
lines = append(lines, fmt.Sprintf(" Video: %s", base.Metadata.VideoCodec))
|
||||
}
|
||||
if base.Metadata.Dimensions != "" {
|
||||
lines = append(lines, fmt.Sprintf(" Resolution: %s", base.Metadata.Dimensions))
|
||||
}
|
||||
if base.Metadata.AudioCodec != "" {
|
||||
lines = append(lines, fmt.Sprintf(" Audio: %s", base.Metadata.AudioCodec))
|
||||
}
|
||||
|
||||
base.Kind = PreviewKindVideo
|
||||
base.Body = fmt.Sprintf("🎬 Video File\n\n%s", strings.Join(lines, "\n"))
|
||||
base.PlainBody = fmt.Sprintf("Video File\n\nDuration: %s\nBitrate: %s\nVideo: %s\nResolution: %s\nAudio: %s",
|
||||
base.Metadata.Duration, base.Metadata.Bitrate, base.Metadata.VideoCodec,
|
||||
base.Metadata.Dimensions, base.Metadata.AudioCodec)
|
||||
return base
|
||||
}
|
||||
|
||||
func DetectImage(data []byte) (string, string, bool) {
|
||||
cfg, format, err := image.DecodeConfig(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return "", "", false
|
||||
}
|
||||
return format, fmt.Sprintf("%dx%d", cfg.Width, cfg.Height), true
|
||||
}
|
||||
|
||||
func kindLabel(entry Entry) string {
|
||||
switch {
|
||||
case entry.IsParent:
|
||||
return "parent"
|
||||
case entry.IsDir:
|
||||
return "directory"
|
||||
case entry.Extension != "":
|
||||
return "file"
|
||||
default:
|
||||
return strings.TrimPrefix(filepath.Ext(entry.Name), ".")
|
||||
}
|
||||
}
|
||||
751
src/vcom-0.2.5/internal/fs/remote/client.go
Normal file
751
src/vcom-0.2.5/internal/fs/remote/client.go
Normal file
|
|
@ -0,0 +1,751 @@
|
|||
package remote
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/sftp"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
// SSHClient wraps an SSH connection and SFTP client for remote filesystem access.
|
||||
type SSHClient struct {
|
||||
// Host is the SSH host configuration used to establish the connection.
|
||||
Host SSHHost
|
||||
|
||||
sshConn *ssh.Client
|
||||
sftpCli *sftp.Client
|
||||
|
||||
keepaliveStop chan struct{}
|
||||
keepaliveWg sync.WaitGroup
|
||||
}
|
||||
|
||||
// Connect establishes an SSH connection to the remote host and opens an SFTP session.
|
||||
// It uses key-based authentication if IdentityFile is set, otherwise falls back to password auth.
|
||||
func Connect(host SSHHost) (*SSHClient, error) {
|
||||
authMethods, err := authMethodsForHost(host)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ssh auth: %w", err)
|
||||
}
|
||||
|
||||
user := host.User
|
||||
if user == "" {
|
||||
user = os.Getenv("USER")
|
||||
}
|
||||
|
||||
config := &ssh.ClientConfig{
|
||||
User: user,
|
||||
Auth: authMethods,
|
||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(), // TODO: support known_hosts verification
|
||||
Timeout: 15 * time.Second,
|
||||
}
|
||||
|
||||
addr := host.Addr()
|
||||
sshConn, err := ssh.Dial("tcp", addr, config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ssh dial %s: %w", addr, err)
|
||||
}
|
||||
|
||||
sftpCli, err := sftp.NewClient(sshConn)
|
||||
if err != nil {
|
||||
sshConn.Close()
|
||||
return nil, fmt.Errorf("sftp client: %w", err)
|
||||
}
|
||||
|
||||
client := &SSHClient{
|
||||
Host: host,
|
||||
sshConn: sshConn,
|
||||
sftpCli: sftpCli,
|
||||
keepaliveStop: make(chan struct{}),
|
||||
}
|
||||
|
||||
// Start keepalive goroutine — sends keepalive@openssh.com every 30s
|
||||
// to prevent the SSH server from dropping the connection during inactivity.
|
||||
client.keepaliveWg.Add(1)
|
||||
go func() {
|
||||
defer client.keepaliveWg.Done()
|
||||
ticker := time.NewTicker(30 * time.Second)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
_, _, err := sshConn.SendRequest("keepalive@openssh.com", true, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
case <-client.keepaliveStop:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// authMethodsForHost returns the appropriate SSH auth methods for the given host.
|
||||
// For SSH config hosts with IdentityFile, it uses public key authentication.
|
||||
// For custom hosts with a password, it uses password authentication.
|
||||
func authMethodsForHost(host SSHHost) ([]ssh.AuthMethod, error) {
|
||||
var methods []ssh.AuthMethod
|
||||
|
||||
// Try key-based auth if identity file is specified
|
||||
if host.IdentityFile != "" {
|
||||
key, err := os.ReadFile(host.IdentityFile)
|
||||
if err == nil {
|
||||
signer, err := ssh.ParsePrivateKey(key)
|
||||
if err == nil {
|
||||
methods = append(methods, ssh.PublicKeys(signer))
|
||||
} else {
|
||||
// If the key is encrypted, try with empty passphrase or common ones
|
||||
// For simplicity, we try without passphrase first
|
||||
// In a real implementation, we might prompt for a passphrase
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try password auth if password is set
|
||||
if host.Password != "" {
|
||||
methods = append(methods, ssh.Password(host.Password))
|
||||
}
|
||||
|
||||
// Always include keyboard-interactive as a fallback (it wraps password)
|
||||
if host.Password != "" {
|
||||
methods = append(methods, ssh.KeyboardInteractive(
|
||||
func(user, instruction string, questions []string, echos []bool) ([]string, error) {
|
||||
answers := make([]string, len(questions))
|
||||
for i := range questions {
|
||||
answers[i] = host.Password
|
||||
}
|
||||
return answers, nil
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
// Always try default SSH agent and default keys as a last resort
|
||||
// This covers the case where the user has an SSH agent running with loaded keys
|
||||
// but no IdentityFile is specified in the config.
|
||||
if host.IdentityFile == "" && host.Password == "" {
|
||||
// Add default key paths
|
||||
home, err := os.UserHomeDir()
|
||||
if err == nil {
|
||||
defaultKeys := []string{
|
||||
home + "/.ssh/id_rsa",
|
||||
home + "/.ssh/id_ed25519",
|
||||
home + "/.ssh/id_ecdsa",
|
||||
home + "/.ssh/id_ecdsa_sk",
|
||||
home + "/.ssh/id_ed25519_sk",
|
||||
home + "/.ssh/identity",
|
||||
}
|
||||
for _, keyPath := range defaultKeys {
|
||||
if key, err := os.ReadFile(keyPath); err == nil {
|
||||
if signer, err := ssh.ParsePrivateKey(key); err == nil {
|
||||
methods = append(methods, ssh.PublicKeys(signer))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(methods) == 0 {
|
||||
return nil, fmt.Errorf("no authentication methods available for host %q", host.Name)
|
||||
}
|
||||
|
||||
return methods, nil
|
||||
}
|
||||
|
||||
// ReadDir reads the contents of a remote directory and returns os.FileInfo entries.
|
||||
func (c *SSHClient) ReadDir(dirPath string) ([]os.FileInfo, error) {
|
||||
if c.sftpCli == nil {
|
||||
return nil, fmt.Errorf("not connected")
|
||||
}
|
||||
return c.sftpCli.ReadDir(dirPath)
|
||||
}
|
||||
|
||||
// Lstat returns file information without following symlinks.
|
||||
func (c *SSHClient) Lstat(path string) (os.FileInfo, error) {
|
||||
if c.sftpCli == nil {
|
||||
return nil, fmt.Errorf("not connected")
|
||||
}
|
||||
return c.sftpCli.Lstat(path)
|
||||
}
|
||||
|
||||
// Stat returns file information following symlinks.
|
||||
func (c *SSHClient) Stat(path string) (os.FileInfo, error) {
|
||||
if c.sftpCli == nil {
|
||||
return nil, fmt.Errorf("not connected")
|
||||
}
|
||||
return c.sftpCli.Stat(path)
|
||||
}
|
||||
|
||||
// ReadLink reads the target of a symbolic link.
|
||||
func (c *SSHClient) ReadLink(linkPath string) (string, error) {
|
||||
if c.sftpCli == nil {
|
||||
return "", fmt.Errorf("not connected")
|
||||
}
|
||||
return c.sftpCli.ReadLink(linkPath)
|
||||
}
|
||||
|
||||
// RealPath resolves a path to its absolute form on the remote server.
|
||||
func (c *SSHClient) RealPath(p string) (string, error) {
|
||||
if c.sftpCli == nil {
|
||||
return "", fmt.Errorf("not connected")
|
||||
}
|
||||
return c.sftpCli.RealPath(p)
|
||||
}
|
||||
|
||||
// ReadFile opens a remote file for reading.
|
||||
func (c *SSHClient) ReadFile(filePath string) (io.ReadCloser, error) {
|
||||
if c.sftpCli == nil {
|
||||
return nil, fmt.Errorf("not connected")
|
||||
}
|
||||
return c.sftpCli.Open(filePath)
|
||||
}
|
||||
|
||||
// CreateFile opens a remote file for writing, creating it if it doesn't exist.
|
||||
func (c *SSHClient) CreateFile(filePath string) (io.WriteCloser, error) {
|
||||
if c.sftpCli == nil {
|
||||
return nil, fmt.Errorf("not connected")
|
||||
}
|
||||
return c.sftpCli.Create(filePath)
|
||||
}
|
||||
|
||||
// MkdirAll creates a remote directory and any necessary parents.
|
||||
// If the directory already exists, it returns nil (no error).
|
||||
func (c *SSHClient) MkdirAll(dirPath string) error {
|
||||
if c.sftpCli == nil {
|
||||
return fmt.Errorf("not connected")
|
||||
}
|
||||
// sftp doesn't have MkdirAll, so we implement it manually
|
||||
// First check if the path already exists
|
||||
_, err := c.sftpCli.Stat(dirPath)
|
||||
if err == nil {
|
||||
return nil // already exists
|
||||
}
|
||||
if !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
// Ensure parent exists first
|
||||
parent := path.Dir(dirPath)
|
||||
if parent != dirPath && parent != "." {
|
||||
if err := c.MkdirAll(parent); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return c.sftpCli.Mkdir(dirPath)
|
||||
}
|
||||
|
||||
// Mkdir creates a single remote directory.
|
||||
func (c *SSHClient) Mkdir(dirPath string) error {
|
||||
if c.sftpCli == nil {
|
||||
return fmt.Errorf("not connected")
|
||||
}
|
||||
return c.sftpCli.Mkdir(dirPath)
|
||||
}
|
||||
|
||||
// Remove deletes a remote file.
|
||||
func (c *SSHClient) Remove(filePath string) error {
|
||||
if c.sftpCli == nil {
|
||||
return fmt.Errorf("not connected")
|
||||
}
|
||||
return c.sftpCli.Remove(filePath)
|
||||
}
|
||||
|
||||
// RemoveDirectory removes a remote directory (must be empty).
|
||||
func (c *SSHClient) RemoveDirectory(dirPath string) error {
|
||||
if c.sftpCli == nil {
|
||||
return fmt.Errorf("not connected")
|
||||
}
|
||||
return c.sftpCli.RemoveDirectory(dirPath)
|
||||
}
|
||||
|
||||
// Rename moves/renames a remote file or directory.
|
||||
func (c *SSHClient) Rename(oldPath, newPath string) error {
|
||||
if c.sftpCli == nil {
|
||||
return fmt.Errorf("not connected")
|
||||
}
|
||||
return c.sftpCli.Rename(oldPath, newPath)
|
||||
}
|
||||
|
||||
// Close closes the SFTP session and SSH connection.
|
||||
func (c *SSHClient) Close() error {
|
||||
// Stop the keepalive goroutine first
|
||||
if c.keepaliveStop != nil {
|
||||
select {
|
||||
case <-c.keepaliveStop:
|
||||
// already closed
|
||||
default:
|
||||
close(c.keepaliveStop)
|
||||
}
|
||||
c.keepaliveWg.Wait()
|
||||
}
|
||||
|
||||
var firstErr error
|
||||
|
||||
if c.sftpCli != nil {
|
||||
if err := c.sftpCli.Close(); err != nil {
|
||||
firstErr = err
|
||||
}
|
||||
c.sftpCli = nil
|
||||
}
|
||||
|
||||
if c.sshConn != nil {
|
||||
if err := c.sshConn.Close(); err != nil && firstErr == nil {
|
||||
firstErr = err
|
||||
}
|
||||
c.sshConn = nil
|
||||
}
|
||||
|
||||
return firstErr
|
||||
}
|
||||
|
||||
// IsConnected returns true if the client has an active connection.
|
||||
func (c *SSHClient) IsConnected() bool {
|
||||
return c.sftpCli != nil && c.sshConn != nil
|
||||
}
|
||||
|
||||
// Exec runs a shell command on the remote server and returns combined output.
|
||||
func (c *SSHClient) Exec(cmd string) ([]byte, error) {
|
||||
if c.sshConn == nil {
|
||||
return nil, fmt.Errorf("not connected")
|
||||
}
|
||||
session, err := c.sshConn.NewSession()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("open session: %w", err)
|
||||
}
|
||||
defer session.Close()
|
||||
return session.CombinedOutput(cmd)
|
||||
}
|
||||
|
||||
// ExecWithProgress runs a shell command on the remote server and calls onLine
|
||||
// for each line of stdout output.
|
||||
func (c *SSHClient) ExecWithProgress(cmd string, onLine func(line string)) error {
|
||||
if c.sshConn == nil {
|
||||
return fmt.Errorf("not connected")
|
||||
}
|
||||
session, err := c.sshConn.NewSession()
|
||||
if err != nil {
|
||||
return fmt.Errorf("open session: %w", err)
|
||||
}
|
||||
defer session.Close()
|
||||
|
||||
stdout, err := session.StdoutPipe()
|
||||
if err != nil {
|
||||
return fmt.Errorf("stdout pipe: %w", err)
|
||||
}
|
||||
|
||||
if err := session.Start(cmd); err != nil {
|
||||
return fmt.Errorf("start command: %w", err)
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(stdout)
|
||||
for scanner.Scan() {
|
||||
onLine(scanner.Text())
|
||||
}
|
||||
|
||||
if scanErr := scanner.Err(); scanErr != nil {
|
||||
return scanErr
|
||||
}
|
||||
|
||||
return session.Wait()
|
||||
}
|
||||
|
||||
// SameHostAs returns true if this client and other are connected to the same server.
|
||||
func (c *SSHClient) SameHostAs(other *SSHClient) bool {
|
||||
if c == nil || other == nil {
|
||||
return false
|
||||
}
|
||||
return c.Host.SameAs(other.Host)
|
||||
}
|
||||
|
||||
// RemoveRecursive recursively deletes a remote file or directory.
|
||||
// For directories, it walks and removes all children first.
|
||||
func (c *SSHClient) RemoveRecursive(path string) error {
|
||||
if c.sftpCli == nil {
|
||||
return fmt.Errorf("not connected")
|
||||
}
|
||||
|
||||
info, err := c.sftpCli.Stat(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !info.IsDir() {
|
||||
return c.sftpCli.Remove(path)
|
||||
}
|
||||
|
||||
// Walk directory and collect all paths (files first, then dirs)
|
||||
var files []string
|
||||
var dirs []string
|
||||
err = c.Walk(path, func(walkPath string, info os.FileInfo, walkErr error) error {
|
||||
if walkErr != nil {
|
||||
return walkErr
|
||||
}
|
||||
if walkPath == path {
|
||||
return nil // skip root
|
||||
}
|
||||
if info.IsDir() {
|
||||
dirs = append(dirs, walkPath)
|
||||
} else {
|
||||
files = append(files, walkPath)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove files first, then directories (reverse order for deepest first)
|
||||
for _, f := range files {
|
||||
if err := c.sftpCli.Remove(f); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for i := len(dirs) - 1; i >= 0; i-- {
|
||||
if err := c.sftpCli.RemoveDirectory(dirs[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Finally remove the root directory
|
||||
return c.sftpCli.RemoveDirectory(path)
|
||||
}
|
||||
|
||||
// CopyFileToRemote copies a local file to a remote destination via SFTP.
|
||||
// It creates parent directories as needed.
|
||||
func (c *SSHClient) CopyFileToRemote(localPath, remotePath string) error {
|
||||
if c.sftpCli == nil {
|
||||
return fmt.Errorf("not connected")
|
||||
}
|
||||
|
||||
localFile, err := os.Open(localPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("open local: %w", err)
|
||||
}
|
||||
defer localFile.Close()
|
||||
|
||||
// Ensure parent directory exists
|
||||
parent := path.Dir(remotePath)
|
||||
if err := c.MkdirAll(parent); err != nil {
|
||||
return fmt.Errorf("mkdir remote: %w", err)
|
||||
}
|
||||
|
||||
remoteFile, err := c.sftpCli.Create(remotePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create remote: %w", err)
|
||||
}
|
||||
defer remoteFile.Close()
|
||||
|
||||
_, err = io.Copy(remoteFile, localFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("copy to remote: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CopyFileFromRemote copies a remote file to a local destination via SFTP.
|
||||
// It creates parent directories as needed.
|
||||
func (c *SSHClient) CopyFileFromRemote(remotePath, localPath string) error {
|
||||
if c.sftpCli == nil {
|
||||
return fmt.Errorf("not connected")
|
||||
}
|
||||
|
||||
remoteFile, err := c.sftpCli.Open(remotePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("open remote: %w", err)
|
||||
}
|
||||
defer remoteFile.Close()
|
||||
|
||||
// Ensure parent directory exists
|
||||
parent := filepath.Dir(localPath)
|
||||
if err := os.MkdirAll(parent, 0o755); err != nil {
|
||||
return fmt.Errorf("mkdir local: %w", err)
|
||||
}
|
||||
|
||||
localFile, err := os.Create(localPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create local: %w", err)
|
||||
}
|
||||
defer localFile.Close()
|
||||
|
||||
_, err = io.Copy(localFile, remoteFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("copy from remote: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DownloadFile downloads a remote file to a local path via SFTP.
|
||||
func (c *SSHClient) DownloadFile(remotePath, localPath string) error {
|
||||
return c.CopyFileFromRemote(remotePath, localPath)
|
||||
}
|
||||
|
||||
// CopyDirToRemote recursively copies a local directory to a remote path.
|
||||
func (c *SSHClient) CopyDirToRemote(localDir, remoteDir string) error {
|
||||
return c.copyDirToRemote(localDir, remoteDir, nil, nil)
|
||||
}
|
||||
|
||||
// CopyDirToRemoteProgress is like CopyDirToRemote but calls onFile after each copy.
|
||||
func (c *SSHClient) CopyDirToRemoteProgress(localDir, remoteDir string, onFile func(path string, done, total int), ctx context.Context) error {
|
||||
return c.copyDirToRemote(localDir, remoteDir, onFile, ctx)
|
||||
}
|
||||
|
||||
func (c *SSHClient) copyDirToRemote(localDir, remoteDir string, onFile func(path string, done, total int), ctx context.Context) error {
|
||||
done := 0
|
||||
return filepath.Walk(localDir, func(localPath string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ctx != nil {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
}
|
||||
}
|
||||
relPath, _ := filepath.Rel(localDir, localPath)
|
||||
remotePath := path.Join(remoteDir, relPath)
|
||||
if info.IsDir() {
|
||||
return c.MkdirAll(remotePath)
|
||||
}
|
||||
if err := c.CopyFileToRemote(localPath, remotePath); err != nil {
|
||||
return err
|
||||
}
|
||||
done++
|
||||
if onFile != nil {
|
||||
onFile(remotePath, done, 0)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// CopyDirFromRemote recursively copies a remote directory to a local path.
|
||||
func (c *SSHClient) CopyDirFromRemote(remoteDir, localDir string) error {
|
||||
return c.copyDirFromRemote(remoteDir, localDir, nil, nil)
|
||||
}
|
||||
|
||||
// CopyDirFromRemoteProgress is like CopyDirFromRemote but calls onFile after each copy.
|
||||
func (c *SSHClient) CopyDirFromRemoteProgress(remoteDir, localDir string, onFile func(path string, done, total int), ctx context.Context) error {
|
||||
return c.copyDirFromRemote(remoteDir, localDir, onFile, ctx)
|
||||
}
|
||||
|
||||
func (c *SSHClient) copyDirFromRemote(remoteDir, localDir string, onFile func(path string, done, total int), ctx context.Context) error {
|
||||
done := 0
|
||||
return c.Walk(remoteDir, func(remotePath string, info os.FileInfo, err error) error {
|
||||
if ctx != nil {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
relPath, _ := filepath.Rel(remoteDir, remotePath)
|
||||
localPath := filepath.Join(localDir, relPath)
|
||||
if info.IsDir() {
|
||||
return os.MkdirAll(localPath, 0o755)
|
||||
}
|
||||
if err := c.CopyFileFromRemote(remotePath, localPath); err != nil {
|
||||
return err
|
||||
}
|
||||
done++
|
||||
if onFile != nil {
|
||||
onFile(localPath, done, 0)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// CopyFileBetweenRemotes copies a single file from one remote host to another
|
||||
// by streaming the file contents through the local machine. Both SFTP connections
|
||||
// must be active (connected).
|
||||
func CopyFileBetweenRemotes(srcClient, dstClient *SSHClient, srcPath, dstPath string) error {
|
||||
if srcClient.sftpCli == nil {
|
||||
return fmt.Errorf("source client not connected")
|
||||
}
|
||||
if dstClient.sftpCli == nil {
|
||||
return fmt.Errorf("destination client not connected")
|
||||
}
|
||||
|
||||
srcFile, err := srcClient.sftpCli.Open(srcPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("open remote source %s: %w", srcPath, err)
|
||||
}
|
||||
defer srcFile.Close()
|
||||
|
||||
// Ensure parent directory exists on the destination
|
||||
parent := path.Dir(dstPath)
|
||||
if err := dstClient.MkdirAll(parent); err != nil {
|
||||
return fmt.Errorf("mkdir remote dest %s: %w", parent, err)
|
||||
}
|
||||
|
||||
dstFile, err := dstClient.sftpCli.Create(dstPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create remote dest %s: %w", dstPath, err)
|
||||
}
|
||||
defer dstFile.Close()
|
||||
|
||||
_, err = io.Copy(dstFile, srcFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("copy remote to remote %s → %s: %w", srcPath, dstPath, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CopyDirBetweenRemotes recursively copies a directory from one remote host to another.
|
||||
func CopyDirBetweenRemotes(srcClient, dstClient *SSHClient, srcDir, dstDir string) error {
|
||||
return copyDirBetweenRemotes(srcClient, dstClient, srcDir, dstDir, nil, nil)
|
||||
}
|
||||
|
||||
func copyDirBetweenRemotes(srcClient, dstClient *SSHClient, srcDir, dstDir string, onFile func(path string, done, total int), ctx context.Context) error {
|
||||
done := 0
|
||||
return srcClient.Walk(srcDir, func(remotePath string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ctx != nil {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
}
|
||||
}
|
||||
relPath, _ := filepath.Rel(srcDir, remotePath)
|
||||
dstPath := path.Join(dstDir, relPath)
|
||||
if info.IsDir() {
|
||||
return dstClient.MkdirAll(dstPath)
|
||||
}
|
||||
if err := CopyFileBetweenRemotes(srcClient, dstClient, remotePath, dstPath); err != nil {
|
||||
return err
|
||||
}
|
||||
done++
|
||||
if onFile != nil {
|
||||
onFile(remotePath, done, 0)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// CopyDirBetweenRemotesProgress is like CopyDirBetweenRemotes with progress and context support.
|
||||
func CopyDirBetweenRemotesProgress(srcClient, dstClient *SSHClient, srcDir, dstDir string, onFile func(path string, done, total int), ctx context.Context) error {
|
||||
return copyDirBetweenRemotes(srcClient, dstClient, srcDir, dstDir, onFile, ctx)
|
||||
}
|
||||
|
||||
// Walk walks the remote filesystem tree rooted at root, calling walkFn for each file/dir.
|
||||
// This is a simplified version of filepath.Walk for SFTP.
|
||||
type walkFunc func(path string, info os.FileInfo, err error) error
|
||||
|
||||
func (c *SSHClient) Walk(root string, walkFn walkFunc) error {
|
||||
return c.walk(root, walkFn, nil)
|
||||
}
|
||||
|
||||
func (c *SSHClient) walk(dirPath string, walkFn walkFunc, info os.FileInfo) error {
|
||||
if info == nil {
|
||||
var err error
|
||||
info, err = c.sftpCli.Stat(dirPath)
|
||||
if err != nil {
|
||||
return walkFn(dirPath, nil, err)
|
||||
}
|
||||
}
|
||||
|
||||
err := walkFn(dirPath, info, nil)
|
||||
if err != nil {
|
||||
if err == filepathSkipDir {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if !info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
entries, err := c.sftpCli.ReadDir(dirPath)
|
||||
if err != nil {
|
||||
return walkFn(dirPath, info, err)
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
childPath := path.Join(dirPath, entry.Name())
|
||||
if entry.IsDir() {
|
||||
err = c.walk(childPath, walkFn, entry)
|
||||
} else {
|
||||
err = walkFn(childPath, entry, nil)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// filepathSkipDir is used as a return value from Walk to skip a directory.
|
||||
var filepathSkipDir = fmt.Errorf("skip this directory")
|
||||
|
||||
// DirectorySize recursively walks a remote directory and sums up file sizes.
|
||||
func (c *SSHClient) DirectorySize(dirPath string) (int64, error) {
|
||||
var total int64
|
||||
err := c.Walk(dirPath, func(_ string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !info.IsDir() {
|
||||
total += info.Size()
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return total, nil
|
||||
}
|
||||
|
||||
// SftpToFileInfo converts an os.FileInfo to a vfs-compatible file info.
|
||||
// This is used for consistent file information handling across local and remote.
|
||||
func SftpToFileInfo(name string, info os.FileInfo) (os.FileInfo, error) {
|
||||
return info, nil
|
||||
}
|
||||
|
||||
// WalkDirEntry wraps os.FileInfo with the file name for directory listings.
|
||||
type WalkDirEntry struct {
|
||||
os.FileInfo
|
||||
entryName string
|
||||
}
|
||||
|
||||
func (e *WalkDirEntry) Name() string {
|
||||
return e.entryName
|
||||
}
|
||||
|
||||
// NewWalkDirEntry creates a new WalkDirEntry with an overridden name.
|
||||
func NewWalkDirEntry(info os.FileInfo, name string) *WalkDirEntry {
|
||||
return &WalkDirEntry{FileInfo: info, entryName: name}
|
||||
}
|
||||
|
||||
// DialTimeout is the timeout for establishing SSH connections.
|
||||
const DialTimeout = 15 * time.Second
|
||||
|
||||
// DefaultPort is the default SSH port.
|
||||
const DefaultPort = "22"
|
||||
|
||||
// ResolveAddr returns the SSH address for the given host, applying the default port if needed.
|
||||
func ResolveAddr(hostname, port string) string {
|
||||
host := strings.TrimSpace(hostname)
|
||||
if port == "" || port == "0" {
|
||||
port = DefaultPort
|
||||
}
|
||||
return net.JoinHostPort(host, port)
|
||||
}
|
||||
209
src/vcom-0.2.5/internal/fs/remote/config.go
Normal file
209
src/vcom-0.2.5/internal/fs/remote/config.go
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
package remote
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ParseSSHConfig parses ~/.ssh/config and returns a list of SSH hosts.
|
||||
// It handles the most common SSH config directives: Host, HostName, Port, User, IdentityFile.
|
||||
func ParseSSHConfig() []SSHHost {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
configPath := filepath.Join(home, ".ssh", "config")
|
||||
return parseSSHConfigFile(configPath)
|
||||
}
|
||||
|
||||
func parseSSHConfigFile(path string) []SSHHost {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var hosts []SSHHost
|
||||
var current *SSHHost
|
||||
var currentNames []string
|
||||
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
|
||||
// Skip empty lines and comments
|
||||
if line == "" || strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
|
||||
// Remove inline comments (everything after # that's not in quotes)
|
||||
if idx := strings.Index(line, "#"); idx >= 0 {
|
||||
line = strings.TrimSpace(line[:idx])
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
parts := strings.Fields(line)
|
||||
if len(parts) < 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
keyword := strings.ToLower(parts[0])
|
||||
value := strings.Join(parts[1:], " ")
|
||||
|
||||
switch keyword {
|
||||
case "host":
|
||||
// Save previous host block
|
||||
if current != nil && len(currentNames) > 0 {
|
||||
for _, name := range currentNames {
|
||||
if !isWildcardPattern(name) {
|
||||
host := *current
|
||||
host.Name = name
|
||||
hosts = append(hosts, host)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start new host block
|
||||
current = &SSHHost{
|
||||
Port: "22",
|
||||
FromSSHConfig: true,
|
||||
}
|
||||
currentNames = strings.Fields(value)
|
||||
|
||||
case "hostname":
|
||||
if current != nil {
|
||||
current.HostName = value
|
||||
}
|
||||
|
||||
case "port":
|
||||
if current != nil {
|
||||
current.Port = value
|
||||
}
|
||||
|
||||
case "user":
|
||||
if current != nil {
|
||||
current.User = value
|
||||
}
|
||||
|
||||
case "identityfile":
|
||||
if current != nil {
|
||||
// Handle ~ expansion and relative paths
|
||||
resolved := resolveIdentityPath(value)
|
||||
if resolved != "" {
|
||||
current.IdentityFile = resolved
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save last host block
|
||||
if current != nil && len(currentNames) > 0 {
|
||||
for _, name := range currentNames {
|
||||
if !isWildcardPattern(name) {
|
||||
host := *current
|
||||
host.Name = name
|
||||
hosts = append(hosts, host)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hosts
|
||||
}
|
||||
|
||||
// isWildcardPattern returns true if the pattern contains wildcard characters.
|
||||
func isWildcardPattern(pattern string) bool {
|
||||
return strings.ContainsAny(pattern, "*?")
|
||||
}
|
||||
|
||||
// resolveIdentityPath resolves a path from SSH config (handles ~ and relative paths).
|
||||
func resolveIdentityPath(path string) string {
|
||||
if path == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Handle ~/ or $HOME/
|
||||
if strings.HasPrefix(path, "~/") {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return path
|
||||
}
|
||||
path = filepath.Join(home, path[2:])
|
||||
}
|
||||
|
||||
// Handle relative paths (relative to ~/.ssh/)
|
||||
if !filepath.IsAbs(path) {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return path
|
||||
}
|
||||
path = filepath.Join(home, ".ssh", path)
|
||||
}
|
||||
|
||||
return filepath.Clean(path)
|
||||
}
|
||||
|
||||
// SSHConfigPath returns the path to the user's SSH config file.
|
||||
func SSHConfigPath() string {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return filepath.Join(home, ".ssh", "config")
|
||||
}
|
||||
|
||||
// HostsFilePath returns the path to the custom hosts data file.
|
||||
func HostsFilePath() string {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return filepath.Join(home, ".config", "vcom", "hosts.dat")
|
||||
}
|
||||
|
||||
// GetSSHDir returns the path to the .ssh directory.
|
||||
func GetSSHDir() string {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return filepath.Join(home, ".ssh")
|
||||
}
|
||||
|
||||
// KnownHostsPath returns the path to known_hosts.
|
||||
func KnownHostsPath() string {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return filepath.Join(home, ".ssh", "known_hosts")
|
||||
}
|
||||
|
||||
// ConfigFileExists checks if the SSH config file exists.
|
||||
func ConfigFileExists() bool {
|
||||
path := SSHConfigPath()
|
||||
if path == "" {
|
||||
return false
|
||||
}
|
||||
_, err := os.Stat(path)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// ValidateHost checks if a host entry has the minimum required fields.
|
||||
func ValidateHost(host SSHHost) error {
|
||||
if strings.TrimSpace(host.Name) == "" {
|
||||
return fmt.Errorf("host name is required")
|
||||
}
|
||||
if strings.TrimSpace(host.HostName) == "" {
|
||||
return fmt.Errorf("hostname/address is required")
|
||||
}
|
||||
if strings.TrimSpace(host.User) == "" {
|
||||
return fmt.Errorf("username is required")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
311
src/vcom-0.2.5/internal/fs/remote/host.go
Normal file
311
src/vcom-0.2.5/internal/fs/remote/host.go
Normal file
|
|
@ -0,0 +1,311 @@
|
|||
package remote
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SSHHost represents a single SSH host configuration.
|
||||
type SSHHost struct {
|
||||
// Name is the host alias (e.g. "myserver").
|
||||
Name string `json:"name"`
|
||||
// HostName is the actual hostname or IP address.
|
||||
HostName string `json:"hostname"`
|
||||
// Port is the SSH port (default 22).
|
||||
Port string `json:"port,omitempty"`
|
||||
// User is the SSH username.
|
||||
User string `json:"user,omitempty"`
|
||||
// IdentityFile is the path to the private key file (for key-based auth).
|
||||
IdentityFile string `json:"identity_file,omitempty"`
|
||||
// Password is stored encrypted (for password-based auth, user-added hosts).
|
||||
Password string `json:"password,omitempty"`
|
||||
// FromSSHConfig indicates this host came from ~/.ssh/config.
|
||||
FromSSHConfig bool `json:"from_ssh_config"`
|
||||
}
|
||||
|
||||
// DisplayName returns the host display name.
|
||||
func (h SSHHost) DisplayName() string {
|
||||
addr := h.HostName
|
||||
if h.Port != "" && h.Port != "22" {
|
||||
addr = fmt.Sprintf("%s:%s", addr, h.Port)
|
||||
}
|
||||
if h.User != "" {
|
||||
return fmt.Sprintf("%s (%s@%s)", h.Name, h.User, addr)
|
||||
}
|
||||
return fmt.Sprintf("%s (%s)", h.Name, addr)
|
||||
}
|
||||
|
||||
// Addr returns the SSH address string (host:port).
|
||||
func (h SSHHost) Addr() string {
|
||||
if h.Port == "" || h.Port == "22" {
|
||||
return h.HostName + ":22"
|
||||
}
|
||||
return h.HostName + ":" + h.Port
|
||||
}
|
||||
|
||||
// SameAs returns true if two hosts point to the same server.
|
||||
func (h SSHHost) SameAs(other SSHHost) bool {
|
||||
return h.HostName == other.HostName &&
|
||||
(h.Port == other.Port || (h.Port == "" && other.Port == "22") || (h.Port == "22" && other.Port == ""))
|
||||
}
|
||||
|
||||
// HostStore manages SSH hosts from both ~/.ssh/config and user-added hosts.
|
||||
type HostStore struct {
|
||||
customHosts []SSHHost
|
||||
configPath string
|
||||
cipherKey []byte
|
||||
}
|
||||
|
||||
// NewHostStore creates a new HostStore.
|
||||
func NewHostStore() (*HostStore, error) {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("home dir: %w", err)
|
||||
}
|
||||
|
||||
store := &HostStore{
|
||||
configPath: filepath.Join(home, ".config", "vcom", "hosts.dat"),
|
||||
}
|
||||
|
||||
// Load or create encryption key
|
||||
keyPath := filepath.Join(home, ".config", "vcom", ".hosts-key")
|
||||
store.cipherKey, err = loadOrCreateKey(keyPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("encryption key: %w", err)
|
||||
}
|
||||
|
||||
// Load custom hosts
|
||||
if err := store.load(); err != nil {
|
||||
// Ignore load errors for missing file
|
||||
if !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return store, nil
|
||||
}
|
||||
|
||||
// loadOrCreateKey loads an existing AES key or creates a new one.
|
||||
func loadOrCreateKey(path string) ([]byte, error) {
|
||||
if data, err := os.ReadFile(path); err == nil {
|
||||
key, err := base64.StdEncoding.DecodeString(strings.TrimSpace(string(data)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(key) == 32 {
|
||||
return key, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Generate new 32-byte key for AES-256
|
||||
key := make([]byte, 32)
|
||||
if _, err := rand.Read(key); err != nil {
|
||||
return nil, fmt.Errorf("generate key: %w", err)
|
||||
}
|
||||
|
||||
dir := filepath.Dir(path)
|
||||
if err := os.MkdirAll(dir, 0o700); err != nil {
|
||||
return nil, fmt.Errorf("mkdir: %w", err)
|
||||
}
|
||||
|
||||
encoded := base64.StdEncoding.EncodeToString(key)
|
||||
if err := os.WriteFile(path, []byte(encoded), 0o600); err != nil {
|
||||
return nil, fmt.Errorf("write key: %w", err)
|
||||
}
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
type storedHosts struct {
|
||||
Hosts []storedHost `json:"hosts"`
|
||||
}
|
||||
|
||||
type storedHost struct {
|
||||
Name string `json:"name"`
|
||||
HostName string `json:"hostname"`
|
||||
Port string `json:"port,omitempty"`
|
||||
User string `json:"user,omitempty"`
|
||||
Password string `json:"password,omitempty"` // encrypted
|
||||
IdentityFile string `json:"identity_file,omitempty"`
|
||||
}
|
||||
|
||||
func (s *HostStore) load() error {
|
||||
data, err := os.ReadFile(s.configPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Decrypt
|
||||
decrypted, err := decrypt(data, s.cipherKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("decrypt hosts: %w", err)
|
||||
}
|
||||
|
||||
var stored storedHosts
|
||||
if err := json.Unmarshal(decrypted, &stored); err != nil {
|
||||
return fmt.Errorf("parse hosts: %w", err)
|
||||
}
|
||||
|
||||
s.customHosts = make([]SSHHost, len(stored.Hosts))
|
||||
for i, h := range stored.Hosts {
|
||||
password := ""
|
||||
if h.Password != "" {
|
||||
pwd, err := decrypt([]byte(h.Password), s.cipherKey)
|
||||
if err == nil {
|
||||
password = string(pwd)
|
||||
}
|
||||
}
|
||||
s.customHosts[i] = SSHHost{
|
||||
Name: h.Name,
|
||||
HostName: h.HostName,
|
||||
Port: h.Port,
|
||||
User: h.User,
|
||||
Password: password,
|
||||
IdentityFile: h.IdentityFile,
|
||||
FromSSHConfig: false,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Save persists custom hosts to disk (encrypted).
|
||||
func (s *HostStore) Save() error {
|
||||
stored := storedHosts{
|
||||
Hosts: make([]storedHost, len(s.customHosts)),
|
||||
}
|
||||
for i, h := range s.customHosts {
|
||||
password := ""
|
||||
if h.Password != "" {
|
||||
enc, err := encrypt([]byte(h.Password), s.cipherKey)
|
||||
if err == nil {
|
||||
password = string(enc)
|
||||
}
|
||||
}
|
||||
stored.Hosts[i] = storedHost{
|
||||
Name: h.Name,
|
||||
HostName: h.HostName,
|
||||
Port: h.Port,
|
||||
User: h.User,
|
||||
Password: password,
|
||||
IdentityFile: h.IdentityFile,
|
||||
}
|
||||
}
|
||||
|
||||
data, err := json.Marshal(stored)
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshal hosts: %w", err)
|
||||
}
|
||||
|
||||
encrypted, err := encrypt(data, s.cipherKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("encrypt hosts: %w", err)
|
||||
}
|
||||
|
||||
dir := filepath.Dir(s.configPath)
|
||||
if err := os.MkdirAll(dir, 0o700); err != nil {
|
||||
return fmt.Errorf("mkdir: %w", err)
|
||||
}
|
||||
|
||||
return os.WriteFile(s.configPath, encrypted, 0o600)
|
||||
}
|
||||
|
||||
// AddHost adds a custom host and saves.
|
||||
func (s *HostStore) AddHost(host SSHHost) error {
|
||||
host.FromSSHConfig = false
|
||||
s.customHosts = append(s.customHosts, host)
|
||||
return s.Save()
|
||||
}
|
||||
|
||||
// RemoveHost removes a custom host by name.
|
||||
func (s *HostStore) RemoveHost(name string) error {
|
||||
for i, h := range s.customHosts {
|
||||
if h.Name == name {
|
||||
s.customHosts = append(s.customHosts[:i], s.customHosts[i+1:]...)
|
||||
return s.Save()
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("host %q not found", name)
|
||||
}
|
||||
|
||||
// AllHosts returns all hosts (from ssh config + custom).
|
||||
func (s *HostStore) AllHosts() []SSHHost {
|
||||
sshConfigHosts := ParseSSHConfig()
|
||||
result := make([]SSHHost, 0, len(sshConfigHosts)+len(s.customHosts))
|
||||
|
||||
// Build a set of names from ssh config to avoid duplicates
|
||||
seen := make(map[string]bool)
|
||||
for _, h := range sshConfigHosts {
|
||||
lower := strings.ToLower(h.Name)
|
||||
seen[lower] = true
|
||||
result = append(result, h)
|
||||
}
|
||||
|
||||
for _, h := range s.customHosts {
|
||||
lower := strings.ToLower(h.Name)
|
||||
if !seen[lower] {
|
||||
result = append(result, h)
|
||||
seen[lower] = true
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// FindByName looks up a host by its Name field. Returns nil if not found.
|
||||
func (s *HostStore) FindByName(name string) *SSHHost {
|
||||
all := s.AllHosts()
|
||||
for i := range all {
|
||||
if strings.EqualFold(all[i].Name, name) {
|
||||
return &all[i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func encrypt(plaintext []byte, key []byte) ([]byte, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nonce := make([]byte, gcm.NonceSize())
|
||||
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return gcm.Seal(nonce, nonce, plaintext, nil), nil
|
||||
}
|
||||
|
||||
func decrypt(ciphertext []byte, key []byte) ([]byte, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nonceSize := gcm.NonceSize()
|
||||
if len(ciphertext) < nonceSize {
|
||||
return nil, fmt.Errorf("ciphertext too short")
|
||||
}
|
||||
|
||||
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
|
||||
return gcm.Open(nil, nonce, ciphertext, nil)
|
||||
}
|
||||
275
src/vcom-0.2.5/internal/fs/scan.go
Normal file
275
src/vcom-0.2.5/internal/fs/scan.go
Normal file
|
|
@ -0,0 +1,275 @@
|
|||
package vfs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
type ListOptions struct {
|
||||
ShowHidden bool
|
||||
DirsFirst bool
|
||||
SortBy string
|
||||
SortReverse bool
|
||||
}
|
||||
|
||||
func ListDir(path string, options ListOptions) ([]Entry, error) {
|
||||
resolvedPath := path
|
||||
if resolvedPath == "" {
|
||||
resolvedPath = "."
|
||||
}
|
||||
|
||||
dirEntries, err := os.ReadDir(resolvedPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read dir %s: %w", resolvedPath, err)
|
||||
}
|
||||
|
||||
entries := make([]Entry, 0, len(dirEntries)+1)
|
||||
if parent := filepath.Dir(resolvedPath); parent != resolvedPath {
|
||||
entries = append(entries, Entry{
|
||||
Name: "..",
|
||||
Path: parent,
|
||||
IsDir: true,
|
||||
IsParent: true,
|
||||
})
|
||||
}
|
||||
|
||||
for _, dirEntry := range dirEntries {
|
||||
name := dirEntry.Name()
|
||||
hidden := strings.HasPrefix(name, ".")
|
||||
if hidden && !options.ShowHidden {
|
||||
continue
|
||||
}
|
||||
|
||||
fullPath := filepath.Join(resolvedPath, name)
|
||||
info, err := dirEntry.Info()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
entry := Entry{
|
||||
Name: name,
|
||||
Path: fullPath,
|
||||
Extension: ext(name),
|
||||
Mode: info.Mode(),
|
||||
Size: info.Size(),
|
||||
ModifiedAt: info.ModTime(),
|
||||
IsDir: info.IsDir(),
|
||||
IsHidden: hidden,
|
||||
}
|
||||
|
||||
if createdAt, ok := statBirthTime(fullPath); ok {
|
||||
entry.CreatedAt = createdAt
|
||||
entry.CreatedKnown = true
|
||||
}
|
||||
|
||||
entries = append(entries, entry)
|
||||
}
|
||||
|
||||
sort.SliceStable(entries, func(i, j int) bool {
|
||||
left, right := entries[i], entries[j]
|
||||
|
||||
if left.IsParent != right.IsParent {
|
||||
return left.IsParent
|
||||
}
|
||||
if options.DirsFirst && left.IsDir != right.IsDir {
|
||||
return left.IsDir
|
||||
}
|
||||
comparison := compareEntries(left, right, options.SortBy)
|
||||
if options.SortReverse {
|
||||
return comparison > 0
|
||||
}
|
||||
return comparison < 0
|
||||
})
|
||||
|
||||
return entries, nil
|
||||
}
|
||||
|
||||
func compareEntries(left Entry, right Entry, sortBy string) int {
|
||||
switch strings.ToLower(strings.TrimSpace(sortBy)) {
|
||||
case "size":
|
||||
if left.Size != right.Size {
|
||||
return cmpInt64(left.Size, right.Size)
|
||||
}
|
||||
case "modified":
|
||||
if !left.ModifiedAt.Equal(right.ModifiedAt) {
|
||||
return cmpTimeDesc(left.ModifiedAt, right.ModifiedAt)
|
||||
}
|
||||
case "created":
|
||||
if left.CreatedKnown != right.CreatedKnown {
|
||||
if left.CreatedKnown {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
}
|
||||
if !left.CreatedAt.Equal(right.CreatedAt) {
|
||||
return cmpTimeDesc(left.CreatedAt, right.CreatedAt)
|
||||
}
|
||||
case "extension":
|
||||
if left.Extension != right.Extension {
|
||||
return strings.Compare(left.Extension, right.Extension)
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Compare(strings.ToLower(left.Name), strings.ToLower(right.Name))
|
||||
}
|
||||
|
||||
func cmpInt64(left int64, right int64) int {
|
||||
switch {
|
||||
case left < right:
|
||||
return -1
|
||||
case left > right:
|
||||
return 1
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func cmpTimeDesc(left time.Time, right time.Time) int {
|
||||
switch {
|
||||
case left.Equal(right):
|
||||
return 0
|
||||
case left.After(right):
|
||||
return -1
|
||||
default:
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
func statBirthTime(path string) (time.Time, bool) {
|
||||
var stx unix.Statx_t
|
||||
if err := unix.Statx(unix.AT_FDCWD, path, unix.AT_STATX_SYNC_AS_STAT, unix.STATX_BTIME, &stx); err == nil {
|
||||
if stx.Mask&unix.STATX_BTIME != 0 {
|
||||
return time.Unix(int64(stx.Btime.Sec), int64(stx.Btime.Nsec)), true
|
||||
}
|
||||
}
|
||||
|
||||
info, err := os.Lstat(path)
|
||||
if err != nil {
|
||||
return time.Time{}, false
|
||||
}
|
||||
|
||||
stat, ok := info.Sys().(*syscall.Stat_t)
|
||||
if !ok {
|
||||
return time.Time{}, false
|
||||
}
|
||||
|
||||
seconds := int64(stat.Ctim.Sec)
|
||||
nanos := int64(stat.Ctim.Nsec)
|
||||
if seconds == 0 && nanos == 0 {
|
||||
return time.Time{}, false
|
||||
}
|
||||
return time.Unix(seconds, nanos), true
|
||||
}
|
||||
|
||||
func DirectorySize(path string) (int64, error) {
|
||||
var total int64
|
||||
|
||||
err := filepath.WalkDir(path, func(current string, d fs.DirEntry, walkErr error) error {
|
||||
if walkErr != nil {
|
||||
return walkErr
|
||||
}
|
||||
if d.IsDir() {
|
||||
return nil
|
||||
}
|
||||
info, err := d.Info()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
total += info.Size()
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return total, nil
|
||||
}
|
||||
|
||||
func FindSelected(entries []Entry, key string) int {
|
||||
for idx, entry := range entries {
|
||||
if entry.MatchKey() == key {
|
||||
return idx
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func HumanSize(size int64) string {
|
||||
if size < 0 {
|
||||
return "?"
|
||||
}
|
||||
if size < 1024 {
|
||||
return fmt.Sprintf("%d B", size)
|
||||
}
|
||||
units := []string{"KB", "MB", "GB", "TB"}
|
||||
value := float64(size)
|
||||
for _, unit := range units {
|
||||
value /= 1024
|
||||
if value < 1024 {
|
||||
return fmt.Sprintf("%.1f %s", value, unit)
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("%.1f PB", value/1024)
|
||||
}
|
||||
|
||||
func ShortTime(t time.Time) string {
|
||||
if t.IsZero() {
|
||||
return "n/a"
|
||||
}
|
||||
return t.Format("2006-01-02 15:04")
|
||||
}
|
||||
|
||||
func CompactTime(t time.Time) string {
|
||||
if t.IsZero() {
|
||||
return "n/a"
|
||||
}
|
||||
return t.Format("01-02 15:04")
|
||||
}
|
||||
|
||||
func Permissions(mode fs.FileMode) string {
|
||||
return mode.String()
|
||||
}
|
||||
|
||||
func IsBinarySample(data []byte) bool {
|
||||
if len(data) == 0 {
|
||||
return false
|
||||
}
|
||||
var controls int
|
||||
for _, b := range data {
|
||||
if b == 0 {
|
||||
return true
|
||||
}
|
||||
if b < 9 || (b > 13 && b < 32) {
|
||||
controls++
|
||||
}
|
||||
}
|
||||
return controls > len(data)/10
|
||||
}
|
||||
|
||||
func SafeBase(path string) string {
|
||||
base := filepath.Base(path)
|
||||
if base == "." || base == string(filepath.Separator) {
|
||||
return path
|
||||
}
|
||||
return base
|
||||
}
|
||||
|
||||
func JoinPath(path string, name string) string {
|
||||
return filepath.Join(path, name)
|
||||
}
|
||||
|
||||
func ErrOverwrite(path string) error {
|
||||
return fmt.Errorf("target already exists: %s", path)
|
||||
}
|
||||
|
||||
func IsNotExist(err error) bool {
|
||||
return errors.Is(err, os.ErrNotExist)
|
||||
}
|
||||
836
src/vcom-0.2.5/internal/theme/theme.go
Normal file
836
src/vcom-0.2.5/internal/theme/theme.go
Normal file
|
|
@ -0,0 +1,836 @@
|
|||
package theme
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
)
|
||||
|
||||
type Palette struct {
|
||||
Name string
|
||||
Background lipgloss.Color
|
||||
Panel lipgloss.Color
|
||||
PanelInactive lipgloss.Color
|
||||
PanelElevated lipgloss.Color
|
||||
StatusBar lipgloss.Color
|
||||
Footer lipgloss.Color
|
||||
Border lipgloss.Color
|
||||
BorderActive lipgloss.Color
|
||||
Text lipgloss.Color
|
||||
Muted lipgloss.Color
|
||||
Accent lipgloss.Color
|
||||
Info lipgloss.Color
|
||||
Success lipgloss.Color
|
||||
Selection lipgloss.Color
|
||||
Hover lipgloss.Color
|
||||
Marked lipgloss.Color
|
||||
Warning lipgloss.Color
|
||||
Danger lipgloss.Color
|
||||
ActivePath lipgloss.Color
|
||||
ConfirmButton lipgloss.Color
|
||||
CancelButton lipgloss.Color
|
||||
ProgressFill lipgloss.Color
|
||||
ProgressEmpty lipgloss.Color
|
||||
HelpNav lipgloss.Color
|
||||
HelpPanels lipgloss.Color
|
||||
HelpDialogs lipgloss.Color
|
||||
HelpMouse lipgloss.Color
|
||||
Folder lipgloss.Color
|
||||
TextFile lipgloss.Color
|
||||
ConfigFile lipgloss.Color
|
||||
ExecFile lipgloss.Color
|
||||
ImageFile lipgloss.Color
|
||||
BinaryFile lipgloss.Color
|
||||
FooterKey lipgloss.Color
|
||||
}
|
||||
|
||||
var builtInThemes = []string{
|
||||
"catppuccin-mocha",
|
||||
"catppuccin-macchiato",
|
||||
"catppuccin-lavender",
|
||||
"tokyo-night",
|
||||
"gruvbox-dark",
|
||||
"nord",
|
||||
"one-dark",
|
||||
"everforest",
|
||||
"github-dark",
|
||||
"ayu-dark",
|
||||
"breeze",
|
||||
"cyberpunk",
|
||||
"dracula",
|
||||
"eldritch",
|
||||
"kanagawa",
|
||||
"kanagawa-paper",
|
||||
"rose-pine",
|
||||
"solarized-dark",
|
||||
"vesper",
|
||||
}
|
||||
|
||||
func Names() []string {
|
||||
values := make([]string, len(builtInThemes))
|
||||
copy(values, builtInThemes)
|
||||
return values
|
||||
}
|
||||
|
||||
func Next(current string) string {
|
||||
values := Names()
|
||||
if len(values) == 0 {
|
||||
return current
|
||||
}
|
||||
current = strings.ToLower(strings.TrimSpace(current))
|
||||
for idx, value := range values {
|
||||
if value == current {
|
||||
return values[(idx+1)%len(values)]
|
||||
}
|
||||
}
|
||||
return values[0]
|
||||
}
|
||||
|
||||
func Resolve(name string) (Palette, error) {
|
||||
switch strings.ToLower(name) {
|
||||
case "catppuccin-mocha":
|
||||
return Palette{
|
||||
Name: "catppuccin-mocha",
|
||||
Background: lipgloss.Color("#11111B"),
|
||||
Panel: lipgloss.Color("#181825"),
|
||||
PanelInactive: lipgloss.Color("#1E1E2E"),
|
||||
PanelElevated: lipgloss.Color("#24273A"),
|
||||
StatusBar: lipgloss.Color("#1E1E2E"),
|
||||
Footer: lipgloss.Color("#11111B"),
|
||||
Border: lipgloss.Color("#45475A"),
|
||||
BorderActive: lipgloss.Color("#89B4FA"),
|
||||
Text: lipgloss.Color("#CDD6F4"),
|
||||
Muted: lipgloss.Color("#A6ADC8"),
|
||||
Accent: lipgloss.Color("#F5C2E7"),
|
||||
Info: lipgloss.Color("#89DCEB"),
|
||||
Success: lipgloss.Color("#A6E3A1"),
|
||||
Selection: lipgloss.Color("#313244"),
|
||||
Hover: lipgloss.Color("#2A2B3C"),
|
||||
Marked: lipgloss.Color("#F38BA8"),
|
||||
Warning: lipgloss.Color("#F9E2AF"),
|
||||
Danger: lipgloss.Color("#F38BA8"),
|
||||
ActivePath: lipgloss.Color("#89DCEB"),
|
||||
ConfirmButton: lipgloss.Color("#A6E3A1"),
|
||||
CancelButton: lipgloss.Color("#F38BA8"),
|
||||
ProgressFill: lipgloss.Color("#89B4FA"),
|
||||
ProgressEmpty: lipgloss.Color("#45475A"),
|
||||
HelpNav: lipgloss.Color("#89B4FA"),
|
||||
HelpPanels: lipgloss.Color("#F9E2AF"),
|
||||
HelpDialogs: lipgloss.Color("#CBA6F7"),
|
||||
HelpMouse: lipgloss.Color("#F38BA8"),
|
||||
Folder: lipgloss.Color("#89B4FA"),
|
||||
TextFile: lipgloss.Color("#A6E3A1"),
|
||||
ConfigFile: lipgloss.Color("#F9E2AF"),
|
||||
ExecFile: lipgloss.Color("#FAB387"),
|
||||
ImageFile: lipgloss.Color("#94E2D5"),
|
||||
BinaryFile: lipgloss.Color("#CBA6F7"),
|
||||
FooterKey: lipgloss.Color("#89DCEB"),
|
||||
}, nil
|
||||
|
||||
case "catppuccin-lavender":
|
||||
return Palette{
|
||||
Name: "catppuccin-lavender",
|
||||
Background: lipgloss.Color("#11111B"),
|
||||
Panel: lipgloss.Color("#181825"),
|
||||
PanelInactive: lipgloss.Color("#1E1E2E"),
|
||||
PanelElevated: lipgloss.Color("#24273A"),
|
||||
StatusBar: lipgloss.Color("#1E1E2E"),
|
||||
Footer: lipgloss.Color("#11111B"),
|
||||
Border: lipgloss.Color("#45475A"),
|
||||
BorderActive: lipgloss.Color("#B4BEFE"),
|
||||
Text: lipgloss.Color("#CDD6F4"),
|
||||
Muted: lipgloss.Color("#A6ADC8"),
|
||||
Accent: lipgloss.Color("#B4BEFE"),
|
||||
Info: lipgloss.Color("#89DCEB"),
|
||||
Success: lipgloss.Color("#A6E3A1"),
|
||||
Selection: lipgloss.Color("#313244"),
|
||||
Hover: lipgloss.Color("#2A2B3C"),
|
||||
Marked: lipgloss.Color("#F38BA8"),
|
||||
Warning: lipgloss.Color("#F9E2AF"),
|
||||
Danger: lipgloss.Color("#F38BA8"),
|
||||
ActivePath: lipgloss.Color("#B4BEFE"),
|
||||
ConfirmButton: lipgloss.Color("#A6E3A1"),
|
||||
CancelButton: lipgloss.Color("#F38BA8"),
|
||||
ProgressFill: lipgloss.Color("#B4BEFE"),
|
||||
ProgressEmpty: lipgloss.Color("#45475A"),
|
||||
HelpNav: lipgloss.Color("#B4BEFE"),
|
||||
HelpPanels: lipgloss.Color("#F9E2AF"),
|
||||
HelpDialogs: lipgloss.Color("#CBA6F7"),
|
||||
HelpMouse: lipgloss.Color("#F38BA8"),
|
||||
Folder: lipgloss.Color("#B4BEFE"),
|
||||
TextFile: lipgloss.Color("#A6E3A1"),
|
||||
ConfigFile: lipgloss.Color("#F9E2AF"),
|
||||
ExecFile: lipgloss.Color("#FAB387"),
|
||||
ImageFile: lipgloss.Color("#89DCEB"),
|
||||
BinaryFile: lipgloss.Color("#CBA6F7"),
|
||||
FooterKey: lipgloss.Color("#B4BEFE"),
|
||||
}, nil
|
||||
|
||||
case "tokyo-night":
|
||||
return Palette{
|
||||
Name: "tokyo-night",
|
||||
Background: lipgloss.Color("#16161E"),
|
||||
Panel: lipgloss.Color("#1A1B26"),
|
||||
PanelInactive: lipgloss.Color("#24283B"),
|
||||
PanelElevated: lipgloss.Color("#2A2F44"),
|
||||
StatusBar: lipgloss.Color("#24283B"),
|
||||
Footer: lipgloss.Color("#16161E"),
|
||||
Border: lipgloss.Color("#3B4261"),
|
||||
BorderActive: lipgloss.Color("#7AA2F7"),
|
||||
Text: lipgloss.Color("#C0CAF5"),
|
||||
Muted: lipgloss.Color("#9AA5CE"),
|
||||
Accent: lipgloss.Color("#BB9AF7"),
|
||||
Info: lipgloss.Color("#73DACA"),
|
||||
Success: lipgloss.Color("#9ECE6A"),
|
||||
Selection: lipgloss.Color("#292E42"),
|
||||
Hover: lipgloss.Color("#252A3D"),
|
||||
Marked: lipgloss.Color("#F7768E"),
|
||||
Warning: lipgloss.Color("#E0AF68"),
|
||||
Danger: lipgloss.Color("#F7768E"),
|
||||
ActivePath: lipgloss.Color("#73DACA"),
|
||||
ConfirmButton: lipgloss.Color("#9ECE6A"),
|
||||
CancelButton: lipgloss.Color("#F7768E"),
|
||||
ProgressFill: lipgloss.Color("#7AA2F7"),
|
||||
ProgressEmpty: lipgloss.Color("#3B4261"),
|
||||
HelpNav: lipgloss.Color("#7AA2F7"),
|
||||
HelpPanels: lipgloss.Color("#E0AF68"),
|
||||
HelpDialogs: lipgloss.Color("#BB9AF7"),
|
||||
HelpMouse: lipgloss.Color("#F7768E"),
|
||||
Folder: lipgloss.Color("#7AA2F7"),
|
||||
TextFile: lipgloss.Color("#9ECE6A"),
|
||||
ConfigFile: lipgloss.Color("#E0AF68"),
|
||||
ExecFile: lipgloss.Color("#FF9E64"),
|
||||
ImageFile: lipgloss.Color("#73DACA"),
|
||||
BinaryFile: lipgloss.Color("#BB9AF7"),
|
||||
FooterKey: lipgloss.Color("#73DACA"),
|
||||
}, nil
|
||||
|
||||
case "gruvbox-dark":
|
||||
return Palette{
|
||||
Name: name,
|
||||
Background: lipgloss.Color("#1D2021"),
|
||||
Panel: lipgloss.Color("#282828"),
|
||||
PanelInactive: lipgloss.Color("#32302F"),
|
||||
PanelElevated: lipgloss.Color("#3C3836"),
|
||||
StatusBar: lipgloss.Color("#32302F"),
|
||||
Footer: lipgloss.Color("#1D2021"),
|
||||
Border: lipgloss.Color("#504945"),
|
||||
BorderActive: lipgloss.Color("#FABD2F"),
|
||||
Text: lipgloss.Color("#EBDBB2"),
|
||||
Muted: lipgloss.Color("#BDAE93"),
|
||||
Accent: lipgloss.Color("#83A598"),
|
||||
Info: lipgloss.Color("#8EC07C"),
|
||||
Success: lipgloss.Color("#B8BB26"),
|
||||
Selection: lipgloss.Color("#3C3836"),
|
||||
Hover: lipgloss.Color("#45403D"),
|
||||
Marked: lipgloss.Color("#FB4934"),
|
||||
Warning: lipgloss.Color("#FE8019"),
|
||||
Danger: lipgloss.Color("#FB4934"),
|
||||
ActivePath: lipgloss.Color("#8EC07C"),
|
||||
ConfirmButton: lipgloss.Color("#B8BB26"),
|
||||
CancelButton: lipgloss.Color("#FB4934"),
|
||||
ProgressFill: lipgloss.Color("#FABD2F"),
|
||||
ProgressEmpty: lipgloss.Color("#504945"),
|
||||
HelpNav: lipgloss.Color("#83A598"),
|
||||
HelpPanels: lipgloss.Color("#FABD2F"),
|
||||
HelpDialogs: lipgloss.Color("#D3869B"),
|
||||
HelpMouse: lipgloss.Color("#FB4934"),
|
||||
Folder: lipgloss.Color("#83A598"),
|
||||
TextFile: lipgloss.Color("#B8BB26"),
|
||||
ConfigFile: lipgloss.Color("#FABD2F"),
|
||||
ExecFile: lipgloss.Color("#FE8019"),
|
||||
ImageFile: lipgloss.Color("#8EC07C"),
|
||||
BinaryFile: lipgloss.Color("#D3869B"),
|
||||
FooterKey: lipgloss.Color("#8EC07C"),
|
||||
}, nil
|
||||
|
||||
case "nord":
|
||||
return Palette{
|
||||
Name: name,
|
||||
Background: lipgloss.Color("#2E3440"),
|
||||
Panel: lipgloss.Color("#3B4252"),
|
||||
PanelInactive: lipgloss.Color("#434C5E"),
|
||||
PanelElevated: lipgloss.Color("#4C566A"),
|
||||
StatusBar: lipgloss.Color("#434C5E"),
|
||||
Footer: lipgloss.Color("#2E3440"),
|
||||
Border: lipgloss.Color("#4C566A"),
|
||||
BorderActive: lipgloss.Color("#88C0D0"),
|
||||
Text: lipgloss.Color("#ECEFF4"),
|
||||
Muted: lipgloss.Color("#D8DEE9"),
|
||||
Accent: lipgloss.Color("#81A1C1"),
|
||||
Info: lipgloss.Color("#8FBCBB"),
|
||||
Success: lipgloss.Color("#A3BE8C"),
|
||||
Selection: lipgloss.Color("#434C5E"),
|
||||
Hover: lipgloss.Color("#505A70"),
|
||||
Marked: lipgloss.Color("#BF616A"),
|
||||
Warning: lipgloss.Color("#EBCB8B"),
|
||||
Danger: lipgloss.Color("#BF616A"),
|
||||
ActivePath: lipgloss.Color("#8FBCBB"),
|
||||
ConfirmButton: lipgloss.Color("#A3BE8C"),
|
||||
CancelButton: lipgloss.Color("#BF616A"),
|
||||
ProgressFill: lipgloss.Color("#88C0D0"),
|
||||
ProgressEmpty: lipgloss.Color("#4C566A"),
|
||||
HelpNav: lipgloss.Color("#81A1C1"),
|
||||
HelpPanels: lipgloss.Color("#EBCB8B"),
|
||||
HelpDialogs: lipgloss.Color("#B48EAD"),
|
||||
HelpMouse: lipgloss.Color("#BF616A"),
|
||||
Folder: lipgloss.Color("#81A1C1"),
|
||||
TextFile: lipgloss.Color("#A3BE8C"),
|
||||
ConfigFile: lipgloss.Color("#EBCB8B"),
|
||||
ExecFile: lipgloss.Color("#D08770"),
|
||||
ImageFile: lipgloss.Color("#8FBCBB"),
|
||||
BinaryFile: lipgloss.Color("#B48EAD"),
|
||||
FooterKey: lipgloss.Color("#8FBCBB"),
|
||||
}, nil
|
||||
|
||||
case "one-dark":
|
||||
return Palette{
|
||||
Name: "one-dark",
|
||||
Background: lipgloss.Color("#282C34"),
|
||||
Panel: lipgloss.Color("#21252B"),
|
||||
PanelInactive: lipgloss.Color("#1B1D23"),
|
||||
PanelElevated: lipgloss.Color("#2C313A"),
|
||||
StatusBar: lipgloss.Color("#21252B"),
|
||||
Footer: lipgloss.Color("#282C34"),
|
||||
Border: lipgloss.Color("#3B4048"),
|
||||
BorderActive: lipgloss.Color("#61AFEF"),
|
||||
Text: lipgloss.Color("#ABB2BF"),
|
||||
Muted: lipgloss.Color("#5C6370"),
|
||||
Accent: lipgloss.Color("#61AFEF"),
|
||||
Info: lipgloss.Color("#56B6C2"),
|
||||
Success: lipgloss.Color("#98C379"),
|
||||
Selection: lipgloss.Color("#3E4451"),
|
||||
Hover: lipgloss.Color("#333841"),
|
||||
Marked: lipgloss.Color("#E06C75"),
|
||||
Warning: lipgloss.Color("#E5C07B"),
|
||||
Danger: lipgloss.Color("#E06C75"),
|
||||
ActivePath: lipgloss.Color("#56B6C2"),
|
||||
ConfirmButton: lipgloss.Color("#98C379"),
|
||||
CancelButton: lipgloss.Color("#E06C75"),
|
||||
ProgressFill: lipgloss.Color("#61AFEF"),
|
||||
ProgressEmpty: lipgloss.Color("#3B4048"),
|
||||
HelpNav: lipgloss.Color("#61AFEF"),
|
||||
HelpPanels: lipgloss.Color("#E5C07B"),
|
||||
HelpDialogs: lipgloss.Color("#C678DD"),
|
||||
HelpMouse: lipgloss.Color("#E06C75"),
|
||||
Folder: lipgloss.Color("#61AFEF"),
|
||||
TextFile: lipgloss.Color("#98C379"),
|
||||
ConfigFile: lipgloss.Color("#E5C07B"),
|
||||
ExecFile: lipgloss.Color("#D19A66"),
|
||||
ImageFile: lipgloss.Color("#56B6C2"),
|
||||
BinaryFile: lipgloss.Color("#C678DD"),
|
||||
FooterKey: lipgloss.Color("#56B6C2"),
|
||||
}, nil
|
||||
|
||||
case "everforest":
|
||||
return Palette{
|
||||
Name: "everforest",
|
||||
Background: lipgloss.Color("#2D353B"),
|
||||
Panel: lipgloss.Color("#272E33"),
|
||||
PanelInactive: lipgloss.Color("#232A2E"),
|
||||
PanelElevated: lipgloss.Color("#333C43"),
|
||||
StatusBar: lipgloss.Color("#232A2E"),
|
||||
Footer: lipgloss.Color("#2D353B"),
|
||||
Border: lipgloss.Color("#475258"),
|
||||
BorderActive: lipgloss.Color("#A7C080"),
|
||||
Text: lipgloss.Color("#D3C6AA"),
|
||||
Muted: lipgloss.Color("#859289"),
|
||||
Accent: lipgloss.Color("#A7C080"),
|
||||
Info: lipgloss.Color("#83C092"),
|
||||
Success: lipgloss.Color("#A7C080"),
|
||||
Selection: lipgloss.Color("#3A454A"),
|
||||
Hover: lipgloss.Color("#364147"),
|
||||
Marked: lipgloss.Color("#E67E80"),
|
||||
Warning: lipgloss.Color("#DBBC7F"),
|
||||
Danger: lipgloss.Color("#E67E80"),
|
||||
ActivePath: lipgloss.Color("#83C092"),
|
||||
ConfirmButton: lipgloss.Color("#A7C080"),
|
||||
CancelButton: lipgloss.Color("#E67E80"),
|
||||
ProgressFill: lipgloss.Color("#A7C080"),
|
||||
ProgressEmpty: lipgloss.Color("#475258"),
|
||||
HelpNav: lipgloss.Color("#A7C080"),
|
||||
HelpPanels: lipgloss.Color("#DBBC7F"),
|
||||
HelpDialogs: lipgloss.Color("#D699B6"),
|
||||
HelpMouse: lipgloss.Color("#E67E80"),
|
||||
Folder: lipgloss.Color("#A7C080"),
|
||||
TextFile: lipgloss.Color("#D3C6AA"),
|
||||
ConfigFile: lipgloss.Color("#DBBC7F"),
|
||||
ExecFile: lipgloss.Color("#E69875"),
|
||||
ImageFile: lipgloss.Color("#83C092"),
|
||||
BinaryFile: lipgloss.Color("#D699B6"),
|
||||
FooterKey: lipgloss.Color("#83C092"),
|
||||
}, nil
|
||||
|
||||
case "github-dark":
|
||||
return Palette{
|
||||
Name: "github-dark",
|
||||
Background: lipgloss.Color("#0D1117"),
|
||||
Panel: lipgloss.Color("#161B22"),
|
||||
PanelInactive: lipgloss.Color("#1C2128"),
|
||||
PanelElevated: lipgloss.Color("#21262D"),
|
||||
StatusBar: lipgloss.Color("#1C2128"),
|
||||
Footer: lipgloss.Color("#0D1117"),
|
||||
Border: lipgloss.Color("#30363D"),
|
||||
BorderActive: lipgloss.Color("#58A6FF"),
|
||||
Text: lipgloss.Color("#E6EDF3"),
|
||||
Muted: lipgloss.Color("#8B949E"),
|
||||
Accent: lipgloss.Color("#58A6FF"),
|
||||
Info: lipgloss.Color("#39D353"),
|
||||
Success: lipgloss.Color("#3FB950"),
|
||||
Selection: lipgloss.Color("#21262D"),
|
||||
Hover: lipgloss.Color("#262C36"),
|
||||
Marked: lipgloss.Color("#F85149"),
|
||||
Warning: lipgloss.Color("#D29922"),
|
||||
Danger: lipgloss.Color("#F85149"),
|
||||
ActivePath: lipgloss.Color("#39D353"),
|
||||
ConfirmButton: lipgloss.Color("#3FB950"),
|
||||
CancelButton: lipgloss.Color("#F85149"),
|
||||
ProgressFill: lipgloss.Color("#58A6FF"),
|
||||
ProgressEmpty: lipgloss.Color("#30363D"),
|
||||
HelpNav: lipgloss.Color("#58A6FF"),
|
||||
HelpPanels: lipgloss.Color("#D29922"),
|
||||
HelpDialogs: lipgloss.Color("#BC8CFF"),
|
||||
HelpMouse: lipgloss.Color("#F85149"),
|
||||
Folder: lipgloss.Color("#58A6FF"),
|
||||
TextFile: lipgloss.Color("#7EE787"),
|
||||
ConfigFile: lipgloss.Color("#D29922"),
|
||||
ExecFile: lipgloss.Color("#F0883E"),
|
||||
ImageFile: lipgloss.Color("#39D353"),
|
||||
BinaryFile: lipgloss.Color("#BC8CFF"),
|
||||
FooterKey: lipgloss.Color("#39D353"),
|
||||
}, nil
|
||||
|
||||
case "catppuccin-macchiato":
|
||||
return Palette{
|
||||
Name: "catppuccin-macchiato",
|
||||
Background: lipgloss.Color("#181926"),
|
||||
Panel: lipgloss.Color("#1E2030"),
|
||||
PanelInactive: lipgloss.Color("#24273A"),
|
||||
PanelElevated: lipgloss.Color("#2A2E3F"),
|
||||
StatusBar: lipgloss.Color("#24273A"),
|
||||
Footer: lipgloss.Color("#181926"),
|
||||
Border: lipgloss.Color("#363A4F"),
|
||||
BorderActive: lipgloss.Color("#C6A0F6"),
|
||||
Text: lipgloss.Color("#CAD3F5"),
|
||||
Muted: lipgloss.Color("#A5ADCB"),
|
||||
Accent: lipgloss.Color("#C6A0F6"),
|
||||
Info: lipgloss.Color("#91D7E3"),
|
||||
Success: lipgloss.Color("#A6DA95"),
|
||||
Selection: lipgloss.Color("#363A4F"),
|
||||
Hover: lipgloss.Color("#2E3248"),
|
||||
Marked: lipgloss.Color("#ED8796"),
|
||||
Warning: lipgloss.Color("#F5A97F"),
|
||||
Danger: lipgloss.Color("#ED8796"),
|
||||
ActivePath: lipgloss.Color("#91D7E3"),
|
||||
ConfirmButton: lipgloss.Color("#A6DA95"),
|
||||
CancelButton: lipgloss.Color("#ED8796"),
|
||||
ProgressFill: lipgloss.Color("#C6A0F6"),
|
||||
ProgressEmpty: lipgloss.Color("#363A4F"),
|
||||
HelpNav: lipgloss.Color("#C6A0F6"),
|
||||
HelpPanels: lipgloss.Color("#F5A97F"),
|
||||
HelpDialogs: lipgloss.Color("#C6A0F6"),
|
||||
HelpMouse: lipgloss.Color("#ED8796"),
|
||||
Folder: lipgloss.Color("#C6A0F6"),
|
||||
TextFile: lipgloss.Color("#A6DA95"),
|
||||
ConfigFile: lipgloss.Color("#F5A97F"),
|
||||
ExecFile: lipgloss.Color("#EE99A0"),
|
||||
ImageFile: lipgloss.Color("#91D7E3"),
|
||||
BinaryFile: lipgloss.Color("#C6A0F6"),
|
||||
FooterKey: lipgloss.Color("#91D7E3"),
|
||||
}, nil
|
||||
|
||||
case "ayu-dark":
|
||||
return Palette{
|
||||
Name: "ayu-dark",
|
||||
Background: lipgloss.Color("#0A0E14"),
|
||||
Panel: lipgloss.Color("#0D1017"),
|
||||
PanelInactive: lipgloss.Color("#11151D"),
|
||||
PanelElevated: lipgloss.Color("#151A23"),
|
||||
StatusBar: lipgloss.Color("#11151D"),
|
||||
Footer: lipgloss.Color("#0A0E14"),
|
||||
Border: lipgloss.Color("#1F2430"),
|
||||
BorderActive: lipgloss.Color("#FFCC66"),
|
||||
Text: lipgloss.Color("#B3B1AD"),
|
||||
Muted: lipgloss.Color("#565B66"),
|
||||
Accent: lipgloss.Color("#FF8F40"),
|
||||
Info: lipgloss.Color("#95E6CB"),
|
||||
Success: lipgloss.Color("#7FD962"),
|
||||
Selection: lipgloss.Color("#1F2430"),
|
||||
Hover: lipgloss.Color("#191E27"),
|
||||
Marked: lipgloss.Color("#F26D78"),
|
||||
Warning: lipgloss.Color("#FFCC66"),
|
||||
Danger: lipgloss.Color("#F26D78"),
|
||||
ActivePath: lipgloss.Color("#95E6CB"),
|
||||
ConfirmButton: lipgloss.Color("#7FD962"),
|
||||
CancelButton: lipgloss.Color("#F26D78"),
|
||||
ProgressFill: lipgloss.Color("#FFCC66"),
|
||||
ProgressEmpty: lipgloss.Color("#1F2430"),
|
||||
HelpNav: lipgloss.Color("#FF8F40"),
|
||||
HelpPanels: lipgloss.Color("#FFCC66"),
|
||||
HelpDialogs: lipgloss.Color("#D4A0FF"),
|
||||
HelpMouse: lipgloss.Color("#F26D78"),
|
||||
Folder: lipgloss.Color("#FF8F40"),
|
||||
TextFile: lipgloss.Color("#B3B1AD"),
|
||||
ConfigFile: lipgloss.Color("#FFCC66"),
|
||||
ExecFile: lipgloss.Color("#F29668"),
|
||||
ImageFile: lipgloss.Color("#95E6CB"),
|
||||
BinaryFile: lipgloss.Color("#D4A0FF"),
|
||||
FooterKey: lipgloss.Color("#95E6CB"),
|
||||
}, nil
|
||||
|
||||
case "breeze":
|
||||
return Palette{
|
||||
Name: "breeze",
|
||||
Background: lipgloss.Color("#232629"),
|
||||
Panel: lipgloss.Color("#2A2D30"),
|
||||
PanelInactive: lipgloss.Color("#313437"),
|
||||
PanelElevated: lipgloss.Color("#383B3E"),
|
||||
StatusBar: lipgloss.Color("#313437"),
|
||||
Footer: lipgloss.Color("#232629"),
|
||||
Border: lipgloss.Color("#494D51"),
|
||||
BorderActive: lipgloss.Color("#3DAEE9"),
|
||||
Text: lipgloss.Color("#EFF0F1"),
|
||||
Muted: lipgloss.Color("#B0B5BA"),
|
||||
Accent: lipgloss.Color("#3DAEE9"),
|
||||
Info: lipgloss.Color("#27E6A6"),
|
||||
Success: lipgloss.Color("#27AE60"),
|
||||
Selection: lipgloss.Color("#313437"),
|
||||
Hover: lipgloss.Color("#35383B"),
|
||||
Marked: lipgloss.Color("#ED1515"),
|
||||
Warning: lipgloss.Color("#F67400"),
|
||||
Danger: lipgloss.Color("#ED1515"),
|
||||
ActivePath: lipgloss.Color("#27E6A6"),
|
||||
ConfirmButton: lipgloss.Color("#27AE60"),
|
||||
CancelButton: lipgloss.Color("#ED1515"),
|
||||
ProgressFill: lipgloss.Color("#3DAEE9"),
|
||||
ProgressEmpty: lipgloss.Color("#494D51"),
|
||||
HelpNav: lipgloss.Color("#3DAEE9"),
|
||||
HelpPanels: lipgloss.Color("#F67400"),
|
||||
HelpDialogs: lipgloss.Color("#9B59B6"),
|
||||
HelpMouse: lipgloss.Color("#ED1515"),
|
||||
Folder: lipgloss.Color("#3DAEE9"),
|
||||
TextFile: lipgloss.Color("#27AE60"),
|
||||
ConfigFile: lipgloss.Color("#F67400"),
|
||||
ExecFile: lipgloss.Color("#E67E22"),
|
||||
ImageFile: lipgloss.Color("#27E6A6"),
|
||||
BinaryFile: lipgloss.Color("#9B59B6"),
|
||||
FooterKey: lipgloss.Color("#27E6A6"),
|
||||
}, nil
|
||||
|
||||
case "cyberpunk":
|
||||
return Palette{
|
||||
Name: "cyberpunk",
|
||||
Background: lipgloss.Color("#000B1A"),
|
||||
Panel: lipgloss.Color("#0A1628"),
|
||||
PanelInactive: lipgloss.Color("#0F1D30"),
|
||||
PanelElevated: lipgloss.Color("#142338"),
|
||||
StatusBar: lipgloss.Color("#0F1D30"),
|
||||
Footer: lipgloss.Color("#000B1A"),
|
||||
Border: lipgloss.Color("#1E3A5F"),
|
||||
BorderActive: lipgloss.Color("#00FFF0"),
|
||||
Text: lipgloss.Color("#E0E0E0"),
|
||||
Muted: lipgloss.Color("#808080"),
|
||||
Accent: lipgloss.Color("#FF00FF"),
|
||||
Info: lipgloss.Color("#00FFF0"),
|
||||
Success: lipgloss.Color("#00FF41"),
|
||||
Selection: lipgloss.Color("#142338"),
|
||||
Hover: lipgloss.Color("#192C42"),
|
||||
Marked: lipgloss.Color("#FF0055"),
|
||||
Warning: lipgloss.Color("#FFB000"),
|
||||
Danger: lipgloss.Color("#FF0055"),
|
||||
ActivePath: lipgloss.Color("#00FFF0"),
|
||||
ConfirmButton: lipgloss.Color("#00FF41"),
|
||||
CancelButton: lipgloss.Color("#FF0055"),
|
||||
ProgressFill: lipgloss.Color("#FF00FF"),
|
||||
ProgressEmpty: lipgloss.Color("#1E3A5F"),
|
||||
HelpNav: lipgloss.Color("#FF00FF"),
|
||||
HelpPanels: lipgloss.Color("#FFB000"),
|
||||
HelpDialogs: lipgloss.Color("#FF00FF"),
|
||||
HelpMouse: lipgloss.Color("#FF0055"),
|
||||
Folder: lipgloss.Color("#00FFF0"),
|
||||
TextFile: lipgloss.Color("#00FF41"),
|
||||
ConfigFile: lipgloss.Color("#FFB000"),
|
||||
ExecFile: lipgloss.Color("#FF6600"),
|
||||
ImageFile: lipgloss.Color("#00FFF0"),
|
||||
BinaryFile: lipgloss.Color("#FF00FF"),
|
||||
FooterKey: lipgloss.Color("#00FFF0"),
|
||||
}, nil
|
||||
|
||||
case "dracula":
|
||||
return Palette{
|
||||
Name: "dracula",
|
||||
Background: lipgloss.Color("#21222C"),
|
||||
Panel: lipgloss.Color("#282A36"),
|
||||
PanelInactive: lipgloss.Color("#2F3242"),
|
||||
PanelElevated: lipgloss.Color("#363850"),
|
||||
StatusBar: lipgloss.Color("#2F3242"),
|
||||
Footer: lipgloss.Color("#21222C"),
|
||||
Border: lipgloss.Color("#44475A"),
|
||||
BorderActive: lipgloss.Color("#BD93F9"),
|
||||
Text: lipgloss.Color("#F8F8F2"),
|
||||
Muted: lipgloss.Color("#6272A4"),
|
||||
Accent: lipgloss.Color("#FF79C6"),
|
||||
Info: lipgloss.Color("#8BE9FD"),
|
||||
Success: lipgloss.Color("#50FA7B"),
|
||||
Selection: lipgloss.Color("#44475A"),
|
||||
Hover: lipgloss.Color("#3A3D52"),
|
||||
Marked: lipgloss.Color("#FF5555"),
|
||||
Warning: lipgloss.Color("#F1FA8C"),
|
||||
Danger: lipgloss.Color("#FF5555"),
|
||||
ActivePath: lipgloss.Color("#8BE9FD"),
|
||||
ConfirmButton: lipgloss.Color("#50FA7B"),
|
||||
CancelButton: lipgloss.Color("#FF5555"),
|
||||
ProgressFill: lipgloss.Color("#FF79C6"),
|
||||
ProgressEmpty: lipgloss.Color("#44475A"),
|
||||
HelpNav: lipgloss.Color("#BD93F9"),
|
||||
HelpPanels: lipgloss.Color("#F1FA8C"),
|
||||
HelpDialogs: lipgloss.Color("#FF79C6"),
|
||||
HelpMouse: lipgloss.Color("#FF5555"),
|
||||
Folder: lipgloss.Color("#BD93F9"),
|
||||
TextFile: lipgloss.Color("#50FA7B"),
|
||||
ConfigFile: lipgloss.Color("#F1FA8C"),
|
||||
ExecFile: lipgloss.Color("#FFB86C"),
|
||||
ImageFile: lipgloss.Color("#8BE9FD"),
|
||||
BinaryFile: lipgloss.Color("#FF79C6"),
|
||||
FooterKey: lipgloss.Color("#8BE9FD"),
|
||||
}, nil
|
||||
|
||||
case "eldritch":
|
||||
return Palette{
|
||||
Name: "eldritch",
|
||||
Background: lipgloss.Color("#0B0D15"),
|
||||
Panel: lipgloss.Color("#10121A"),
|
||||
PanelInactive: lipgloss.Color("#161822"),
|
||||
PanelElevated: lipgloss.Color("#1C1F2B"),
|
||||
StatusBar: lipgloss.Color("#161822"),
|
||||
Footer: lipgloss.Color("#0B0D15"),
|
||||
Border: lipgloss.Color("#262A3B"),
|
||||
BorderActive: lipgloss.Color("#67B0E8"),
|
||||
Text: lipgloss.Color("#D3D7E0"),
|
||||
Muted: lipgloss.Color("#8B8FA6"),
|
||||
Accent: lipgloss.Color("#C278E8"),
|
||||
Info: lipgloss.Color("#67B0E8"),
|
||||
Success: lipgloss.Color("#74C287"),
|
||||
Selection: lipgloss.Color("#1C1F2B"),
|
||||
Hover: lipgloss.Color("#222638"),
|
||||
Marked: lipgloss.Color("#E06868"),
|
||||
Warning: lipgloss.Color("#E0A868"),
|
||||
Danger: lipgloss.Color("#E06868"),
|
||||
ActivePath: lipgloss.Color("#67B0E8"),
|
||||
ConfirmButton: lipgloss.Color("#74C287"),
|
||||
CancelButton: lipgloss.Color("#E06868"),
|
||||
ProgressFill: lipgloss.Color("#C278E8"),
|
||||
ProgressEmpty: lipgloss.Color("#262A3B"),
|
||||
HelpNav: lipgloss.Color("#C278E8"),
|
||||
HelpPanels: lipgloss.Color("#E0A868"),
|
||||
HelpDialogs: lipgloss.Color("#C278E8"),
|
||||
HelpMouse: lipgloss.Color("#E06868"),
|
||||
Folder: lipgloss.Color("#67B0E8"),
|
||||
TextFile: lipgloss.Color("#74C287"),
|
||||
ConfigFile: lipgloss.Color("#E0A868"),
|
||||
ExecFile: lipgloss.Color("#E08868"),
|
||||
ImageFile: lipgloss.Color("#67B0E8"),
|
||||
BinaryFile: lipgloss.Color("#C278E8"),
|
||||
FooterKey: lipgloss.Color("#67B0E8"),
|
||||
}, nil
|
||||
|
||||
case "kanagawa":
|
||||
return Palette{
|
||||
Name: "kanagawa",
|
||||
Background: lipgloss.Color("#1F1F28"),
|
||||
Panel: lipgloss.Color("#252535"),
|
||||
PanelInactive: lipgloss.Color("#2A2A3C"),
|
||||
PanelElevated: lipgloss.Color("#363646"),
|
||||
StatusBar: lipgloss.Color("#2A2A3C"),
|
||||
Footer: lipgloss.Color("#1F1F28"),
|
||||
Border: lipgloss.Color("#54546D"),
|
||||
BorderActive: lipgloss.Color("#7FB4CA"),
|
||||
Text: lipgloss.Color("#DCD7BA"),
|
||||
Muted: lipgloss.Color("#938AA9"),
|
||||
Accent: lipgloss.Color("#DCA561"),
|
||||
Info: lipgloss.Color("#7FB4CA"),
|
||||
Success: lipgloss.Color("#76946A"),
|
||||
Selection: lipgloss.Color("#363646"),
|
||||
Hover: lipgloss.Color("#30304A"),
|
||||
Marked: lipgloss.Color("#C34043"),
|
||||
Warning: lipgloss.Color("#DCA561"),
|
||||
Danger: lipgloss.Color("#C34043"),
|
||||
ActivePath: lipgloss.Color("#7FB4CA"),
|
||||
ConfirmButton: lipgloss.Color("#76946A"),
|
||||
CancelButton: lipgloss.Color("#C34043"),
|
||||
ProgressFill: lipgloss.Color("#DCA561"),
|
||||
ProgressEmpty: lipgloss.Color("#54546D"),
|
||||
HelpNav: lipgloss.Color("#DCA561"),
|
||||
HelpPanels: lipgloss.Color("#DCA561"),
|
||||
HelpDialogs: lipgloss.Color("#957FB8"),
|
||||
HelpMouse: lipgloss.Color("#C34043"),
|
||||
Folder: lipgloss.Color("#7FB4CA"),
|
||||
TextFile: lipgloss.Color("#76946A"),
|
||||
ConfigFile: lipgloss.Color("#DCA561"),
|
||||
ExecFile: lipgloss.Color("#E6C384"),
|
||||
ImageFile: lipgloss.Color("#7FB4CA"),
|
||||
BinaryFile: lipgloss.Color("#957FB8"),
|
||||
FooterKey: lipgloss.Color("#7FB4CA"),
|
||||
}, nil
|
||||
|
||||
case "kanagawa-paper":
|
||||
return Palette{
|
||||
Name: "kanagawa-paper",
|
||||
Background: lipgloss.Color("#1A1A22"),
|
||||
Panel: lipgloss.Color("#222233"),
|
||||
PanelInactive: lipgloss.Color("#2A2A3E"),
|
||||
PanelElevated: lipgloss.Color("#323248"),
|
||||
StatusBar: lipgloss.Color("#2A2A3E"),
|
||||
Footer: lipgloss.Color("#1A1A22"),
|
||||
Border: lipgloss.Color("#4A4A5E"),
|
||||
BorderActive: lipgloss.Color("#9EC1C9"),
|
||||
Text: lipgloss.Color("#C8C2B0"),
|
||||
Muted: lipgloss.Color("#8B849E"),
|
||||
Accent: lipgloss.Color("#C0A36E"),
|
||||
Info: lipgloss.Color("#9EC1C9"),
|
||||
Success: lipgloss.Color("#8EAA7A"),
|
||||
Selection: lipgloss.Color("#323248"),
|
||||
Hover: lipgloss.Color("#2C2C42"),
|
||||
Marked: lipgloss.Color("#B5534E"),
|
||||
Warning: lipgloss.Color("#C0A36E"),
|
||||
Danger: lipgloss.Color("#B5534E"),
|
||||
ActivePath: lipgloss.Color("#9EC1C9"),
|
||||
ConfirmButton: lipgloss.Color("#8EAA7A"),
|
||||
CancelButton: lipgloss.Color("#B5534E"),
|
||||
ProgressFill: lipgloss.Color("#C0A36E"),
|
||||
ProgressEmpty: lipgloss.Color("#4A4A5E"),
|
||||
HelpNav: lipgloss.Color("#C0A36E"),
|
||||
HelpPanels: lipgloss.Color("#C0A36E"),
|
||||
HelpDialogs: lipgloss.Color("#A58DB8"),
|
||||
HelpMouse: lipgloss.Color("#B5534E"),
|
||||
Folder: lipgloss.Color("#9EC1C9"),
|
||||
TextFile: lipgloss.Color("#8EAA7A"),
|
||||
ConfigFile: lipgloss.Color("#C0A36E"),
|
||||
ExecFile: lipgloss.Color("#D4BE8A"),
|
||||
ImageFile: lipgloss.Color("#9EC1C9"),
|
||||
BinaryFile: lipgloss.Color("#A58DB8"),
|
||||
FooterKey: lipgloss.Color("#9EC1C9"),
|
||||
}, nil
|
||||
|
||||
case "rose-pine":
|
||||
return Palette{
|
||||
Name: "rose-pine",
|
||||
Background: lipgloss.Color("#191724"),
|
||||
Panel: lipgloss.Color("#1F1D2E"),
|
||||
PanelInactive: lipgloss.Color("#26233A"),
|
||||
PanelElevated: lipgloss.Color("#2A273F"),
|
||||
StatusBar: lipgloss.Color("#26233A"),
|
||||
Footer: lipgloss.Color("#191724"),
|
||||
Border: lipgloss.Color("#3B355A"),
|
||||
BorderActive: lipgloss.Color("#C4A7E7"),
|
||||
Text: lipgloss.Color("#E0DEF4"),
|
||||
Muted: lipgloss.Color("#908CAA"),
|
||||
Accent: lipgloss.Color("#EB6F92"),
|
||||
Info: lipgloss.Color("#9CCFD8"),
|
||||
Success: lipgloss.Color("#3E8FB0"),
|
||||
Selection: lipgloss.Color("#312F44"),
|
||||
Hover: lipgloss.Color("#2A2740"),
|
||||
Marked: lipgloss.Color("#EB6F92"),
|
||||
Warning: lipgloss.Color("#F6C177"),
|
||||
Danger: lipgloss.Color("#EB6F92"),
|
||||
ActivePath: lipgloss.Color("#9CCFD8"),
|
||||
ConfirmButton: lipgloss.Color("#3E8FB0"),
|
||||
CancelButton: lipgloss.Color("#EB6F92"),
|
||||
ProgressFill: lipgloss.Color("#C4A7E7"),
|
||||
ProgressEmpty: lipgloss.Color("#3B355A"),
|
||||
HelpNav: lipgloss.Color("#C4A7E7"),
|
||||
HelpPanels: lipgloss.Color("#F6C177"),
|
||||
HelpDialogs: lipgloss.Color("#C4A7E7"),
|
||||
HelpMouse: lipgloss.Color("#EB6F92"),
|
||||
Folder: lipgloss.Color("#C4A7E7"),
|
||||
TextFile: lipgloss.Color("#3E8FB0"),
|
||||
ConfigFile: lipgloss.Color("#F6C177"),
|
||||
ExecFile: lipgloss.Color("#E0DEF4"),
|
||||
ImageFile: lipgloss.Color("#9CCFD8"),
|
||||
BinaryFile: lipgloss.Color("#C4A7E7"),
|
||||
FooterKey: lipgloss.Color("#9CCFD8"),
|
||||
}, nil
|
||||
|
||||
case "solarized-dark":
|
||||
return Palette{
|
||||
Name: "solarized-dark",
|
||||
Background: lipgloss.Color("#002B36"),
|
||||
Panel: lipgloss.Color("#073642"),
|
||||
PanelInactive: lipgloss.Color("#0D4A56"),
|
||||
PanelElevated: lipgloss.Color("#125A68"),
|
||||
StatusBar: lipgloss.Color("#0D4A56"),
|
||||
Footer: lipgloss.Color("#002B36"),
|
||||
Border: lipgloss.Color("#586E75"),
|
||||
BorderActive: lipgloss.Color("#268BD2"),
|
||||
Text: lipgloss.Color("#93A1A1"),
|
||||
Muted: lipgloss.Color("#657B83"),
|
||||
Accent: lipgloss.Color("#D33682"),
|
||||
Info: lipgloss.Color("#2AA198"),
|
||||
Success: lipgloss.Color("#859900"),
|
||||
Selection: lipgloss.Color("#073642"),
|
||||
Hover: lipgloss.Color("#0B4A56"),
|
||||
Marked: lipgloss.Color("#DC322F"),
|
||||
Warning: lipgloss.Color("#B58900"),
|
||||
Danger: lipgloss.Color("#DC322F"),
|
||||
ActivePath: lipgloss.Color("#2AA198"),
|
||||
ConfirmButton: lipgloss.Color("#859900"),
|
||||
CancelButton: lipgloss.Color("#DC322F"),
|
||||
ProgressFill: lipgloss.Color("#268BD2"),
|
||||
ProgressEmpty: lipgloss.Color("#586E75"),
|
||||
HelpNav: lipgloss.Color("#268BD2"),
|
||||
HelpPanels: lipgloss.Color("#B58900"),
|
||||
HelpDialogs: lipgloss.Color("#D33682"),
|
||||
HelpMouse: lipgloss.Color("#DC322F"),
|
||||
Folder: lipgloss.Color("#268BD2"),
|
||||
TextFile: lipgloss.Color("#859900"),
|
||||
ConfigFile: lipgloss.Color("#B58900"),
|
||||
ExecFile: lipgloss.Color("#CB4B16"),
|
||||
ImageFile: lipgloss.Color("#2AA198"),
|
||||
BinaryFile: lipgloss.Color("#D33682"),
|
||||
FooterKey: lipgloss.Color("#2AA198"),
|
||||
}, nil
|
||||
|
||||
case "vesper":
|
||||
return Palette{
|
||||
Name: "vesper",
|
||||
Background: lipgloss.Color("#101010"),
|
||||
Panel: lipgloss.Color("#181820"),
|
||||
PanelInactive: lipgloss.Color("#1E1E30"),
|
||||
PanelElevated: lipgloss.Color("#252540"),
|
||||
StatusBar: lipgloss.Color("#1E1E30"),
|
||||
Footer: lipgloss.Color("#101010"),
|
||||
Border: lipgloss.Color("#303050"),
|
||||
BorderActive: lipgloss.Color("#A0A0FF"),
|
||||
Text: lipgloss.Color("#E0E0F0"),
|
||||
Muted: lipgloss.Color("#8888AA"),
|
||||
Accent: lipgloss.Color("#C0C0FF"),
|
||||
Info: lipgloss.Color("#8080FF"),
|
||||
Success: lipgloss.Color("#80FF80"),
|
||||
Selection: lipgloss.Color("#252540"),
|
||||
Hover: lipgloss.Color("#2A2A48"),
|
||||
Marked: lipgloss.Color("#FF6080"),
|
||||
Warning: lipgloss.Color("#FFB040"),
|
||||
Danger: lipgloss.Color("#FF6080"),
|
||||
ActivePath: lipgloss.Color("#8080FF"),
|
||||
ConfirmButton: lipgloss.Color("#80FF80"),
|
||||
CancelButton: lipgloss.Color("#FF6080"),
|
||||
ProgressFill: lipgloss.Color("#C0C0FF"),
|
||||
ProgressEmpty: lipgloss.Color("#303050"),
|
||||
HelpNav: lipgloss.Color("#A0A0FF"),
|
||||
HelpPanels: lipgloss.Color("#FFB040"),
|
||||
HelpDialogs: lipgloss.Color("#C0C0FF"),
|
||||
HelpMouse: lipgloss.Color("#FF6080"),
|
||||
Folder: lipgloss.Color("#8080FF"),
|
||||
TextFile: lipgloss.Color("#80FF80"),
|
||||
ConfigFile: lipgloss.Color("#FFB040"),
|
||||
ExecFile: lipgloss.Color("#FF8040"),
|
||||
ImageFile: lipgloss.Color("#8080FF"),
|
||||
BinaryFile: lipgloss.Color("#C0C0FF"),
|
||||
FooterKey: lipgloss.Color("#8080FF"),
|
||||
}, nil
|
||||
|
||||
default:
|
||||
return Palette{}, fmt.Errorf("unknown theme %q", name)
|
||||
}
|
||||
}
|
||||
37
src/vcom-0.2.5/internal/ui/icon_mode.go
Normal file
37
src/vcom-0.2.5/internal/ui/icon_mode.go
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
package ui
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func resolveIconMode(mode string) (bool, string) {
|
||||
switch strings.ToLower(strings.TrimSpace(mode)) {
|
||||
case "ascii":
|
||||
return false, "Icon mode: ASCII"
|
||||
case "nerd":
|
||||
return true, ""
|
||||
case "", "auto":
|
||||
default:
|
||||
return true, ""
|
||||
}
|
||||
|
||||
if runtime.GOOS != "linux" {
|
||||
return true, ""
|
||||
}
|
||||
if _, err := exec.LookPath("fc-list"); err != nil {
|
||||
return true, ""
|
||||
}
|
||||
|
||||
out, err := exec.Command("fc-list", ":", "family").Output()
|
||||
if err != nil {
|
||||
return true, ""
|
||||
}
|
||||
text := strings.ToLower(string(out))
|
||||
if strings.Contains(text, "nerd font") {
|
||||
return true, ""
|
||||
}
|
||||
|
||||
return false, "Nerd Font not found: using ASCII icons"
|
||||
}
|
||||
416
src/vcom-0.2.5/internal/ui/image_overlay.go
Normal file
416
src/vcom-0.2.5/internal/ui/image_overlay.go
Normal file
|
|
@ -0,0 +1,416 @@
|
|||
package ui
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/png"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
_ "image/gif"
|
||||
_ "image/jpeg"
|
||||
_ "image/png"
|
||||
)
|
||||
|
||||
type overlayRect struct {
|
||||
x int
|
||||
y int
|
||||
width int
|
||||
height int
|
||||
}
|
||||
|
||||
type imageOverlayManager struct {
|
||||
cmd *exec.Cmd
|
||||
stdin io.WriteCloser
|
||||
running bool
|
||||
identifier string
|
||||
visible bool
|
||||
backend string
|
||||
backends []string
|
||||
lastPath string
|
||||
lastRect overlayRect
|
||||
kittyTried bool
|
||||
kittyOK bool
|
||||
}
|
||||
|
||||
const kittyImageID = 31337
|
||||
const assumedCellAspect = 0.5
|
||||
const kittyPixelsPerCell = 16
|
||||
|
||||
func minFloat(a float64, b float64) float64 {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func newImageOverlayManager() *imageOverlayManager {
|
||||
return &imageOverlayManager{identifier: "vcom-preview"}
|
||||
}
|
||||
|
||||
func (m *imageOverlayManager) isKittyTerminal() bool {
|
||||
term := strings.ToLower(os.Getenv("TERM"))
|
||||
termProgram := strings.ToLower(os.Getenv("TERM_PROGRAM"))
|
||||
return os.Getenv("KITTY_WINDOW_ID") != "" || strings.Contains(term, "kitty") || strings.Contains(termProgram, "kitty")
|
||||
}
|
||||
|
||||
func (m *imageOverlayManager) canUseKitty() bool {
|
||||
if !m.isKittyTerminal() {
|
||||
return false
|
||||
}
|
||||
if m.kittyTried {
|
||||
return m.kittyOK
|
||||
}
|
||||
m.kittyTried = true
|
||||
m.kittyOK = true
|
||||
return m.kittyOK
|
||||
}
|
||||
|
||||
func writeKittyEscape(control string, payload []byte) error {
|
||||
if payload == nil {
|
||||
_, err := fmt.Fprintf(os.Stdout, "\x1b_G%s\x1b\\", control)
|
||||
return err
|
||||
}
|
||||
|
||||
encoded := base64.StdEncoding.EncodeToString(payload)
|
||||
_, err := fmt.Fprintf(os.Stdout, "\x1b_G%s;%s\x1b\\", control, encoded)
|
||||
return err
|
||||
}
|
||||
|
||||
func resizeNearest(src image.Image, target image.Point) image.Image {
|
||||
bounds := src.Bounds()
|
||||
srcSize := bounds.Size()
|
||||
if target.X <= 0 || target.Y <= 0 || srcSize.X <= target.X && srcSize.Y <= target.Y {
|
||||
return src
|
||||
}
|
||||
|
||||
dst := image.NewRGBA(image.Rect(0, 0, target.X, target.Y))
|
||||
for y := 0; y < target.Y; y++ {
|
||||
srcY := bounds.Min.Y + (y * srcSize.Y / target.Y)
|
||||
for x := 0; x < target.X; x++ {
|
||||
srcX := bounds.Min.X + (x * srcSize.X / target.X)
|
||||
dst.Set(x, y, src.At(srcX, srcY))
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
func scaleImageToRect(img image.Image, rect overlayRect) image.Image {
|
||||
size := img.Bounds().Size()
|
||||
if size.X <= 0 || size.Y <= 0 || rect.width <= 0 || rect.height <= 0 {
|
||||
return img
|
||||
}
|
||||
|
||||
maxWidth := rect.width * kittyPixelsPerCell
|
||||
maxHeight := rect.height * kittyPixelsPerCell
|
||||
if maxWidth <= 0 || maxHeight <= 0 {
|
||||
return img
|
||||
}
|
||||
|
||||
scale := minFloat(float64(maxWidth)/float64(size.X), float64(maxHeight)/float64(size.Y))
|
||||
if scale >= 1 {
|
||||
return img
|
||||
}
|
||||
|
||||
target := image.Point{
|
||||
X: max(int(float64(size.X)*scale), 1),
|
||||
Y: max(int(float64(size.Y)*scale), 1),
|
||||
}
|
||||
return resizeNearest(img, target)
|
||||
}
|
||||
|
||||
func loadKittyPayload(path string, rect overlayRect) ([]byte, image.Point, error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, image.Point{}, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
img, _, err := image.Decode(file)
|
||||
if err != nil {
|
||||
return nil, image.Point{}, err
|
||||
}
|
||||
|
||||
img = scaleImageToRect(img, rect)
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := png.Encode(&buf, img); err != nil {
|
||||
return nil, image.Point{}, err
|
||||
}
|
||||
return buf.Bytes(), img.Bounds().Size(), nil
|
||||
}
|
||||
|
||||
func kittyPlacementControl(rect overlayRect, size image.Point) string {
|
||||
if size.X <= 0 || size.Y <= 0 {
|
||||
return fmt.Sprintf("a=T,f=100,t=d,i=%d,c=%d,C=1", kittyImageID, rect.width)
|
||||
}
|
||||
|
||||
availableAspect := (float64(rect.width) * assumedCellAspect) / float64(rect.height)
|
||||
imageAspect := float64(size.X) / float64(size.Y)
|
||||
if imageAspect >= availableAspect {
|
||||
return fmt.Sprintf("a=T,f=100,t=d,i=%d,c=%d,C=1", kittyImageID, rect.width)
|
||||
}
|
||||
return fmt.Sprintf("a=T,f=100,t=d,i=%d,r=%d,C=1", kittyImageID, rect.height)
|
||||
}
|
||||
|
||||
func (m *imageOverlayManager) showWithKitty(path string, rect overlayRect) error {
|
||||
data, size, err := loadKittyPayload(path, rect)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := writeKittyEscape(fmt.Sprintf("a=d,d=I,i=%d", kittyImageID), nil); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := fmt.Fprintf(os.Stdout, "\x1b7\x1b[%d;%dH", rect.y+1, rect.x+1); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
const chunkSize = 4096
|
||||
for offset := 0; offset < len(data); offset += chunkSize {
|
||||
end := min(offset+chunkSize, len(data))
|
||||
chunk := data[offset:end]
|
||||
more := 0
|
||||
if end < len(data) {
|
||||
more = 1
|
||||
}
|
||||
|
||||
control := fmt.Sprintf("m=%d", more)
|
||||
if offset == 0 {
|
||||
control = fmt.Sprintf("%s,m=%d", kittyPlacementControl(rect, size), more)
|
||||
}
|
||||
if err := writeKittyEscape(control, chunk); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
_, err = fmt.Fprint(os.Stdout, "\x1b8")
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *imageOverlayManager) clearKitty() {
|
||||
_ = writeKittyEscape(fmt.Sprintf("a=d,d=I,i=%d", kittyImageID), nil)
|
||||
}
|
||||
|
||||
func (m *imageOverlayManager) backendOutput() string {
|
||||
term := strings.ToLower(os.Getenv("TERM"))
|
||||
order := make([]string, 0, 5)
|
||||
switch {
|
||||
case strings.Contains(term, "kitty"):
|
||||
order = append(order, "kitty")
|
||||
case os.Getenv("WAYLAND_DISPLAY") != "":
|
||||
order = append(order, "wayland")
|
||||
case os.Getenv("DISPLAY") != "":
|
||||
order = append(order, "x11")
|
||||
}
|
||||
order = append(order, "wayland", "x11", "sixel", "kitty")
|
||||
|
||||
unique := make([]string, 0, len(order))
|
||||
for _, backend := range order {
|
||||
if !slices.Contains(unique, backend) {
|
||||
unique = append(unique, backend)
|
||||
}
|
||||
}
|
||||
return strings.Join(unique, ",")
|
||||
}
|
||||
|
||||
func (m *imageOverlayManager) backendList() []string {
|
||||
if len(m.backends) != 0 {
|
||||
return m.backends
|
||||
}
|
||||
m.backends = strings.Split(m.backendOutput(), ",")
|
||||
return m.backends
|
||||
}
|
||||
|
||||
func (m *imageOverlayManager) startBackend(backend string) error {
|
||||
cmd := exec.Command("ueberzugpp", "layer", "-o", backend)
|
||||
stdin, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd.Stdout = io.Discard
|
||||
cmd.Stderr = io.Discard
|
||||
if err := cmd.Start(); err != nil {
|
||||
_ = stdin.Close()
|
||||
return err
|
||||
}
|
||||
m.cmd = cmd
|
||||
m.stdin = stdin
|
||||
m.running = true
|
||||
m.backend = backend
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *imageOverlayManager) startLegacyBackend() error {
|
||||
cmd := exec.Command("ueberzug", "layer", "--parser", "json")
|
||||
stdin, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd.Stdout = io.Discard
|
||||
cmd.Stderr = io.Discard
|
||||
if err := cmd.Start(); err != nil {
|
||||
_ = stdin.Close()
|
||||
return err
|
||||
}
|
||||
m.cmd = cmd
|
||||
m.stdin = stdin
|
||||
m.running = true
|
||||
m.backend = "ueberzug"
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *imageOverlayManager) ensureStarted() error {
|
||||
if m.running {
|
||||
return nil
|
||||
}
|
||||
|
||||
if _, err := exec.LookPath("ueberzugpp"); err == nil {
|
||||
var lastErr error
|
||||
for _, backend := range m.backendList() {
|
||||
if err := m.startBackend(backend); err != nil {
|
||||
lastErr = err
|
||||
continue
|
||||
}
|
||||
// Probe command channel right away; some backends terminate instantly.
|
||||
if err := m.send(map[string]any{
|
||||
"action": "remove",
|
||||
"identifier": m.identifier,
|
||||
}); err != nil {
|
||||
lastErr = err
|
||||
m.stop()
|
||||
continue
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if lastErr != nil {
|
||||
return lastErr
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := exec.LookPath("ueberzug"); err == nil {
|
||||
if err := m.startLegacyBackend(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := m.send(map[string]any{
|
||||
"action": "remove",
|
||||
"identifier": m.identifier,
|
||||
}); err != nil {
|
||||
m.stop()
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("could not start image overlay backend")
|
||||
}
|
||||
|
||||
func (m *imageOverlayManager) send(payload map[string]any) error {
|
||||
if !m.running || m.stdin == nil {
|
||||
return fmt.Errorf("overlay not running")
|
||||
}
|
||||
data, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = io.WriteString(m.stdin, string(data)+"\n")
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *imageOverlayManager) show(path string, rect overlayRect) error {
|
||||
if rect.width <= 1 || rect.height <= 1 {
|
||||
return nil
|
||||
}
|
||||
if m.visible && m.lastPath == path && m.lastRect == rect {
|
||||
return nil
|
||||
}
|
||||
|
||||
if m.canUseKitty() {
|
||||
if m.backend == "ueberzugpp" {
|
||||
m.hide()
|
||||
}
|
||||
if err := m.showWithKitty(path, rect); err == nil {
|
||||
m.backend = "kitty"
|
||||
m.visible = true
|
||||
m.lastPath = path
|
||||
m.lastRect = rect
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < len(m.backendList()); i++ {
|
||||
if err := m.ensureStarted(); err != nil {
|
||||
return err
|
||||
}
|
||||
payload := map[string]any{
|
||||
"action": "add",
|
||||
"identifier": m.identifier,
|
||||
"path": path,
|
||||
"x": rect.x,
|
||||
"y": rect.y,
|
||||
}
|
||||
if m.backend == "ueberzug" {
|
||||
payload["width"] = rect.width
|
||||
payload["height"] = rect.height
|
||||
} else {
|
||||
payload["max_width"] = rect.width
|
||||
payload["max_height"] = rect.height
|
||||
payload["scaler"] = "fit_contain"
|
||||
}
|
||||
if err := m.send(payload); err == nil {
|
||||
m.visible = true
|
||||
m.lastPath = path
|
||||
m.lastRect = rect
|
||||
return nil
|
||||
}
|
||||
|
||||
m.stop()
|
||||
if m.backend != "ueberzug" && len(m.backends) > 0 {
|
||||
m.backends = append(m.backends[1:], m.backends[0])
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("could not render image overlay")
|
||||
}
|
||||
|
||||
func (m *imageOverlayManager) hide() {
|
||||
if !m.visible {
|
||||
return
|
||||
}
|
||||
switch m.backend {
|
||||
case "kitty":
|
||||
m.clearKitty()
|
||||
case "ueberzugpp", "ueberzug":
|
||||
if m.running {
|
||||
_ = m.send(map[string]any{
|
||||
"action": "remove",
|
||||
"identifier": m.identifier,
|
||||
})
|
||||
}
|
||||
}
|
||||
m.visible = false
|
||||
m.lastPath = ""
|
||||
m.lastRect = overlayRect{}
|
||||
}
|
||||
|
||||
func (m *imageOverlayManager) stop() {
|
||||
m.hide()
|
||||
if m.stdin != nil {
|
||||
_ = m.stdin.Close()
|
||||
m.stdin = nil
|
||||
}
|
||||
if m.cmd != nil && m.cmd.Process != nil {
|
||||
_ = m.cmd.Process.Kill()
|
||||
_, _ = m.cmd.Process.Wait()
|
||||
}
|
||||
m.cmd = nil
|
||||
m.running = false
|
||||
m.backend = ""
|
||||
}
|
||||
97
src/vcom-0.2.5/internal/ui/keymap.go
Normal file
97
src/vcom-0.2.5/internal/ui/keymap.go
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
package ui
|
||||
|
||||
import "github.com/charmbracelet/bubbles/key"
|
||||
|
||||
type KeyMap struct {
|
||||
Help key.Binding
|
||||
Visual key.Binding
|
||||
Caret key.Binding
|
||||
View key.Binding
|
||||
Rename key.Binding
|
||||
Info key.Binding
|
||||
Archive key.Binding
|
||||
SelectText key.Binding
|
||||
ToggleHidden key.Binding
|
||||
CycleTheme key.Binding
|
||||
CycleSort key.Binding
|
||||
SSH key.Binding
|
||||
Mirror key.Binding
|
||||
Up key.Binding
|
||||
Down key.Binding
|
||||
SelectUp key.Binding
|
||||
SelectDown key.Binding
|
||||
PageUp key.Binding
|
||||
PageDown key.Binding
|
||||
Open key.Binding
|
||||
Back key.Binding
|
||||
Switch key.Binding
|
||||
Filter key.Binding
|
||||
Refresh key.Binding
|
||||
DirSize key.Binding
|
||||
Copy key.Binding
|
||||
Move key.Binding
|
||||
Mkdir key.Binding
|
||||
Delete key.Binding
|
||||
Unpack key.Binding
|
||||
Confirm key.Binding
|
||||
Background key.Binding
|
||||
ProgressCancel key.Binding
|
||||
Cancel key.Binding
|
||||
Quit key.Binding
|
||||
HistoryBack key.Binding
|
||||
HistoryForward key.Binding
|
||||
}
|
||||
|
||||
func DefaultKeyMap() KeyMap {
|
||||
return KeyMap{
|
||||
Help: key.NewBinding(key.WithKeys("f1", "?"), key.WithHelp("F1/?", "help")),
|
||||
Rename: key.NewBinding(key.WithKeys("f2", "r"), key.WithHelp("F2/r", "rename")),
|
||||
View: key.NewBinding(key.WithKeys("f3", "v"), key.WithHelp("F3/v", "view")),
|
||||
Visual: key.NewBinding(key.WithKeys("v"), key.WithHelp("v", "visual")),
|
||||
Caret: key.NewBinding(key.WithKeys("i"), key.WithHelp("i", "caret")),
|
||||
Archive: key.NewBinding(key.WithKeys("f4", "a"), key.WithHelp("F4/a", "archive")),
|
||||
Info: key.NewBinding(key.WithKeys("f9", "o"), key.WithHelp("F9/o", "info")),
|
||||
SelectText: key.NewBinding(key.WithKeys("ctrl+t"), key.WithHelp("C-t", "text select")),
|
||||
ToggleHidden: key.NewBinding(key.WithKeys("."), key.WithHelp(".", "hidden")),
|
||||
CycleTheme: key.NewBinding(key.WithKeys("t"), key.WithHelp("t", "select theme")),
|
||||
CycleSort: key.NewBinding(key.WithKeys("g"), key.WithHelp("g", "sort")),
|
||||
SSH: key.NewBinding(key.WithKeys("f12", "s"), key.WithHelp("F12/s", "ssh")),
|
||||
Mirror: key.NewBinding(key.WithKeys("p"), key.WithHelp("p", "mirror pane")),
|
||||
Up: key.NewBinding(key.WithKeys("up", "k"), key.WithHelp("↑/k", "up")),
|
||||
Down: key.NewBinding(key.WithKeys("down", "j"), key.WithHelp("↓/j", "down")),
|
||||
SelectUp: key.NewBinding(key.WithKeys("shift+up", "K"), key.WithHelp("S-↑/K", "select up")),
|
||||
SelectDown: key.NewBinding(key.WithKeys("shift+down", "J"), key.WithHelp("S-↓/J", "select down")),
|
||||
PageUp: key.NewBinding(key.WithKeys("pgup"), key.WithHelp("PgUp", "page up")),
|
||||
PageDown: key.NewBinding(),
|
||||
Filter: key.NewBinding(key.WithKeys("/"), key.WithHelp("/", "filter")),
|
||||
Open: key.NewBinding(key.WithKeys("enter", "right"), key.WithHelp("Enter", "open")),
|
||||
Back: key.NewBinding(key.WithKeys("backspace", "left"), key.WithHelp("←", "parent")),
|
||||
Switch: key.NewBinding(key.WithKeys("tab", "h", "l"), key.WithHelp("Tab/h/l", "switch pane")),
|
||||
Refresh: key.NewBinding(key.WithKeys("ctrl+r"), key.WithHelp("C-r", "refresh")),
|
||||
DirSize: key.NewBinding(key.WithKeys(" "), key.WithHelp("Space", "dir size")),
|
||||
Copy: key.NewBinding(key.WithKeys("f5", "c"), key.WithHelp("F5/c", "copy")),
|
||||
Move: key.NewBinding(key.WithKeys("f6", "m"), key.WithHelp("F6/m", "move")),
|
||||
Mkdir: key.NewBinding(key.WithKeys("f7", "n"), key.WithHelp("F7/n", "mkdir")),
|
||||
Delete: key.NewBinding(key.WithKeys("f8", "delete", "x"), key.WithHelp("F8/x", "delete")),
|
||||
Unpack: key.NewBinding(key.WithKeys("f11", "e"), key.WithHelp("F11/e", "unpack")),
|
||||
Confirm: key.NewBinding(key.WithKeys("enter", "y"), key.WithHelp("Enter/y", "confirm")),
|
||||
Background: key.NewBinding(key.WithKeys("b"), key.WithHelp("b", "background")),
|
||||
ProgressCancel: key.NewBinding(key.WithKeys("c"), key.WithHelp("c", "cancel transfer")),
|
||||
HistoryBack: key.NewBinding(key.WithKeys("alt+left"), key.WithHelp("A-←", "back")),
|
||||
HistoryForward: key.NewBinding(key.WithKeys("alt+right"), key.WithHelp("A-→", "forward")),
|
||||
Cancel: key.NewBinding(key.WithKeys("esc"), key.WithHelp("Esc", "cancel")),
|
||||
Quit: key.NewBinding(key.WithKeys("f10", "q", "ctrl+c"), key.WithHelp("F10/q", "quit")),
|
||||
}
|
||||
}
|
||||
|
||||
func (k KeyMap) ShortHelp() []key.Binding {
|
||||
return []key.Binding{k.Help, k.Rename, k.View, k.Archive, k.Copy, k.Move, k.Mkdir, k.Delete, k.Info, k.Quit, k.Unpack, k.SSH}
|
||||
}
|
||||
|
||||
func (k KeyMap) FullHelp() [][]key.Binding {
|
||||
return [][]key.Binding{
|
||||
{k.Help, k.Up, k.Down, k.SelectUp, k.SelectDown, k.Open, k.Back},
|
||||
{k.Rename, k.View, k.Caret, k.Archive, k.Copy, k.Move, k.Delete},
|
||||
{k.Unpack, k.SelectText, k.DirSize, k.Refresh, k.ToggleHidden, k.CycleSort, k.CycleTheme, k.Quit},
|
||||
}
|
||||
}
|
||||
52
src/vcom-0.2.5/internal/ui/layout.go
Normal file
52
src/vcom-0.2.5/internal/ui/layout.go
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
package ui
|
||||
|
||||
import tea "github.com/charmbracelet/bubbletea"
|
||||
|
||||
// cyrillicToLatin maps Russian ЙЦУКЕН characters to their QWERTY positional
|
||||
// equivalents. This allows all single-letter commands to work regardless of
|
||||
// whether the user has a Russian or Latin keyboard layout active.
|
||||
var cyrillicToLatin = map[rune]rune{
|
||||
// Lowercase — same physical key position
|
||||
'й': 'q', 'ц': 'w', 'у': 'e', 'к': 'r', 'е': 't', 'н': 'y',
|
||||
'г': 'u', 'ш': 'i', 'щ': 'o', 'з': 'p', 'х': '[', 'ъ': ']',
|
||||
'ф': 'a', 'ы': 's', 'в': 'd', 'а': 'f', 'п': 'g', 'р': 'h',
|
||||
'о': 'j', 'л': 'k', 'д': 'l', 'ж': ';', 'э': '\'',
|
||||
'я': 'z', 'ч': 'x', 'с': 'c', 'м': 'v', 'и': 'b', 'т': 'n',
|
||||
'ь': 'm', 'б': ',', 'ю': '.', 'ё': '`',
|
||||
|
||||
// Uppercase — same physical key position with Shift
|
||||
'Й': 'Q', 'Ц': 'W', 'У': 'E', 'К': 'R', 'Е': 'T', 'Н': 'Y',
|
||||
'Г': 'U', 'Ш': 'I', 'Щ': 'O', 'З': 'P', 'Х': '{', 'Ъ': '}',
|
||||
'Ф': 'A', 'Ы': 'S', 'В': 'D', 'А': 'F', 'П': 'G', 'Р': 'H',
|
||||
'О': 'J', 'Л': 'K', 'Д': 'L', 'Ж': ':', 'Э': '"',
|
||||
'Я': 'Z', 'Ч': 'X', 'С': 'C', 'М': 'V', 'И': 'B', 'Т': 'N',
|
||||
'Ь': 'M', 'Б': '<', 'Ю': '>', 'Ё': '~',
|
||||
}
|
||||
|
||||
// translateKeyMsg translates Cyrillic characters in a KeyMsg to their Latin
|
||||
// positional equivalents (ЙЦУКЕН → QWERTY). If no translation is needed, the
|
||||
// original message is returned unchanged.
|
||||
func translateKeyMsg(msg tea.KeyMsg) tea.KeyMsg {
|
||||
if msg.Type != tea.KeyRunes || len(msg.Runes) == 0 {
|
||||
return msg
|
||||
}
|
||||
translated := make([]rune, len(msg.Runes))
|
||||
changed := false
|
||||
for i, r := range msg.Runes {
|
||||
if latin, ok := cyrillicToLatin[r]; ok {
|
||||
translated[i] = latin
|
||||
changed = true
|
||||
} else {
|
||||
translated[i] = r
|
||||
}
|
||||
}
|
||||
if !changed {
|
||||
return msg
|
||||
}
|
||||
return tea.KeyMsg{
|
||||
Type: msg.Type,
|
||||
Runes: translated,
|
||||
Alt: msg.Alt,
|
||||
Paste: msg.Paste,
|
||||
}
|
||||
}
|
||||
6742
src/vcom-0.2.5/internal/ui/model.go
Normal file
6742
src/vcom-0.2.5/internal/ui/model.go
Normal file
File diff suppressed because it is too large
Load diff
850
src/vcom-0.2.5/internal/ui/pane.go
Normal file
850
src/vcom-0.2.5/internal/ui/pane.go
Normal file
|
|
@ -0,0 +1,850 @@
|
|||
package ui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
|
||||
"vcom/internal/config"
|
||||
vfs "vcom/internal/fs"
|
||||
"vcom/internal/fs/remote"
|
||||
"vcom/internal/theme"
|
||||
)
|
||||
|
||||
type PaneID string
|
||||
|
||||
const (
|
||||
PaneLeft PaneID = "left"
|
||||
PaneRight PaneID = "right"
|
||||
)
|
||||
|
||||
type BrowserPane struct {
|
||||
ID PaneID
|
||||
Path string
|
||||
Entries []vfs.Entry
|
||||
Cursor int
|
||||
Offset int
|
||||
Marked map[string]struct{}
|
||||
Archive []ArchiveMount
|
||||
Remote []RemoteMount
|
||||
|
||||
dirHistory []string
|
||||
dirFuture []string
|
||||
|
||||
// cursorMemory remembers the last selected entry display name per directory
|
||||
// within a session. Keyed by directory path. Restored when re-entering a dir.
|
||||
cursorMemory map[string]string
|
||||
}
|
||||
|
||||
type ArchiveMount struct {
|
||||
SourcePath string
|
||||
ParentPath string
|
||||
RootPath string
|
||||
TempDir string
|
||||
}
|
||||
|
||||
// RemoteMount represents an active SSH/SFTP remote filesystem connection.
|
||||
type RemoteMount struct {
|
||||
Host remote.SSHHost
|
||||
RemotePath string
|
||||
Client *remote.SSHClient
|
||||
Connected bool
|
||||
}
|
||||
|
||||
func (p *BrowserPane) Selected() (vfs.Entry, bool) {
|
||||
if len(p.Entries) == 0 || p.Cursor < 0 || p.Cursor >= len(p.Entries) {
|
||||
return vfs.Entry{}, false
|
||||
}
|
||||
return p.Entries[p.Cursor], true
|
||||
}
|
||||
|
||||
func (p *BrowserPane) SetEntries(entries []vfs.Entry, preserveKey string) {
|
||||
p.Entries = entries
|
||||
p.PruneMarks()
|
||||
if len(entries) == 0 {
|
||||
p.Cursor = 0
|
||||
p.Offset = 0
|
||||
return
|
||||
}
|
||||
if preserveKey != "" {
|
||||
oldCursor := p.Cursor
|
||||
p.Cursor = vfs.FindSelected(entries, preserveKey)
|
||||
if p.Cursor != oldCursor {
|
||||
p.Offset = 0
|
||||
}
|
||||
}
|
||||
if p.Cursor >= len(entries) {
|
||||
p.Cursor = len(entries) - 1
|
||||
}
|
||||
if p.Cursor < 0 {
|
||||
p.Cursor = 0
|
||||
}
|
||||
if p.Offset > p.Cursor {
|
||||
p.Offset = p.Cursor
|
||||
}
|
||||
}
|
||||
|
||||
func (p *BrowserPane) Move(delta int, pageSize int) {
|
||||
if len(p.Entries) == 0 {
|
||||
p.Cursor = 0
|
||||
return
|
||||
}
|
||||
p.Cursor += delta
|
||||
if p.Cursor < 0 {
|
||||
p.Cursor = 0
|
||||
}
|
||||
if p.Cursor >= len(p.Entries) {
|
||||
p.Cursor = len(p.Entries) - 1
|
||||
}
|
||||
|
||||
if p.Cursor < p.Offset {
|
||||
p.Offset = p.Cursor
|
||||
}
|
||||
if pageSize > 0 && p.Cursor >= p.Offset+pageSize {
|
||||
p.Offset = p.Cursor - pageSize + 1
|
||||
}
|
||||
if p.Offset < 0 {
|
||||
p.Offset = 0
|
||||
}
|
||||
}
|
||||
|
||||
func (p *BrowserPane) EnsureMarked(path string) {
|
||||
if strings.TrimSpace(path) == "" {
|
||||
return
|
||||
}
|
||||
if p.Marked == nil {
|
||||
p.Marked = map[string]struct{}{}
|
||||
}
|
||||
p.Marked[path] = struct{}{}
|
||||
}
|
||||
|
||||
func (p *BrowserPane) ToggleMarked(path string) {
|
||||
if strings.TrimSpace(path) == "" {
|
||||
return
|
||||
}
|
||||
if p.Marked == nil {
|
||||
p.Marked = map[string]struct{}{}
|
||||
}
|
||||
if _, ok := p.Marked[path]; ok {
|
||||
delete(p.Marked, path)
|
||||
if len(p.Marked) == 0 {
|
||||
p.Marked = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
p.Marked[path] = struct{}{}
|
||||
}
|
||||
|
||||
func (p *BrowserPane) IsMarked(path string) bool {
|
||||
if p.Marked == nil {
|
||||
return false
|
||||
}
|
||||
_, ok := p.Marked[path]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (p *BrowserPane) ClearMarks() {
|
||||
p.Marked = nil
|
||||
}
|
||||
|
||||
func (p *BrowserPane) PruneMarks() {
|
||||
if len(p.Marked) == 0 {
|
||||
return
|
||||
}
|
||||
valid := map[string]struct{}{}
|
||||
for _, entry := range p.Entries {
|
||||
if entry.IsParent {
|
||||
continue
|
||||
}
|
||||
valid[entry.Path] = struct{}{}
|
||||
}
|
||||
for path := range p.Marked {
|
||||
if _, ok := valid[path]; !ok {
|
||||
delete(p.Marked, path)
|
||||
}
|
||||
}
|
||||
if len(p.Marked) == 0 {
|
||||
p.Marked = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (p *BrowserPane) MarkedEntries() []vfs.Entry {
|
||||
if len(p.Marked) == 0 {
|
||||
return nil
|
||||
}
|
||||
result := make([]vfs.Entry, 0, len(p.Marked))
|
||||
for _, entry := range p.Entries {
|
||||
if entry.IsParent {
|
||||
continue
|
||||
}
|
||||
if p.IsMarked(entry.Path) {
|
||||
result = append(result, entry)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (p *BrowserPane) EnsureVisible(pageSize int) {
|
||||
if pageSize <= 0 {
|
||||
return
|
||||
}
|
||||
if p.Cursor < p.Offset {
|
||||
p.Offset = p.Cursor
|
||||
}
|
||||
if p.Cursor >= p.Offset+pageSize {
|
||||
p.Offset = p.Cursor - pageSize + 1
|
||||
}
|
||||
if p.Offset < 0 {
|
||||
p.Offset = 0
|
||||
}
|
||||
}
|
||||
|
||||
func (p *BrowserPane) SaveCursor(dirPath string, entryName string) {
|
||||
if dirPath == "" || entryName == "" {
|
||||
return
|
||||
}
|
||||
if p.cursorMemory == nil {
|
||||
p.cursorMemory = map[string]string{}
|
||||
}
|
||||
p.cursorMemory[dirPath] = entryName
|
||||
}
|
||||
|
||||
// LoadCursor returns the saved entry name for a directory, or empty string.
|
||||
func (p *BrowserPane) LoadCursor(dirPath string) string {
|
||||
if p.cursorMemory == nil {
|
||||
return ""
|
||||
}
|
||||
return p.cursorMemory[dirPath]
|
||||
}
|
||||
|
||||
func (p *BrowserPane) InArchive() bool {
|
||||
return len(p.Archive) > 0
|
||||
}
|
||||
|
||||
// PushHistory saves the current path to the back-stack and clears the forward-stack.
|
||||
func (p *BrowserPane) PushHistory(path string) {
|
||||
p.dirHistory = append(p.dirHistory, path)
|
||||
p.dirFuture = nil
|
||||
}
|
||||
|
||||
// PopHistory returns the most recent path from the back-stack.
|
||||
func (p *BrowserPane) PopHistory() (string, bool) {
|
||||
if len(p.dirHistory) == 0 {
|
||||
return "", false
|
||||
}
|
||||
path := p.dirHistory[len(p.dirHistory)-1]
|
||||
p.dirHistory = p.dirHistory[:len(p.dirHistory)-1]
|
||||
return path, true
|
||||
}
|
||||
|
||||
// PushFuture saves the current path to the forward-stack.
|
||||
func (p *BrowserPane) PushFuture(path string) {
|
||||
p.dirFuture = append(p.dirFuture, path)
|
||||
}
|
||||
|
||||
// PopFuture returns the most recent path from the forward-stack.
|
||||
func (p *BrowserPane) PopFuture() (string, bool) {
|
||||
if len(p.dirFuture) == 0 {
|
||||
return "", false
|
||||
}
|
||||
path := p.dirFuture[len(p.dirFuture)-1]
|
||||
p.dirFuture = p.dirFuture[:len(p.dirFuture)-1]
|
||||
return path, true
|
||||
}
|
||||
|
||||
// HasHistory returns true if there are entries in the back-stack.
|
||||
func (p *BrowserPane) HasHistory() bool {
|
||||
return len(p.dirHistory) > 0
|
||||
}
|
||||
|
||||
// HasFuture returns true if there are entries in the forward-stack.
|
||||
func (p *BrowserPane) HasFuture() bool {
|
||||
return len(p.dirFuture) > 0
|
||||
}
|
||||
|
||||
// HistoryDepth returns the number of entries in the back-stack.
|
||||
func (p *BrowserPane) HistoryDepth() int {
|
||||
return len(p.dirHistory)
|
||||
}
|
||||
|
||||
// FutureDepth returns the number of entries in the forward-stack.
|
||||
func (p *BrowserPane) FutureDepth() int {
|
||||
return len(p.dirFuture)
|
||||
}
|
||||
|
||||
func (p *BrowserPane) PushArchive(mount ArchiveMount) {
|
||||
p.Archive = append(p.Archive, mount)
|
||||
}
|
||||
|
||||
func (p *BrowserPane) PopArchive() (ArchiveMount, bool) {
|
||||
if len(p.Archive) == 0 {
|
||||
return ArchiveMount{}, false
|
||||
}
|
||||
last := p.Archive[len(p.Archive)-1]
|
||||
p.Archive = p.Archive[:len(p.Archive)-1]
|
||||
return last, true
|
||||
}
|
||||
|
||||
func (p *BrowserPane) CurrentArchive() (ArchiveMount, bool) {
|
||||
if len(p.Archive) == 0 {
|
||||
return ArchiveMount{}, false
|
||||
}
|
||||
return p.Archive[len(p.Archive)-1], true
|
||||
}
|
||||
|
||||
func (p *BrowserPane) ClearArchives() []ArchiveMount {
|
||||
if len(p.Archive) == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]ArchiveMount, len(p.Archive))
|
||||
copy(out, p.Archive)
|
||||
p.Archive = nil
|
||||
return out
|
||||
}
|
||||
|
||||
func (p *BrowserPane) PushRemote(mount RemoteMount) {
|
||||
p.Remote = append(p.Remote, mount)
|
||||
}
|
||||
|
||||
func (p *BrowserPane) PopRemote() (RemoteMount, bool) {
|
||||
if len(p.Remote) == 0 {
|
||||
return RemoteMount{}, false
|
||||
}
|
||||
last := p.Remote[len(p.Remote)-1]
|
||||
p.Remote = p.Remote[:len(p.Remote)-1]
|
||||
return last, true
|
||||
}
|
||||
|
||||
func (p *BrowserPane) CurrentRemote() (RemoteMount, bool) {
|
||||
if len(p.Remote) == 0 {
|
||||
return RemoteMount{}, false
|
||||
}
|
||||
return p.Remote[len(p.Remote)-1], true
|
||||
}
|
||||
|
||||
func (p *BrowserPane) InRemote() bool {
|
||||
return len(p.Remote) > 0
|
||||
}
|
||||
|
||||
func (p *BrowserPane) ClearRemotes() []RemoteMount {
|
||||
if len(p.Remote) == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]RemoteMount, len(p.Remote))
|
||||
copy(out, p.Remote)
|
||||
p.Remote = nil
|
||||
return out
|
||||
}
|
||||
|
||||
func (p *BrowserPane) DisplayPath() string {
|
||||
if len(p.Remote) > 0 {
|
||||
top := p.Remote[len(p.Remote)-1]
|
||||
statusIcon := ""
|
||||
if !top.Connected {
|
||||
statusIcon = ""
|
||||
}
|
||||
if top.RemotePath == "/" || top.RemotePath == "" {
|
||||
return fmt.Sprintf("%s %s:", statusIcon, top.Host.Name)
|
||||
}
|
||||
return fmt.Sprintf("%s %s:%s", statusIcon, top.Host.Name, top.RemotePath)
|
||||
}
|
||||
if len(p.Archive) == 0 {
|
||||
return p.Path
|
||||
}
|
||||
top := p.Archive[len(p.Archive)-1]
|
||||
rel, err := filepath.Rel(top.RootPath, p.Path)
|
||||
if err != nil {
|
||||
return p.Path
|
||||
}
|
||||
rel = filepath.ToSlash(rel)
|
||||
if rel == "." {
|
||||
rel = ""
|
||||
}
|
||||
if rel == "" {
|
||||
return top.SourcePath + "::"
|
||||
}
|
||||
return top.SourcePath + "::/" + rel
|
||||
}
|
||||
|
||||
func renderPane(
|
||||
pane BrowserPane,
|
||||
cfg config.Config,
|
||||
palette theme.Palette,
|
||||
width int,
|
||||
height int,
|
||||
active bool,
|
||||
hoverIndex int,
|
||||
useNerdIcons bool,
|
||||
) string {
|
||||
if width <= 0 || height <= 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
borderColor := palette.Border
|
||||
headerBg := palette.PanelInactive
|
||||
bodyBg := palette.Panel
|
||||
if active {
|
||||
borderColor = palette.BorderActive
|
||||
headerBg = palette.Selection
|
||||
}
|
||||
|
||||
innerWidth := max(width-2, 1)
|
||||
innerHeight := max(height-2, 1)
|
||||
|
||||
box := lipgloss.NewStyle().
|
||||
Width(innerWidth).
|
||||
Height(innerHeight).
|
||||
Background(bodyBg).
|
||||
Foreground(palette.Text).
|
||||
BorderStyle(borderStyle(cfg.UI.Border)).
|
||||
BorderForeground(borderColor).
|
||||
BorderBackground(bodyBg)
|
||||
|
||||
header := lipgloss.NewStyle().
|
||||
Render(renderPaneHeader(pane, cfg, palette, innerWidth, active, headerBg))
|
||||
|
||||
isSSHHostList := pane.Path == "ssh://"
|
||||
rowsHeight := max(innerHeight-2, 1)
|
||||
headerRow := renderColumnsHeader(cfg, innerWidth, palette, bodyBg, useNerdIcons, isSSHHostList)
|
||||
rows := renderPaneRows(pane, cfg, palette, innerWidth, rowsHeight, active, hoverIndex, bodyBg, useNerdIcons, isSSHHostList)
|
||||
content := lipgloss.JoinVertical(lipgloss.Left, header, headerRow, rows)
|
||||
return box.Render(content)
|
||||
}
|
||||
|
||||
func renderPaneHeader(pane BrowserPane, cfg config.Config, palette theme.Palette, width int, active bool, headerBg lipgloss.Color) string {
|
||||
pathWidth := max(width, 4)
|
||||
pathStyle := lipgloss.NewStyle().
|
||||
Width(pathWidth).
|
||||
Background(headerBg).
|
||||
Foreground(palette.Text).
|
||||
Bold(active)
|
||||
if active {
|
||||
pathStyle = pathStyle.Foreground(palette.ActivePath)
|
||||
}
|
||||
|
||||
return lipgloss.NewStyle().
|
||||
Width(width).
|
||||
Background(headerBg).
|
||||
Render(pathStyle.Render(truncateMiddle(compactPath(pane.DisplayPath(), cfg.UI.PathDisplay), pathWidth)))
|
||||
}
|
||||
|
||||
func renderColumnsHeader(cfg config.Config, width int, palette theme.Palette, background lipgloss.Color, useNerdIcons bool, hideExtraCols bool) string {
|
||||
columns := buildColumns(cfg, width, useNerdIcons, hideExtraCols)
|
||||
parts := make([]string, 0, len(columns))
|
||||
for idx, column := range columns {
|
||||
style := lipgloss.NewStyle().
|
||||
Width(column.Width).
|
||||
Foreground(palette.Muted).
|
||||
Background(background).
|
||||
Bold(true)
|
||||
if column.AlignRight {
|
||||
style = style.Align(lipgloss.Right)
|
||||
}
|
||||
parts = append(parts, style.Render(truncateRight(column.Title, column.Width)))
|
||||
if idx < len(columns)-1 {
|
||||
parts = append(parts, columnSeparator(column.Key, palette, background))
|
||||
}
|
||||
}
|
||||
return lipgloss.NewStyle().
|
||||
Width(width).
|
||||
Background(background).
|
||||
Render(strings.Join(parts, ""))
|
||||
}
|
||||
|
||||
func renderPaneRows(pane BrowserPane, cfg config.Config, palette theme.Palette, width int, height int, active bool, hoverIndex int, background lipgloss.Color, useNerdIcons bool, hideExtraCols bool) string {
|
||||
if len(pane.Entries) == 0 {
|
||||
return lipgloss.NewStyle().
|
||||
Width(width).
|
||||
Height(height).
|
||||
Padding(1, 1).
|
||||
Background(background).
|
||||
Foreground(palette.Muted).
|
||||
Render("Empty directory")
|
||||
}
|
||||
|
||||
visibleHeight := max(height, 1)
|
||||
pane.EnsureVisible(visibleHeight)
|
||||
end := min(len(pane.Entries), pane.Offset+visibleHeight)
|
||||
|
||||
lines := make([]string, 0, visibleHeight)
|
||||
for idx := pane.Offset; idx < end; idx++ {
|
||||
entry := pane.Entries[idx]
|
||||
isSelected := idx == pane.Cursor && active
|
||||
marked := !entry.IsParent && pane.IsMarked(entry.Path)
|
||||
row := renderEntryRow(entry, cfg, width, isSelected, marked, idx == hoverIndex, active, palette, background, useNerdIcons, hideExtraCols)
|
||||
lines = append(lines, row)
|
||||
}
|
||||
for len(lines) < visibleHeight {
|
||||
lines = append(lines, lipgloss.NewStyle().Width(width).Background(background).Render(""))
|
||||
}
|
||||
|
||||
return lipgloss.NewStyle().
|
||||
Width(width).
|
||||
Background(background).
|
||||
Render(strings.Join(lines, "\n"))
|
||||
}
|
||||
|
||||
func renderEntryRow(entry vfs.Entry, cfg config.Config, width int, selected bool, marked bool, hovered bool, active bool, palette theme.Palette, baseBackground lipgloss.Color, useNerdIcons bool, hideExtraCols bool) string {
|
||||
columns := buildColumns(cfg, width, useNerdIcons, hideExtraCols)
|
||||
rowBackground := baseBackground
|
||||
switch {
|
||||
case marked:
|
||||
rowBackground = palette.Marked
|
||||
case selected:
|
||||
rowBackground = palette.Selection
|
||||
case hovered:
|
||||
rowBackground = palette.Hover
|
||||
}
|
||||
|
||||
parts := make([]string, 0, len(columns))
|
||||
for idx, column := range columns {
|
||||
value := column.Value(entry, cfg.Browser.HumanReadableSize)
|
||||
foreground := entryColor(entry, palette)
|
||||
if marked {
|
||||
foreground = palette.Background
|
||||
}
|
||||
style := lipgloss.NewStyle().
|
||||
Width(column.Width).
|
||||
Foreground(foreground).
|
||||
Background(rowBackground)
|
||||
|
||||
if entry.IsHidden && !marked {
|
||||
style = style.Foreground(palette.Muted)
|
||||
}
|
||||
if column.AlignRight {
|
||||
style = style.Align(lipgloss.Right)
|
||||
}
|
||||
parts = append(parts, style.Render(truncateForColumn(value, column.Width, column.AlignRight)))
|
||||
if idx < len(columns)-1 {
|
||||
parts = append(parts, columnSeparator(column.Key, palette, rowBackground))
|
||||
}
|
||||
}
|
||||
|
||||
rowStyle := lipgloss.NewStyle().Width(width).Background(rowBackground)
|
||||
if selected && active {
|
||||
rowStyle = rowStyle.Bold(true)
|
||||
}
|
||||
|
||||
return rowStyle.Render(strings.Join(parts, ""))
|
||||
}
|
||||
|
||||
type columnSpec struct {
|
||||
Key string
|
||||
Title string
|
||||
Width int
|
||||
MinWidth int
|
||||
AlignRight bool
|
||||
Value func(entry vfs.Entry, human bool) string
|
||||
}
|
||||
|
||||
func buildColumns(cfg config.Config, totalWidth int, useNerdIcons bool, hideExtraCols bool) []columnSpec {
|
||||
fixed := []columnSpec{}
|
||||
|
||||
if cfg.Browser.Columns.Permissions {
|
||||
fixed = append(fixed, columnSpec{
|
||||
Key: "permissions",
|
||||
Title: "Perms",
|
||||
Width: 10,
|
||||
MinWidth: 9,
|
||||
Value: func(entry vfs.Entry, _ bool) string {
|
||||
return vfs.Permissions(entry.Mode)
|
||||
},
|
||||
})
|
||||
}
|
||||
if cfg.Browser.Columns.Extension {
|
||||
fixed = append(fixed, columnSpec{
|
||||
Key: "extension",
|
||||
Title: "Ext",
|
||||
Width: 6,
|
||||
MinWidth: 4,
|
||||
Value: func(entry vfs.Entry, _ bool) string {
|
||||
if entry.IsDir {
|
||||
return ""
|
||||
}
|
||||
return entry.Extension
|
||||
},
|
||||
})
|
||||
}
|
||||
if cfg.Browser.Columns.Size && !hideExtraCols {
|
||||
fixed = append(fixed, columnSpec{
|
||||
Key: "size",
|
||||
Title: "Size",
|
||||
Width: 9,
|
||||
MinWidth: 6,
|
||||
AlignRight: true,
|
||||
Value: func(entry vfs.Entry, human bool) string {
|
||||
if entry.IsDir {
|
||||
if !entry.DirSizeKnown {
|
||||
return ""
|
||||
}
|
||||
if human {
|
||||
return vfs.HumanSize(entry.Size)
|
||||
}
|
||||
return fmt.Sprintf("%d", entry.Size)
|
||||
}
|
||||
if human {
|
||||
return vfs.HumanSize(entry.Size)
|
||||
}
|
||||
return fmt.Sprintf("%d", entry.Size)
|
||||
},
|
||||
})
|
||||
}
|
||||
if cfg.Browser.Columns.Created {
|
||||
fixed = append(fixed, columnSpec{
|
||||
Key: "created",
|
||||
Title: "Created",
|
||||
Width: 11,
|
||||
MinWidth: 8,
|
||||
Value: func(entry vfs.Entry, _ bool) string {
|
||||
if !entry.CreatedKnown {
|
||||
return "n/a"
|
||||
}
|
||||
return vfs.CompactTime(entry.CreatedAt)
|
||||
},
|
||||
})
|
||||
}
|
||||
if cfg.Browser.Columns.Modified && !hideExtraCols {
|
||||
fixed = append(fixed, columnSpec{
|
||||
Key: "modified",
|
||||
Title: "Modified",
|
||||
Width: 11,
|
||||
MinWidth: 8,
|
||||
Value: func(entry vfs.Entry, _ bool) string {
|
||||
if entry.IsParent {
|
||||
return ""
|
||||
}
|
||||
return vfs.CompactTime(entry.ModifiedAt)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
minNameWidth := 4
|
||||
gaps := 0
|
||||
for _, column := range fixed {
|
||||
gaps += separatorWidth(column.Key)
|
||||
}
|
||||
availableForColumns := totalWidth - gaps
|
||||
if availableForColumns < minNameWidth {
|
||||
availableForColumns = minNameWidth
|
||||
}
|
||||
|
||||
fixedWidth := 0
|
||||
for _, column := range fixed {
|
||||
fixedWidth += column.Width
|
||||
}
|
||||
for fixedWidth+minNameWidth > availableForColumns {
|
||||
changed := false
|
||||
for idx := len(fixed) - 1; idx >= 0 && fixedWidth+minNameWidth > availableForColumns; idx-- {
|
||||
if fixed[idx].Width > fixed[idx].MinWidth {
|
||||
fixed[idx].Width--
|
||||
fixedWidth--
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
if !changed {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
nameWidth := max(availableForColumns-fixedWidth, minNameWidth)
|
||||
name := columnSpec{
|
||||
Key: "name",
|
||||
Title: "Name",
|
||||
Width: nameWidth,
|
||||
MinWidth: minNameWidth,
|
||||
Value: func(entry vfs.Entry, _ bool) string {
|
||||
return entryIcon(entry, useNerdIcons) + " " + entry.DisplayName()
|
||||
},
|
||||
}
|
||||
|
||||
return append([]columnSpec{name}, fixed...)
|
||||
}
|
||||
|
||||
func columnSeparator(columnKey string, palette theme.Palette, background lipgloss.Color) string {
|
||||
width := separatorWidth(columnKey)
|
||||
style := lipgloss.NewStyle().
|
||||
Width(width).
|
||||
Foreground(palette.Border)
|
||||
if background != lipgloss.Color("") {
|
||||
style = style.Background(background)
|
||||
}
|
||||
return style.Render(strings.Repeat(" ", width))
|
||||
}
|
||||
|
||||
func separatorWidth(columnKey string) int {
|
||||
if columnKey == "size" {
|
||||
return 2
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func borderStyle(value string) lipgloss.Border {
|
||||
switch strings.ToLower(value) {
|
||||
case "double":
|
||||
return lipgloss.DoubleBorder()
|
||||
case "thick":
|
||||
return lipgloss.ThickBorder()
|
||||
default:
|
||||
return lipgloss.RoundedBorder()
|
||||
}
|
||||
}
|
||||
|
||||
func compactPath(path string, mode string) string {
|
||||
switch strings.ToLower(mode) {
|
||||
case "full":
|
||||
return path
|
||||
case "smart":
|
||||
return smartPath(path, 42)
|
||||
default:
|
||||
return vfs.SafeBase(path)
|
||||
}
|
||||
}
|
||||
|
||||
func smartPath(path string, maxWidth int) string {
|
||||
if lipgloss.Width(path) <= maxWidth {
|
||||
return path
|
||||
}
|
||||
return truncateMiddle(path, maxWidth)
|
||||
}
|
||||
|
||||
func truncateMiddle(value string, maxWidth int) string {
|
||||
if maxWidth <= 0 || lipgloss.Width(value) <= maxWidth {
|
||||
return value
|
||||
}
|
||||
if maxWidth <= 3 {
|
||||
return trimToWidthRight(value, maxWidth)
|
||||
}
|
||||
left := maxWidth/2 - 1
|
||||
right := maxWidth - left - 1
|
||||
if left < 1 {
|
||||
left = 1
|
||||
}
|
||||
if right < 1 {
|
||||
right = 1
|
||||
}
|
||||
return trimToWidthRight(value, left) + "…" + trimToWidthLeft(value, right)
|
||||
}
|
||||
|
||||
func truncateRight(value string, maxWidth int) string {
|
||||
if maxWidth <= 0 || lipgloss.Width(value) <= maxWidth {
|
||||
return value
|
||||
}
|
||||
if maxWidth == 1 {
|
||||
return trimToWidthRight(value, 1)
|
||||
}
|
||||
return trimToWidthRight(value, maxWidth-1) + "…"
|
||||
}
|
||||
|
||||
func truncateForColumn(value string, maxWidth int, alignRight bool) string {
|
||||
if lipgloss.Width(value) <= maxWidth {
|
||||
return value
|
||||
}
|
||||
if alignRight {
|
||||
if maxWidth <= 1 {
|
||||
return trimToWidthLeft(value, 1)
|
||||
}
|
||||
return "…" + trimToWidthLeft(value, maxWidth-1)
|
||||
}
|
||||
return truncateRight(value, maxWidth)
|
||||
}
|
||||
|
||||
func trimToWidthRight(value string, maxWidth int) string {
|
||||
if maxWidth <= 0 {
|
||||
return ""
|
||||
}
|
||||
width := 0
|
||||
var builder strings.Builder
|
||||
for _, r := range value {
|
||||
rw := lipgloss.Width(string(r))
|
||||
if width+rw > maxWidth {
|
||||
break
|
||||
}
|
||||
builder.WriteRune(r)
|
||||
width += rw
|
||||
}
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
func trimToWidthLeft(value string, maxWidth int) string {
|
||||
if maxWidth <= 0 {
|
||||
return ""
|
||||
}
|
||||
runes := []rune(value)
|
||||
width := 0
|
||||
start := len(runes)
|
||||
for i := len(runes) - 1; i >= 0; i-- {
|
||||
rw := lipgloss.Width(string(runes[i]))
|
||||
if width+rw > maxWidth {
|
||||
break
|
||||
}
|
||||
width += rw
|
||||
start = i
|
||||
}
|
||||
return string(runes[start:])
|
||||
}
|
||||
|
||||
func entryIcon(entry vfs.Entry, useNerdIcons bool) string {
|
||||
if !useNerdIcons {
|
||||
switch entry.Category() {
|
||||
case "parent":
|
||||
return "<-"
|
||||
case "remote":
|
||||
return "[SV]"
|
||||
case "directory":
|
||||
return "[D]"
|
||||
case "config":
|
||||
return "[C]"
|
||||
case "text":
|
||||
return "[T]"
|
||||
case "image":
|
||||
return "[I]"
|
||||
case "executable":
|
||||
return "[X]"
|
||||
case "archive":
|
||||
return "[A]"
|
||||
default:
|
||||
return "[F]"
|
||||
}
|
||||
}
|
||||
switch entry.Category() {
|
||||
case "parent":
|
||||
return "↩"
|
||||
case "remote":
|
||||
return ""
|
||||
case "directory":
|
||||
return ""
|
||||
case "config":
|
||||
return ""
|
||||
case "text":
|
||||
return ""
|
||||
case "image":
|
||||
return ""
|
||||
case "executable":
|
||||
return ""
|
||||
case "archive":
|
||||
return ""
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func entryColor(entry vfs.Entry, palette theme.Palette) lipgloss.Color {
|
||||
switch entry.Category() {
|
||||
case "remote":
|
||||
return palette.ExecFile
|
||||
case "directory", "parent":
|
||||
return palette.Folder
|
||||
case "config":
|
||||
return palette.ConfigFile
|
||||
case "text":
|
||||
return palette.TextFile
|
||||
case "image":
|
||||
return palette.ImageFile
|
||||
case "executable":
|
||||
return palette.ExecFile
|
||||
default:
|
||||
return palette.BinaryFile
|
||||
}
|
||||
}
|
||||
244
src/vcom-0.2.5/internal/ui/ssh.go
Normal file
244
src/vcom-0.2.5/internal/ui/ssh.go
Normal file
|
|
@ -0,0 +1,244 @@
|
|||
package ui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
vfs "vcom/internal/fs"
|
||||
"vcom/internal/fs/remote"
|
||||
|
||||
"github.com/charmbracelet/bubbles/textinput"
|
||||
)
|
||||
|
||||
// isRemoteHostEntry returns true if the entry represents an SSH host.
|
||||
func isRemoteHostEntry(entry vfs.Entry) bool {
|
||||
return entry.IsRemote
|
||||
}
|
||||
|
||||
// isRemoteAddHostEntry returns true if the entry is the "Add host" special item.
|
||||
func isRemoteAddHostEntry(entry vfs.Entry) bool {
|
||||
return entry.IsRemote && entry.Name == "+ Add host"
|
||||
}
|
||||
|
||||
// buildSSHHostEntries creates virtual directory entries for SSH hosts.
|
||||
// connectedHosts optionally specifies which hosts have active connections
|
||||
// (host name -> true). When non-nil, entries show a connection status prefix.
|
||||
func buildSSHHostEntries(store *remote.HostStore, connectedHosts map[string]bool) []vfs.Entry {
|
||||
hosts := store.AllHosts()
|
||||
entries := make([]vfs.Entry, 0, len(hosts)+1)
|
||||
|
||||
// Find the longest host name so we can pad them all to the same width,
|
||||
// making the (user@host) suffix align vertically across entries.
|
||||
maxNameLen := 0
|
||||
for _, h := range hosts {
|
||||
if l := len(h.Name); l > maxNameLen {
|
||||
maxNameLen = l
|
||||
}
|
||||
}
|
||||
|
||||
for _, h := range hosts {
|
||||
isConnected := connectedHosts != nil && connectedHosts[h.Name]
|
||||
|
||||
addr := h.HostName
|
||||
if h.Port != "" && h.Port != "22" {
|
||||
addr = fmt.Sprintf("%s:%s", addr, h.Port)
|
||||
}
|
||||
suffix := ""
|
||||
if h.User != "" {
|
||||
suffix = fmt.Sprintf(" (%s@%s)", h.User, addr)
|
||||
} else {
|
||||
suffix = fmt.Sprintf(" (%s)", addr)
|
||||
}
|
||||
paddedName := h.Name + strings.Repeat(" ", maxNameLen-len(h.Name))
|
||||
|
||||
entries = append(entries, vfs.Entry{
|
||||
Name: paddedName + suffix,
|
||||
Path: "ssh://" + h.Name,
|
||||
IsDir: true,
|
||||
IsRemote: true,
|
||||
Connected: isConnected,
|
||||
RemoteHostName: h.Name,
|
||||
Mode: 0o755,
|
||||
})
|
||||
}
|
||||
|
||||
// Add "Add host" entry at the bottom
|
||||
entries = append(entries, vfs.Entry{
|
||||
Name: "+ Add host",
|
||||
Path: "ssh://add-host",
|
||||
IsDir: false,
|
||||
IsRemote: true,
|
||||
})
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
// SSHConnectDialogInputs creates the text input fields for the SSH connect dialog.
|
||||
func SSHConnectDialogInputs() []textinput.Model {
|
||||
fields := make([]textinput.Model, 5)
|
||||
|
||||
placeholders := []string{"my-server", "192.168.1.100", "22", "root", ""}
|
||||
|
||||
for i := range fields {
|
||||
ti := textinput.New()
|
||||
ti.Placeholder = placeholders[i]
|
||||
ti.CharLimit = 128
|
||||
ti.Width = 40
|
||||
if i == 4 {
|
||||
ti.EchoMode = textinput.EchoPassword
|
||||
ti.EchoCharacter = '•'
|
||||
}
|
||||
if i == 0 {
|
||||
ti.Focus()
|
||||
}
|
||||
fields[i] = ti
|
||||
}
|
||||
|
||||
return fields
|
||||
}
|
||||
|
||||
// sshDialogLabel returns the label for an SSH dialog field at the given index.
|
||||
func sshDialogLabel(index int) string {
|
||||
lbls := []string{"Name:", "Hostname/IP:", "Port:", "User:", "Password:"}
|
||||
if index >= 0 && index < len(lbls) {
|
||||
return lbls[index]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// sshDialogValue returns the value for an SSH dialog field.
|
||||
func sshDialogValue(inputs []textinput.Model, index int) string {
|
||||
if index >= 0 && index < len(inputs) {
|
||||
return inputs[index].Value()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// buildSSHHostFromDialog creates an SSHHost from dialog input values.
|
||||
func buildSSHHostFromDialog(inputs []textinput.Model) remote.SSHHost {
|
||||
host := remote.SSHHost{
|
||||
Name: strings.TrimSpace(sshDialogValue(inputs, 0)),
|
||||
HostName: strings.TrimSpace(sshDialogValue(inputs, 1)),
|
||||
Port: strings.TrimSpace(sshDialogValue(inputs, 2)),
|
||||
User: strings.TrimSpace(sshDialogValue(inputs, 3)),
|
||||
Password: sshDialogValue(inputs, 4),
|
||||
}
|
||||
if host.Port == "" {
|
||||
host.Port = "22"
|
||||
}
|
||||
return host
|
||||
}
|
||||
|
||||
// formatSSHConnectHelp returns the help text for the SSH connect dialog.
|
||||
func formatSSHConnectHelp() string {
|
||||
return `Fields:
|
||||
Name — alias for this host (required)
|
||||
Hostname/IP — server address (required)
|
||||
Port — SSH port, default 22
|
||||
User — login username (required)
|
||||
Password — password for password-based auth
|
||||
|
||||
Navigation: Tab / Shift+Tab between fields
|
||||
Confirm: Enter
|
||||
Close: Esc / q
|
||||
Help: F1 / ?`
|
||||
}
|
||||
|
||||
// remoteDirToEntries reads a remote directory via SFTP and converts to vfs.Entry slice.
|
||||
func remoteDirToEntries(remotePath string, sshClient *remote.SSHClient) ([]vfs.Entry, error) {
|
||||
fileInfos, err := sshClient.ReadDir(remotePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
entries := make([]vfs.Entry, 0, len(fileInfos)+1)
|
||||
|
||||
// Add parent directory entry if not at root
|
||||
if remotePath != "/" {
|
||||
parent := path.Dir(remotePath)
|
||||
if parent == "" {
|
||||
parent = "/"
|
||||
}
|
||||
entries = append(entries, vfs.Entry{
|
||||
Name: "..",
|
||||
Path: parent,
|
||||
IsDir: true,
|
||||
IsParent: true,
|
||||
})
|
||||
}
|
||||
|
||||
for _, info := range fileInfos {
|
||||
name := info.Name()
|
||||
isDir := info.IsDir()
|
||||
fullPath := path.Join(remotePath, name)
|
||||
|
||||
ext := ""
|
||||
if idx := strings.LastIndex(name, "."); idx > 0 {
|
||||
ext = strings.ToLower(name[idx+1:])
|
||||
}
|
||||
entry := vfs.Entry{
|
||||
Name: name,
|
||||
Path: fullPath,
|
||||
Extension: ext,
|
||||
Mode: info.Mode(),
|
||||
Size: info.Size(),
|
||||
ModifiedAt: info.ModTime(),
|
||||
IsDir: isDir,
|
||||
IsHidden: strings.HasPrefix(name, "."),
|
||||
}
|
||||
|
||||
entries = append(entries, entry)
|
||||
}
|
||||
|
||||
return entries, nil
|
||||
}
|
||||
|
||||
// sshState holds SSH-related state for the model.
|
||||
type sshState struct {
|
||||
store *remote.HostStore
|
||||
inputs []textinput.Model
|
||||
inputFocus int
|
||||
showHelp bool
|
||||
|
||||
// Connection test state
|
||||
testingConn bool // true while an async connection test is in progress
|
||||
cancelTest func() // call to abort a pending connection test
|
||||
|
||||
// connectedHosts tracks which host names have active SSH connections.
|
||||
// Updated when connections are established or closed.
|
||||
connectedHosts map[string]bool
|
||||
|
||||
// activeClients stores SSH clients for hosts whose connections are kept alive
|
||||
// after returning to the host list. Clients are closed on full SSH mode exit.
|
||||
activeClients map[string]*remote.SSHClient
|
||||
}
|
||||
|
||||
// cycleInput shifts focus between dialog inputs by delta (+1/-1).
|
||||
func (s *sshState) cycleInput(delta int) {
|
||||
if s == nil || len(s.inputs) == 0 {
|
||||
return
|
||||
}
|
||||
s.inputs[s.inputFocus].Blur()
|
||||
s.inputFocus = (s.inputFocus + delta) % len(s.inputs)
|
||||
if s.inputFocus < 0 {
|
||||
s.inputFocus += len(s.inputs)
|
||||
}
|
||||
s.inputs[s.inputFocus].Focus()
|
||||
}
|
||||
|
||||
// newSSHState creates a new sshState with a HostStore.
|
||||
func newSSHState() (*sshState, error) {
|
||||
store, err := remote.NewHostStore()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &sshState{
|
||||
store: store,
|
||||
inputs: SSHConnectDialogInputs(),
|
||||
inputFocus: 0,
|
||||
activeClients: make(map[string]*remote.SSHClient),
|
||||
showHelp: false,
|
||||
connectedHosts: make(map[string]bool),
|
||||
}, nil
|
||||
}
|
||||
497
src/vcom-0.2.5/plans/extended-preview-feature.md
Normal file
497
src/vcom-0.2.5/plans/extended-preview-feature.md
Normal file
|
|
@ -0,0 +1,497 @@
|
|||
# Extended Preview — PDF, Audio, Video via External Utilities
|
||||
|
||||
## Overview
|
||||
|
||||
Add rich preview support for three new file categories by leveraging external CLI tools. If a tool is not installed, the file falls back to the existing binary-file display.
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[BuildPreview entry] --> B{Is directory?}
|
||||
B -->|Yes| C[buildDirectoryPreview]
|
||||
B -->|No| D[Open file, read header]
|
||||
D --> E{Detect image?}
|
||||
E -->|Yes| F[PreviewKindImage]
|
||||
E -->|No| G{Detect PDF ext?}
|
||||
G -->|Yes| H{pdftotext available?}
|
||||
H -->|Yes| I[PreviewKindPDF - extract text]
|
||||
H -->|No| J[Fallback to binary]
|
||||
G -->|No| K{Detect audio ext?}
|
||||
K -->|Yes| L{ffprobe available?}
|
||||
L -->|Yes| M[PreviewKindAudio - show metadata]
|
||||
L -->|No| N[Fallback to binary]
|
||||
K -->|No| O{Detect video ext?}
|
||||
O -->|Yes| P{ffprobe available?}
|
||||
P -->|Yes| Q[PreviewKindVideo - show metadata]
|
||||
P -->|No| R[Fallback to binary]
|
||||
O -->|No| S[Is binary sample?]
|
||||
S -->|Yes| T[PreviewKindBinary]
|
||||
S -->|No| U[PreviewKindText + syntax highlight]
|
||||
```
|
||||
|
||||
## 1. New PreviewKind Constants
|
||||
|
||||
File: [`internal/fs/preview.go`](internal/fs/preview.go) (around line 28-35)
|
||||
|
||||
Add three new constants to the `PreviewKind` type:
|
||||
- `PreviewKindPDF PreviewKind = "pdf"`
|
||||
- `PreviewKindAudio PreviewKind = "audio"`
|
||||
- `PreviewKindVideo PreviewKind = "video"`
|
||||
|
||||
## 2. Extended Metadata Struct
|
||||
|
||||
File: [`internal/fs/preview.go`](internal/fs/preview.go) — `Metadata` struct (line 37-48)
|
||||
|
||||
Add new optional fields:
|
||||
|
||||
```go
|
||||
type Metadata struct {
|
||||
// ... existing fields ...
|
||||
|
||||
// Extended preview metadata
|
||||
Duration string // audio/video duration (e.g. "3:42")
|
||||
Bitrate string // audio/video bitrate (e.g. "320 kbps")
|
||||
AudioCodec string // audio codec (e.g. "aac", "mp3")
|
||||
VideoCodec string // video codec (e.g. "h264", "vp9")
|
||||
SampleRate string // audio sample rate (e.g. "44100 Hz")
|
||||
Channels string // audio channels (e.g. "stereo")
|
||||
PageCount string // PDF page count
|
||||
Dimensions string // video dimensions (e.g. "1920x1080")
|
||||
}
|
||||
```
|
||||
|
||||
## 3. New Extension Maps
|
||||
|
||||
File: [`internal/fs/entry.go`](internal/fs/entry.go) (around line 55-63)
|
||||
|
||||
Add three new extension sets:
|
||||
|
||||
```go
|
||||
pdfExtensions = map[string]struct{}{
|
||||
"pdf": {},
|
||||
}
|
||||
audioExtensions = map[string]struct{}{
|
||||
"mp3": {}, "flac": {}, "ogg": {}, "opus": {}, "wav": {},
|
||||
"aac": {}, "m4a": {}, "wma": {}, "dsf": {}, "ape": {},
|
||||
}
|
||||
videoExtensions = map[string]struct{}{
|
||||
"mp4": {}, "mkv": {}, "mov": {}, "avi": {}, "webm": {},
|
||||
"m4v": {}, "wmv": {}, "flv": {}, "ts": {}, "mts": {},
|
||||
}
|
||||
```
|
||||
|
||||
## 4. New Categories in `Category()` Method
|
||||
|
||||
File: [`internal/fs/entry.go`](internal/fs/entry.go) — `Category()` method (line 102-123)
|
||||
|
||||
Add three new cases in the switch statement (before `default`):
|
||||
|
||||
```go
|
||||
case hasExt(pdfExtensions, e.Extension):
|
||||
return "pdf"
|
||||
case hasExt(audioExtensions, e.Extension):
|
||||
return "audio"
|
||||
case hasExt(videoExtensions, e.Extension):
|
||||
return "video"
|
||||
```
|
||||
|
||||
## 5. External Utility Detection
|
||||
|
||||
File: [`internal/fs/preview.go`](internal/fs/preview.go) — new helper functions
|
||||
|
||||
```go
|
||||
func findTool(name string) string {
|
||||
path, err := exec.LookPath(name)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return path
|
||||
}
|
||||
```
|
||||
|
||||
Add `"os/exec"` to imports.
|
||||
|
||||
## 6. Modified `BuildPreview()` Flow
|
||||
|
||||
File: [`internal/fs/preview.go`](internal/fs/preview.go) — `BuildPreview()` (line 73)
|
||||
|
||||
Insert the new checks after the image detection block (after line 131) and before the `IsBinarySample()` check:
|
||||
|
||||
```go
|
||||
// PDF preview via pdftotext
|
||||
if hasExt(pdfExtensions, entry.Extension) {
|
||||
return buildPDFPreview(entry, options, preview)
|
||||
}
|
||||
|
||||
// Audio preview via ffprobe
|
||||
if hasExt(audioExtensions, entry.Extension) {
|
||||
return buildAudioPreview(entry, options, preview)
|
||||
}
|
||||
|
||||
// Video preview via ffprobe
|
||||
if hasExt(videoExtensions, entry.Extension) {
|
||||
return buildVideoPreview(entry, options, preview)
|
||||
}
|
||||
```
|
||||
|
||||
This placement ensures:
|
||||
- Images are still detected by magic bytes (works for any extension)
|
||||
- PDF/audio/video get their custom handling
|
||||
- Files that don't match any category fall through to the existing binary/text detection
|
||||
|
||||
## 7. New Preview Builder Functions
|
||||
|
||||
File: [`internal/fs/preview.go`](internal/fs/preview.go) — new functions
|
||||
|
||||
### 7a. `buildPDFPreview()`
|
||||
|
||||
```go
|
||||
func buildPDFPreview(entry Entry, options PreviewOptions, base Preview) Preview {
|
||||
pdftotext := findTool("pdftotext")
|
||||
if pdftotext == "" {
|
||||
base.Kind = PreviewKindBinary
|
||||
base.Body = "PDF file detected.\nInstall poppler-utils (pdftotext) for text preview."
|
||||
base.PlainBody = base.Body
|
||||
return base
|
||||
}
|
||||
|
||||
// Extract text
|
||||
cmd := exec.Command(pdftotext, "-layout", "-nopgbrk", entry.Path, "-")
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
base.Kind = PreviewKindError
|
||||
base.Body = fmt.Sprintf("pdftotext error:\n\n%s", err)
|
||||
base.PlainBody = base.Body
|
||||
return base
|
||||
}
|
||||
|
||||
text := string(out)
|
||||
if len(text) > int(options.MaxPreviewBytes) {
|
||||
text = text[:options.MaxPreviewBytes]
|
||||
}
|
||||
|
||||
// Get page count via pdfinfo if available
|
||||
pdfinfo := findTool("pdfinfo")
|
||||
if pdfinfo != "" {
|
||||
infoCmd := exec.Command(pdfinfo, entry.Path)
|
||||
if infoOut, err := infoCmd.Output(); err == nil {
|
||||
for _, line := range strings.Split(string(infoOut), "\n") {
|
||||
if strings.HasPrefix(strings.ToLower(line), "pages:") {
|
||||
base.Metadata.PageCount = strings.TrimSpace(strings.TrimPrefix(line, "Pages:"))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
base.Kind = PreviewKindPDF
|
||||
base.PlainBody = text
|
||||
base.Body = highlightText(entry.Path, text, options.ThemeName)
|
||||
return base
|
||||
}
|
||||
```
|
||||
|
||||
### 7b. `buildAudioPreview()`
|
||||
|
||||
```go
|
||||
func buildAudioPreview(entry Entry, options PreviewOptions, base Preview) Preview {
|
||||
ffprobe := findTool("ffprobe")
|
||||
if ffprobe == "" {
|
||||
base.Kind = PreviewKindBinary
|
||||
base.Body = "Audio file detected.\nInstall ffmpeg (ffprobe) for metadata preview."
|
||||
base.PlainBody = base.Body
|
||||
return base
|
||||
}
|
||||
|
||||
// Use ffprobe to extract metadata as JSON
|
||||
cmd := exec.Command(ffprobe,
|
||||
"-v", "quiet",
|
||||
"-print_format", "json",
|
||||
"-show_format",
|
||||
"-show_streams",
|
||||
entry.Path,
|
||||
)
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
base.Kind = PreviewKindError
|
||||
base.Body = fmt.Sprintf("ffprobe error:\n\n%s", err)
|
||||
base.PlainBody = base.Body
|
||||
return base
|
||||
}
|
||||
|
||||
var info struct {
|
||||
Format struct {
|
||||
Duration string `json:"duration"`
|
||||
Bitrate string `json:"bit_rate"`
|
||||
} `json:"format"`
|
||||
Streams []struct {
|
||||
CodecType string `json:"codec_type"`
|
||||
CodecName string `json:"codec_name"`
|
||||
SampleRate string `json:"sample_rate"`
|
||||
Channels int `json:"channels"`
|
||||
} `json:"streams"`
|
||||
}
|
||||
if err := json.Unmarshal(out, &info); err != nil {
|
||||
base.Kind = PreviewKindError
|
||||
base.Body = fmt.Sprintf("Could not parse ffprobe output:\n\n%s", err)
|
||||
base.PlainBody = base.Body
|
||||
return base
|
||||
}
|
||||
|
||||
// Format duration
|
||||
if info.Format.Duration != "" {
|
||||
if secs, err := strconv.ParseFloat(info.Format.Duration, 64); err == nil {
|
||||
mins := int(secs) / 60
|
||||
secsRem := int(secs) % 60
|
||||
base.Metadata.Duration = fmt.Sprintf("%d:%02d", mins, secsRem)
|
||||
}
|
||||
}
|
||||
|
||||
if info.Format.Bitrate != "" {
|
||||
if bps, err := strconv.ParseInt(info.Format.Bitrate, 10, 64); err == nil {
|
||||
base.Metadata.Bitrate = fmt.Sprintf("%d kbps", bps/1000)
|
||||
}
|
||||
}
|
||||
|
||||
for _, stream := range info.Streams {
|
||||
if stream.CodecType == "audio" {
|
||||
base.Metadata.AudioCodec = stream.CodecName
|
||||
if stream.SampleRate != "" {
|
||||
base.Metadata.SampleRate = stream.SampleRate + " Hz"
|
||||
}
|
||||
switch stream.Channels {
|
||||
case 1:
|
||||
base.Metadata.Channels = "mono"
|
||||
case 2:
|
||||
base.Metadata.Channels = "stereo"
|
||||
case 6:
|
||||
base.Metadata.Channels = "5.1"
|
||||
case 8:
|
||||
base.Metadata.Channels = "7.1"
|
||||
default:
|
||||
base.Metadata.Channels = fmt.Sprintf("%d ch", stream.Channels)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Build a rich metadata body
|
||||
var lines []string
|
||||
lines = append(lines, fmt.Sprintf(" Duration: %s", base.Metadata.Duration))
|
||||
if base.Metadata.Bitrate != "" {
|
||||
lines = append(lines, fmt.Sprintf(" Bitrate: %s", base.Metadata.Bitrate))
|
||||
}
|
||||
if base.Metadata.AudioCodec != "" {
|
||||
lines = append(lines, fmt.Sprintf(" Codec: %s", base.Metadata.AudioCodec))
|
||||
}
|
||||
if base.Metadata.SampleRate != "" {
|
||||
lines = append(lines, fmt.Sprintf(" Rate: %s", base.Metadata.SampleRate))
|
||||
}
|
||||
if base.Metadata.Channels != "" {
|
||||
lines = append(lines, fmt.Sprintf(" Channels: %s", base.Metadata.Channels))
|
||||
}
|
||||
|
||||
base.Kind = PreviewKindAudio
|
||||
base.Body = fmt.Sprintf("🎵 Audio File\n\n%s", strings.Join(lines, "\n"))
|
||||
base.PlainBody = fmt.Sprintf("Audio File\n\nDuration: %s\nBitrate: %s\nCodec: %s\nRate: %s\nChannels: %s",
|
||||
base.Metadata.Duration, base.Metadata.Bitrate, base.Metadata.AudioCodec,
|
||||
base.Metadata.SampleRate, base.Metadata.Channels)
|
||||
return base
|
||||
}
|
||||
```
|
||||
|
||||
### 7c. `buildVideoPreview()`
|
||||
|
||||
```go
|
||||
func buildVideoPreview(entry Entry, options PreviewOptions, base Preview) Preview {
|
||||
ffprobe := findTool("ffprobe")
|
||||
if ffprobe == "" {
|
||||
base.Kind = PreviewKindBinary
|
||||
base.Body = "Video file detected.\nInstall ffmpeg (ffprobe) for metadata preview."
|
||||
base.PlainBody = base.Body
|
||||
return base
|
||||
}
|
||||
|
||||
// Use ffprobe to extract metadata as JSON
|
||||
cmd := exec.Command(ffprobe,
|
||||
"-v", "quiet",
|
||||
"-print_format", "json",
|
||||
"-show_format",
|
||||
"-show_streams",
|
||||
entry.Path,
|
||||
)
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
base.Kind = PreviewKindError
|
||||
base.Body = fmt.Sprintf("ffprobe error:\n\n%s", err)
|
||||
base.PlainBody = base.Body
|
||||
return base
|
||||
}
|
||||
|
||||
var info struct {
|
||||
Format struct {
|
||||
Duration string `json:"duration"`
|
||||
Bitrate string `json:"bit_rate"`
|
||||
} `json:"format"`
|
||||
Streams []struct {
|
||||
CodecType string `json:"codec_type"`
|
||||
CodecName string `json:"codec_name"`
|
||||
Width int `json:"width"`
|
||||
Height int `json:"height"`
|
||||
} `json:"streams"`
|
||||
}
|
||||
if err := json.Unmarshal(out, &info); err != nil {
|
||||
base.Kind = PreviewKindError
|
||||
base.Body = fmt.Sprintf("Could not parse ffprobe output:\n\n%s", err)
|
||||
base.PlainBody = base.Body
|
||||
return base
|
||||
}
|
||||
|
||||
// Format duration
|
||||
if info.Format.Duration != "" {
|
||||
if secs, err := strconv.ParseFloat(info.Format.Duration, 64); err == nil {
|
||||
hrs := int(secs) / 3600
|
||||
mins := (int(secs) % 3600) / 60
|
||||
secsRem := int(secs) % 60
|
||||
if hrs > 0 {
|
||||
base.Metadata.Duration = fmt.Sprintf("%d:%02d:%02d", hrs, mins, secsRem)
|
||||
} else {
|
||||
base.Metadata.Duration = fmt.Sprintf("%d:%02d", mins, secsRem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if info.Format.Bitrate != "" {
|
||||
if bps, err := strconv.ParseInt(info.Format.Bitrate, 10, 64); err == nil {
|
||||
base.Metadata.Bitrate = fmt.Sprintf("%d kbps", bps/1000)
|
||||
}
|
||||
}
|
||||
|
||||
for _, stream := range info.Streams {
|
||||
switch stream.CodecType {
|
||||
case "video":
|
||||
base.Metadata.VideoCodec = stream.CodecName
|
||||
if stream.Width > 0 && stream.Height > 0 {
|
||||
base.Metadata.Dimensions = fmt.Sprintf("%dx%d", stream.Width, stream.Height)
|
||||
}
|
||||
case "audio":
|
||||
if base.Metadata.AudioCodec == "" {
|
||||
base.Metadata.AudioCodec = stream.CodecName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build a rich metadata body
|
||||
var lines []string
|
||||
lines = append(lines, fmt.Sprintf(" Duration: %s", base.Metadata.Duration))
|
||||
if base.Metadata.Bitrate != "" {
|
||||
lines = append(lines, fmt.Sprintf(" Bitrate: %s", base.Metadata.Bitrate))
|
||||
}
|
||||
if base.Metadata.VideoCodec != "" {
|
||||
lines = append(lines, fmt.Sprintf(" Video: %s", base.Metadata.VideoCodec))
|
||||
}
|
||||
if base.Metadata.Dimensions != "" {
|
||||
lines = append(lines, fmt.Sprintf(" Resolution: %s", base.Metadata.Dimensions))
|
||||
}
|
||||
if base.Metadata.AudioCodec != "" {
|
||||
lines = append(lines, fmt.Sprintf(" Audio: %s", base.Metadata.AudioCodec))
|
||||
}
|
||||
|
||||
base.Kind = PreviewKindVideo
|
||||
base.Body = fmt.Sprintf("🎬 Video File\n\n%s", strings.Join(lines, "\n"))
|
||||
base.PlainBody = fmt.Sprintf("Video File\n\nDuration: %s\nBitrate: %s\nVideo: %s\nResolution: %s\nAudio: %s",
|
||||
base.Metadata.Duration, base.Metadata.Bitrate, base.Metadata.VideoCodec,
|
||||
base.Metadata.Dimensions, base.Metadata.AudioCodec)
|
||||
return base
|
||||
}
|
||||
```
|
||||
|
||||
## 8. UI: Update `renderMetadata()`
|
||||
|
||||
File: [`internal/ui/model.go`](internal/ui/model.go) — `renderMetadata()` (line 2714)
|
||||
|
||||
Add new metadata fields to the right column after the image info block:
|
||||
|
||||
```go
|
||||
// After: if meta.ImageFormat != "" { ... }
|
||||
|
||||
if meta.Duration != "" {
|
||||
rightRows = append(rightRows, fmt.Sprintf("duration: %s", meta.Duration))
|
||||
}
|
||||
if meta.Bitrate != "" {
|
||||
rightRows = append(rightRows, fmt.Sprintf("bitrate: %s", meta.Bitrate))
|
||||
}
|
||||
if meta.AudioCodec != "" {
|
||||
rightRows = append(rightRows, fmt.Sprintf("audio: %s", meta.AudioCodec))
|
||||
}
|
||||
if meta.VideoCodec != "" {
|
||||
rightRows = append(rightRows, fmt.Sprintf("video: %s", meta.VideoCodec))
|
||||
}
|
||||
if meta.Dimensions != "" {
|
||||
rightRows = append(rightRows, fmt.Sprintf("resolution: %s", meta.Dimensions))
|
||||
}
|
||||
if meta.SampleRate != "" {
|
||||
rightRows = append(rightRows, fmt.Sprintf("rate: %s", meta.SampleRate))
|
||||
}
|
||||
if meta.Channels != "" {
|
||||
rightRows = append(rightRows, fmt.Sprintf("channels: %s", meta.Channels))
|
||||
}
|
||||
if meta.PageCount != "" {
|
||||
rightRows = append(rightRows, fmt.Sprintf("pages: %s", meta.PageCount))
|
||||
}
|
||||
```
|
||||
|
||||
## 9. UI: Update `previewIcon()`
|
||||
|
||||
File: [`internal/ui/model.go`](internal/ui/model.go) — `previewIcon()` (line 3545)
|
||||
|
||||
Add new Nerd Font icons:
|
||||
|
||||
```go
|
||||
case vfs.PreviewKindPDF:
|
||||
return "" // nf-oct-file-pdf
|
||||
case vfs.PreviewKindAudio:
|
||||
return "" // nf-custom-audio-file
|
||||
case vfs.PreviewKindVideo:
|
||||
return "" // nf-custom-video-file
|
||||
```
|
||||
|
||||
And ASCII fallback:
|
||||
```go
|
||||
case "pdf":
|
||||
return "[P]"
|
||||
case "audio":
|
||||
return "[A]"
|
||||
case "video":
|
||||
return "[V]"
|
||||
```
|
||||
|
||||
## 10. UI: Update `syncImageOverlay()`
|
||||
|
||||
File: [`internal/ui/model.go`](internal/ui/model.go) — `syncImageOverlay()` (line 4225)
|
||||
|
||||
The overlay should only show for `PreviewKindImage`, which it already checks. No change needed — video files should NOT use the image overlay since we don't have video thumbnail support yet.
|
||||
|
||||
## 11. Files Changed Summary
|
||||
|
||||
| File | Changes |
|
||||
|------|---------|
|
||||
| [`internal/fs/entry.go`](internal/fs/entry.go) | Add `pdfExtensions`, `audioExtensions`, `videoExtensions` maps; update `Category()` method |
|
||||
| [`internal/fs/preview.go`](internal/fs/preview.go) | Add `PreviewKindPDF/Audio/Video` constants; add fields to `Metadata`; add `findTool()`, `buildPDFPreview()`, `buildAudioPreview()`, `buildVideoPreview()`; modify `BuildPreview()` insertion point; add `"os/exec"`, `"encoding/json"` imports |
|
||||
| [`internal/ui/model.go`](internal/ui/model.go) | Update `renderMetadata()` with new fields; update `previewIcon()` with new icons |
|
||||
|
||||
## 12. Edge Cases & Considerations
|
||||
|
||||
- **Missing external tool**: If `pdftotext` or `ffprobe` is not installed, the user sees a helpful message telling them which package to install, and the preview falls back to `PreviewKindBinary` (same as before).
|
||||
- **Large PDFs**: Text extraction respects `MaxPreviewBytes` limit — truncated output is still highlighted.
|
||||
- **Corrupt files**: `ffprobe` and `pdftotext` handle errors gracefully; any non-zero exit returns `PreviewKindError` with the error message.
|
||||
- **Performance**: External processes are launched synchronously in the preview command (which already runs in a goroutine via `loadPreviewCmd()`), so the UI remains responsive.
|
||||
- **No new dependencies**: All tools are invoked via `os/exec` (stdlib). No Go modules needed.
|
||||
|
||||
## 13. Test Plan
|
||||
|
||||
1. Open `test.pdf` — verify text is extracted and syntax-highlighted, page count shown in metadata
|
||||
2. Open `test.mp3` — verify duration, bitrate, codec, sample rate, channels shown
|
||||
3. Open `test.flac` — same as above
|
||||
4. Open `test.mp4` — verify duration, bitrate, video codec, resolution, audio codec shown
|
||||
5. Open PDF/audio/video on a system without `pdftotext`/`ffprobe` — verify fallback message
|
||||
6. Verify `go build ./...` and `go vet ./...` pass
|
||||
44
src/vcom-0.2.5/plans/feature-roadmap.md
Normal file
44
src/vcom-0.2.5/plans/feature-roadmap.md
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
# Feature Roadmap — vcom
|
||||
|
||||
## Выбранные фичи (по приоритету)
|
||||
|
||||
### 1. Поиск/фильтрация файлов (`/`)
|
||||
- [ ] **Filter mode**: при нажатии `/` открывается текстовый инпут внизу экрана (поверх footer, как модальное окно)
|
||||
- [ ] Фильтрация `[]Entry` в активной панели на лету по `strings.Contains`/fuzzy-match
|
||||
- [ ] Подсветка совпадений в строках (изменить `renderEntryRow` — передать query, подсветить matched part)
|
||||
- [ ] `Esc` — выход из filter mode, восстановление полного списка
|
||||
- [ ] `Enter` — зафиксировать фильтр (оставить отфильтрованный список), выход из filter mode
|
||||
- [ ] При смене директории фильтр сбрасывается
|
||||
|
||||
### 2. Bulk rename (массовое переименование)
|
||||
- [ ] Выделить файлы (`Shift+↑/↓`), нажать `Ctrl+R` (новая клавиша)
|
||||
- [ ] Модальное окно с текстовым полем для паттерна: `prefix_%N.ext`
|
||||
- [ ] Превью результата (старое имя → новое имя)
|
||||
- [ ] Выполнить rename для всех выделенных
|
||||
|
||||
### 3. Корзина (trash support)
|
||||
- [ ] `Delete/F8` — перемещать в `~/.local/share/Trash/` по freedesktop spec
|
||||
- [ ] `Shift+Delete` —永久ное удаление (как сейчас)
|
||||
- [ ] `browser.confirm_delete` применяется к永久ному удалению
|
||||
- [ ] Новая опция конфига: `behavior.use_trash = true` (default: true)
|
||||
|
||||
### 4. Directory history (назад/вперед)
|
||||
- [ ] `[]string` стек истории на каждую панель
|
||||
- [ ] `Alt+←` / `Alt+→` — навигация назад/вперед
|
||||
- [ ] При переходе в новую директорию (Enter, Backspace, клик) — push в history
|
||||
- [ ] При навигации по истории — не создавать новые записи
|
||||
|
||||
### 5. Расширенный превью форматов
|
||||
- [ ] PDF — извлечение текста через `pdftotext` (если доступен)
|
||||
- [ ] Аудио — метаданные через `ffprobe` (битрейт, длительность, кодек)
|
||||
- [ ] Видео — метаданные + превью через `ffmpegthumbnailer`/`ffprobe`
|
||||
- [ ] Fallback если утилита не установлена
|
||||
|
||||
---
|
||||
|
||||
## Процесс
|
||||
|
||||
1. Каждая фича реализуется в отдельной ветке `feature/N-имя`
|
||||
2. После реализации — commit + push
|
||||
3. После апрува — merge в main
|
||||
4. Порядок: 1 → 4 → 3 → 2 → 5 (от простого к сложному)
|
||||
88
src/vcom-0.2.5/plans/mirror-and-cursor-memory.md
Normal file
88
src/vcom-0.2.5/plans/mirror-and-cursor-memory.md
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
# Feature Plan: Mirror Pane & Per-Directory Cursor Memory
|
||||
|
||||
## Feature 1: Mirror active pane directory to opposite pane
|
||||
|
||||
### Keybinding research
|
||||
- **Midnight Commander (MC)**: `Alt-i` — makes the other panel equal to current directory
|
||||
- **Total Commander**: `Ctrl-PgDn` opens directory under cursor in opposite panel
|
||||
- **Far Manager**: `Ctrl-Left/Right` — opens in opposite panel
|
||||
|
||||
**Recommendation**: Bind to `p` (for "Pane"). `Ctrl-i` sends the same byte as `Tab` (ASCII 0x09) so it conflicts with Switch. `p` is free, accessible, and "Pane" reflects the meaning.
|
||||
|
||||
### How it works
|
||||
1. Read active pane's current path (including remote mount state)
|
||||
2. Apply that path to the passive (opposite) pane
|
||||
3. If active is in a remote mount, also clone the remote mount stack to the passive pane
|
||||
4. Reload both panes to reflect the change
|
||||
|
||||
### Code changes
|
||||
|
||||
#### [`internal/ui/keymap.go`](internal/ui/keymap.go)
|
||||
- Add `Mirror` key binding field to `KeyMap` struct
|
||||
- Bind to `"p"` with help text `"p"` / `"mirror pane"`
|
||||
|
||||
#### [`internal/ui/model.go`](internal/ui/model.go)
|
||||
- Add handler `handleMirrorPane()`:
|
||||
1. Get active pane path and remote mount state
|
||||
2. Switch passive pane to match (copy remote stack if applicable)
|
||||
3. Reload passive pane (using `reloadPane` or `reloadRemotePane`)
|
||||
4. Set status: `"Mirrored: <path>"`
|
||||
- Add key match case before the `default` switch
|
||||
|
||||
---
|
||||
|
||||
## Feature 2: Per-directory cursor position memory (session scope)
|
||||
|
||||
### Current state
|
||||
- `enterSelected()` saves `pane.Path` to history for back-nav, then reloads target dir with `selected.Name` as preserve key — but `selected.Name` is the name of the directory being entered, not a cursor anchor
|
||||
- `goParent()` pops history and reloads parent using the child directory name as preserve — cursor lands on the directory we came from
|
||||
- **Missing**: When navigating back into a previously-visited directory, there's no saved cursor position for it
|
||||
|
||||
### Design
|
||||
Add a `cursorMemory` field to `BrowserPane` — a `map[string]string` mapping **directory path → last selected entry display name**.
|
||||
|
||||
This integrates cleanly with the existing `SetEntries(entries, preserveKey)` / `FindSelected()` infrastructure.
|
||||
|
||||
### Flow
|
||||
|
||||
```
|
||||
enterSelected() / enterRemoteDir():
|
||||
1. Save to cursorMemory: pane.Path → selected entry's Name
|
||||
2. Push history (existing)
|
||||
3. Set path, reload (existing)
|
||||
4. The reload already uses preserve=selected.Name for the new dir,
|
||||
but cursorMemory will help when coming back later
|
||||
|
||||
reloadPane() / reloadRemotePane():
|
||||
1. Try preserve key first (existing behavior)
|
||||
2. If preserve is empty, check cursorMemory[pane.Path]
|
||||
and use that as preserveKey instead
|
||||
```
|
||||
|
||||
### Code changes
|
||||
|
||||
#### [`internal/ui/pane.go`](internal/ui/pane.go)
|
||||
- Add field to `BrowserPane`: `CursorMemory map[string]string`
|
||||
- Method `SaveCursor(path string, name string)` — stores cursor position
|
||||
- Method `LoadCursor(path string) string` — retrieves saved cursor
|
||||
|
||||
#### [`internal/ui/model.go`](internal/ui/model.go)
|
||||
- In `enterSelected()` (line ~1886): after `pane.PushHistory(pane.Path)`, add `pane.SaveCursor(pane.Path, selected.Name)`
|
||||
- In `enterRemoteDir()` (line ~5726): same save logic
|
||||
- In `goParent()`: save cursor before navigating away (already partially done via history)
|
||||
- In `reloadPane()` (line ~1672): after existing preserve logic, if no preserve key and directory has cursor memory, use it
|
||||
- In `reloadRemotePane()` (line ~5535): same fallback
|
||||
|
||||
---
|
||||
|
||||
## Files modified
|
||||
| File | Changes |
|
||||
|------|---------|
|
||||
| `internal/ui/keymap.go` | Add `Mirror` field, binding `p` |
|
||||
| `internal/ui/pane.go` | Add `CursorMemory` field + methods |
|
||||
| `internal/ui/model.go` | Add `handleMirrorPane()`, save/restore cursor in navigation methods |
|
||||
|
||||
## Not changed
|
||||
- `internal/fs/` — storage layer unaffected
|
||||
- `internal/config/` — no config changes needed
|
||||
- `internal/theme/` — no theme changes needed
|
||||
193
src/vcom-0.2.5/plans/theme-selector-dialog.md
Normal file
193
src/vcom-0.2.5/plans/theme-selector-dialog.md
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
# Plan: Theme Selector Dialog with Live Preview
|
||||
|
||||
## Summary
|
||||
Replace the current `cycleTheme()` (simple cycle through themes on `t`) with a modal dialog that shows all themes, their base colors, supports live preview via Up/Down navigation, and commit/revert on Enter/Esc.
|
||||
|
||||
## Changes
|
||||
|
||||
### 1. `internal/ui/model.go` — New modal kind
|
||||
|
||||
Add to `modalKind` const (around line 34):
|
||||
```go
|
||||
modalThemeSelect
|
||||
```
|
||||
|
||||
### 2. `internal/ui/model.go` — New theme selector state
|
||||
|
||||
Add new struct and field to `Model`:
|
||||
|
||||
```go
|
||||
type themeSelectorState struct {
|
||||
names []string // all theme names in order
|
||||
cursor int // current cursor index in the list
|
||||
original string // the theme name before opening dialog (for Esc revert)
|
||||
}
|
||||
|
||||
type Model struct {
|
||||
// ... existing fields ...
|
||||
themeSelector *themeSelectorState // nil when not in theme selector dialog
|
||||
}
|
||||
```
|
||||
|
||||
### 3. `internal/ui/model.go` — New handler `openThemeSelector()`
|
||||
|
||||
Replace the `cycleTheme()` call with `openThemeSelector()`:
|
||||
|
||||
1. Read current theme name from `m.cfg.UI.Theme`
|
||||
2. Get all theme names via `theme.Names()`
|
||||
3. Find current theme index in the list
|
||||
4. Create `themeSelectorState` with names, cursor=current index, original=current theme
|
||||
5. Set `m.modal.kind = modalThemeSelect`
|
||||
6. Set modal title/body (body may be empty since list is rendered in `renderThemeSelectModal`)
|
||||
|
||||
The `t` key match (`case key.Matches(msg, m.keys.CycleTheme)`) now calls `m.openThemeSelector()` instead of `m.cycleTheme()`.
|
||||
|
||||
### 4. `internal/ui/model.go` — Modal key handling
|
||||
|
||||
Add a new case in `handleModalKey()` for `modalThemeSelect`:
|
||||
|
||||
```go
|
||||
case modalThemeSelect:
|
||||
switch {
|
||||
case msg.String() == "up" || msg.String() == "k":
|
||||
// Move cursor up, clamp to 0, apply theme preview
|
||||
m.themeSelector.cursor--
|
||||
if m.themeSelector.cursor < 0 { m.themeSelector.cursor = 0 }
|
||||
m.applyThemePreview(m.themeSelector.names[m.themeSelector.cursor])
|
||||
return m, nil
|
||||
case msg.String() == "down" || msg.String() == "j":
|
||||
// Move cursor down, clamp to len-1, apply theme preview
|
||||
m.themeSelector.cursor++
|
||||
if m.themeSelector.cursor >= len(m.themeSelector.names) {
|
||||
m.themeSelector.cursor = len(m.themeSelector.names) - 1
|
||||
}
|
||||
m.applyThemePreview(m.themeSelector.names[m.themeSelector.cursor])
|
||||
return m, nil
|
||||
case key.Matches(msg, m.keys.Confirm): // Enter
|
||||
// Apply selected theme and save
|
||||
selected := m.themeSelector.names[m.themeSelector.cursor]
|
||||
m.finalizeTheme(selected)
|
||||
m.themeSelector = nil
|
||||
m.modal = modalState{}
|
||||
m.status = fmt.Sprintf("Theme: %s", selected)
|
||||
return m, nil
|
||||
case msg.String() == "esc":
|
||||
// Revert to original theme
|
||||
m.applyThemePreview(m.themeSelector.original)
|
||||
m.themeSelector = nil
|
||||
m.modal = modalState{}
|
||||
m.status = "Theme unchanged"
|
||||
return m, nil
|
||||
}
|
||||
```
|
||||
|
||||
### 5. `internal/ui/model.go` — applyThemePreview helper
|
||||
|
||||
A new method that applies a theme palette to `m.palette` **without saving to config**:
|
||||
|
||||
```go
|
||||
func (m *Model) applyThemePreview(name string) {
|
||||
palette, err := theme.Resolve(name)
|
||||
if err != nil {
|
||||
return // silently ignore resolve errors during preview
|
||||
}
|
||||
m.palette = palette
|
||||
// Don't update m.cfg.UI.Theme or save — that's only on Enter
|
||||
}
|
||||
```
|
||||
|
||||
### 6. `internal/ui/model.go` — finalizeTheme helper
|
||||
|
||||
A new method that applies the theme AND saves to config:
|
||||
|
||||
```go
|
||||
func (m *Model) finalizeTheme(name string) {
|
||||
palette, err := theme.Resolve(name)
|
||||
if err != nil {
|
||||
m.status = err.Error()
|
||||
return
|
||||
}
|
||||
m.cfg.UI.Theme = name
|
||||
m.palette = palette
|
||||
savedPath, saveErr := config.Save(m.cfg, m.configPath)
|
||||
if saveErr != nil {
|
||||
m.status = fmt.Sprintf("Theme: %s (save failed: %v)", name, saveErr)
|
||||
return
|
||||
}
|
||||
m.configPath = savedPath
|
||||
}
|
||||
```
|
||||
|
||||
### 7. `internal/ui/model.go` — renderThemeSelectModal
|
||||
|
||||
Create a new render function `renderThemeSelectModal()`:
|
||||
|
||||
```go
|
||||
func renderThemeSelectModal(m Model, palette theme.Palette, width int) string {
|
||||
outerWidth := max(width, 8)
|
||||
contentWidth := max(outerWidth-6, 1)
|
||||
|
||||
// Styles
|
||||
titleStyle := lipgloss.NewStyle()...
|
||||
box := lipgloss.NewStyle()...
|
||||
|
||||
// Build theme list rows
|
||||
lines := []string{titleStyle.Render("Select Theme"), spacer}
|
||||
lines = append(lines, instructions)
|
||||
lines = append(lines, spacer)
|
||||
|
||||
for i, name := range m.themeSelector.names {
|
||||
resolved, err := theme.Resolve(name)
|
||||
// skip if error
|
||||
|
||||
// Selection indicator + theme name
|
||||
// Color swatches: Background, Panel, Accent, Text, Selection
|
||||
// Highlight current item
|
||||
}
|
||||
|
||||
return box.Render(strings.Join(lines, "\n"))
|
||||
}
|
||||
```
|
||||
|
||||
Each row shows:
|
||||
- Cursor indicator (`▸` or ` `)
|
||||
- Theme name
|
||||
- Color swatches as small colored blocks: Background, Panel, Accent, Text, Selection
|
||||
- Currently selected item is highlighted with `Selection` background color
|
||||
|
||||
### 8. `internal/ui/model.go` — Update View dispatch
|
||||
|
||||
Add a new condition in `renderModal()` (around line 3698) to dispatch to `renderThemeSelectModal` when `modalKind == modalThemeSelect`:
|
||||
|
||||
```go
|
||||
if m.modal.kind == modalThemeSelect {
|
||||
return renderThemeSelectModal(m, palette, width)
|
||||
}
|
||||
```
|
||||
|
||||
### 9. `internal/ui/model.go` — Remove old cycleTheme (optional)
|
||||
|
||||
The old `cycleTheme()` method can be kept for backward compatibility or removed. The `t` key will now open the dialog instead.
|
||||
|
||||
### 10. Help dialog update (optional)
|
||||
|
||||
Update the help dialog to reflect the new behavior: `"t open theme selector"` instead of `"t cycle theme"`.
|
||||
|
||||
## Files modified
|
||||
| File | Changes |
|
||||
|------|---------|
|
||||
| `internal/ui/model.go` | Add `modalThemeSelect` kind, `themeSelectorState` struct, `openThemeSelector()`, `applyThemePreview()`, `finalizeTheme()`, key handling in `handleModalKey`, `renderThemeSelectModal()`, update `renderModal()` dispatch |
|
||||
| (none else) | Theme data, config saving, keymap — all remain unchanged |
|
||||
|
||||
## Key design points
|
||||
1. **Live preview**: Every Up/Down key press instantly resolves the new theme palette and applies it to `m.palette`. The user sees the full UI change in real time.
|
||||
2. **Safe revert**: On Esc, the original theme (saved in `themeSelectorState.original`) is restored.
|
||||
3. **Config save only on Enter**: Only `finalizeTheme()` persists to config file.
|
||||
4. **Color swatches**: Each theme row shows 5 small colored blocks (Background, Panel, Accent, Text, Selection) so the user can visually compare themes.
|
||||
5. **Clean state management**: `themeSelector` is `nil` when the dialog is closed, making it easy to check state.
|
||||
|
||||
## Not changed
|
||||
- `internal/theme/` — storage layer unchanged
|
||||
- `internal/config/` — no config changes
|
||||
- `internal/ui/keymap.go` — `t` binding unchanged, only behavior changes
|
||||
- `internal/ui/pane.go` — no changes
|
||||
37
src/vcom-0.2.5/scripts/build-deb.sh
Executable file
37
src/vcom-0.2.5/scripts/build-deb.sh
Executable file
|
|
@ -0,0 +1,37 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
if [[ $# -ne 1 ]]; then
|
||||
echo "usage: $0 <version>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
version="$1"
|
||||
pkgroot="target/debian/pkgroot"
|
||||
outdir="target/debian"
|
||||
|
||||
rm -rf "$pkgroot"
|
||||
mkdir -p \
|
||||
"$pkgroot/DEBIAN" \
|
||||
"$pkgroot/usr/bin" \
|
||||
"$pkgroot/usr/share/doc/vcom" \
|
||||
"$pkgroot/usr/share/licenses/vcom"
|
||||
|
||||
install -Dm755 "target/release/vcom" "$pkgroot/usr/bin/vcom"
|
||||
install -Dm644 "README.md" "$pkgroot/usr/share/doc/vcom/README.md"
|
||||
install -Dm644 "vcom.toml" "$pkgroot/usr/share/doc/vcom/vcom.toml"
|
||||
install -Dm644 "LICENSE" "$pkgroot/usr/share/licenses/vcom/LICENSE"
|
||||
|
||||
cat > "$pkgroot/DEBIAN/control" <<EOF
|
||||
Package: vcom
|
||||
Version: ${version}
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Architecture: amd64
|
||||
Maintainer: Roman Vrubel <roman@vrubel.dev>
|
||||
Depends: ueberzug | ueberzugpp
|
||||
Description: Terminal file manager inspired by Midnight Commander
|
||||
A two-pane terminal file manager with inspect mode and text previews.
|
||||
EOF
|
||||
|
||||
dpkg-deb --build "$pkgroot" "$outdir/vcom_${version}_amd64.deb"
|
||||
45
src/vcom-0.2.5/vcom.toml
Normal file
45
src/vcom-0.2.5/vcom.toml
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
[startup]
|
||||
left_path = ''
|
||||
right_path = ''
|
||||
|
||||
[ui]
|
||||
app_title = 'vcom'
|
||||
theme = 'github-dark'
|
||||
icon_mode = 'auto'
|
||||
show_title_bar = true
|
||||
show_footer = true
|
||||
border = 'rounded'
|
||||
path_display = 'smart'
|
||||
pane_gap = 1
|
||||
center_width_percent = 30
|
||||
|
||||
[browser]
|
||||
show_hidden = true
|
||||
dirs_first = true
|
||||
human_readable_size = true
|
||||
|
||||
[browser.sort]
|
||||
by = 'name'
|
||||
reverse = false
|
||||
|
||||
[browser.columns]
|
||||
name = true
|
||||
size = true
|
||||
modified = true
|
||||
created = false
|
||||
permissions = false
|
||||
extension = false
|
||||
|
||||
[preview]
|
||||
show_metadata = true
|
||||
wrap_text = false
|
||||
max_preview_bytes = 65536
|
||||
directory_preview_limit = 80
|
||||
|
||||
[behavior]
|
||||
confirm_delete = true
|
||||
confirm_overwrite = true
|
||||
calculate_dir_size_on_space = true
|
||||
follow_symlinks = false
|
||||
auto_refresh = true
|
||||
auto_refresh_interval = 5
|
||||
17
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/.editorconfig
generated
vendored
Normal file
17
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/.editorconfig
generated
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = tab
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.xml]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = false
|
||||
|
||||
[*.yml]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
28
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/.gitignore
generated
vendored
Normal file
28
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/.gitignore
generated
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# Binaries for programs and plugins
|
||||
.git
|
||||
.idea
|
||||
.vscode
|
||||
.hermit
|
||||
*.exe
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
/cmd/chroma/chroma
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
|
||||
.glide/
|
||||
|
||||
_models/
|
||||
|
||||
_examples/
|
||||
*.min.*
|
||||
build/
|
||||
|
||||
cmd/chromad/static/chroma.wasm
|
||||
cmd/chromad/static/wasm_exec.js
|
||||
89
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/.golangci.yml
generated
vendored
Normal file
89
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/.golangci.yml
generated
vendored
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
run:
|
||||
tests: true
|
||||
|
||||
output:
|
||||
print-issued-lines: false
|
||||
|
||||
linters:
|
||||
enable-all: true
|
||||
disable:
|
||||
- lll
|
||||
- gocyclo
|
||||
- dupl
|
||||
- gochecknoglobals
|
||||
- funlen
|
||||
- godox
|
||||
- wsl
|
||||
- gocognit
|
||||
- nolintlint
|
||||
- testpackage
|
||||
- godot
|
||||
- nestif
|
||||
- paralleltest
|
||||
- nlreturn
|
||||
- cyclop
|
||||
- gci
|
||||
- gofumpt
|
||||
- errorlint
|
||||
- exhaustive
|
||||
- wrapcheck
|
||||
- stylecheck
|
||||
- thelper
|
||||
- nonamedreturns
|
||||
- revive
|
||||
- dupword
|
||||
- exhaustruct
|
||||
- varnamelen
|
||||
- forcetypeassert
|
||||
- ireturn
|
||||
- maintidx
|
||||
- govet
|
||||
- testableexamples
|
||||
- musttag
|
||||
- depguard
|
||||
- goconst
|
||||
- perfsprint
|
||||
- mnd
|
||||
- predeclared
|
||||
- recvcheck
|
||||
- tenv
|
||||
- err113
|
||||
|
||||
linters-settings:
|
||||
gocyclo:
|
||||
min-complexity: 10
|
||||
dupl:
|
||||
threshold: 100
|
||||
goconst:
|
||||
min-len: 8
|
||||
min-occurrences: 3
|
||||
forbidigo:
|
||||
#forbid:
|
||||
# - (Must)?NewLexer$
|
||||
exclude_godoc_examples: false
|
||||
|
||||
|
||||
issues:
|
||||
exclude-dirs:
|
||||
- _examples
|
||||
max-per-linter: 0
|
||||
max-same: 0
|
||||
exclude-use-default: false
|
||||
exclude:
|
||||
# Captured by errcheck.
|
||||
- '^(G104|G204):'
|
||||
# Very commonly not checked.
|
||||
- 'Error return value of .(.*\.Help|.*\.MarkFlagRequired|(os\.)?std(out|err)\..*|.*Close|.*Flush|os\.Remove(All)?|.*printf?|os\.(Un)?Setenv). is not checked'
|
||||
- 'exported method (.*\.MarshalJSON|.*\.UnmarshalJSON|.*\.EntityURN|.*\.GoString|.*\.Pos) should have comment or be unexported'
|
||||
- 'composite literal uses unkeyed fields'
|
||||
- 'declaration of "err" shadows declaration'
|
||||
- 'should not use dot imports'
|
||||
- 'Potential file inclusion via variable'
|
||||
- 'should have comment or be unexported'
|
||||
- 'comment on exported var .* should be of the form'
|
||||
- 'at least one file in a package should have a package comment'
|
||||
- 'string literal contains the Unicode'
|
||||
- 'methods on the same type should have the same receiver name'
|
||||
- '_TokenType_name should be _TokenTypeName'
|
||||
- '`_TokenType_map` should be `_TokenTypeMap`'
|
||||
- 'rewrite if-else to switch statement'
|
||||
34
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/.goreleaser.yml
generated
vendored
Normal file
34
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/.goreleaser.yml
generated
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
project_name: chroma
|
||||
release:
|
||||
github:
|
||||
owner: alecthomas
|
||||
name: chroma
|
||||
brews:
|
||||
- install: bin.install "chroma"
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
builds:
|
||||
- goos:
|
||||
- linux
|
||||
- darwin
|
||||
- windows
|
||||
goarch:
|
||||
- arm64
|
||||
- amd64
|
||||
- "386"
|
||||
goarm:
|
||||
- "6"
|
||||
dir: ./cmd/chroma
|
||||
main: .
|
||||
ldflags: -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}
|
||||
binary: chroma
|
||||
archives:
|
||||
- format: tar.gz
|
||||
name_template: "{{ .Binary }}-{{ .Version }}-{{ .Os }}-{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
|
||||
files:
|
||||
- COPYING
|
||||
- README*
|
||||
snapshot:
|
||||
name_template: SNAPSHOT-{{ .Commit }}
|
||||
checksum:
|
||||
name_template: "{{ .ProjectName }}-{{ .Version }}-checksums.txt"
|
||||
11
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/AGENTS.md
generated
vendored
Normal file
11
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/AGENTS.md
generated
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
Chroma is a syntax highlighting library, tool and web playground for Go. It is based on Pygments and includes importers for it, so most of the same concepts from Pygments apply to Chroma.
|
||||
|
||||
This project is written in Go, uses Hermit to manage tooling, and Just for helper commands. Helper scripts are in ./scripts.
|
||||
|
||||
Language definitions are XML files defined in ./lexers/embedded/*.xml.
|
||||
|
||||
Styles/themes are defined in ./styles/*.xml.
|
||||
|
||||
The CLI can be run with `chroma`.
|
||||
|
||||
The web playground can be run with `chromad --csrf-key=moo`. It blocks, so should generally be run in the background. It also does not hot reload, so has to be manually restarted. The playground has two modes - for local development it uses the server itself to render, while for production running `just chromad` will compile ./cmd/libchromawasm into a WASM module that is bundled into `chromad`.
|
||||
24
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/Bitfile
generated
vendored
Normal file
24
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/Bitfile
generated
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
VERSION = %(git describe --tags --dirty --always)%
|
||||
export CGOENABLED = 0
|
||||
|
||||
tokentype_enumer.go: types.go
|
||||
build: go generate
|
||||
|
||||
# Regenerate the list of lexers in the README
|
||||
README.md: lexers/*.go lexers/*/*.xml table.py
|
||||
build: ./table.py
|
||||
-clean
|
||||
|
||||
implicit %{1}%{2}.min.%{3}: **/*.{css,js}
|
||||
build: esbuild --bundle %{IN} --minify --outfile=%{OUT}
|
||||
|
||||
implicit build/%{1}: cmd/*
|
||||
cd cmd/%{1}
|
||||
inputs: cmd/%{1}/**/* **/*.go
|
||||
build: go build -ldflags="-X 'main.version=%{VERSION}'" -o ../../build/%{1} .
|
||||
|
||||
#upload: chromad
|
||||
# build:
|
||||
# scp chromad root@swapoff.org:
|
||||
# ssh root@swapoff.org 'install -m755 ./chromad /srv/http/swapoff.org/bin && service chromad restart'
|
||||
# touch upload
|
||||
19
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/COPYING
generated
vendored
Normal file
19
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/COPYING
generated
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
Copyright (C) 2017 Alec Thomas
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
64
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/Dockerfile
generated
vendored
Normal file
64
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/Dockerfile
generated
vendored
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
# Multi-stage Dockerfile for chromad Go application using Hermit-managed tools
|
||||
|
||||
# Build stage
|
||||
FROM ubuntu:24.04 AS builder
|
||||
|
||||
# Install system dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
curl \
|
||||
git \
|
||||
ca-certificates \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy the entire project (including bin directory with Hermit tools)
|
||||
COPY . .
|
||||
|
||||
# Make Hermit tools executable and add to PATH
|
||||
ENV PATH="/app/bin:${PATH}"
|
||||
|
||||
# Set Go environment variables for static compilation
|
||||
ENV CGO_ENABLED=0
|
||||
ENV GOOS=linux
|
||||
ENV GOARCH=amd64
|
||||
|
||||
# Build the application using just
|
||||
RUN just chromad
|
||||
|
||||
# Runtime stage
|
||||
FROM alpine:3.23 AS runtime
|
||||
|
||||
# Install ca-certificates for HTTPS requests
|
||||
RUN apk --no-cache add ca-certificates curl
|
||||
|
||||
# Create a non-root user
|
||||
RUN addgroup -g 1001 chromad && \
|
||||
adduser -D -s /bin/sh -u 1001 -G chromad chromad
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy the binary from build stage
|
||||
COPY --from=builder /app/build/chromad /app/chromad
|
||||
|
||||
# Change ownership to non-root user
|
||||
RUN chown chromad:chromad /app/chromad
|
||||
|
||||
# Switch to non-root user
|
||||
USER chromad
|
||||
|
||||
# Expose port (default is 8080, but can be overridden via PORT env var)
|
||||
EXPOSE 8080
|
||||
|
||||
# Set default environment variables
|
||||
ENV PORT=8080
|
||||
ENV CHROMA_CSRF_KEY="testtest"
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD curl -fsSL http://127.0.0.1:8080/ > /dev/null
|
||||
|
||||
# Run the application
|
||||
CMD ["sh", "-c", "./chromad --csrf-key=$CHROMA_CSRF_KEY --bind=0.0.0.0:$PORT"]
|
||||
55
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/Justfile
generated
vendored
Normal file
55
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/Justfile
generated
vendored
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
set positional-arguments := true
|
||||
set shell := ["bash", "-c"]
|
||||
|
||||
version := `git describe --tags --dirty --always`
|
||||
export GOOS := env("GOOS", "linux")
|
||||
export GOARCH := env("GOARCH", "amd64")
|
||||
|
||||
_help:
|
||||
@just -l
|
||||
|
||||
# Generate README.md from lexer definitions
|
||||
readme:
|
||||
#!/usr/bin/env bash
|
||||
GOOS= GOARCH= ./table.py
|
||||
|
||||
# Generate tokentype_string.go
|
||||
tokentype-string:
|
||||
go generate
|
||||
|
||||
# Format JavaScript files
|
||||
format-js:
|
||||
biome format --write cmd/chromad/static/index.js cmd/chromad/static/chroma.js
|
||||
|
||||
# Build chromad binary
|
||||
chromad: wasm-exec chroma-wasm
|
||||
#!/usr/bin/env bash
|
||||
rm -rf build
|
||||
mk cmd/chromad/static/index.min.js : cmd/chromad/static/{index,chroma}.js -- \
|
||||
esbuild --platform=browser --format=esm --bundle cmd/chromad/static/index.js --minify --external:./wasm_exec.js --outfile=cmd/chromad/static/index.min.js
|
||||
mk cmd/chromad/static/index.min.css : cmd/chromad/static/index.css -- \
|
||||
esbuild --bundle cmd/chromad/static/index.css --minify --outfile=cmd/chromad/static/index.min.css
|
||||
cd cmd/chromad && CGOENABLED=0 go build -ldflags="-X 'main.version={{ version }}'" -o ../../build/chromad .
|
||||
|
||||
# Copy wasm_exec.js from TinyGo
|
||||
wasm-exec:
|
||||
#!/usr/bin/env bash
|
||||
tinygoroot=$(tinygo env TINYGOROOT)
|
||||
mk cmd/chromad/static/wasm_exec.js : "$tinygoroot/targets/wasm_exec.js" -- \
|
||||
install -m644 "$tinygoroot/targets/wasm_exec.js" cmd/chromad/static/wasm_exec.js
|
||||
|
||||
# Build WASM binary
|
||||
chroma-wasm:
|
||||
#!/usr/bin/env bash
|
||||
if type tinygo > /dev/null 2>&1; then
|
||||
mk cmd/chromad/static/chroma.wasm : cmd/libchromawasm/main.go -- \
|
||||
tinygo build -no-debug -target wasm -o cmd/chromad/static/chroma.wasm cmd/libchromawasm/main.go
|
||||
else
|
||||
mk cmd/chromad/static/chroma.wasm : cmd/libchromawasm/main.go -- \
|
||||
GOOS=js GOARCH=wasm go build -o cmd/chromad/static/chroma.wasm cmd/libchromawasm/main.go
|
||||
fi
|
||||
|
||||
# Upload chromad to server
|
||||
upload: chromad
|
||||
scp build/chromad root@swapoff.org:
|
||||
ssh root@swapoff.org 'install -m755 ./chromad /srv/http/swapoff.org/bin && service chromad restart'
|
||||
307
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/README.md
generated
vendored
Normal file
307
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,307 @@
|
|||

|
||||
|
||||
# A general purpose syntax highlighter in pure Go
|
||||
|
||||
[](https://pkg.go.dev/github.com/alecthomas/chroma/v2) [](https://github.com/alecthomas/chroma/actions/workflows/ci.yml) [](https://invite.slack.golangbridge.org/)
|
||||
|
||||
|
||||
Chroma takes source code and other structured text and converts it into syntax
|
||||
highlighted HTML, ANSI-coloured text, etc.
|
||||
|
||||
Chroma is based heavily on [Pygments](http://pygments.org/), and includes
|
||||
translators for Pygments lexers and styles.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
<!-- TOC -->
|
||||
|
||||
1. [Supported languages](#supported-languages)
|
||||
2. [Try it](#try-it)
|
||||
3. [Using the library](#using-the-library)
|
||||
1. [Quick start](#quick-start)
|
||||
2. [Identifying the language](#identifying-the-language)
|
||||
3. [Formatting the output](#formatting-the-output)
|
||||
4. [The HTML formatter](#the-html-formatter)
|
||||
4. [More detail](#more-detail)
|
||||
1. [Lexers](#lexers)
|
||||
2. [Formatters](#formatters)
|
||||
3. [Styles](#styles)
|
||||
5. [Command-line interface](#command-line-interface)
|
||||
6. [Testing lexers](#testing-lexers)
|
||||
7. [What's missing compared to Pygments?](#whats-missing-compared-to-pygments)
|
||||
|
||||
<!-- /TOC -->
|
||||
|
||||
## Supported languages
|
||||
|
||||
| Prefix | Language
|
||||
| :----: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
| A | ABAP, ABNF, ActionScript, ActionScript 3, Ada, Agda, AL, Alloy, Angular2, ANTLR, ApacheConf, APL, AppleScript, ArangoDB AQL, Arduino, ArmAsm, ATL, AutoHotkey, AutoIt, Awk
|
||||
| B | Ballerina, Bash, Bash Session, Batchfile, Beef, BibTeX, Bicep, BlitzBasic, BNF, BQN, Brainfuck
|
||||
| C | C, C#, C++, C3, Caddyfile, Caddyfile Directives, Cap'n Proto, Cassandra CQL, Ceylon, CFEngine3, cfstatement, ChaiScript, Chapel, Cheetah, Clojure, CMake, COBOL, CoffeeScript, Common Lisp, Coq, Core, Crystal, CSS, CSV, CUE, Cython
|
||||
| D | D, Dart, Dax, Desktop file, Diff, Django/Jinja, dns, Docker, DTD, Dylan
|
||||
| E | EBNF, Elixir, Elm, EmacsLisp, Erlang
|
||||
| F | Factor, Fennel, Fish, Forth, Fortran, FortranFixed, FSharp
|
||||
| G | GAS, GDScript, GDScript3, Gemtext, Genshi, Genshi HTML, Genshi Text, Gherkin, Gleam, GLSL, Gnuplot, Go, Go HTML Template, Go Template, Go Text Template, GraphQL, Groff, Groovy
|
||||
| H | Handlebars, Hare, Haskell, Haxe, HCL, Hexdump, HLB, HLSL, HolyC, HTML, HTTP, Hy
|
||||
| I | Idris, Igor, INI, Io, ISCdhcpd
|
||||
| J | J, Janet, Java, JavaScript, JSON, JSONata, Jsonnet, Julia, Jungle
|
||||
| K | Kakoune, Kotlin
|
||||
| L | Lean4, Lighttpd configuration file, LLVM, lox, Lua
|
||||
| M | Makefile, Mako, markdown, Markless, Mason, Materialize SQL dialect, Mathematica, Matlab, MCFunction, Meson, Metal, MiniZinc, MLIR, Modelica, Modula-2, Mojo, MonkeyC, MoonScript, MorrowindScript, Myghty, MySQL
|
||||
| N | NASM, Natural, NDISASM, Newspeak, Nginx configuration file, Nim, Nix, NSIS, Nu
|
||||
| O | Objective-C, ObjectPascal, OCaml, Octave, Odin, OnesEnterprise, OpenEdge ABL, OpenSCAD, Org Mode
|
||||
| P | PacmanConf, Perl, PHP, PHTML, Pig, PkgConfig, PL/pgSQL, plaintext, Plutus Core, Pony, PostgreSQL SQL dialect, PostScript, POVRay, PowerQuery, PowerShell, Prolog, Promela, PromQL, properties, Protocol Buffer, Protocol Buffer Text Format, PRQL, PSL, Puppet, Python, Python 2
|
||||
| Q | QBasic, QML
|
||||
| R | R, Racket, Ragel, Raku, react, ReasonML, reg, Rego, reStructuredText, Rexx, RGBDS Assembly, Ring, RPGLE, RPMSpec, Ruby, Rust
|
||||
| S | SAS, Sass, Scala, Scheme, Scilab, SCSS, Sed, Sieve, Smali, Smalltalk, Smarty, SNBT, Snobol, Solidity, SourcePawn, SPARQL, SQL, SquidConf, Standard ML, stas, Stylus, Svelte, Swift, SYSTEMD, systemverilog
|
||||
| T | TableGen, Tal, TASM, Tcl, Tcsh, Termcap, Terminfo, Terraform, TeX, Thrift, TOML, TradingView, Transact-SQL, Turing, Turtle, Twig, TypeScript, TypoScript, TypoScriptCssData, TypoScriptHtmlData, Typst
|
||||
| U | ucode
|
||||
| V | V, V shell, Vala, VB.net, verilog, VHDL, VHS, VimL, vue
|
||||
| W | WDTE, WebAssembly Text Format, WebGPU Shading Language, WebVTT, Whiley
|
||||
| X | XML, Xorg
|
||||
| Y | YAML, YANG
|
||||
| Z | Z80 Assembly, Zed, Zig
|
||||
|
||||
_I will attempt to keep this section up to date, but an authoritative list can be
|
||||
displayed with `chroma --list`._
|
||||
|
||||
## Try it
|
||||
|
||||
Try out various languages and styles on the [Chroma Playground](https://swapoff.org/chroma/playground/).
|
||||
|
||||
## Using the library
|
||||
|
||||
This is version 2 of Chroma, use the import path:
|
||||
|
||||
```go
|
||||
import "github.com/alecthomas/chroma/v2"
|
||||
```
|
||||
|
||||
Chroma, like Pygments, has the concepts of
|
||||
[lexers](https://github.com/alecthomas/chroma/tree/master/lexers),
|
||||
[formatters](https://github.com/alecthomas/chroma/tree/master/formatters) and
|
||||
[styles](https://github.com/alecthomas/chroma/tree/master/styles).
|
||||
|
||||
Lexers convert source text into a stream of tokens, styles specify how token
|
||||
types are mapped to colours, and formatters convert tokens and styles into
|
||||
formatted output.
|
||||
|
||||
A package exists for each of these, containing a global `Registry` variable
|
||||
with all of the registered implementations. There are also helper functions
|
||||
for using the registry in each package, such as looking up lexers by name or
|
||||
matching filenames, etc.
|
||||
|
||||
In all cases, if a lexer, formatter or style can not be determined, `nil` will
|
||||
be returned. In this situation you may want to default to the `Fallback`
|
||||
value in each respective package, which provides sane defaults.
|
||||
|
||||
### Quick start
|
||||
|
||||
A convenience function exists that can be used to simply format some source
|
||||
text, without any effort:
|
||||
|
||||
```go
|
||||
err := quick.Highlight(os.Stdout, someSourceCode, "go", "html", "monokai")
|
||||
```
|
||||
|
||||
### Identifying the language
|
||||
|
||||
To highlight code, you'll first have to identify what language the code is
|
||||
written in. There are three primary ways to do that:
|
||||
|
||||
1. Detect the language from its filename.
|
||||
|
||||
```go
|
||||
lexer := lexers.Match("foo.go")
|
||||
```
|
||||
|
||||
2. Explicitly specify the language by its Chroma syntax ID (a full list is available from `lexers.Names()`).
|
||||
|
||||
```go
|
||||
lexer := lexers.Get("go")
|
||||
```
|
||||
|
||||
3. Detect the language from its content.
|
||||
|
||||
```go
|
||||
lexer := lexers.Analyse("package main\n\nfunc main()\n{\n}\n")
|
||||
```
|
||||
|
||||
In all cases, `nil` will be returned if the language can not be identified.
|
||||
|
||||
```go
|
||||
if lexer == nil {
|
||||
lexer = lexers.Fallback
|
||||
}
|
||||
```
|
||||
|
||||
At this point, it should be noted that some lexers can be extremely chatty. To
|
||||
mitigate this, you can use the coalescing lexer to coalesce runs of identical
|
||||
token types into a single token:
|
||||
|
||||
```go
|
||||
lexer = chroma.Coalesce(lexer)
|
||||
```
|
||||
|
||||
### Formatting the output
|
||||
|
||||
Once a language is identified you will need to pick a formatter and a style (theme).
|
||||
|
||||
```go
|
||||
style := styles.Get("swapoff")
|
||||
if style == nil {
|
||||
style = styles.Fallback
|
||||
}
|
||||
formatter := formatters.Get("html")
|
||||
if formatter == nil {
|
||||
formatter = formatters.Fallback
|
||||
}
|
||||
```
|
||||
|
||||
Then obtain an iterator over the tokens:
|
||||
|
||||
```go
|
||||
contents, err := ioutil.ReadAll(r)
|
||||
iterator, err := lexer.Tokenise(nil, string(contents))
|
||||
```
|
||||
|
||||
And finally, format the tokens from the iterator:
|
||||
|
||||
```go
|
||||
err := formatter.Format(w, style, iterator)
|
||||
```
|
||||
|
||||
### The HTML formatter
|
||||
|
||||
By default the `html` registered formatter generates standalone HTML with
|
||||
embedded CSS. More flexibility is available through the `formatters/html` package.
|
||||
|
||||
Firstly, the output generated by the formatter can be customised with the
|
||||
following constructor options:
|
||||
|
||||
- `Standalone()` - generate standalone HTML with embedded CSS.
|
||||
- `WithClasses()` - use classes rather than inlined style attributes.
|
||||
- `ClassPrefix(prefix)` - prefix each generated CSS class.
|
||||
- `TabWidth(width)` - Set the rendered tab width, in characters.
|
||||
- `WithLineNumbers()` - Render line numbers (style with `LineNumbers`).
|
||||
- `WithLinkableLineNumbers()` - Make the line numbers linkable and be a link to themselves.
|
||||
- `HighlightLines(ranges)` - Highlight lines in these ranges (style with `LineHighlight`).
|
||||
- `LineNumbersInTable()` - Use a table for formatting line numbers and code, rather than spans.
|
||||
|
||||
If `WithClasses()` is used, the corresponding CSS can be obtained from the formatter with:
|
||||
|
||||
```go
|
||||
formatter := html.New(html.WithClasses(true))
|
||||
err := formatter.WriteCSS(w, style)
|
||||
```
|
||||
|
||||
## More detail
|
||||
|
||||
### Lexers
|
||||
|
||||
See the [Pygments documentation](http://pygments.org/docs/lexerdevelopment/)
|
||||
for details on implementing lexers. Most concepts apply directly to Chroma,
|
||||
but see existing lexer implementations for real examples.
|
||||
|
||||
In many cases lexers can be automatically converted directly from Pygments by
|
||||
using the included Python 3 script `pygments2chroma_xml.py`. I use something like
|
||||
the following:
|
||||
|
||||
```sh
|
||||
uv run --script _tools/pygments2chroma_xml.py \
|
||||
pygments.lexers.jvm.KotlinLexer \
|
||||
> lexers/embedded/kotlin.xml
|
||||
```
|
||||
|
||||
A list of all lexers available in Pygments can be found in [pygments-lexers.txt](https://github.com/alecthomas/chroma/blob/master/pygments-lexers.txt).
|
||||
|
||||
### Formatters
|
||||
|
||||
Chroma supports HTML output, as well as terminal output in 8 colour, 256 colour, and true-colour.
|
||||
|
||||
A `noop` formatter is included that outputs the token text only, and a `tokens`
|
||||
formatter outputs raw tokens. The latter is useful for debugging lexers.
|
||||
|
||||
### Styles
|
||||
|
||||
Chroma styles are defined in XML. The style entries use the
|
||||
[same syntax](http://pygments.org/docs/styles/) as Pygments.
|
||||
|
||||
All Pygments styles have been converted to Chroma using the `_tools/style.py`
|
||||
script.
|
||||
|
||||
When you work with one of [Chroma's styles](https://github.com/alecthomas/chroma/tree/master/styles),
|
||||
know that the `Background` token type provides the default style for tokens. It does so
|
||||
by defining a foreground color and background color.
|
||||
|
||||
For example, this gives each token name not defined in the style a default color
|
||||
of `#f8f8f8` and uses `#000000` for the highlighted code block's background:
|
||||
|
||||
```xml
|
||||
<entry type="Background" style="#f8f8f2 bg:#000000"/>
|
||||
```
|
||||
|
||||
Also, token types in a style file are hierarchical. For instance, when `CommentSpecial` is not defined, Chroma uses the token style from `Comment`. So when several comment tokens use the same color, you'll only need to define `Comment` and override the one that has a different color.
|
||||
|
||||
For a quick overview of the available styles and how they look, check out the [Chroma Style Gallery](https://xyproto.github.io/splash/docs/).
|
||||
|
||||
## Command-line interface
|
||||
|
||||
A command-line interface to Chroma is included.
|
||||
|
||||
Binaries are available to install from [the releases page](https://github.com/alecthomas/chroma/releases).
|
||||
|
||||
The CLI can be used as a preprocessor to colorise output of `less(1)`,
|
||||
see documentation for the `LESSOPEN` environment variable.
|
||||
|
||||
The `--fail` flag can be used to suppress output and return with exit status
|
||||
1 to facilitate falling back to some other preprocessor in case chroma
|
||||
does not resolve a specific lexer to use for the given file. For example:
|
||||
|
||||
```shell
|
||||
export LESSOPEN='| p() { chroma --fail "$1" || cat "$1"; }; p "%s"'
|
||||
```
|
||||
|
||||
Replace `cat` with your favourite fallback preprocessor.
|
||||
|
||||
When invoked as `.lessfilter`, the `--fail` flag is automatically turned
|
||||
on under the hood for easy integration with [lesspipe shipping with
|
||||
Debian and derivatives](https://manpages.debian.org/lesspipe#USER_DEFINED_FILTERS);
|
||||
for that setup the `chroma` executable can be just symlinked to `~/.lessfilter`.
|
||||
|
||||
## Projects using Chroma
|
||||
|
||||
* [`moor`](https://github.com/walles/moor) is a full-blown pager that colorizes
|
||||
its input using Chroma
|
||||
* [Hugo](https://gohugo.io/) is a static site generator that [uses Chroma for syntax
|
||||
highlighting code examples](https://gohugo.io/content-management/syntax-highlighting/)
|
||||
|
||||
## Testing lexers
|
||||
|
||||
If you edit some lexers and want to try it, open a shell in `cmd/chromad` and run:
|
||||
|
||||
```shell
|
||||
go run . --csrf-key=securekey
|
||||
```
|
||||
|
||||
A Link will be printed. Open it in your Browser. Now you can test on the Playground with your local changes.
|
||||
|
||||
If you want to run the tests and the lexers, open a shell in the root directory and run:
|
||||
|
||||
```shell
|
||||
go test ./lexers
|
||||
```
|
||||
|
||||
When updating or adding a lexer, please add tests. See [lexers/README.md](lexers/README.md) for more.
|
||||
|
||||
## What's missing compared to Pygments?
|
||||
|
||||
- Quite a few lexers, for various reasons (pull-requests welcome):
|
||||
- Pygments lexers for complex languages often include custom code to
|
||||
handle certain aspects, such as Raku's ability to nest code inside
|
||||
regular expressions. These require time and effort to convert.
|
||||
- I mostly only converted languages I had heard of, to reduce the porting cost.
|
||||
- Some more esoteric features of Pygments are omitted for simplicity.
|
||||
- Though the Chroma API supports content detection, very few languages support them.
|
||||
I have plans to implement a statistical analyser at some point, but not enough time.
|
||||
6
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/biome.json
generated
vendored
Normal file
6
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/biome.json
generated
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"$schema": "https://biomejs.dev/schemas/2.0.5/schema.json",
|
||||
"formatter": {
|
||||
"indentStyle": "space"
|
||||
}
|
||||
}
|
||||
BIN
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/chroma.jpg
generated
vendored
Normal file
BIN
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/chroma.jpg
generated
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 79 KiB |
35
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/coalesce.go
generated
vendored
Normal file
35
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/coalesce.go
generated
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
package chroma
|
||||
|
||||
// Coalesce is a Lexer interceptor that collapses runs of common types into a single token.
|
||||
func Coalesce(lexer Lexer) Lexer { return &coalescer{lexer} }
|
||||
|
||||
type coalescer struct{ Lexer }
|
||||
|
||||
func (d *coalescer) Tokenise(options *TokeniseOptions, text string) (Iterator, error) {
|
||||
var prev Token
|
||||
it, err := d.Lexer.Tokenise(options, text)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return func() Token {
|
||||
for token := it(); token != (EOF); token = it() {
|
||||
if len(token.Value) == 0 {
|
||||
continue
|
||||
}
|
||||
if prev == EOF {
|
||||
prev = token
|
||||
} else {
|
||||
if prev.Type == token.Type && len(prev.Value) < 8192 {
|
||||
prev.Value += token.Value
|
||||
} else {
|
||||
out := prev
|
||||
prev = token
|
||||
return out
|
||||
}
|
||||
}
|
||||
}
|
||||
out := prev
|
||||
prev = EOF
|
||||
return out
|
||||
}, nil
|
||||
}
|
||||
192
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/colour.go
generated
vendored
Normal file
192
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/colour.go
generated
vendored
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
package chroma
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ANSI2RGB maps ANSI colour names, as supported by Chroma, to hex RGB values.
|
||||
var ANSI2RGB = map[string]string{
|
||||
"#ansiblack": "000000",
|
||||
"#ansidarkred": "7f0000",
|
||||
"#ansidarkgreen": "007f00",
|
||||
"#ansibrown": "7f7fe0",
|
||||
"#ansidarkblue": "00007f",
|
||||
"#ansipurple": "7f007f",
|
||||
"#ansiteal": "007f7f",
|
||||
"#ansilightgray": "e5e5e5",
|
||||
// Normal
|
||||
"#ansidarkgray": "555555",
|
||||
"#ansired": "ff0000",
|
||||
"#ansigreen": "00ff00",
|
||||
"#ansiyellow": "ffff00",
|
||||
"#ansiblue": "0000ff",
|
||||
"#ansifuchsia": "ff00ff",
|
||||
"#ansiturquoise": "00ffff",
|
||||
"#ansiwhite": "ffffff",
|
||||
|
||||
// Aliases without the "ansi" prefix, because...why?
|
||||
"#black": "000000",
|
||||
"#darkred": "7f0000",
|
||||
"#darkgreen": "007f00",
|
||||
"#brown": "7f7fe0",
|
||||
"#darkblue": "00007f",
|
||||
"#purple": "7f007f",
|
||||
"#teal": "007f7f",
|
||||
"#lightgray": "e5e5e5",
|
||||
// Normal
|
||||
"#darkgray": "555555",
|
||||
"#red": "ff0000",
|
||||
"#green": "00ff00",
|
||||
"#yellow": "ffff00",
|
||||
"#blue": "0000ff",
|
||||
"#fuchsia": "ff00ff",
|
||||
"#turquoise": "00ffff",
|
||||
"#white": "ffffff",
|
||||
}
|
||||
|
||||
// Colour represents an RGB colour.
|
||||
type Colour int32
|
||||
|
||||
// NewColour creates a Colour directly from RGB values.
|
||||
func NewColour(r, g, b uint8) Colour {
|
||||
return ParseColour(fmt.Sprintf("%02x%02x%02x", r, g, b))
|
||||
}
|
||||
|
||||
// Distance between this colour and another.
|
||||
//
|
||||
// This uses the approach described here (https://www.compuphase.com/cmetric.htm).
|
||||
// This is not as accurate as LAB, et. al. but is *vastly* simpler and sufficient for our needs.
|
||||
func (c Colour) Distance(e2 Colour) float64 {
|
||||
ar, ag, ab := int64(c.Red()), int64(c.Green()), int64(c.Blue())
|
||||
br, bg, bb := int64(e2.Red()), int64(e2.Green()), int64(e2.Blue())
|
||||
rmean := (ar + br) / 2
|
||||
r := ar - br
|
||||
g := ag - bg
|
||||
b := ab - bb
|
||||
return math.Sqrt(float64((((512 + rmean) * r * r) >> 8) + 4*g*g + (((767 - rmean) * b * b) >> 8)))
|
||||
}
|
||||
|
||||
// Brighten returns a copy of this colour with its brightness adjusted.
|
||||
//
|
||||
// If factor is negative, the colour is darkened.
|
||||
//
|
||||
// Uses approach described here (http://www.pvladov.com/2012/09/make-color-lighter-or-darker.html).
|
||||
func (c Colour) Brighten(factor float64) Colour {
|
||||
r := float64(c.Red())
|
||||
g := float64(c.Green())
|
||||
b := float64(c.Blue())
|
||||
|
||||
if factor < 0 {
|
||||
factor++
|
||||
r *= factor
|
||||
g *= factor
|
||||
b *= factor
|
||||
} else {
|
||||
r = (255-r)*factor + r
|
||||
g = (255-g)*factor + g
|
||||
b = (255-b)*factor + b
|
||||
}
|
||||
return NewColour(uint8(r), uint8(g), uint8(b))
|
||||
}
|
||||
|
||||
// BrightenOrDarken brightens a colour if it is < 0.5 brightness or darkens if > 0.5 brightness.
|
||||
func (c Colour) BrightenOrDarken(factor float64) Colour {
|
||||
if c.Brightness() < 0.5 {
|
||||
return c.Brighten(factor)
|
||||
}
|
||||
return c.Brighten(-factor)
|
||||
}
|
||||
|
||||
// ClampBrightness returns a copy of this colour with its brightness adjusted such that
|
||||
// it falls within the range [min, max] (or very close to it due to rounding errors).
|
||||
// The supplied values use the same [0.0, 1.0] range as Brightness.
|
||||
func (c Colour) ClampBrightness(min, max float64) Colour {
|
||||
if !c.IsSet() {
|
||||
return c
|
||||
}
|
||||
|
||||
min = math.Max(min, 0)
|
||||
max = math.Min(max, 1)
|
||||
current := c.Brightness()
|
||||
target := math.Min(math.Max(current, min), max)
|
||||
if current == target {
|
||||
return c
|
||||
}
|
||||
|
||||
r := float64(c.Red())
|
||||
g := float64(c.Green())
|
||||
b := float64(c.Blue())
|
||||
rgb := r + g + b
|
||||
if target > current {
|
||||
// Solve for x: target == ((255-r)*x + r + (255-g)*x + g + (255-b)*x + b) / 255 / 3
|
||||
return c.Brighten((target*255*3 - rgb) / (255*3 - rgb))
|
||||
}
|
||||
// Solve for x: target == (r*(x+1) + g*(x+1) + b*(x+1)) / 255 / 3
|
||||
return c.Brighten((target*255*3)/rgb - 1)
|
||||
}
|
||||
|
||||
// Brightness of the colour (roughly) in the range 0.0 to 1.0.
|
||||
func (c Colour) Brightness() float64 {
|
||||
return (float64(c.Red()) + float64(c.Green()) + float64(c.Blue())) / 255.0 / 3.0
|
||||
}
|
||||
|
||||
// ParseColour in the forms #rgb, #rrggbb, #ansi<colour>, or #<colour>.
|
||||
// Will return an "unset" colour if invalid.
|
||||
func ParseColour(colour string) Colour {
|
||||
colour = normaliseColour(colour)
|
||||
n, err := strconv.ParseUint(colour, 16, 32)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return Colour(n + 1) //nolint:gosec
|
||||
}
|
||||
|
||||
// MustParseColour is like ParseColour except it panics if the colour is invalid.
|
||||
//
|
||||
// Will panic if colour is in an invalid format.
|
||||
func MustParseColour(colour string) Colour {
|
||||
parsed := ParseColour(colour)
|
||||
if !parsed.IsSet() {
|
||||
panic(fmt.Errorf("invalid colour %q", colour))
|
||||
}
|
||||
return parsed
|
||||
}
|
||||
|
||||
// IsSet returns true if the colour is set.
|
||||
func (c Colour) IsSet() bool { return c != 0 }
|
||||
|
||||
func (c Colour) String() string { return fmt.Sprintf("#%06x", int(c-1)) }
|
||||
func (c Colour) GoString() string { return fmt.Sprintf("Colour(0x%06x)", int(c-1)) }
|
||||
|
||||
// Red component of colour.
|
||||
func (c Colour) Red() uint8 { return uint8(((c - 1) >> 16) & 0xff) } //nolint:gosec
|
||||
|
||||
// Green component of colour.
|
||||
func (c Colour) Green() uint8 { return uint8(((c - 1) >> 8) & 0xff) } //nolint:gosec
|
||||
|
||||
// Blue component of colour.
|
||||
func (c Colour) Blue() uint8 { return uint8((c - 1) & 0xff) } //nolint:gosec
|
||||
|
||||
// Colours is an orderable set of colours.
|
||||
type Colours []Colour
|
||||
|
||||
func (c Colours) Len() int { return len(c) }
|
||||
func (c Colours) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
|
||||
func (c Colours) Less(i, j int) bool { return c[i] < c[j] }
|
||||
|
||||
// Convert colours to #rrggbb.
|
||||
func normaliseColour(colour string) string {
|
||||
if ansi, ok := ANSI2RGB[colour]; ok {
|
||||
return ansi
|
||||
}
|
||||
if strings.HasPrefix(colour, "#") {
|
||||
colour = colour[1:]
|
||||
if len(colour) == 3 {
|
||||
return colour[0:1] + colour[0:1] + colour[1:2] + colour[1:2] + colour[2:3] + colour[2:3]
|
||||
}
|
||||
}
|
||||
return colour
|
||||
}
|
||||
161
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/delegate.go
generated
vendored
Normal file
161
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/delegate.go
generated
vendored
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
package chroma
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
)
|
||||
|
||||
type delegatingLexer struct {
|
||||
root Lexer
|
||||
language Lexer
|
||||
}
|
||||
|
||||
// DelegatingLexer combines two lexers to handle the common case of a language embedded inside another, such as PHP
|
||||
// inside HTML or PHP inside plain text.
|
||||
//
|
||||
// It takes two lexer as arguments: a root lexer and a language lexer. First everything is scanned using the language
|
||||
// lexer, which must return "Other" for unrecognised tokens. Then all "Other" tokens are lexed using the root lexer.
|
||||
// Finally, these two sets of tokens are merged.
|
||||
//
|
||||
// The lexers from the template lexer package use this base lexer.
|
||||
func DelegatingLexer(root Lexer, language Lexer) Lexer {
|
||||
return &delegatingLexer{
|
||||
root: root,
|
||||
language: language,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *delegatingLexer) SetTracing(enable bool) {
|
||||
if l, ok := d.language.(TracingLexer); ok {
|
||||
l.SetTracing(enable)
|
||||
}
|
||||
if l, ok := d.root.(TracingLexer); ok {
|
||||
l.SetTracing(enable)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *delegatingLexer) AnalyseText(text string) float32 {
|
||||
return d.root.AnalyseText(text)
|
||||
}
|
||||
|
||||
func (d *delegatingLexer) SetAnalyser(analyser func(text string) float32) Lexer {
|
||||
d.root.SetAnalyser(analyser)
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *delegatingLexer) SetRegistry(r *LexerRegistry) Lexer {
|
||||
d.root.SetRegistry(r)
|
||||
d.language.SetRegistry(r)
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *delegatingLexer) Config() *Config {
|
||||
return d.language.Config()
|
||||
}
|
||||
|
||||
// An insertion is the character range where language tokens should be inserted.
|
||||
type insertion struct {
|
||||
start, end int
|
||||
tokens []Token
|
||||
}
|
||||
|
||||
func (d *delegatingLexer) Tokenise(options *TokeniseOptions, text string) (Iterator, error) { // nolint: gocognit
|
||||
tokens, err := Tokenise(Coalesce(d.language), options, text)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Compute insertions and gather "Other" tokens.
|
||||
others := &bytes.Buffer{}
|
||||
insertions := []*insertion{}
|
||||
var insert *insertion
|
||||
offset := 0
|
||||
var last Token
|
||||
for _, t := range tokens {
|
||||
if t.Type == Other {
|
||||
if last != EOF && insert != nil && last.Type != Other {
|
||||
insert.end = offset
|
||||
}
|
||||
others.WriteString(t.Value)
|
||||
} else {
|
||||
if last == EOF || last.Type == Other {
|
||||
insert = &insertion{start: offset}
|
||||
insertions = append(insertions, insert)
|
||||
}
|
||||
insert.tokens = append(insert.tokens, t)
|
||||
}
|
||||
last = t
|
||||
offset += len(t.Value)
|
||||
}
|
||||
|
||||
if len(insertions) == 0 {
|
||||
return d.root.Tokenise(options, text)
|
||||
}
|
||||
|
||||
// Lex the other tokens.
|
||||
rootTokens, err := Tokenise(Coalesce(d.root), options, others.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Interleave the two sets of tokens.
|
||||
var out []Token
|
||||
offset = 0 // Offset into text.
|
||||
tokenIndex := 0
|
||||
nextToken := func() Token {
|
||||
if tokenIndex >= len(rootTokens) {
|
||||
return EOF
|
||||
}
|
||||
t := rootTokens[tokenIndex]
|
||||
tokenIndex++
|
||||
return t
|
||||
}
|
||||
insertionIndex := 0
|
||||
nextInsertion := func() *insertion {
|
||||
if insertionIndex >= len(insertions) {
|
||||
return nil
|
||||
}
|
||||
i := insertions[insertionIndex]
|
||||
insertionIndex++
|
||||
return i
|
||||
}
|
||||
t := nextToken()
|
||||
i := nextInsertion()
|
||||
for t != EOF || i != nil {
|
||||
// fmt.Printf("%d->%d:%q %d->%d:%q\n", offset, offset+len(t.Value), t.Value, i.start, i.end, Stringify(i.tokens...))
|
||||
if t == EOF || (i != nil && i.start < offset+len(t.Value)) {
|
||||
var l Token
|
||||
l, t = splitToken(t, i.start-offset)
|
||||
if l != EOF {
|
||||
out = append(out, l)
|
||||
offset += len(l.Value)
|
||||
}
|
||||
out = append(out, i.tokens...)
|
||||
offset += i.end - i.start
|
||||
if t == EOF {
|
||||
t = nextToken()
|
||||
}
|
||||
i = nextInsertion()
|
||||
} else {
|
||||
out = append(out, t)
|
||||
offset += len(t.Value)
|
||||
t = nextToken()
|
||||
}
|
||||
}
|
||||
return Literator(out...), nil
|
||||
}
|
||||
|
||||
func splitToken(t Token, offset int) (l Token, r Token) {
|
||||
if t == EOF {
|
||||
return EOF, EOF
|
||||
}
|
||||
if offset == 0 {
|
||||
return EOF, t
|
||||
}
|
||||
if offset == len(t.Value) {
|
||||
return t, EOF
|
||||
}
|
||||
l = t.Clone()
|
||||
r = t.Clone()
|
||||
l.Value = l.Value[:offset]
|
||||
r.Value = r.Value[offset:]
|
||||
return
|
||||
}
|
||||
7
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/doc.go
generated
vendored
Normal file
7
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
// Package chroma takes source code and other structured text and converts it into syntax highlighted HTML, ANSI-
|
||||
// coloured text, etc.
|
||||
//
|
||||
// Chroma is based heavily on Pygments, and includes translators for Pygments lexers and styles.
|
||||
//
|
||||
// For more information, go here: https://github.com/alecthomas/chroma
|
||||
package chroma
|
||||
233
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/emitters.go
generated
vendored
Normal file
233
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/emitters.go
generated
vendored
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
package chroma
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// An Emitter takes group matches and returns tokens.
|
||||
type Emitter interface {
|
||||
// Emit tokens for the given regex groups.
|
||||
Emit(groups []string, state *LexerState) Iterator
|
||||
}
|
||||
|
||||
// ValidatingEmitter is an Emitter that can validate against a compiled rule.
|
||||
type ValidatingEmitter interface {
|
||||
Emitter
|
||||
ValidateEmitter(rule *CompiledRule) error
|
||||
}
|
||||
|
||||
// SerialisableEmitter is an Emitter that can be serialised and deserialised to/from JSON.
|
||||
type SerialisableEmitter interface {
|
||||
Emitter
|
||||
EmitterKind() string
|
||||
}
|
||||
|
||||
// EmitterFunc is a function that is an Emitter.
|
||||
type EmitterFunc func(groups []string, state *LexerState) Iterator
|
||||
|
||||
// Emit tokens for groups.
|
||||
func (e EmitterFunc) Emit(groups []string, state *LexerState) Iterator {
|
||||
return e(groups, state)
|
||||
}
|
||||
|
||||
type Emitters []Emitter
|
||||
|
||||
type byGroupsEmitter struct {
|
||||
Emitters
|
||||
}
|
||||
|
||||
var _ ValidatingEmitter = (*byGroupsEmitter)(nil)
|
||||
|
||||
// ByGroups emits a token for each matching group in the rule's regex.
|
||||
func ByGroups(emitters ...Emitter) Emitter {
|
||||
return &byGroupsEmitter{Emitters: emitters}
|
||||
}
|
||||
|
||||
func (b *byGroupsEmitter) EmitterKind() string { return "bygroups" }
|
||||
|
||||
func (b *byGroupsEmitter) ValidateEmitter(rule *CompiledRule) error {
|
||||
if len(rule.Regexp.GetGroupNumbers())-1 != len(b.Emitters) {
|
||||
return fmt.Errorf("number of groups %d does not match number of emitters %d", len(rule.Regexp.GetGroupNumbers())-1, len(b.Emitters))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *byGroupsEmitter) Emit(groups []string, state *LexerState) Iterator {
|
||||
iterators := make([]Iterator, 0, len(groups)-1)
|
||||
if len(b.Emitters) != len(groups)-1 {
|
||||
iterators = append(iterators, Error.Emit(groups, state))
|
||||
// panic(errors.Errorf("number of groups %q does not match number of emitters %v", groups, emitters))
|
||||
} else {
|
||||
for i, group := range groups[1:] {
|
||||
if b.Emitters[i] != nil {
|
||||
iterators = append(iterators, b.Emitters[i].Emit([]string{group}, state))
|
||||
}
|
||||
}
|
||||
}
|
||||
return Concaterator(iterators...)
|
||||
}
|
||||
|
||||
// ByGroupNames emits a token for each named matching group in the rule's regex.
|
||||
func ByGroupNames(emitters map[string]Emitter) Emitter {
|
||||
return EmitterFunc(func(groups []string, state *LexerState) Iterator {
|
||||
iterators := make([]Iterator, 0, len(state.NamedGroups)-1)
|
||||
if len(state.NamedGroups)-1 == 0 {
|
||||
if emitter, ok := emitters[`0`]; ok {
|
||||
iterators = append(iterators, emitter.Emit(groups, state))
|
||||
} else {
|
||||
iterators = append(iterators, Error.Emit(groups, state))
|
||||
}
|
||||
} else {
|
||||
ruleRegex := state.Rules[state.State][state.Rule].Regexp
|
||||
for i := 1; i < len(state.NamedGroups); i++ {
|
||||
groupName := ruleRegex.GroupNameFromNumber(i)
|
||||
group := state.NamedGroups[groupName]
|
||||
if emitter, ok := emitters[groupName]; ok {
|
||||
if emitter != nil {
|
||||
iterators = append(iterators, emitter.Emit([]string{group}, state))
|
||||
}
|
||||
} else {
|
||||
iterators = append(iterators, Error.Emit([]string{group}, state))
|
||||
}
|
||||
}
|
||||
}
|
||||
return Concaterator(iterators...)
|
||||
})
|
||||
}
|
||||
|
||||
// UsingByGroup emits tokens for the matched groups in the regex using a
|
||||
// sublexer. Used when lexing code blocks where the name of a sublexer is
|
||||
// contained within the block, for example on a Markdown text block or SQL
|
||||
// language block.
|
||||
//
|
||||
// An attempt to load the sublexer will be made using the captured value from
|
||||
// the text of the matched sublexerNameGroup. If a sublexer matching the
|
||||
// sublexerNameGroup is available, then tokens for the matched codeGroup will
|
||||
// be emitted using the sublexer. Otherwise, if no sublexer is available, then
|
||||
// tokens will be emitted from the passed emitter.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// var Markdown = internal.Register(MustNewLexer(
|
||||
// &Config{
|
||||
// Name: "markdown",
|
||||
// Aliases: []string{"md", "mkd"},
|
||||
// Filenames: []string{"*.md", "*.mkd", "*.markdown"},
|
||||
// MimeTypes: []string{"text/x-markdown"},
|
||||
// },
|
||||
// Rules{
|
||||
// "root": {
|
||||
// {"^(```)(\\w+)(\\n)([\\w\\W]*?)(^```$)",
|
||||
// UsingByGroup(
|
||||
// 2, 4,
|
||||
// String, String, String, Text, String,
|
||||
// ),
|
||||
// nil,
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// ))
|
||||
//
|
||||
// See the lexers/markdown.go for the complete example.
|
||||
//
|
||||
// Note: panic's if the number of emitters does not equal the number of matched
|
||||
// groups in the regex.
|
||||
func UsingByGroup(sublexerNameGroup, codeGroup int, emitters ...Emitter) Emitter {
|
||||
return &usingByGroup{
|
||||
SublexerNameGroup: sublexerNameGroup,
|
||||
CodeGroup: codeGroup,
|
||||
Emitters: emitters,
|
||||
}
|
||||
}
|
||||
|
||||
type usingByGroup struct {
|
||||
SublexerNameGroup int `xml:"sublexer_name_group"`
|
||||
CodeGroup int `xml:"code_group"`
|
||||
Emitters Emitters `xml:"emitters"`
|
||||
}
|
||||
|
||||
func (u *usingByGroup) EmitterKind() string { return "usingbygroup" }
|
||||
func (u *usingByGroup) Emit(groups []string, state *LexerState) Iterator {
|
||||
// bounds check
|
||||
if len(u.Emitters) != len(groups)-1 {
|
||||
panic("UsingByGroup expects number of emitters to be the same as len(groups)-1")
|
||||
}
|
||||
|
||||
// grab sublexer
|
||||
sublexer := state.Registry.Get(groups[u.SublexerNameGroup])
|
||||
|
||||
// build iterators
|
||||
iterators := make([]Iterator, len(groups)-1)
|
||||
for i, group := range groups[1:] {
|
||||
if i == u.CodeGroup-1 && sublexer != nil {
|
||||
var err error
|
||||
iterators[i], err = sublexer.Tokenise(nil, groups[u.CodeGroup])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} else if u.Emitters[i] != nil {
|
||||
iterators[i] = u.Emitters[i].Emit([]string{group}, state)
|
||||
}
|
||||
}
|
||||
return Concaterator(iterators...)
|
||||
}
|
||||
|
||||
// UsingLexer returns an Emitter that uses a given Lexer for parsing and emitting.
|
||||
//
|
||||
// This Emitter is not serialisable.
|
||||
func UsingLexer(lexer Lexer) Emitter {
|
||||
return EmitterFunc(func(groups []string, _ *LexerState) Iterator {
|
||||
it, err := lexer.Tokenise(&TokeniseOptions{State: "root", Nested: true}, groups[0])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return it
|
||||
})
|
||||
}
|
||||
|
||||
type usingEmitter struct {
|
||||
Lexer string `xml:"lexer,attr"`
|
||||
}
|
||||
|
||||
func (u *usingEmitter) EmitterKind() string { return "using" }
|
||||
|
||||
func (u *usingEmitter) Emit(groups []string, state *LexerState) Iterator {
|
||||
if state.Registry == nil {
|
||||
panic(fmt.Sprintf("no LexerRegistry available for Using(%q)", u.Lexer))
|
||||
}
|
||||
lexer := state.Registry.Get(u.Lexer)
|
||||
if lexer == nil {
|
||||
panic(fmt.Sprintf("no such lexer %q", u.Lexer))
|
||||
}
|
||||
it, err := lexer.Tokenise(&TokeniseOptions{State: "root", Nested: true}, groups[0])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return it
|
||||
}
|
||||
|
||||
// Using returns an Emitter that uses a given Lexer reference for parsing and emitting.
|
||||
//
|
||||
// The referenced lexer must be stored in the same LexerRegistry.
|
||||
func Using(lexer string) Emitter {
|
||||
return &usingEmitter{Lexer: lexer}
|
||||
}
|
||||
|
||||
type usingSelfEmitter struct {
|
||||
State string `xml:"state,attr"`
|
||||
}
|
||||
|
||||
func (u *usingSelfEmitter) EmitterKind() string { return "usingself" }
|
||||
|
||||
func (u *usingSelfEmitter) Emit(groups []string, state *LexerState) Iterator {
|
||||
it, err := state.Lexer.Tokenise(&TokeniseOptions{State: u.State, Nested: true}, groups[0])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return it
|
||||
}
|
||||
|
||||
// UsingSelf is like Using, but uses the current Lexer.
|
||||
func UsingSelf(stateName string) Emitter {
|
||||
return &usingSelfEmitter{stateName}
|
||||
}
|
||||
43
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/formatter.go
generated
vendored
Normal file
43
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/formatter.go
generated
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
package chroma
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// A Formatter for Chroma lexers.
|
||||
type Formatter interface {
|
||||
// Format returns a formatting function for tokens.
|
||||
//
|
||||
// If the iterator panics, the Formatter should recover.
|
||||
Format(w io.Writer, style *Style, iterator Iterator) error
|
||||
}
|
||||
|
||||
// A FormatterFunc is a Formatter implemented as a function.
|
||||
//
|
||||
// Guards against iterator panics.
|
||||
type FormatterFunc func(w io.Writer, style *Style, iterator Iterator) error
|
||||
|
||||
func (f FormatterFunc) Format(w io.Writer, s *Style, it Iterator) (err error) { // nolint
|
||||
defer func() {
|
||||
if perr := recover(); perr != nil {
|
||||
err = perr.(error)
|
||||
}
|
||||
}()
|
||||
return f(w, s, it)
|
||||
}
|
||||
|
||||
type recoveringFormatter struct {
|
||||
Formatter
|
||||
}
|
||||
|
||||
func (r recoveringFormatter) Format(w io.Writer, s *Style, it Iterator) (err error) {
|
||||
defer func() {
|
||||
if perr := recover(); perr != nil {
|
||||
err = perr.(error)
|
||||
}
|
||||
}()
|
||||
return r.Formatter.Format(w, s, it)
|
||||
}
|
||||
|
||||
// RecoveringFormatter wraps a formatter with panic recovery.
|
||||
func RecoveringFormatter(formatter Formatter) Formatter { return recoveringFormatter{formatter} }
|
||||
57
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/formatters/api.go
generated
vendored
Normal file
57
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/formatters/api.go
generated
vendored
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
package formatters
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sort"
|
||||
|
||||
"github.com/alecthomas/chroma/v2"
|
||||
"github.com/alecthomas/chroma/v2/formatters/html"
|
||||
"github.com/alecthomas/chroma/v2/formatters/svg"
|
||||
)
|
||||
|
||||
var (
|
||||
// NoOp formatter.
|
||||
NoOp = Register("noop", chroma.FormatterFunc(func(w io.Writer, s *chroma.Style, iterator chroma.Iterator) error {
|
||||
for t := iterator(); t != chroma.EOF; t = iterator() {
|
||||
if _, err := io.WriteString(w, t.Value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}))
|
||||
// Default HTML formatter outputs self-contained HTML.
|
||||
htmlFull = Register("html", html.New(html.Standalone(true), html.WithClasses(true))) // nolint
|
||||
SVG = Register("svg", svg.New(svg.EmbedFont("Liberation Mono", svg.FontLiberationMono, svg.WOFF)))
|
||||
)
|
||||
|
||||
// Fallback formatter.
|
||||
var Fallback = NoOp
|
||||
|
||||
// Registry of Formatters.
|
||||
var Registry = map[string]chroma.Formatter{}
|
||||
|
||||
// Names of registered formatters.
|
||||
func Names() []string {
|
||||
out := []string{}
|
||||
for name := range Registry {
|
||||
out = append(out, name)
|
||||
}
|
||||
sort.Strings(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// Get formatter by name.
|
||||
//
|
||||
// If the given formatter is not found, the Fallback formatter will be returned.
|
||||
func Get(name string) chroma.Formatter {
|
||||
if f, ok := Registry[name]; ok {
|
||||
return f
|
||||
}
|
||||
return Fallback
|
||||
}
|
||||
|
||||
// Register a named formatter.
|
||||
func Register(name string, formatter chroma.Formatter) chroma.Formatter {
|
||||
Registry[name] = formatter
|
||||
return formatter
|
||||
}
|
||||
648
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/formatters/html/html.go
generated
vendored
Normal file
648
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/formatters/html/html.go
generated
vendored
Normal file
|
|
@ -0,0 +1,648 @@
|
|||
package html
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html"
|
||||
"io"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/alecthomas/chroma/v2"
|
||||
)
|
||||
|
||||
// Option sets an option of the HTML formatter.
|
||||
type Option func(f *Formatter)
|
||||
|
||||
// Standalone configures the HTML formatter for generating a standalone HTML document.
|
||||
func Standalone(b bool) Option { return func(f *Formatter) { f.standalone = b } }
|
||||
|
||||
// ClassPrefix sets the CSS class prefix.
|
||||
func ClassPrefix(prefix string) Option { return func(f *Formatter) { f.prefix = prefix } }
|
||||
|
||||
// WithClasses emits HTML using CSS classes, rather than inline styles.
|
||||
func WithClasses(b bool) Option { return func(f *Formatter) { f.Classes = b } }
|
||||
|
||||
// WithAllClasses disables an optimisation that omits redundant CSS classes.
|
||||
func WithAllClasses(b bool) Option { return func(f *Formatter) { f.allClasses = b } }
|
||||
|
||||
// WithCustomCSS sets user's custom CSS styles.
|
||||
func WithCustomCSS(css map[chroma.TokenType]string) Option {
|
||||
return func(f *Formatter) {
|
||||
f.customCSS = css
|
||||
}
|
||||
}
|
||||
|
||||
// WithCSSComments adds prefixe comments to the css classes. Defaults to true.
|
||||
func WithCSSComments(b bool) Option { return func(f *Formatter) { f.writeCSSComments = b } }
|
||||
|
||||
// TabWidth sets the number of characters for a tab. Defaults to 8.
|
||||
func TabWidth(width int) Option { return func(f *Formatter) { f.tabWidth = width } }
|
||||
|
||||
// PreventSurroundingPre prevents the surrounding pre tags around the generated code.
|
||||
func PreventSurroundingPre(b bool) Option {
|
||||
return func(f *Formatter) {
|
||||
f.preventSurroundingPre = b
|
||||
|
||||
if b {
|
||||
f.preWrapper = nopPreWrapper
|
||||
} else {
|
||||
f.preWrapper = defaultPreWrapper
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// InlineCode creates inline code wrapped in a code tag.
|
||||
func InlineCode(b bool) Option {
|
||||
return func(f *Formatter) {
|
||||
f.inlineCode = b
|
||||
f.preWrapper = preWrapper{
|
||||
start: func(code bool, styleAttr string) string {
|
||||
if code {
|
||||
return fmt.Sprintf(`<code%s>`, styleAttr)
|
||||
}
|
||||
|
||||
return ``
|
||||
},
|
||||
end: func(code bool) string {
|
||||
if code {
|
||||
return `</code>`
|
||||
}
|
||||
|
||||
return ``
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithPreWrapper allows control of the surrounding pre tags.
|
||||
func WithPreWrapper(wrapper PreWrapper) Option {
|
||||
return func(f *Formatter) {
|
||||
f.preWrapper = wrapper
|
||||
}
|
||||
}
|
||||
|
||||
// WrapLongLines wraps long lines.
|
||||
func WrapLongLines(b bool) Option {
|
||||
return func(f *Formatter) {
|
||||
f.wrapLongLines = b
|
||||
}
|
||||
}
|
||||
|
||||
// WithLineNumbers formats output with line numbers.
|
||||
func WithLineNumbers(b bool) Option {
|
||||
return func(f *Formatter) {
|
||||
f.lineNumbers = b
|
||||
}
|
||||
}
|
||||
|
||||
// LineNumbersInTable will, when combined with WithLineNumbers, separate the line numbers
|
||||
// and code in table td's, which make them copy-and-paste friendly.
|
||||
func LineNumbersInTable(b bool) Option {
|
||||
return func(f *Formatter) {
|
||||
f.lineNumbersInTable = b
|
||||
}
|
||||
}
|
||||
|
||||
// WithLinkableLineNumbers decorates the line numbers HTML elements with an "id"
|
||||
// attribute so they can be linked.
|
||||
func WithLinkableLineNumbers(b bool, prefix string) Option {
|
||||
return func(f *Formatter) {
|
||||
f.linkableLineNumbers = b
|
||||
f.lineNumbersIDPrefix = prefix
|
||||
}
|
||||
}
|
||||
|
||||
// HighlightLines higlights the given line ranges with the Highlight style.
|
||||
//
|
||||
// A range is the beginning and ending of a range as 1-based line numbers, inclusive.
|
||||
func HighlightLines(ranges [][2]int) Option {
|
||||
return func(f *Formatter) {
|
||||
f.highlightRanges = ranges
|
||||
sort.Sort(f.highlightRanges)
|
||||
}
|
||||
}
|
||||
|
||||
// BaseLineNumber sets the initial number to start line numbering at. Defaults to 1.
|
||||
func BaseLineNumber(n int) Option {
|
||||
return func(f *Formatter) {
|
||||
f.baseLineNumber = n
|
||||
}
|
||||
}
|
||||
|
||||
// New HTML formatter.
|
||||
func New(options ...Option) *Formatter {
|
||||
f := &Formatter{
|
||||
baseLineNumber: 1,
|
||||
preWrapper: defaultPreWrapper,
|
||||
writeCSSComments: true,
|
||||
}
|
||||
f.styleCache = newStyleCache(f)
|
||||
for _, option := range options {
|
||||
option(f)
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
// PreWrapper defines the operations supported in WithPreWrapper.
|
||||
type PreWrapper interface {
|
||||
// Start is called to write a start <pre> element.
|
||||
// The code flag tells whether this block surrounds
|
||||
// highlighted code. This will be false when surrounding
|
||||
// line numbers.
|
||||
Start(code bool, styleAttr string) string
|
||||
|
||||
// End is called to write the end </pre> element.
|
||||
End(code bool) string
|
||||
}
|
||||
|
||||
type preWrapper struct {
|
||||
start func(code bool, styleAttr string) string
|
||||
end func(code bool) string
|
||||
}
|
||||
|
||||
func (p preWrapper) Start(code bool, styleAttr string) string {
|
||||
return p.start(code, styleAttr)
|
||||
}
|
||||
|
||||
func (p preWrapper) End(code bool) string {
|
||||
return p.end(code)
|
||||
}
|
||||
|
||||
var (
|
||||
nopPreWrapper = preWrapper{
|
||||
start: func(code bool, styleAttr string) string { return "" },
|
||||
end: func(code bool) string { return "" },
|
||||
}
|
||||
defaultPreWrapper = preWrapper{
|
||||
start: func(code bool, styleAttr string) string {
|
||||
if code {
|
||||
return fmt.Sprintf(`<pre%s><code>`, styleAttr)
|
||||
}
|
||||
|
||||
return fmt.Sprintf(`<pre%s>`, styleAttr)
|
||||
},
|
||||
end: func(code bool) string {
|
||||
if code {
|
||||
return `</code></pre>`
|
||||
}
|
||||
|
||||
return `</pre>`
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// Formatter that generates HTML.
|
||||
type Formatter struct {
|
||||
styleCache *styleCache
|
||||
standalone bool
|
||||
prefix string
|
||||
Classes bool // Exported field to detect when classes are being used
|
||||
allClasses bool
|
||||
customCSS map[chroma.TokenType]string
|
||||
writeCSSComments bool
|
||||
preWrapper PreWrapper
|
||||
inlineCode bool
|
||||
preventSurroundingPre bool
|
||||
tabWidth int
|
||||
wrapLongLines bool
|
||||
lineNumbers bool
|
||||
lineNumbersInTable bool
|
||||
linkableLineNumbers bool
|
||||
lineNumbersIDPrefix string
|
||||
highlightRanges highlightRanges
|
||||
baseLineNumber int
|
||||
}
|
||||
|
||||
type highlightRanges [][2]int
|
||||
|
||||
func (h highlightRanges) Len() int { return len(h) }
|
||||
func (h highlightRanges) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
|
||||
func (h highlightRanges) Less(i, j int) bool { return h[i][0] < h[j][0] }
|
||||
|
||||
func (f *Formatter) Format(w io.Writer, style *chroma.Style, iterator chroma.Iterator) (err error) {
|
||||
return f.writeHTML(w, style, iterator.Tokens())
|
||||
}
|
||||
|
||||
// We deliberately don't use html/template here because it is two orders of magnitude slower (benchmarked).
|
||||
//
|
||||
// OTOH we need to be super careful about correct escaping...
|
||||
func (f *Formatter) writeHTML(w io.Writer, style *chroma.Style, tokens []chroma.Token) (err error) { // nolint: gocyclo
|
||||
css := f.styleCache.get(style, true)
|
||||
if f.standalone {
|
||||
fmt.Fprint(w, "<html>\n")
|
||||
if f.Classes {
|
||||
fmt.Fprint(w, "<style type=\"text/css\">\n")
|
||||
err = f.WriteCSS(w, style)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(w, "body { %s; }\n", css[chroma.Background])
|
||||
fmt.Fprint(w, "</style>")
|
||||
}
|
||||
fmt.Fprintf(w, "<body%s>\n", f.styleAttr(css, chroma.Background))
|
||||
}
|
||||
|
||||
wrapInTable := f.lineNumbers && f.lineNumbersInTable
|
||||
|
||||
lines := chroma.SplitTokensIntoLines(tokens)
|
||||
lineDigits := len(strconv.Itoa(f.baseLineNumber + len(lines) - 1))
|
||||
highlightIndex := 0
|
||||
|
||||
if wrapInTable {
|
||||
// List line numbers in its own <td>
|
||||
fmt.Fprintf(w, "<div%s>\n", f.styleAttr(css, chroma.PreWrapper))
|
||||
fmt.Fprintf(w, "<table%s><tr>", f.styleAttr(css, chroma.LineTable))
|
||||
fmt.Fprintf(w, "<td%s>\n", f.styleAttr(css, chroma.LineTableTD))
|
||||
fmt.Fprintf(w, "%s", f.preWrapper.Start(false, f.styleAttr(css, chroma.PreWrapper)))
|
||||
for index := range lines {
|
||||
line := f.baseLineNumber + index
|
||||
highlight, next := f.shouldHighlight(highlightIndex, line)
|
||||
if next {
|
||||
highlightIndex++
|
||||
}
|
||||
if highlight {
|
||||
fmt.Fprintf(w, "<span%s>", f.styleAttr(css, chroma.LineHighlight))
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "<span%s%s>%s\n</span>", f.styleAttr(css, chroma.LineNumbersTable), f.lineIDAttribute(line), f.lineTitleWithLinkIfNeeded(css, lineDigits, line))
|
||||
|
||||
if highlight {
|
||||
fmt.Fprintf(w, "</span>")
|
||||
}
|
||||
}
|
||||
fmt.Fprint(w, f.preWrapper.End(false))
|
||||
fmt.Fprint(w, "</td>\n")
|
||||
fmt.Fprintf(w, "<td%s>\n", f.styleAttr(css, chroma.LineTableTD, "width:100%"))
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "%s", f.preWrapper.Start(true, f.styleAttr(css, chroma.PreWrapper)))
|
||||
|
||||
highlightIndex = 0
|
||||
for index, tokens := range lines {
|
||||
// 1-based line number.
|
||||
line := f.baseLineNumber + index
|
||||
highlight, next := f.shouldHighlight(highlightIndex, line)
|
||||
if next {
|
||||
highlightIndex++
|
||||
}
|
||||
|
||||
if !(f.preventSurroundingPre || f.inlineCode) {
|
||||
// Start of Line
|
||||
fmt.Fprint(w, `<span`)
|
||||
|
||||
if highlight {
|
||||
// Line + LineHighlight
|
||||
if f.Classes {
|
||||
fmt.Fprintf(w, ` class="%s %s"`, f.class(chroma.Line), f.class(chroma.LineHighlight))
|
||||
} else {
|
||||
fmt.Fprintf(w, ` style="%s %s"`, css[chroma.Line], css[chroma.LineHighlight])
|
||||
}
|
||||
fmt.Fprint(w, `>`)
|
||||
} else {
|
||||
fmt.Fprintf(w, "%s>", f.styleAttr(css, chroma.Line))
|
||||
}
|
||||
|
||||
// Line number
|
||||
if f.lineNumbers && !wrapInTable {
|
||||
fmt.Fprintf(w, "<span%s%s>%s</span>", f.styleAttr(css, chroma.LineNumbers), f.lineIDAttribute(line), f.lineTitleWithLinkIfNeeded(css, lineDigits, line))
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, `<span%s>`, f.styleAttr(css, chroma.CodeLine))
|
||||
}
|
||||
|
||||
for _, token := range tokens {
|
||||
html := html.EscapeString(token.String())
|
||||
attr := f.styleAttr(css, token.Type)
|
||||
if attr != "" {
|
||||
html = fmt.Sprintf("<span%s>%s</span>", attr, html)
|
||||
}
|
||||
fmt.Fprint(w, html)
|
||||
}
|
||||
|
||||
if !(f.preventSurroundingPre || f.inlineCode) {
|
||||
fmt.Fprint(w, `</span>`) // End of CodeLine
|
||||
|
||||
fmt.Fprint(w, `</span>`) // End of Line
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(w, "%s", f.preWrapper.End(true))
|
||||
|
||||
if wrapInTable {
|
||||
fmt.Fprint(w, "</td></tr></table>\n")
|
||||
fmt.Fprint(w, "</div>\n")
|
||||
}
|
||||
|
||||
if f.standalone {
|
||||
fmt.Fprint(w, "\n</body>\n")
|
||||
fmt.Fprint(w, "</html>\n")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Formatter) lineIDAttribute(line int) string {
|
||||
if !f.linkableLineNumbers {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf(" id=\"%s\"", f.lineID(line))
|
||||
}
|
||||
|
||||
func (f *Formatter) lineTitleWithLinkIfNeeded(css map[chroma.TokenType]string, lineDigits, line int) string {
|
||||
title := fmt.Sprintf("%*d", lineDigits, line)
|
||||
if !f.linkableLineNumbers {
|
||||
return title
|
||||
}
|
||||
return fmt.Sprintf("<a%s href=\"#%s\">%s</a>", f.styleAttr(css, chroma.LineLink), f.lineID(line), title)
|
||||
}
|
||||
|
||||
func (f *Formatter) lineID(line int) string {
|
||||
return fmt.Sprintf("%s%d", f.lineNumbersIDPrefix, line)
|
||||
}
|
||||
|
||||
func (f *Formatter) shouldHighlight(highlightIndex, line int) (bool, bool) {
|
||||
next := false
|
||||
for highlightIndex < len(f.highlightRanges) && line > f.highlightRanges[highlightIndex][1] {
|
||||
highlightIndex++
|
||||
next = true
|
||||
}
|
||||
if highlightIndex < len(f.highlightRanges) {
|
||||
hrange := f.highlightRanges[highlightIndex]
|
||||
if line >= hrange[0] && line <= hrange[1] {
|
||||
return true, next
|
||||
}
|
||||
}
|
||||
return false, next
|
||||
}
|
||||
|
||||
func (f *Formatter) class(t chroma.TokenType) string {
|
||||
for t != 0 {
|
||||
if cls, ok := chroma.StandardTypes[t]; ok {
|
||||
if cls != "" {
|
||||
return f.prefix + cls
|
||||
}
|
||||
return ""
|
||||
}
|
||||
t = t.Parent()
|
||||
}
|
||||
if cls := chroma.StandardTypes[t]; cls != "" {
|
||||
return f.prefix + cls
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (f *Formatter) styleAttr(styles map[chroma.TokenType]string, tt chroma.TokenType, extraCSS ...string) string {
|
||||
if f.Classes {
|
||||
cls := f.class(tt)
|
||||
if cls == "" {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf(` class="%s"`, cls)
|
||||
}
|
||||
if _, ok := styles[tt]; !ok {
|
||||
tt = tt.SubCategory()
|
||||
if _, ok := styles[tt]; !ok {
|
||||
tt = tt.Category()
|
||||
if _, ok := styles[tt]; !ok {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
}
|
||||
css := []string{styles[tt]}
|
||||
css = append(css, extraCSS...)
|
||||
return fmt.Sprintf(` style="%s"`, strings.Join(css, ";"))
|
||||
}
|
||||
|
||||
func (f *Formatter) tabWidthStyle() string {
|
||||
if f.tabWidth != 0 && f.tabWidth != 8 {
|
||||
return fmt.Sprintf("-moz-tab-size: %[1]d; -o-tab-size: %[1]d; tab-size: %[1]d;", f.tabWidth)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (f *Formatter) writeCSSRule(w io.Writer, comment string, selector string, styles string) error {
|
||||
if styles == "" {
|
||||
return nil
|
||||
}
|
||||
if f.writeCSSComments && comment != "" {
|
||||
if _, err := fmt.Fprintf(w, "/* %s */ ", comment); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if _, err := fmt.Fprintf(w, "%s { %s }\n", selector, styles); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteCSS writes CSS style definitions (without any surrounding HTML).
|
||||
func (f *Formatter) WriteCSS(w io.Writer, style *chroma.Style) error {
|
||||
css := f.styleCache.get(style, false)
|
||||
|
||||
// Special-case background as it is mapped to the outer ".chroma" class.
|
||||
if err := f.writeCSSRule(w, chroma.Background.String(), fmt.Sprintf(".%sbg", f.prefix), css[chroma.Background]); err != nil {
|
||||
return err
|
||||
}
|
||||
// Special-case PreWrapper as it is the ".chroma" class.
|
||||
if err := f.writeCSSRule(w, chroma.PreWrapper.String(), fmt.Sprintf(".%schroma", f.prefix), css[chroma.PreWrapper]); err != nil {
|
||||
return err
|
||||
}
|
||||
// Special-case code column of table to expand width.
|
||||
if f.lineNumbers && f.lineNumbersInTable {
|
||||
selector := fmt.Sprintf(".%schroma .%s:last-child", f.prefix, f.class(chroma.LineTableTD))
|
||||
if err := f.writeCSSRule(w, chroma.LineTableTD.String(), selector, "width: 100%;"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Special-case line number highlighting when targeted.
|
||||
if f.lineNumbers || f.lineNumbersInTable {
|
||||
targetedLineCSS := StyleEntryToCSS(style.Get(chroma.LineHighlight))
|
||||
for _, tt := range []chroma.TokenType{chroma.LineNumbers, chroma.LineNumbersTable} {
|
||||
comment := fmt.Sprintf("%s targeted by URL anchor", tt)
|
||||
selector := fmt.Sprintf(".%schroma .%s:target", f.prefix, f.class(tt))
|
||||
if err := f.writeCSSRule(w, comment, selector, targetedLineCSS); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
tts := []int{}
|
||||
for tt := range css {
|
||||
tts = append(tts, int(tt))
|
||||
}
|
||||
sort.Ints(tts)
|
||||
for _, ti := range tts {
|
||||
tt := chroma.TokenType(ti)
|
||||
switch tt {
|
||||
case chroma.Background, chroma.PreWrapper:
|
||||
continue
|
||||
}
|
||||
class := f.class(tt)
|
||||
if class == "" {
|
||||
continue
|
||||
}
|
||||
if err := f.writeCSSRule(w, tt.String(), fmt.Sprintf(".%schroma .%s", f.prefix, class), css[tt]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Formatter) styleToCSS(style *chroma.Style) map[chroma.TokenType]string {
|
||||
classes := map[chroma.TokenType]string{}
|
||||
bg := style.Get(chroma.Background)
|
||||
// Convert the style.
|
||||
for t := range chroma.StandardTypes {
|
||||
entry := style.Get(t)
|
||||
if t != chroma.Background {
|
||||
entry = entry.Sub(bg)
|
||||
}
|
||||
|
||||
// Inherit from custom CSS provided by user
|
||||
tokenCategory := t.Category()
|
||||
tokenSubCategory := t.SubCategory()
|
||||
if t != tokenCategory {
|
||||
if css, ok := f.customCSS[tokenCategory]; ok {
|
||||
classes[t] = css
|
||||
}
|
||||
}
|
||||
if tokenCategory != tokenSubCategory {
|
||||
if css, ok := f.customCSS[tokenSubCategory]; ok {
|
||||
classes[t] += css
|
||||
}
|
||||
}
|
||||
// Add custom CSS provided by user
|
||||
if css, ok := f.customCSS[t]; ok {
|
||||
classes[t] += css
|
||||
}
|
||||
|
||||
if !f.allClasses && entry.IsZero() && classes[t] == `` {
|
||||
continue
|
||||
}
|
||||
|
||||
styleEntryCSS := StyleEntryToCSS(entry)
|
||||
if styleEntryCSS != `` && classes[t] != `` {
|
||||
styleEntryCSS += `;`
|
||||
}
|
||||
classes[t] = styleEntryCSS + classes[t]
|
||||
}
|
||||
classes[chroma.Background] += `;` + f.tabWidthStyle()
|
||||
classes[chroma.PreWrapper] += classes[chroma.Background]
|
||||
classes[chroma.PreWrapper] += ` -webkit-text-size-adjust: none;`
|
||||
// Make PreWrapper a grid to show highlight style with full width.
|
||||
if len(f.highlightRanges) > 0 && f.customCSS[chroma.PreWrapper] == `` {
|
||||
classes[chroma.PreWrapper] += `display: grid;`
|
||||
}
|
||||
// Make PreWrapper wrap long lines.
|
||||
if f.wrapLongLines {
|
||||
classes[chroma.PreWrapper] += `white-space: pre-wrap; word-break: break-word;`
|
||||
}
|
||||
lineNumbersStyle := `white-space: pre; -webkit-user-select: none; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;`
|
||||
// All rules begin with default rules followed by user provided rules
|
||||
classes[chroma.Line] = `display: flex;` + classes[chroma.Line]
|
||||
classes[chroma.LineNumbers] = lineNumbersStyle + classes[chroma.LineNumbers]
|
||||
classes[chroma.LineNumbersTable] = lineNumbersStyle + classes[chroma.LineNumbersTable]
|
||||
classes[chroma.LineTable] = "border-spacing: 0; padding: 0; margin: 0; border: 0;" + classes[chroma.LineTable]
|
||||
classes[chroma.LineTableTD] = "vertical-align: top; padding: 0; margin: 0; border: 0;" + classes[chroma.LineTableTD]
|
||||
classes[chroma.LineLink] = "outline: none; text-decoration: none; color: inherit" + classes[chroma.LineLink]
|
||||
return classes
|
||||
}
|
||||
|
||||
// StyleEntryToCSS converts a chroma.StyleEntry to CSS attributes.
|
||||
func StyleEntryToCSS(e chroma.StyleEntry) string {
|
||||
styles := []string{}
|
||||
if e.Colour.IsSet() {
|
||||
styles = append(styles, "color: "+e.Colour.String())
|
||||
}
|
||||
if e.Background.IsSet() {
|
||||
styles = append(styles, "background-color: "+e.Background.String())
|
||||
}
|
||||
if e.Bold == chroma.Yes {
|
||||
styles = append(styles, "font-weight: bold")
|
||||
}
|
||||
if e.Italic == chroma.Yes {
|
||||
styles = append(styles, "font-style: italic")
|
||||
}
|
||||
if e.Underline == chroma.Yes {
|
||||
styles = append(styles, "text-decoration: underline")
|
||||
}
|
||||
return strings.Join(styles, "; ")
|
||||
}
|
||||
|
||||
// Compress CSS attributes - remove spaces, transform 6-digit colours to 3.
|
||||
func compressStyle(s string) string {
|
||||
parts := strings.Split(s, ";")
|
||||
out := []string{}
|
||||
for _, p := range parts {
|
||||
p = strings.Join(strings.Fields(p), " ")
|
||||
p = strings.Replace(p, ": ", ":", 1)
|
||||
if strings.Contains(p, "#") {
|
||||
c := p[len(p)-6:]
|
||||
if c[0] == c[1] && c[2] == c[3] && c[4] == c[5] {
|
||||
p = p[:len(p)-6] + c[0:1] + c[2:3] + c[4:5]
|
||||
}
|
||||
}
|
||||
out = append(out, p)
|
||||
}
|
||||
return strings.Join(out, ";")
|
||||
}
|
||||
|
||||
const styleCacheLimit = 32
|
||||
|
||||
type styleCacheEntry struct {
|
||||
style *chroma.Style
|
||||
compressed bool
|
||||
cache map[chroma.TokenType]string
|
||||
}
|
||||
|
||||
type styleCache struct {
|
||||
mu sync.Mutex
|
||||
// LRU cache of compiled (and possibly compressed) styles. This is a slice
|
||||
// because the cache size is small, and a slice is sufficiently fast for
|
||||
// small N.
|
||||
cache []styleCacheEntry
|
||||
f *Formatter
|
||||
}
|
||||
|
||||
func newStyleCache(f *Formatter) *styleCache {
|
||||
return &styleCache{f: f}
|
||||
}
|
||||
|
||||
func (l *styleCache) get(style *chroma.Style, compress bool) map[chroma.TokenType]string {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
|
||||
// Look for an existing entry.
|
||||
for i := len(l.cache) - 1; i >= 0; i-- {
|
||||
entry := l.cache[i]
|
||||
if entry.style == style && entry.compressed == compress {
|
||||
// Top of the cache, no need to adjust the order.
|
||||
if i == len(l.cache)-1 {
|
||||
return entry.cache
|
||||
}
|
||||
// Move this entry to the end of the LRU
|
||||
copy(l.cache[i:], l.cache[i+1:])
|
||||
l.cache[len(l.cache)-1] = entry
|
||||
return entry.cache
|
||||
}
|
||||
}
|
||||
|
||||
// No entry, create one.
|
||||
cached := l.f.styleToCSS(style)
|
||||
if !l.f.Classes {
|
||||
for t, style := range cached {
|
||||
cached[t] = compressStyle(style)
|
||||
}
|
||||
}
|
||||
if compress {
|
||||
for t, style := range cached {
|
||||
cached[t] = compressStyle(style)
|
||||
}
|
||||
}
|
||||
// Evict the oldest entry.
|
||||
if len(l.cache) >= styleCacheLimit {
|
||||
l.cache = l.cache[0:copy(l.cache, l.cache[1:])]
|
||||
}
|
||||
l.cache = append(l.cache, styleCacheEntry{style: style, cache: cached, compressed: compress})
|
||||
return cached
|
||||
}
|
||||
39
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/formatters/json.go
generated
vendored
Normal file
39
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/formatters/json.go
generated
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
package formatters
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/alecthomas/chroma/v2"
|
||||
)
|
||||
|
||||
// JSON formatter outputs the raw token structures as JSON.
|
||||
var JSON = Register("json", chroma.FormatterFunc(func(w io.Writer, s *chroma.Style, it chroma.Iterator) error {
|
||||
if _, err := fmt.Fprintln(w, "["); err != nil {
|
||||
return err
|
||||
}
|
||||
i := 0
|
||||
for t := it(); t != chroma.EOF; t = it() {
|
||||
if i > 0 {
|
||||
if _, err := fmt.Fprintln(w, ","); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
i++
|
||||
bytes, err := json.Marshal(t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := fmt.Fprint(w, " "+string(bytes)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if _, err := fmt.Fprintln(w); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := fmt.Fprintln(w, "]"); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}))
|
||||
51
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/formatters/svg/font_liberation_mono.go
generated
vendored
Normal file
51
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/formatters/svg/font_liberation_mono.go
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
222
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/formatters/svg/svg.go
generated
vendored
Normal file
222
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/formatters/svg/svg.go
generated
vendored
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
// Package svg contains an SVG formatter.
|
||||
package svg
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/chroma/v2"
|
||||
)
|
||||
|
||||
// Option sets an option of the SVG formatter.
|
||||
type Option func(f *Formatter)
|
||||
|
||||
// FontFamily sets the font-family.
|
||||
func FontFamily(fontFamily string) Option { return func(f *Formatter) { f.fontFamily = fontFamily } }
|
||||
|
||||
// EmbedFontFile embeds given font file
|
||||
func EmbedFontFile(fontFamily string, fileName string) (option Option, err error) {
|
||||
var format FontFormat
|
||||
switch path.Ext(fileName) {
|
||||
case ".woff":
|
||||
format = WOFF
|
||||
case ".woff2":
|
||||
format = WOFF2
|
||||
case ".ttf":
|
||||
format = TRUETYPE
|
||||
default:
|
||||
return nil, errors.New("unexpected font file suffix")
|
||||
}
|
||||
|
||||
var content []byte
|
||||
if content, err = os.ReadFile(fileName); err == nil {
|
||||
option = EmbedFont(fontFamily, base64.StdEncoding.EncodeToString(content), format)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// EmbedFont embeds given base64 encoded font
|
||||
func EmbedFont(fontFamily string, font string, format FontFormat) Option {
|
||||
return func(f *Formatter) { f.fontFamily = fontFamily; f.embeddedFont = font; f.fontFormat = format }
|
||||
}
|
||||
|
||||
// New SVG formatter.
|
||||
func New(options ...Option) *Formatter {
|
||||
f := &Formatter{fontFamily: "Consolas, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace"}
|
||||
for _, option := range options {
|
||||
option(f)
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
// Formatter that generates SVG.
|
||||
type Formatter struct {
|
||||
fontFamily string
|
||||
embeddedFont string
|
||||
fontFormat FontFormat
|
||||
}
|
||||
|
||||
func (f *Formatter) Format(w io.Writer, style *chroma.Style, iterator chroma.Iterator) (err error) {
|
||||
f.writeSVG(w, style, iterator.Tokens())
|
||||
return err
|
||||
}
|
||||
|
||||
var svgEscaper = strings.NewReplacer(
|
||||
`&`, "&",
|
||||
`<`, "<",
|
||||
`>`, ">",
|
||||
`"`, """,
|
||||
` `, " ",
|
||||
` `, "    ",
|
||||
)
|
||||
|
||||
// EscapeString escapes special characters.
|
||||
func escapeString(s string) string {
|
||||
return svgEscaper.Replace(s)
|
||||
}
|
||||
|
||||
func (f *Formatter) writeSVG(w io.Writer, style *chroma.Style, tokens []chroma.Token) { // nolint: gocyclo
|
||||
svgStyles := f.styleToSVG(style)
|
||||
lines := chroma.SplitTokensIntoLines(tokens)
|
||||
|
||||
fmt.Fprint(w, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
|
||||
fmt.Fprint(w, "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\" \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n")
|
||||
fmt.Fprintf(w, "<svg width=\"%dpx\" height=\"%dpx\" xmlns=\"http://www.w3.org/2000/svg\">\n", 8*maxLineWidth(lines), 10+int(16.8*float64(len(lines)+1)))
|
||||
|
||||
if f.embeddedFont != "" {
|
||||
f.writeFontStyle(w)
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "<rect width=\"100%%\" height=\"100%%\" fill=\"%s\"/>\n", style.Get(chroma.Background).Background.String())
|
||||
fmt.Fprintf(w, "<g font-family=\"%s\" font-size=\"14px\" fill=\"%s\">\n", f.fontFamily, style.Get(chroma.Text).Colour.String())
|
||||
|
||||
f.writeTokenBackgrounds(w, lines, style)
|
||||
|
||||
for index, tokens := range lines {
|
||||
fmt.Fprintf(w, "<text x=\"0\" y=\"%fem\" xml:space=\"preserve\">", 1.2*float64(index+1))
|
||||
|
||||
for _, token := range tokens {
|
||||
text := escapeString(token.String())
|
||||
attr := f.styleAttr(svgStyles, token.Type)
|
||||
if attr != "" {
|
||||
text = fmt.Sprintf("<tspan %s>%s</tspan>", attr, text)
|
||||
}
|
||||
fmt.Fprint(w, text)
|
||||
}
|
||||
fmt.Fprint(w, "</text>")
|
||||
}
|
||||
|
||||
fmt.Fprint(w, "\n</g>\n")
|
||||
fmt.Fprint(w, "</svg>\n")
|
||||
}
|
||||
|
||||
func maxLineWidth(lines [][]chroma.Token) int {
|
||||
maxWidth := 0
|
||||
for _, tokens := range lines {
|
||||
length := 0
|
||||
for _, token := range tokens {
|
||||
length += len(strings.ReplaceAll(token.String(), ` `, " "))
|
||||
}
|
||||
if length > maxWidth {
|
||||
maxWidth = length
|
||||
}
|
||||
}
|
||||
return maxWidth
|
||||
}
|
||||
|
||||
// There is no background attribute for text in SVG so simply calculate the position and text
|
||||
// of tokens with a background color that differs from the default and add a rectangle for each before
|
||||
// adding the token.
|
||||
func (f *Formatter) writeTokenBackgrounds(w io.Writer, lines [][]chroma.Token, style *chroma.Style) {
|
||||
for index, tokens := range lines {
|
||||
lineLength := 0
|
||||
for _, token := range tokens {
|
||||
length := len(strings.ReplaceAll(token.String(), ` `, " "))
|
||||
tokenBackground := style.Get(token.Type).Background
|
||||
if tokenBackground.IsSet() && tokenBackground != style.Get(chroma.Background).Background {
|
||||
fmt.Fprintf(w, "<rect id=\"%s\" x=\"%dch\" y=\"%fem\" width=\"%dch\" height=\"1.2em\" fill=\"%s\" />\n", escapeString(token.String()), lineLength, 1.2*float64(index)+0.25, length, style.Get(token.Type).Background.String())
|
||||
}
|
||||
lineLength += length
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type FontFormat int
|
||||
|
||||
// https://transfonter.org/formats
|
||||
const (
|
||||
WOFF FontFormat = iota
|
||||
WOFF2
|
||||
TRUETYPE
|
||||
)
|
||||
|
||||
var fontFormats = [...]string{
|
||||
"woff",
|
||||
"woff2",
|
||||
"truetype",
|
||||
}
|
||||
|
||||
func (f *Formatter) writeFontStyle(w io.Writer) {
|
||||
fmt.Fprintf(w, `<style>
|
||||
@font-face {
|
||||
font-family: '%s';
|
||||
src: url(data:application/x-font-%s;charset=utf-8;base64,%s) format('%s');'
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
</style>`, f.fontFamily, fontFormats[f.fontFormat], f.embeddedFont, fontFormats[f.fontFormat])
|
||||
}
|
||||
|
||||
func (f *Formatter) styleAttr(styles map[chroma.TokenType]string, tt chroma.TokenType) string {
|
||||
if _, ok := styles[tt]; !ok {
|
||||
tt = tt.SubCategory()
|
||||
if _, ok := styles[tt]; !ok {
|
||||
tt = tt.Category()
|
||||
if _, ok := styles[tt]; !ok {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
}
|
||||
return styles[tt]
|
||||
}
|
||||
|
||||
func (f *Formatter) styleToSVG(style *chroma.Style) map[chroma.TokenType]string {
|
||||
converted := map[chroma.TokenType]string{}
|
||||
bg := style.Get(chroma.Background)
|
||||
// Convert the style.
|
||||
for t := range chroma.StandardTypes {
|
||||
entry := style.Get(t)
|
||||
if t != chroma.Background {
|
||||
entry = entry.Sub(bg)
|
||||
}
|
||||
if entry.IsZero() {
|
||||
continue
|
||||
}
|
||||
converted[t] = StyleEntryToSVG(entry)
|
||||
}
|
||||
return converted
|
||||
}
|
||||
|
||||
// StyleEntryToSVG converts a chroma.StyleEntry to SVG attributes.
|
||||
func StyleEntryToSVG(e chroma.StyleEntry) string {
|
||||
var styles []string
|
||||
|
||||
if e.Colour.IsSet() {
|
||||
styles = append(styles, "fill=\""+e.Colour.String()+"\"")
|
||||
}
|
||||
if e.Bold == chroma.Yes {
|
||||
styles = append(styles, "font-weight=\"bold\"")
|
||||
}
|
||||
if e.Italic == chroma.Yes {
|
||||
styles = append(styles, "font-style=\"italic\"")
|
||||
}
|
||||
if e.Underline == chroma.Yes {
|
||||
styles = append(styles, "text-decoration=\"underline\"")
|
||||
}
|
||||
return strings.Join(styles, " ")
|
||||
}
|
||||
18
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/formatters/tokens.go
generated
vendored
Normal file
18
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/formatters/tokens.go
generated
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package formatters
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/alecthomas/chroma/v2"
|
||||
)
|
||||
|
||||
// Tokens formatter outputs the raw token structures.
|
||||
var Tokens = Register("tokens", chroma.FormatterFunc(func(w io.Writer, s *chroma.Style, it chroma.Iterator) error {
|
||||
for t := it(); t != chroma.EOF; t = it() {
|
||||
if _, err := fmt.Fprintln(w, t.GoString()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}))
|
||||
284
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/formatters/tty_indexed.go
generated
vendored
Normal file
284
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/formatters/tty_indexed.go
generated
vendored
Normal file
|
|
@ -0,0 +1,284 @@
|
|||
package formatters
|
||||
|
||||
import (
|
||||
"io"
|
||||
"math"
|
||||
|
||||
"github.com/alecthomas/chroma/v2"
|
||||
)
|
||||
|
||||
type ttyTable struct {
|
||||
foreground map[chroma.Colour]string
|
||||
background map[chroma.Colour]string
|
||||
}
|
||||
|
||||
var c = chroma.MustParseColour
|
||||
|
||||
var ttyTables = map[int]*ttyTable{
|
||||
8: {
|
||||
foreground: map[chroma.Colour]string{
|
||||
c("#000000"): "\033[30m", c("#7f0000"): "\033[31m", c("#007f00"): "\033[32m", c("#7f7fe0"): "\033[33m",
|
||||
c("#00007f"): "\033[34m", c("#7f007f"): "\033[35m", c("#007f7f"): "\033[36m", c("#e5e5e5"): "\033[37m",
|
||||
c("#555555"): "\033[1m\033[30m", c("#ff0000"): "\033[1m\033[31m", c("#00ff00"): "\033[1m\033[32m", c("#ffff00"): "\033[1m\033[33m",
|
||||
c("#0000ff"): "\033[1m\033[34m", c("#ff00ff"): "\033[1m\033[35m", c("#00ffff"): "\033[1m\033[36m", c("#ffffff"): "\033[1m\033[37m",
|
||||
},
|
||||
background: map[chroma.Colour]string{
|
||||
c("#000000"): "\033[40m", c("#7f0000"): "\033[41m", c("#007f00"): "\033[42m", c("#7f7fe0"): "\033[43m",
|
||||
c("#00007f"): "\033[44m", c("#7f007f"): "\033[45m", c("#007f7f"): "\033[46m", c("#e5e5e5"): "\033[47m",
|
||||
c("#555555"): "\033[1m\033[40m", c("#ff0000"): "\033[1m\033[41m", c("#00ff00"): "\033[1m\033[42m", c("#ffff00"): "\033[1m\033[43m",
|
||||
c("#0000ff"): "\033[1m\033[44m", c("#ff00ff"): "\033[1m\033[45m", c("#00ffff"): "\033[1m\033[46m", c("#ffffff"): "\033[1m\033[47m",
|
||||
},
|
||||
},
|
||||
16: {
|
||||
foreground: map[chroma.Colour]string{
|
||||
c("#000000"): "\033[30m", c("#7f0000"): "\033[31m", c("#007f00"): "\033[32m", c("#7f7fe0"): "\033[33m",
|
||||
c("#00007f"): "\033[34m", c("#7f007f"): "\033[35m", c("#007f7f"): "\033[36m", c("#e5e5e5"): "\033[37m",
|
||||
c("#555555"): "\033[90m", c("#ff0000"): "\033[91m", c("#00ff00"): "\033[92m", c("#ffff00"): "\033[93m",
|
||||
c("#0000ff"): "\033[94m", c("#ff00ff"): "\033[95m", c("#00ffff"): "\033[96m", c("#ffffff"): "\033[97m",
|
||||
},
|
||||
background: map[chroma.Colour]string{
|
||||
c("#000000"): "\033[40m", c("#7f0000"): "\033[41m", c("#007f00"): "\033[42m", c("#7f7fe0"): "\033[43m",
|
||||
c("#00007f"): "\033[44m", c("#7f007f"): "\033[45m", c("#007f7f"): "\033[46m", c("#e5e5e5"): "\033[47m",
|
||||
c("#555555"): "\033[100m", c("#ff0000"): "\033[101m", c("#00ff00"): "\033[102m", c("#ffff00"): "\033[103m",
|
||||
c("#0000ff"): "\033[104m", c("#ff00ff"): "\033[105m", c("#00ffff"): "\033[106m", c("#ffffff"): "\033[107m",
|
||||
},
|
||||
},
|
||||
256: {
|
||||
foreground: map[chroma.Colour]string{
|
||||
c("#000000"): "\033[38;5;0m", c("#800000"): "\033[38;5;1m", c("#008000"): "\033[38;5;2m", c("#808000"): "\033[38;5;3m",
|
||||
c("#000080"): "\033[38;5;4m", c("#800080"): "\033[38;5;5m", c("#008080"): "\033[38;5;6m", c("#c0c0c0"): "\033[38;5;7m",
|
||||
c("#808080"): "\033[38;5;8m", c("#ff0000"): "\033[38;5;9m", c("#00ff00"): "\033[38;5;10m", c("#ffff00"): "\033[38;5;11m",
|
||||
c("#0000ff"): "\033[38;5;12m", c("#ff00ff"): "\033[38;5;13m", c("#00ffff"): "\033[38;5;14m", c("#ffffff"): "\033[38;5;15m",
|
||||
c("#000000"): "\033[38;5;16m", c("#00005f"): "\033[38;5;17m", c("#000087"): "\033[38;5;18m", c("#0000af"): "\033[38;5;19m",
|
||||
c("#0000d7"): "\033[38;5;20m", c("#0000ff"): "\033[38;5;21m", c("#005f00"): "\033[38;5;22m", c("#005f5f"): "\033[38;5;23m",
|
||||
c("#005f87"): "\033[38;5;24m", c("#005faf"): "\033[38;5;25m", c("#005fd7"): "\033[38;5;26m", c("#005fff"): "\033[38;5;27m",
|
||||
c("#008700"): "\033[38;5;28m", c("#00875f"): "\033[38;5;29m", c("#008787"): "\033[38;5;30m", c("#0087af"): "\033[38;5;31m",
|
||||
c("#0087d7"): "\033[38;5;32m", c("#0087ff"): "\033[38;5;33m", c("#00af00"): "\033[38;5;34m", c("#00af5f"): "\033[38;5;35m",
|
||||
c("#00af87"): "\033[38;5;36m", c("#00afaf"): "\033[38;5;37m", c("#00afd7"): "\033[38;5;38m", c("#00afff"): "\033[38;5;39m",
|
||||
c("#00d700"): "\033[38;5;40m", c("#00d75f"): "\033[38;5;41m", c("#00d787"): "\033[38;5;42m", c("#00d7af"): "\033[38;5;43m",
|
||||
c("#00d7d7"): "\033[38;5;44m", c("#00d7ff"): "\033[38;5;45m", c("#00ff00"): "\033[38;5;46m", c("#00ff5f"): "\033[38;5;47m",
|
||||
c("#00ff87"): "\033[38;5;48m", c("#00ffaf"): "\033[38;5;49m", c("#00ffd7"): "\033[38;5;50m", c("#00ffff"): "\033[38;5;51m",
|
||||
c("#5f0000"): "\033[38;5;52m", c("#5f005f"): "\033[38;5;53m", c("#5f0087"): "\033[38;5;54m", c("#5f00af"): "\033[38;5;55m",
|
||||
c("#5f00d7"): "\033[38;5;56m", c("#5f00ff"): "\033[38;5;57m", c("#5f5f00"): "\033[38;5;58m", c("#5f5f5f"): "\033[38;5;59m",
|
||||
c("#5f5f87"): "\033[38;5;60m", c("#5f5faf"): "\033[38;5;61m", c("#5f5fd7"): "\033[38;5;62m", c("#5f5fff"): "\033[38;5;63m",
|
||||
c("#5f8700"): "\033[38;5;64m", c("#5f875f"): "\033[38;5;65m", c("#5f8787"): "\033[38;5;66m", c("#5f87af"): "\033[38;5;67m",
|
||||
c("#5f87d7"): "\033[38;5;68m", c("#5f87ff"): "\033[38;5;69m", c("#5faf00"): "\033[38;5;70m", c("#5faf5f"): "\033[38;5;71m",
|
||||
c("#5faf87"): "\033[38;5;72m", c("#5fafaf"): "\033[38;5;73m", c("#5fafd7"): "\033[38;5;74m", c("#5fafff"): "\033[38;5;75m",
|
||||
c("#5fd700"): "\033[38;5;76m", c("#5fd75f"): "\033[38;5;77m", c("#5fd787"): "\033[38;5;78m", c("#5fd7af"): "\033[38;5;79m",
|
||||
c("#5fd7d7"): "\033[38;5;80m", c("#5fd7ff"): "\033[38;5;81m", c("#5fff00"): "\033[38;5;82m", c("#5fff5f"): "\033[38;5;83m",
|
||||
c("#5fff87"): "\033[38;5;84m", c("#5fffaf"): "\033[38;5;85m", c("#5fffd7"): "\033[38;5;86m", c("#5fffff"): "\033[38;5;87m",
|
||||
c("#870000"): "\033[38;5;88m", c("#87005f"): "\033[38;5;89m", c("#870087"): "\033[38;5;90m", c("#8700af"): "\033[38;5;91m",
|
||||
c("#8700d7"): "\033[38;5;92m", c("#8700ff"): "\033[38;5;93m", c("#875f00"): "\033[38;5;94m", c("#875f5f"): "\033[38;5;95m",
|
||||
c("#875f87"): "\033[38;5;96m", c("#875faf"): "\033[38;5;97m", c("#875fd7"): "\033[38;5;98m", c("#875fff"): "\033[38;5;99m",
|
||||
c("#878700"): "\033[38;5;100m", c("#87875f"): "\033[38;5;101m", c("#878787"): "\033[38;5;102m", c("#8787af"): "\033[38;5;103m",
|
||||
c("#8787d7"): "\033[38;5;104m", c("#8787ff"): "\033[38;5;105m", c("#87af00"): "\033[38;5;106m", c("#87af5f"): "\033[38;5;107m",
|
||||
c("#87af87"): "\033[38;5;108m", c("#87afaf"): "\033[38;5;109m", c("#87afd7"): "\033[38;5;110m", c("#87afff"): "\033[38;5;111m",
|
||||
c("#87d700"): "\033[38;5;112m", c("#87d75f"): "\033[38;5;113m", c("#87d787"): "\033[38;5;114m", c("#87d7af"): "\033[38;5;115m",
|
||||
c("#87d7d7"): "\033[38;5;116m", c("#87d7ff"): "\033[38;5;117m", c("#87ff00"): "\033[38;5;118m", c("#87ff5f"): "\033[38;5;119m",
|
||||
c("#87ff87"): "\033[38;5;120m", c("#87ffaf"): "\033[38;5;121m", c("#87ffd7"): "\033[38;5;122m", c("#87ffff"): "\033[38;5;123m",
|
||||
c("#af0000"): "\033[38;5;124m", c("#af005f"): "\033[38;5;125m", c("#af0087"): "\033[38;5;126m", c("#af00af"): "\033[38;5;127m",
|
||||
c("#af00d7"): "\033[38;5;128m", c("#af00ff"): "\033[38;5;129m", c("#af5f00"): "\033[38;5;130m", c("#af5f5f"): "\033[38;5;131m",
|
||||
c("#af5f87"): "\033[38;5;132m", c("#af5faf"): "\033[38;5;133m", c("#af5fd7"): "\033[38;5;134m", c("#af5fff"): "\033[38;5;135m",
|
||||
c("#af8700"): "\033[38;5;136m", c("#af875f"): "\033[38;5;137m", c("#af8787"): "\033[38;5;138m", c("#af87af"): "\033[38;5;139m",
|
||||
c("#af87d7"): "\033[38;5;140m", c("#af87ff"): "\033[38;5;141m", c("#afaf00"): "\033[38;5;142m", c("#afaf5f"): "\033[38;5;143m",
|
||||
c("#afaf87"): "\033[38;5;144m", c("#afafaf"): "\033[38;5;145m", c("#afafd7"): "\033[38;5;146m", c("#afafff"): "\033[38;5;147m",
|
||||
c("#afd700"): "\033[38;5;148m", c("#afd75f"): "\033[38;5;149m", c("#afd787"): "\033[38;5;150m", c("#afd7af"): "\033[38;5;151m",
|
||||
c("#afd7d7"): "\033[38;5;152m", c("#afd7ff"): "\033[38;5;153m", c("#afff00"): "\033[38;5;154m", c("#afff5f"): "\033[38;5;155m",
|
||||
c("#afff87"): "\033[38;5;156m", c("#afffaf"): "\033[38;5;157m", c("#afffd7"): "\033[38;5;158m", c("#afffff"): "\033[38;5;159m",
|
||||
c("#d70000"): "\033[38;5;160m", c("#d7005f"): "\033[38;5;161m", c("#d70087"): "\033[38;5;162m", c("#d700af"): "\033[38;5;163m",
|
||||
c("#d700d7"): "\033[38;5;164m", c("#d700ff"): "\033[38;5;165m", c("#d75f00"): "\033[38;5;166m", c("#d75f5f"): "\033[38;5;167m",
|
||||
c("#d75f87"): "\033[38;5;168m", c("#d75faf"): "\033[38;5;169m", c("#d75fd7"): "\033[38;5;170m", c("#d75fff"): "\033[38;5;171m",
|
||||
c("#d78700"): "\033[38;5;172m", c("#d7875f"): "\033[38;5;173m", c("#d78787"): "\033[38;5;174m", c("#d787af"): "\033[38;5;175m",
|
||||
c("#d787d7"): "\033[38;5;176m", c("#d787ff"): "\033[38;5;177m", c("#d7af00"): "\033[38;5;178m", c("#d7af5f"): "\033[38;5;179m",
|
||||
c("#d7af87"): "\033[38;5;180m", c("#d7afaf"): "\033[38;5;181m", c("#d7afd7"): "\033[38;5;182m", c("#d7afff"): "\033[38;5;183m",
|
||||
c("#d7d700"): "\033[38;5;184m", c("#d7d75f"): "\033[38;5;185m", c("#d7d787"): "\033[38;5;186m", c("#d7d7af"): "\033[38;5;187m",
|
||||
c("#d7d7d7"): "\033[38;5;188m", c("#d7d7ff"): "\033[38;5;189m", c("#d7ff00"): "\033[38;5;190m", c("#d7ff5f"): "\033[38;5;191m",
|
||||
c("#d7ff87"): "\033[38;5;192m", c("#d7ffaf"): "\033[38;5;193m", c("#d7ffd7"): "\033[38;5;194m", c("#d7ffff"): "\033[38;5;195m",
|
||||
c("#ff0000"): "\033[38;5;196m", c("#ff005f"): "\033[38;5;197m", c("#ff0087"): "\033[38;5;198m", c("#ff00af"): "\033[38;5;199m",
|
||||
c("#ff00d7"): "\033[38;5;200m", c("#ff00ff"): "\033[38;5;201m", c("#ff5f00"): "\033[38;5;202m", c("#ff5f5f"): "\033[38;5;203m",
|
||||
c("#ff5f87"): "\033[38;5;204m", c("#ff5faf"): "\033[38;5;205m", c("#ff5fd7"): "\033[38;5;206m", c("#ff5fff"): "\033[38;5;207m",
|
||||
c("#ff8700"): "\033[38;5;208m", c("#ff875f"): "\033[38;5;209m", c("#ff8787"): "\033[38;5;210m", c("#ff87af"): "\033[38;5;211m",
|
||||
c("#ff87d7"): "\033[38;5;212m", c("#ff87ff"): "\033[38;5;213m", c("#ffaf00"): "\033[38;5;214m", c("#ffaf5f"): "\033[38;5;215m",
|
||||
c("#ffaf87"): "\033[38;5;216m", c("#ffafaf"): "\033[38;5;217m", c("#ffafd7"): "\033[38;5;218m", c("#ffafff"): "\033[38;5;219m",
|
||||
c("#ffd700"): "\033[38;5;220m", c("#ffd75f"): "\033[38;5;221m", c("#ffd787"): "\033[38;5;222m", c("#ffd7af"): "\033[38;5;223m",
|
||||
c("#ffd7d7"): "\033[38;5;224m", c("#ffd7ff"): "\033[38;5;225m", c("#ffff00"): "\033[38;5;226m", c("#ffff5f"): "\033[38;5;227m",
|
||||
c("#ffff87"): "\033[38;5;228m", c("#ffffaf"): "\033[38;5;229m", c("#ffffd7"): "\033[38;5;230m", c("#ffffff"): "\033[38;5;231m",
|
||||
c("#080808"): "\033[38;5;232m", c("#121212"): "\033[38;5;233m", c("#1c1c1c"): "\033[38;5;234m", c("#262626"): "\033[38;5;235m",
|
||||
c("#303030"): "\033[38;5;236m", c("#3a3a3a"): "\033[38;5;237m", c("#444444"): "\033[38;5;238m", c("#4e4e4e"): "\033[38;5;239m",
|
||||
c("#585858"): "\033[38;5;240m", c("#626262"): "\033[38;5;241m", c("#6c6c6c"): "\033[38;5;242m", c("#767676"): "\033[38;5;243m",
|
||||
c("#808080"): "\033[38;5;244m", c("#8a8a8a"): "\033[38;5;245m", c("#949494"): "\033[38;5;246m", c("#9e9e9e"): "\033[38;5;247m",
|
||||
c("#a8a8a8"): "\033[38;5;248m", c("#b2b2b2"): "\033[38;5;249m", c("#bcbcbc"): "\033[38;5;250m", c("#c6c6c6"): "\033[38;5;251m",
|
||||
c("#d0d0d0"): "\033[38;5;252m", c("#dadada"): "\033[38;5;253m", c("#e4e4e4"): "\033[38;5;254m", c("#eeeeee"): "\033[38;5;255m",
|
||||
},
|
||||
background: map[chroma.Colour]string{
|
||||
c("#000000"): "\033[48;5;0m", c("#800000"): "\033[48;5;1m", c("#008000"): "\033[48;5;2m", c("#808000"): "\033[48;5;3m",
|
||||
c("#000080"): "\033[48;5;4m", c("#800080"): "\033[48;5;5m", c("#008080"): "\033[48;5;6m", c("#c0c0c0"): "\033[48;5;7m",
|
||||
c("#808080"): "\033[48;5;8m", c("#ff0000"): "\033[48;5;9m", c("#00ff00"): "\033[48;5;10m", c("#ffff00"): "\033[48;5;11m",
|
||||
c("#0000ff"): "\033[48;5;12m", c("#ff00ff"): "\033[48;5;13m", c("#00ffff"): "\033[48;5;14m", c("#ffffff"): "\033[48;5;15m",
|
||||
c("#000000"): "\033[48;5;16m", c("#00005f"): "\033[48;5;17m", c("#000087"): "\033[48;5;18m", c("#0000af"): "\033[48;5;19m",
|
||||
c("#0000d7"): "\033[48;5;20m", c("#0000ff"): "\033[48;5;21m", c("#005f00"): "\033[48;5;22m", c("#005f5f"): "\033[48;5;23m",
|
||||
c("#005f87"): "\033[48;5;24m", c("#005faf"): "\033[48;5;25m", c("#005fd7"): "\033[48;5;26m", c("#005fff"): "\033[48;5;27m",
|
||||
c("#008700"): "\033[48;5;28m", c("#00875f"): "\033[48;5;29m", c("#008787"): "\033[48;5;30m", c("#0087af"): "\033[48;5;31m",
|
||||
c("#0087d7"): "\033[48;5;32m", c("#0087ff"): "\033[48;5;33m", c("#00af00"): "\033[48;5;34m", c("#00af5f"): "\033[48;5;35m",
|
||||
c("#00af87"): "\033[48;5;36m", c("#00afaf"): "\033[48;5;37m", c("#00afd7"): "\033[48;5;38m", c("#00afff"): "\033[48;5;39m",
|
||||
c("#00d700"): "\033[48;5;40m", c("#00d75f"): "\033[48;5;41m", c("#00d787"): "\033[48;5;42m", c("#00d7af"): "\033[48;5;43m",
|
||||
c("#00d7d7"): "\033[48;5;44m", c("#00d7ff"): "\033[48;5;45m", c("#00ff00"): "\033[48;5;46m", c("#00ff5f"): "\033[48;5;47m",
|
||||
c("#00ff87"): "\033[48;5;48m", c("#00ffaf"): "\033[48;5;49m", c("#00ffd7"): "\033[48;5;50m", c("#00ffff"): "\033[48;5;51m",
|
||||
c("#5f0000"): "\033[48;5;52m", c("#5f005f"): "\033[48;5;53m", c("#5f0087"): "\033[48;5;54m", c("#5f00af"): "\033[48;5;55m",
|
||||
c("#5f00d7"): "\033[48;5;56m", c("#5f00ff"): "\033[48;5;57m", c("#5f5f00"): "\033[48;5;58m", c("#5f5f5f"): "\033[48;5;59m",
|
||||
c("#5f5f87"): "\033[48;5;60m", c("#5f5faf"): "\033[48;5;61m", c("#5f5fd7"): "\033[48;5;62m", c("#5f5fff"): "\033[48;5;63m",
|
||||
c("#5f8700"): "\033[48;5;64m", c("#5f875f"): "\033[48;5;65m", c("#5f8787"): "\033[48;5;66m", c("#5f87af"): "\033[48;5;67m",
|
||||
c("#5f87d7"): "\033[48;5;68m", c("#5f87ff"): "\033[48;5;69m", c("#5faf00"): "\033[48;5;70m", c("#5faf5f"): "\033[48;5;71m",
|
||||
c("#5faf87"): "\033[48;5;72m", c("#5fafaf"): "\033[48;5;73m", c("#5fafd7"): "\033[48;5;74m", c("#5fafff"): "\033[48;5;75m",
|
||||
c("#5fd700"): "\033[48;5;76m", c("#5fd75f"): "\033[48;5;77m", c("#5fd787"): "\033[48;5;78m", c("#5fd7af"): "\033[48;5;79m",
|
||||
c("#5fd7d7"): "\033[48;5;80m", c("#5fd7ff"): "\033[48;5;81m", c("#5fff00"): "\033[48;5;82m", c("#5fff5f"): "\033[48;5;83m",
|
||||
c("#5fff87"): "\033[48;5;84m", c("#5fffaf"): "\033[48;5;85m", c("#5fffd7"): "\033[48;5;86m", c("#5fffff"): "\033[48;5;87m",
|
||||
c("#870000"): "\033[48;5;88m", c("#87005f"): "\033[48;5;89m", c("#870087"): "\033[48;5;90m", c("#8700af"): "\033[48;5;91m",
|
||||
c("#8700d7"): "\033[48;5;92m", c("#8700ff"): "\033[48;5;93m", c("#875f00"): "\033[48;5;94m", c("#875f5f"): "\033[48;5;95m",
|
||||
c("#875f87"): "\033[48;5;96m", c("#875faf"): "\033[48;5;97m", c("#875fd7"): "\033[48;5;98m", c("#875fff"): "\033[48;5;99m",
|
||||
c("#878700"): "\033[48;5;100m", c("#87875f"): "\033[48;5;101m", c("#878787"): "\033[48;5;102m", c("#8787af"): "\033[48;5;103m",
|
||||
c("#8787d7"): "\033[48;5;104m", c("#8787ff"): "\033[48;5;105m", c("#87af00"): "\033[48;5;106m", c("#87af5f"): "\033[48;5;107m",
|
||||
c("#87af87"): "\033[48;5;108m", c("#87afaf"): "\033[48;5;109m", c("#87afd7"): "\033[48;5;110m", c("#87afff"): "\033[48;5;111m",
|
||||
c("#87d700"): "\033[48;5;112m", c("#87d75f"): "\033[48;5;113m", c("#87d787"): "\033[48;5;114m", c("#87d7af"): "\033[48;5;115m",
|
||||
c("#87d7d7"): "\033[48;5;116m", c("#87d7ff"): "\033[48;5;117m", c("#87ff00"): "\033[48;5;118m", c("#87ff5f"): "\033[48;5;119m",
|
||||
c("#87ff87"): "\033[48;5;120m", c("#87ffaf"): "\033[48;5;121m", c("#87ffd7"): "\033[48;5;122m", c("#87ffff"): "\033[48;5;123m",
|
||||
c("#af0000"): "\033[48;5;124m", c("#af005f"): "\033[48;5;125m", c("#af0087"): "\033[48;5;126m", c("#af00af"): "\033[48;5;127m",
|
||||
c("#af00d7"): "\033[48;5;128m", c("#af00ff"): "\033[48;5;129m", c("#af5f00"): "\033[48;5;130m", c("#af5f5f"): "\033[48;5;131m",
|
||||
c("#af5f87"): "\033[48;5;132m", c("#af5faf"): "\033[48;5;133m", c("#af5fd7"): "\033[48;5;134m", c("#af5fff"): "\033[48;5;135m",
|
||||
c("#af8700"): "\033[48;5;136m", c("#af875f"): "\033[48;5;137m", c("#af8787"): "\033[48;5;138m", c("#af87af"): "\033[48;5;139m",
|
||||
c("#af87d7"): "\033[48;5;140m", c("#af87ff"): "\033[48;5;141m", c("#afaf00"): "\033[48;5;142m", c("#afaf5f"): "\033[48;5;143m",
|
||||
c("#afaf87"): "\033[48;5;144m", c("#afafaf"): "\033[48;5;145m", c("#afafd7"): "\033[48;5;146m", c("#afafff"): "\033[48;5;147m",
|
||||
c("#afd700"): "\033[48;5;148m", c("#afd75f"): "\033[48;5;149m", c("#afd787"): "\033[48;5;150m", c("#afd7af"): "\033[48;5;151m",
|
||||
c("#afd7d7"): "\033[48;5;152m", c("#afd7ff"): "\033[48;5;153m", c("#afff00"): "\033[48;5;154m", c("#afff5f"): "\033[48;5;155m",
|
||||
c("#afff87"): "\033[48;5;156m", c("#afffaf"): "\033[48;5;157m", c("#afffd7"): "\033[48;5;158m", c("#afffff"): "\033[48;5;159m",
|
||||
c("#d70000"): "\033[48;5;160m", c("#d7005f"): "\033[48;5;161m", c("#d70087"): "\033[48;5;162m", c("#d700af"): "\033[48;5;163m",
|
||||
c("#d700d7"): "\033[48;5;164m", c("#d700ff"): "\033[48;5;165m", c("#d75f00"): "\033[48;5;166m", c("#d75f5f"): "\033[48;5;167m",
|
||||
c("#d75f87"): "\033[48;5;168m", c("#d75faf"): "\033[48;5;169m", c("#d75fd7"): "\033[48;5;170m", c("#d75fff"): "\033[48;5;171m",
|
||||
c("#d78700"): "\033[48;5;172m", c("#d7875f"): "\033[48;5;173m", c("#d78787"): "\033[48;5;174m", c("#d787af"): "\033[48;5;175m",
|
||||
c("#d787d7"): "\033[48;5;176m", c("#d787ff"): "\033[48;5;177m", c("#d7af00"): "\033[48;5;178m", c("#d7af5f"): "\033[48;5;179m",
|
||||
c("#d7af87"): "\033[48;5;180m", c("#d7afaf"): "\033[48;5;181m", c("#d7afd7"): "\033[48;5;182m", c("#d7afff"): "\033[48;5;183m",
|
||||
c("#d7d700"): "\033[48;5;184m", c("#d7d75f"): "\033[48;5;185m", c("#d7d787"): "\033[48;5;186m", c("#d7d7af"): "\033[48;5;187m",
|
||||
c("#d7d7d7"): "\033[48;5;188m", c("#d7d7ff"): "\033[48;5;189m", c("#d7ff00"): "\033[48;5;190m", c("#d7ff5f"): "\033[48;5;191m",
|
||||
c("#d7ff87"): "\033[48;5;192m", c("#d7ffaf"): "\033[48;5;193m", c("#d7ffd7"): "\033[48;5;194m", c("#d7ffff"): "\033[48;5;195m",
|
||||
c("#ff0000"): "\033[48;5;196m", c("#ff005f"): "\033[48;5;197m", c("#ff0087"): "\033[48;5;198m", c("#ff00af"): "\033[48;5;199m",
|
||||
c("#ff00d7"): "\033[48;5;200m", c("#ff00ff"): "\033[48;5;201m", c("#ff5f00"): "\033[48;5;202m", c("#ff5f5f"): "\033[48;5;203m",
|
||||
c("#ff5f87"): "\033[48;5;204m", c("#ff5faf"): "\033[48;5;205m", c("#ff5fd7"): "\033[48;5;206m", c("#ff5fff"): "\033[48;5;207m",
|
||||
c("#ff8700"): "\033[48;5;208m", c("#ff875f"): "\033[48;5;209m", c("#ff8787"): "\033[48;5;210m", c("#ff87af"): "\033[48;5;211m",
|
||||
c("#ff87d7"): "\033[48;5;212m", c("#ff87ff"): "\033[48;5;213m", c("#ffaf00"): "\033[48;5;214m", c("#ffaf5f"): "\033[48;5;215m",
|
||||
c("#ffaf87"): "\033[48;5;216m", c("#ffafaf"): "\033[48;5;217m", c("#ffafd7"): "\033[48;5;218m", c("#ffafff"): "\033[48;5;219m",
|
||||
c("#ffd700"): "\033[48;5;220m", c("#ffd75f"): "\033[48;5;221m", c("#ffd787"): "\033[48;5;222m", c("#ffd7af"): "\033[48;5;223m",
|
||||
c("#ffd7d7"): "\033[48;5;224m", c("#ffd7ff"): "\033[48;5;225m", c("#ffff00"): "\033[48;5;226m", c("#ffff5f"): "\033[48;5;227m",
|
||||
c("#ffff87"): "\033[48;5;228m", c("#ffffaf"): "\033[48;5;229m", c("#ffffd7"): "\033[48;5;230m", c("#ffffff"): "\033[48;5;231m",
|
||||
c("#080808"): "\033[48;5;232m", c("#121212"): "\033[48;5;233m", c("#1c1c1c"): "\033[48;5;234m", c("#262626"): "\033[48;5;235m",
|
||||
c("#303030"): "\033[48;5;236m", c("#3a3a3a"): "\033[48;5;237m", c("#444444"): "\033[48;5;238m", c("#4e4e4e"): "\033[48;5;239m",
|
||||
c("#585858"): "\033[48;5;240m", c("#626262"): "\033[48;5;241m", c("#6c6c6c"): "\033[48;5;242m", c("#767676"): "\033[48;5;243m",
|
||||
c("#808080"): "\033[48;5;244m", c("#8a8a8a"): "\033[48;5;245m", c("#949494"): "\033[48;5;246m", c("#9e9e9e"): "\033[48;5;247m",
|
||||
c("#a8a8a8"): "\033[48;5;248m", c("#b2b2b2"): "\033[48;5;249m", c("#bcbcbc"): "\033[48;5;250m", c("#c6c6c6"): "\033[48;5;251m",
|
||||
c("#d0d0d0"): "\033[48;5;252m", c("#dadada"): "\033[48;5;253m", c("#e4e4e4"): "\033[48;5;254m", c("#eeeeee"): "\033[48;5;255m",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func entryToEscapeSequence(table *ttyTable, entry chroma.StyleEntry) string {
|
||||
out := ""
|
||||
if entry.Bold == chroma.Yes {
|
||||
out += "\033[1m"
|
||||
}
|
||||
if entry.Underline == chroma.Yes {
|
||||
out += "\033[4m"
|
||||
}
|
||||
if entry.Italic == chroma.Yes {
|
||||
out += "\033[3m"
|
||||
}
|
||||
if entry.Colour.IsSet() {
|
||||
out += table.foreground[findClosest(table, entry.Colour)]
|
||||
}
|
||||
if entry.Background.IsSet() {
|
||||
out += table.background[findClosest(table, entry.Background)]
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func findClosest(table *ttyTable, seeking chroma.Colour) chroma.Colour {
|
||||
closestColour := chroma.Colour(0)
|
||||
closest := float64(math.MaxFloat64)
|
||||
for colour := range table.foreground {
|
||||
distance := colour.Distance(seeking)
|
||||
if distance < closest {
|
||||
closest = distance
|
||||
closestColour = colour
|
||||
}
|
||||
}
|
||||
return closestColour
|
||||
}
|
||||
|
||||
func styleToEscapeSequence(table *ttyTable, style *chroma.Style) map[chroma.TokenType]string {
|
||||
style = clearBackground(style)
|
||||
out := map[chroma.TokenType]string{}
|
||||
for _, ttype := range style.Types() {
|
||||
entry := style.Get(ttype)
|
||||
out[ttype] = entryToEscapeSequence(table, entry)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Clear the background colour.
|
||||
func clearBackground(style *chroma.Style) *chroma.Style {
|
||||
builder := style.Builder()
|
||||
bg := builder.Get(chroma.Background)
|
||||
bg.Background = 0
|
||||
bg.NoInherit = true
|
||||
builder.AddEntry(chroma.Background, bg)
|
||||
style, _ = builder.Build()
|
||||
return style
|
||||
}
|
||||
|
||||
type indexedTTYFormatter struct {
|
||||
table *ttyTable
|
||||
}
|
||||
|
||||
func (c *indexedTTYFormatter) Format(w io.Writer, style *chroma.Style, it chroma.Iterator) (err error) {
|
||||
theme := styleToEscapeSequence(c.table, style)
|
||||
for token := it(); token != chroma.EOF; token = it() {
|
||||
clr, ok := theme[token.Type]
|
||||
|
||||
// This search mimics how styles.Get() is used in tty_truecolour.go.
|
||||
if !ok {
|
||||
clr, ok = theme[token.Type.SubCategory()]
|
||||
if !ok {
|
||||
clr, ok = theme[token.Type.Category()]
|
||||
if !ok {
|
||||
clr, ok = theme[chroma.Text]
|
||||
if !ok {
|
||||
clr = theme[chroma.Background]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
writeToken(w, clr, token.Value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TTY is an 8-colour terminal formatter.
|
||||
//
|
||||
// The Lab colour space is used to map RGB values to the most appropriate index colour.
|
||||
var TTY = Register("terminal", &indexedTTYFormatter{ttyTables[8]})
|
||||
|
||||
// TTY8 is an 8-colour terminal formatter.
|
||||
//
|
||||
// The Lab colour space is used to map RGB values to the most appropriate index colour.
|
||||
var TTY8 = Register("terminal8", &indexedTTYFormatter{ttyTables[8]})
|
||||
|
||||
// TTY16 is a 16-colour terminal formatter.
|
||||
//
|
||||
// It uses \033[3xm for normal colours and \033[90Xm for bright colours.
|
||||
//
|
||||
// The Lab colour space is used to map RGB values to the most appropriate index colour.
|
||||
var TTY16 = Register("terminal16", &indexedTTYFormatter{ttyTables[16]})
|
||||
|
||||
// TTY256 is a 256-colour terminal formatter.
|
||||
//
|
||||
// The Lab colour space is used to map RGB values to the most appropriate index colour.
|
||||
var TTY256 = Register("terminal256", &indexedTTYFormatter{ttyTables[256]})
|
||||
76
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/formatters/tty_truecolour.go
generated
vendored
Normal file
76
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/formatters/tty_truecolour.go
generated
vendored
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
package formatters
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
|
||||
"github.com/alecthomas/chroma/v2"
|
||||
)
|
||||
|
||||
// TTY16m is a true-colour terminal formatter.
|
||||
var TTY16m = Register("terminal16m", chroma.FormatterFunc(trueColourFormatter))
|
||||
|
||||
var crOrCrLf = regexp.MustCompile(`\r?\n`)
|
||||
|
||||
// Print the text with the given formatting, resetting the formatting at the end
|
||||
// of each line and resuming it on the next line.
|
||||
//
|
||||
// This way, a pager (like https://github.com/walles/moar for example) can show
|
||||
// any line in the output by itself, and it will get the right formatting.
|
||||
func writeToken(w io.Writer, formatting string, text string) {
|
||||
if formatting == "" {
|
||||
fmt.Fprint(w, text)
|
||||
return
|
||||
}
|
||||
|
||||
newlineIndices := crOrCrLf.FindAllStringIndex(text, -1)
|
||||
|
||||
afterLastNewline := 0
|
||||
for _, indices := range newlineIndices {
|
||||
newlineStart, afterNewline := indices[0], indices[1]
|
||||
fmt.Fprint(w, formatting)
|
||||
fmt.Fprint(w, text[afterLastNewline:newlineStart])
|
||||
fmt.Fprint(w, "\033[0m")
|
||||
fmt.Fprint(w, text[newlineStart:afterNewline])
|
||||
afterLastNewline = afterNewline
|
||||
}
|
||||
|
||||
if afterLastNewline < len(text) {
|
||||
// Print whatever is left after the last newline
|
||||
fmt.Fprint(w, formatting)
|
||||
fmt.Fprint(w, text[afterLastNewline:])
|
||||
fmt.Fprint(w, "\033[0m")
|
||||
}
|
||||
}
|
||||
|
||||
func trueColourFormatter(w io.Writer, style *chroma.Style, it chroma.Iterator) error {
|
||||
style = clearBackground(style)
|
||||
for token := it(); token != chroma.EOF; token = it() {
|
||||
entry := style.Get(token.Type)
|
||||
if entry.IsZero() {
|
||||
fmt.Fprint(w, token.Value)
|
||||
continue
|
||||
}
|
||||
|
||||
formatting := ""
|
||||
if entry.Bold == chroma.Yes {
|
||||
formatting += "\033[1m"
|
||||
}
|
||||
if entry.Underline == chroma.Yes {
|
||||
formatting += "\033[4m"
|
||||
}
|
||||
if entry.Italic == chroma.Yes {
|
||||
formatting += "\033[3m"
|
||||
}
|
||||
if entry.Colour.IsSet() {
|
||||
formatting += fmt.Sprintf("\033[38;2;%d;%d;%dm", entry.Colour.Red(), entry.Colour.Green(), entry.Colour.Blue())
|
||||
}
|
||||
if entry.Background.IsSet() {
|
||||
formatting += fmt.Sprintf("\033[48;2;%d;%d;%dm", entry.Background.Red(), entry.Background.Green(), entry.Background.Blue())
|
||||
}
|
||||
|
||||
writeToken(w, formatting, token.Value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
93
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/iterator.go
generated
vendored
Normal file
93
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/iterator.go
generated
vendored
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
package chroma
|
||||
|
||||
import "strings"
|
||||
|
||||
// An Iterator across tokens.
|
||||
//
|
||||
// EOF will be returned at the end of the Token stream.
|
||||
//
|
||||
// If an error occurs within an Iterator, it may propagate this in a panic. Formatters should recover.
|
||||
type Iterator func() Token
|
||||
|
||||
// Tokens consumes all tokens from the iterator and returns them as a slice.
|
||||
func (i Iterator) Tokens() []Token {
|
||||
var out []Token
|
||||
for t := i(); t != EOF; t = i() {
|
||||
out = append(out, t)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Stdlib converts a Chroma iterator to a Go 1.23-compatible iterator.
|
||||
func (i Iterator) Stdlib() func(yield func(Token) bool) {
|
||||
return func(yield func(Token) bool) {
|
||||
for t := i(); t != EOF; t = i() {
|
||||
if !yield(t) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Concaterator concatenates tokens from a series of iterators.
|
||||
func Concaterator(iterators ...Iterator) Iterator {
|
||||
return func() Token {
|
||||
for len(iterators) > 0 {
|
||||
t := iterators[0]()
|
||||
if t != EOF {
|
||||
return t
|
||||
}
|
||||
iterators = iterators[1:]
|
||||
}
|
||||
return EOF
|
||||
}
|
||||
}
|
||||
|
||||
// Literator converts a sequence of literal Tokens into an Iterator.
|
||||
func Literator(tokens ...Token) Iterator {
|
||||
return func() Token {
|
||||
if len(tokens) == 0 {
|
||||
return EOF
|
||||
}
|
||||
token := tokens[0]
|
||||
tokens = tokens[1:]
|
||||
return token
|
||||
}
|
||||
}
|
||||
|
||||
// SplitTokensIntoLines splits tokens containing newlines in two.
|
||||
func SplitTokensIntoLines(tokens []Token) (out [][]Token) {
|
||||
var line []Token // nolint: prealloc
|
||||
tokenLoop:
|
||||
for _, token := range tokens {
|
||||
for strings.Contains(token.Value, "\n") {
|
||||
parts := strings.SplitAfterN(token.Value, "\n", 2)
|
||||
// Token becomes the tail.
|
||||
token.Value = parts[1]
|
||||
|
||||
// Append the head to the line and flush the line.
|
||||
clone := token.Clone()
|
||||
clone.Value = parts[0]
|
||||
line = append(line, clone)
|
||||
out = append(out, line)
|
||||
line = nil
|
||||
|
||||
// If the tail token is empty, don't emit it.
|
||||
if len(token.Value) == 0 {
|
||||
continue tokenLoop
|
||||
}
|
||||
}
|
||||
line = append(line, token)
|
||||
}
|
||||
if len(line) > 0 {
|
||||
out = append(out, line)
|
||||
}
|
||||
// Strip empty trailing token line.
|
||||
if len(out) > 0 {
|
||||
last := out[len(out)-1]
|
||||
if len(last) == 1 && last[0].Value == "" {
|
||||
out = out[:len(out)-1]
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
179
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexer.go
generated
vendored
Normal file
179
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexer.go
generated
vendored
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
package chroma
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultOptions = &TokeniseOptions{
|
||||
State: "root",
|
||||
EnsureLF: true,
|
||||
}
|
||||
)
|
||||
|
||||
// Config for a lexer.
|
||||
type Config struct {
|
||||
// Name of the lexer.
|
||||
Name string `xml:"name,omitempty"`
|
||||
|
||||
// Shortcuts for the lexer
|
||||
Aliases []string `xml:"alias,omitempty"`
|
||||
|
||||
// File name globs
|
||||
Filenames []string `xml:"filename,omitempty"`
|
||||
|
||||
// Secondary file name globs
|
||||
AliasFilenames []string `xml:"alias_filename,omitempty"`
|
||||
|
||||
// MIME types
|
||||
MimeTypes []string `xml:"mime_type,omitempty"`
|
||||
|
||||
// Regex matching is case-insensitive.
|
||||
CaseInsensitive bool `xml:"case_insensitive,omitempty"`
|
||||
|
||||
// Regex matches all characters.
|
||||
DotAll bool `xml:"dot_all,omitempty"`
|
||||
|
||||
// Regex does not match across lines ($ matches EOL).
|
||||
//
|
||||
// Defaults to multiline.
|
||||
NotMultiline bool `xml:"not_multiline,omitempty"`
|
||||
|
||||
// Don't strip leading and trailing newlines from the input.
|
||||
// DontStripNL bool
|
||||
|
||||
// Strip all leading and trailing whitespace from the input
|
||||
// StripAll bool
|
||||
|
||||
// Make sure that the input ends with a newline. This
|
||||
// is required for some lexers that consume input linewise.
|
||||
EnsureNL bool `xml:"ensure_nl,omitempty"`
|
||||
|
||||
// If given and greater than 0, expand tabs in the input.
|
||||
// TabSize int
|
||||
|
||||
// Priority of lexer.
|
||||
//
|
||||
// If this is 0 it will be treated as a default of 1.
|
||||
Priority float32 `xml:"priority,omitempty"`
|
||||
|
||||
// Analyse is a list of regexes to match against the input.
|
||||
//
|
||||
// If a match is found, the score is returned if single attribute is set to true,
|
||||
// otherwise the sum of all the score of matching patterns will be
|
||||
// used as the final score.
|
||||
Analyse *AnalyseConfig `xml:"analyse,omitempty"`
|
||||
}
|
||||
|
||||
// AnalyseConfig defines the list of regexes analysers.
|
||||
type AnalyseConfig struct {
|
||||
Regexes []RegexConfig `xml:"regex,omitempty"`
|
||||
// If true, the first matching score is returned.
|
||||
First bool `xml:"first,attr"`
|
||||
}
|
||||
|
||||
// RegexConfig defines a single regex pattern and its score in case of match.
|
||||
type RegexConfig struct {
|
||||
Pattern string `xml:"pattern,attr"`
|
||||
Score float32 `xml:"score,attr"`
|
||||
}
|
||||
|
||||
// Token output to formatter.
|
||||
type Token struct {
|
||||
Type TokenType `json:"type"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
func (t *Token) String() string { return t.Value }
|
||||
func (t *Token) GoString() string { return fmt.Sprintf("&Token{%s, %q}", t.Type, t.Value) }
|
||||
|
||||
// Clone returns a clone of the Token.
|
||||
func (t *Token) Clone() Token {
|
||||
return *t
|
||||
}
|
||||
|
||||
// EOF is returned by lexers at the end of input.
|
||||
var EOF Token
|
||||
|
||||
// TokeniseOptions contains options for tokenisers.
|
||||
type TokeniseOptions struct {
|
||||
// State to start tokenisation in. Defaults to "root".
|
||||
State string
|
||||
// Nested tokenisation.
|
||||
Nested bool
|
||||
|
||||
// If true, all EOLs are converted into LF
|
||||
// by replacing CRLF and CR
|
||||
EnsureLF bool
|
||||
}
|
||||
|
||||
// A Lexer for tokenising source code.
|
||||
type Lexer interface {
|
||||
// Config describing the features of the Lexer.
|
||||
Config() *Config
|
||||
// Tokenise returns an Iterator over tokens in text.
|
||||
Tokenise(options *TokeniseOptions, text string) (Iterator, error)
|
||||
// SetRegistry sets the registry this Lexer is associated with.
|
||||
//
|
||||
// The registry should be used by the Lexer if it needs to look up other
|
||||
// lexers.
|
||||
SetRegistry(registry *LexerRegistry) Lexer
|
||||
// SetAnalyser sets a function the Lexer should use for scoring how
|
||||
// likely a fragment of text is to match this lexer, between 0.0 and 1.0.
|
||||
// A value of 1 indicates high confidence.
|
||||
//
|
||||
// Lexers may ignore this if they implement their own analysers.
|
||||
SetAnalyser(analyser func(text string) float32) Lexer
|
||||
// AnalyseText scores how likely a fragment of text is to match
|
||||
// this lexer, between 0.0 and 1.0. A value of 1 indicates high confidence.
|
||||
AnalyseText(text string) float32
|
||||
}
|
||||
|
||||
// Trace is the trace of a tokenisation process.
|
||||
type Trace struct {
|
||||
Lexer string `json:"lexer"`
|
||||
State string `json:"state"`
|
||||
Rule int `json:"rule"`
|
||||
Pattern string `json:"pattern"`
|
||||
Pos int `json:"pos"`
|
||||
Length int `json:"length"`
|
||||
Elapsed float64 `json:"elapsedMs"` // Elapsed time spent matching for this rule.
|
||||
}
|
||||
|
||||
// TracingLexer is a Lexer that can trace its tokenisation process.
|
||||
type TracingLexer interface {
|
||||
Lexer
|
||||
SetTracing(enable bool)
|
||||
}
|
||||
|
||||
// Lexers is a slice of lexers sortable by name.
|
||||
type Lexers []Lexer
|
||||
|
||||
func (l Lexers) Len() int { return len(l) }
|
||||
func (l Lexers) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
|
||||
func (l Lexers) Less(i, j int) bool {
|
||||
return strings.ToLower(l[i].Config().Name) < strings.ToLower(l[j].Config().Name)
|
||||
}
|
||||
|
||||
// PrioritisedLexers is a slice of lexers sortable by priority.
|
||||
type PrioritisedLexers []Lexer
|
||||
|
||||
func (l PrioritisedLexers) Len() int { return len(l) }
|
||||
func (l PrioritisedLexers) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
|
||||
func (l PrioritisedLexers) Less(i, j int) bool {
|
||||
ip := l[i].Config().Priority
|
||||
if ip == 0 {
|
||||
ip = 1
|
||||
}
|
||||
jp := l[j].Config().Priority
|
||||
if jp == 0 {
|
||||
jp = 1
|
||||
}
|
||||
return ip > jp
|
||||
}
|
||||
|
||||
// Analyser determines how appropriate this lexer is for the given text.
|
||||
type Analyser interface {
|
||||
AnalyseText(text string) float32
|
||||
}
|
||||
46
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/README.md
generated
vendored
Normal file
46
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
# Chroma lexers
|
||||
|
||||
All lexers in Chroma should now be defined in XML unless they require custom code.
|
||||
|
||||
## Lexer tests
|
||||
|
||||
The tests in this directory feed a known input `testdata/<name>.actual` into the parser for `<name>` and check
|
||||
that its output matches `<name>.expected`.
|
||||
|
||||
It is also possible to perform several tests on a same parser `<name>`, by placing know inputs `*.actual` into a
|
||||
directory `testdata/<name>/`.
|
||||
|
||||
### Running the tests
|
||||
|
||||
Run the tests as normal:
|
||||
```go
|
||||
go test ./lexers
|
||||
```
|
||||
|
||||
### Update existing tests
|
||||
|
||||
When you add a new test data file (`*.actual`), you need to regenerate all tests. That's how Chroma creates the `*.expected` test file based on the corresponding lexer.
|
||||
|
||||
To regenerate all tests, type in your terminal:
|
||||
|
||||
```go
|
||||
RECORD=true go test ./lexers
|
||||
```
|
||||
|
||||
This first sets the `RECORD` environment variable to `true`. Then it runs `go test` on the `./lexers` directory of the Chroma project.
|
||||
|
||||
(That environment variable tells Chroma it needs to output test data. After running `go test ./lexers` you can remove or reset that variable.)
|
||||
|
||||
#### Windows users
|
||||
|
||||
Windows users will find that the `RECORD=true go test ./lexers` command fails in both the standard command prompt terminal and in PowerShell.
|
||||
|
||||
Instead we have to perform both steps separately:
|
||||
|
||||
- Set the `RECORD` environment variable to `true`.
|
||||
+ In the regular command prompt window, the `set` command sets an environment variable for the current session: `set RECORD=true`. See [this page](https://superuser.com/questions/212150/how-to-set-env-variable-in-windows-cmd-line) for more.
|
||||
+ In PowerShell, you can use the `$env:RECORD = 'true'` command for that. See [this article](https://mcpmag.com/articles/2019/03/28/environment-variables-in-powershell.aspx) for more.
|
||||
+ You can also make a persistent environment variable by hand in the Windows computer settings. See [this article](https://www.computerhope.com/issues/ch000549.htm) for how.
|
||||
- When the environment variable is set, run `go test ./lexers`.
|
||||
|
||||
Chroma will now regenerate the test files and print its results to the console window.
|
||||
275
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/caddyfile.go
generated
vendored
Normal file
275
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/caddyfile.go
generated
vendored
Normal file
|
|
@ -0,0 +1,275 @@
|
|||
package lexers
|
||||
|
||||
import (
|
||||
. "github.com/alecthomas/chroma/v2" // nolint
|
||||
)
|
||||
|
||||
// Matcher token stub for docs, or
|
||||
// Named matcher: @name, or
|
||||
// Path matcher: /foo, or
|
||||
// Wildcard path matcher: *
|
||||
// nolint: gosec
|
||||
var caddyfileMatcherTokenRegexp = `(\[\<matcher\>\]|@[^\s]+|/[^\s]+|\*)`
|
||||
|
||||
// Comment at start of line, or
|
||||
// Comment preceded by whitespace
|
||||
var caddyfileCommentRegexp = `(^|\s+)#.*\n`
|
||||
|
||||
// caddyfileCommon are the rules common to both of the lexer variants
|
||||
func caddyfileCommonRules() Rules {
|
||||
return Rules{
|
||||
"site_block_common": {
|
||||
Include("site_body"),
|
||||
// Any other directive
|
||||
{`[^\s#]+`, Keyword, Push("directive")},
|
||||
Include("base"),
|
||||
},
|
||||
"site_body": {
|
||||
// Import keyword
|
||||
{`\b(import|invoke)\b( [^\s#]+)`, ByGroups(Keyword, Text), Push("subdirective")},
|
||||
// Matcher definition
|
||||
{`@[^\s]+(?=\s)`, NameDecorator, Push("matcher")},
|
||||
// Matcher token stub for docs
|
||||
{`\[\<matcher\>\]`, NameDecorator, Push("matcher")},
|
||||
// These cannot have matchers but may have things that look like
|
||||
// matchers in their arguments, so we just parse as a subdirective.
|
||||
{`\b(try_files|tls|log|bind)\b`, Keyword, Push("subdirective")},
|
||||
// These are special, they can nest more directives
|
||||
{`\b(handle_errors|handle_path|handle_response|replace_status|handle|route)\b`, Keyword, Push("nested_directive")},
|
||||
// uri directive has special syntax
|
||||
{`\b(uri)\b`, Keyword, Push("uri_directive")},
|
||||
},
|
||||
"matcher": {
|
||||
{`\{`, Punctuation, Push("block")},
|
||||
// Not can be one-liner
|
||||
{`not`, Keyword, Push("deep_not_matcher")},
|
||||
// Heredoc for CEL expression
|
||||
Include("heredoc"),
|
||||
// Backtick for CEL expression
|
||||
{"`", StringBacktick, Push("backticks")},
|
||||
// Any other same-line matcher
|
||||
{`[^\s#]+`, Keyword, Push("arguments")},
|
||||
// Terminators
|
||||
{`\s*\n`, Text, Pop(1)},
|
||||
{`\}`, Punctuation, Pop(1)},
|
||||
Include("base"),
|
||||
},
|
||||
"block": {
|
||||
{`\}`, Punctuation, Pop(2)},
|
||||
// Using double quotes doesn't stop at spaces
|
||||
{`"`, StringDouble, Push("double_quotes")},
|
||||
// Using backticks doesn't stop at spaces
|
||||
{"`", StringBacktick, Push("backticks")},
|
||||
// Not can be one-liner
|
||||
{`not`, Keyword, Push("not_matcher")},
|
||||
// Directives & matcher definitions
|
||||
Include("site_body"),
|
||||
// Any directive
|
||||
{`[^\s#]+`, Keyword, Push("subdirective")},
|
||||
Include("base"),
|
||||
},
|
||||
"nested_block": {
|
||||
{`\}`, Punctuation, Pop(2)},
|
||||
// Using double quotes doesn't stop at spaces
|
||||
{`"`, StringDouble, Push("double_quotes")},
|
||||
// Using backticks doesn't stop at spaces
|
||||
{"`", StringBacktick, Push("backticks")},
|
||||
// Not can be one-liner
|
||||
{`not`, Keyword, Push("not_matcher")},
|
||||
// Directives & matcher definitions
|
||||
Include("site_body"),
|
||||
// Any other subdirective
|
||||
{`[^\s#]+`, Keyword, Push("directive")},
|
||||
Include("base"),
|
||||
},
|
||||
"not_matcher": {
|
||||
{`\}`, Punctuation, Pop(2)},
|
||||
{`\{(?=\s)`, Punctuation, Push("block")},
|
||||
{`[^\s#]+`, Keyword, Push("arguments")},
|
||||
{`\s+`, Text, nil},
|
||||
},
|
||||
"deep_not_matcher": {
|
||||
{`\}`, Punctuation, Pop(2)},
|
||||
{`\{(?=\s)`, Punctuation, Push("block")},
|
||||
{`[^\s#]+`, Keyword, Push("deep_subdirective")},
|
||||
{`\s+`, Text, nil},
|
||||
},
|
||||
"directive": {
|
||||
{`\{(?=\s)`, Punctuation, Push("block")},
|
||||
{caddyfileMatcherTokenRegexp, NameDecorator, Push("arguments")},
|
||||
{caddyfileCommentRegexp, CommentSingle, Pop(1)},
|
||||
{`\s*\n`, Text, Pop(1)},
|
||||
Include("base"),
|
||||
},
|
||||
"nested_directive": {
|
||||
{`\{(?=\s)`, Punctuation, Push("nested_block")},
|
||||
{caddyfileMatcherTokenRegexp, NameDecorator, Push("nested_arguments")},
|
||||
{caddyfileCommentRegexp, CommentSingle, Pop(1)},
|
||||
{`\s*\n`, Text, Pop(1)},
|
||||
Include("base"),
|
||||
},
|
||||
"subdirective": {
|
||||
{`\{(?=\s)`, Punctuation, Push("block")},
|
||||
{caddyfileCommentRegexp, CommentSingle, Pop(1)},
|
||||
{`\s*\n`, Text, Pop(1)},
|
||||
Include("base"),
|
||||
},
|
||||
"arguments": {
|
||||
{`\{(?=\s)`, Punctuation, Push("block")},
|
||||
{caddyfileCommentRegexp, CommentSingle, Pop(2)},
|
||||
{`\\\n`, Text, nil}, // Skip escaped newlines
|
||||
{`\s*\n`, Text, Pop(2)},
|
||||
Include("base"),
|
||||
},
|
||||
"nested_arguments": {
|
||||
{`\{(?=\s)`, Punctuation, Push("nested_block")},
|
||||
{caddyfileCommentRegexp, CommentSingle, Pop(2)},
|
||||
{`\\\n`, Text, nil}, // Skip escaped newlines
|
||||
{`\s*\n`, Text, Pop(2)},
|
||||
Include("base"),
|
||||
},
|
||||
"deep_subdirective": {
|
||||
{`\{(?=\s)`, Punctuation, Push("block")},
|
||||
{caddyfileCommentRegexp, CommentSingle, Pop(3)},
|
||||
{`\s*\n`, Text, Pop(3)},
|
||||
Include("base"),
|
||||
},
|
||||
"uri_directive": {
|
||||
{`\{(?=\s)`, Punctuation, Push("block")},
|
||||
{caddyfileMatcherTokenRegexp, NameDecorator, nil},
|
||||
{`(strip_prefix|strip_suffix|replace|path_regexp)`, NameConstant, Push("arguments")},
|
||||
{caddyfileCommentRegexp, CommentSingle, Pop(1)},
|
||||
{`\s*\n`, Text, Pop(1)},
|
||||
Include("base"),
|
||||
},
|
||||
"double_quotes": {
|
||||
Include("placeholder"),
|
||||
{`\\"`, StringDouble, nil},
|
||||
{`[^"]`, StringDouble, nil},
|
||||
{`"`, StringDouble, Pop(1)},
|
||||
},
|
||||
"backticks": {
|
||||
Include("placeholder"),
|
||||
{"\\\\`", StringBacktick, nil},
|
||||
{"[^`]", StringBacktick, nil},
|
||||
{"`", StringBacktick, Pop(1)},
|
||||
},
|
||||
"optional": {
|
||||
// Docs syntax for showing optional parts with [ ]
|
||||
{`\[`, Punctuation, Push("optional")},
|
||||
Include("name_constants"),
|
||||
{`\|`, Punctuation, nil},
|
||||
{`[^\[\]\|]+`, String, nil},
|
||||
{`\]`, Punctuation, Pop(1)},
|
||||
},
|
||||
"heredoc": {
|
||||
{`(<<([a-zA-Z0-9_-]+))(\n(.*|\n)*)(\s*)(\2)`, ByGroups(StringHeredoc, nil, String, String, String, StringHeredoc), nil},
|
||||
},
|
||||
"name_constants": {
|
||||
{`\b(most_recently_modified|largest_size|smallest_size|first_exist|internal|disable_redirects|ignore_loaded_certs|disable_certs|private_ranges|first|last|before|after|on|off)\b(\||(?=\]|\s|$))`, ByGroups(NameConstant, Punctuation), nil},
|
||||
},
|
||||
"placeholder": {
|
||||
// Placeholder with dots, colon for default value, brackets for args[0:]
|
||||
{`\{[\w+.\[\]\:\$-]+\}`, StringEscape, nil},
|
||||
// Handle opening brackets with no matching closing one
|
||||
{`\{[^\}\s]*\b`, String, nil},
|
||||
},
|
||||
"base": {
|
||||
{caddyfileCommentRegexp, CommentSingle, nil},
|
||||
{`\[\<matcher\>\]`, NameDecorator, nil},
|
||||
Include("name_constants"),
|
||||
Include("heredoc"),
|
||||
{`(https?://)?([a-z0-9.-]+)(:)([0-9]+)([^\s]*)`, ByGroups(Name, Name, Punctuation, NumberInteger, Name), nil},
|
||||
{`\[`, Punctuation, Push("optional")},
|
||||
{"`", StringBacktick, Push("backticks")},
|
||||
{`"`, StringDouble, Push("double_quotes")},
|
||||
Include("placeholder"),
|
||||
{`[a-z-]+/[a-z-+]+`, String, nil},
|
||||
{`[0-9]+([smhdk]|ns|us|µs|ms)?\b`, NumberInteger, nil},
|
||||
{`[^\s\n#\{]+`, String, nil},
|
||||
{`/[^\s#]*`, Name, nil},
|
||||
{`\s+`, Text, nil},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Caddyfile lexer.
|
||||
var Caddyfile = Register(MustNewLexer(
|
||||
&Config{
|
||||
Name: "Caddyfile",
|
||||
Aliases: []string{"caddyfile", "caddy"},
|
||||
Filenames: []string{"Caddyfile*"},
|
||||
MimeTypes: []string{},
|
||||
},
|
||||
caddyfileRules,
|
||||
))
|
||||
|
||||
func caddyfileRules() Rules {
|
||||
return Rules{
|
||||
"root": {
|
||||
{caddyfileCommentRegexp, CommentSingle, nil},
|
||||
// Global options block
|
||||
{`^\s*(\{)\s*$`, ByGroups(Punctuation), Push("globals")},
|
||||
// Top level import
|
||||
{`(import)(\s+)([^\s]+)`, ByGroups(Keyword, Text, NameVariableMagic), nil},
|
||||
// Snippets
|
||||
{`(&?\([^\s#]+\))(\s*)(\{)`, ByGroups(NameVariableAnonymous, Text, Punctuation), Push("snippet")},
|
||||
// Site label
|
||||
{`[^#{(\s,]+`, GenericHeading, Push("label")},
|
||||
// Site label with placeholder
|
||||
{`\{[\w+.\[\]\:\$-]+\}`, StringEscape, Push("label")},
|
||||
{`\s+`, Text, nil},
|
||||
},
|
||||
"globals": {
|
||||
{`\}`, Punctuation, Pop(1)},
|
||||
// Global options are parsed as subdirectives (no matcher)
|
||||
{`[^\s#]+`, Keyword, Push("subdirective")},
|
||||
Include("base"),
|
||||
},
|
||||
"snippet": {
|
||||
{`\}`, Punctuation, Pop(1)},
|
||||
Include("site_body"),
|
||||
// Any other directive
|
||||
{`[^\s#]+`, Keyword, Push("directive")},
|
||||
Include("base"),
|
||||
},
|
||||
"label": {
|
||||
// Allow multiple labels, comma separated, newlines after
|
||||
// a comma means another label is coming
|
||||
{`,\s*\n?`, Text, nil},
|
||||
{` `, Text, nil},
|
||||
// Site label with placeholder
|
||||
Include("placeholder"),
|
||||
// Site label
|
||||
{`[^#{(\s,]+`, GenericHeading, nil},
|
||||
// Comment after non-block label (hack because comments end in \n)
|
||||
{`#.*\n`, CommentSingle, Push("site_block")},
|
||||
// Note: if \n, we'll never pop out of the site_block, it's valid
|
||||
{`\{(?=\s)|\n`, Punctuation, Push("site_block")},
|
||||
},
|
||||
"site_block": {
|
||||
{`\}`, Punctuation, Pop(2)},
|
||||
Include("site_block_common"),
|
||||
},
|
||||
}.Merge(caddyfileCommonRules())
|
||||
}
|
||||
|
||||
// Caddyfile directive-only lexer.
|
||||
var CaddyfileDirectives = Register(MustNewLexer(
|
||||
&Config{
|
||||
Name: "Caddyfile Directives",
|
||||
Aliases: []string{"caddyfile-directives", "caddyfile-d", "caddy-d"},
|
||||
Filenames: []string{},
|
||||
MimeTypes: []string{},
|
||||
},
|
||||
caddyfileDirectivesRules,
|
||||
))
|
||||
|
||||
func caddyfileDirectivesRules() Rules {
|
||||
return Rules{
|
||||
// Same as "site_block" in Caddyfile
|
||||
"root": {
|
||||
Include("site_block_common"),
|
||||
},
|
||||
}.Merge(caddyfileCommonRules())
|
||||
}
|
||||
243
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/cl.go
generated
vendored
Normal file
243
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/cl.go
generated
vendored
Normal file
|
|
@ -0,0 +1,243 @@
|
|||
package lexers
|
||||
|
||||
import (
|
||||
. "github.com/alecthomas/chroma/v2" // nolint
|
||||
)
|
||||
|
||||
var (
|
||||
clBuiltinFunctions = []string{
|
||||
"<", "<=", "=", ">", ">=", "-", "/", "/=", "*", "+", "1-", "1+",
|
||||
"abort", "abs", "acons", "acos", "acosh", "add-method", "adjoin",
|
||||
"adjustable-array-p", "adjust-array", "allocate-instance",
|
||||
"alpha-char-p", "alphanumericp", "append", "apply", "apropos",
|
||||
"apropos-list", "aref", "arithmetic-error-operands",
|
||||
"arithmetic-error-operation", "array-dimension", "array-dimensions",
|
||||
"array-displacement", "array-element-type", "array-has-fill-pointer-p",
|
||||
"array-in-bounds-p", "arrayp", "array-rank", "array-row-major-index",
|
||||
"array-total-size", "ash", "asin", "asinh", "assoc", "assoc-if",
|
||||
"assoc-if-not", "atan", "atanh", "atom", "bit", "bit-and", "bit-andc1",
|
||||
"bit-andc2", "bit-eqv", "bit-ior", "bit-nand", "bit-nor", "bit-not",
|
||||
"bit-orc1", "bit-orc2", "bit-vector-p", "bit-xor", "boole",
|
||||
"both-case-p", "boundp", "break", "broadcast-stream-streams",
|
||||
"butlast", "byte", "byte-position", "byte-size", "caaaar", "caaadr",
|
||||
"caaar", "caadar", "caaddr", "caadr", "caar", "cadaar", "cadadr",
|
||||
"cadar", "caddar", "cadddr", "caddr", "cadr", "call-next-method", "car",
|
||||
"cdaaar", "cdaadr", "cdaar", "cdadar", "cdaddr", "cdadr", "cdar",
|
||||
"cddaar", "cddadr", "cddar", "cdddar", "cddddr", "cdddr", "cddr", "cdr",
|
||||
"ceiling", "cell-error-name", "cerror", "change-class", "char", "char<",
|
||||
"char<=", "char=", "char>", "char>=", "char/=", "character",
|
||||
"characterp", "char-code", "char-downcase", "char-equal",
|
||||
"char-greaterp", "char-int", "char-lessp", "char-name",
|
||||
"char-not-equal", "char-not-greaterp", "char-not-lessp", "char-upcase",
|
||||
"cis", "class-name", "class-of", "clear-input", "clear-output",
|
||||
"close", "clrhash", "code-char", "coerce", "compile",
|
||||
"compiled-function-p", "compile-file", "compile-file-pathname",
|
||||
"compiler-macro-function", "complement", "complex", "complexp",
|
||||
"compute-applicable-methods", "compute-restarts", "concatenate",
|
||||
"concatenated-stream-streams", "conjugate", "cons", "consp",
|
||||
"constantly", "constantp", "continue", "copy-alist", "copy-list",
|
||||
"copy-pprint-dispatch", "copy-readtable", "copy-seq", "copy-structure",
|
||||
"copy-symbol", "copy-tree", "cos", "cosh", "count", "count-if",
|
||||
"count-if-not", "decode-float", "decode-universal-time", "delete",
|
||||
"delete-duplicates", "delete-file", "delete-if", "delete-if-not",
|
||||
"delete-package", "denominator", "deposit-field", "describe",
|
||||
"describe-object", "digit-char", "digit-char-p", "directory",
|
||||
"directory-namestring", "disassemble", "documentation", "dpb",
|
||||
"dribble", "echo-stream-input-stream", "echo-stream-output-stream",
|
||||
"ed", "eighth", "elt", "encode-universal-time", "endp",
|
||||
"enough-namestring", "ensure-directories-exist",
|
||||
"ensure-generic-function", "eq", "eql", "equal", "equalp", "error",
|
||||
"eval", "evenp", "every", "exp", "export", "expt", "fboundp",
|
||||
"fceiling", "fdefinition", "ffloor", "fifth", "file-author",
|
||||
"file-error-pathname", "file-length", "file-namestring",
|
||||
"file-position", "file-string-length", "file-write-date",
|
||||
"fill", "fill-pointer", "find", "find-all-symbols", "find-class",
|
||||
"find-if", "find-if-not", "find-method", "find-package", "find-restart",
|
||||
"find-symbol", "finish-output", "first", "float", "float-digits",
|
||||
"floatp", "float-precision", "float-radix", "float-sign", "floor",
|
||||
"fmakunbound", "force-output", "format", "fourth", "fresh-line",
|
||||
"fround", "ftruncate", "funcall", "function-keywords",
|
||||
"function-lambda-expression", "functionp", "gcd", "gensym", "gentemp",
|
||||
"get", "get-decoded-time", "get-dispatch-macro-character", "getf",
|
||||
"gethash", "get-internal-real-time", "get-internal-run-time",
|
||||
"get-macro-character", "get-output-stream-string", "get-properties",
|
||||
"get-setf-expansion", "get-universal-time", "graphic-char-p",
|
||||
"hash-table-count", "hash-table-p", "hash-table-rehash-size",
|
||||
"hash-table-rehash-threshold", "hash-table-size", "hash-table-test",
|
||||
"host-namestring", "identity", "imagpart", "import",
|
||||
"initialize-instance", "input-stream-p", "inspect",
|
||||
"integer-decode-float", "integer-length", "integerp",
|
||||
"interactive-stream-p", "intern", "intersection",
|
||||
"invalid-method-error", "invoke-debugger", "invoke-restart",
|
||||
"invoke-restart-interactively", "isqrt", "keywordp", "last", "lcm",
|
||||
"ldb", "ldb-test", "ldiff", "length", "lisp-implementation-type",
|
||||
"lisp-implementation-version", "list", "list*", "list-all-packages",
|
||||
"listen", "list-length", "listp", "load",
|
||||
"load-logical-pathname-translations", "log", "logand", "logandc1",
|
||||
"logandc2", "logbitp", "logcount", "logeqv", "logical-pathname",
|
||||
"logical-pathname-translations", "logior", "lognand", "lognor",
|
||||
"lognot", "logorc1", "logorc2", "logtest", "logxor", "long-site-name",
|
||||
"lower-case-p", "machine-instance", "machine-type", "machine-version",
|
||||
"macroexpand", "macroexpand-1", "macro-function", "make-array",
|
||||
"make-broadcast-stream", "make-concatenated-stream", "make-condition",
|
||||
"make-dispatch-macro-character", "make-echo-stream", "make-hash-table",
|
||||
"make-instance", "make-instances-obsolete", "make-list",
|
||||
"make-load-form", "make-load-form-saving-slots", "make-package",
|
||||
"make-pathname", "make-random-state", "make-sequence", "make-string",
|
||||
"make-string-input-stream", "make-string-output-stream", "make-symbol",
|
||||
"make-synonym-stream", "make-two-way-stream", "makunbound", "map",
|
||||
"mapc", "mapcan", "mapcar", "mapcon", "maphash", "map-into", "mapl",
|
||||
"maplist", "mask-field", "max", "member", "member-if", "member-if-not",
|
||||
"merge", "merge-pathnames", "method-combination-error",
|
||||
"method-qualifiers", "min", "minusp", "mismatch", "mod",
|
||||
"muffle-warning", "name-char", "namestring", "nbutlast", "nconc",
|
||||
"next-method-p", "nintersection", "ninth", "no-applicable-method",
|
||||
"no-next-method", "not", "notany", "notevery", "nreconc", "nreverse",
|
||||
"nset-difference", "nset-exclusive-or", "nstring-capitalize",
|
||||
"nstring-downcase", "nstring-upcase", "nsublis", "nsubst", "nsubst-if",
|
||||
"nsubst-if-not", "nsubstitute", "nsubstitute-if", "nsubstitute-if-not",
|
||||
"nth", "nthcdr", "null", "numberp", "numerator", "nunion", "oddp",
|
||||
"open", "open-stream-p", "output-stream-p", "package-error-package",
|
||||
"package-name", "package-nicknames", "packagep",
|
||||
"package-shadowing-symbols", "package-used-by-list", "package-use-list",
|
||||
"pairlis", "parse-integer", "parse-namestring", "pathname",
|
||||
"pathname-device", "pathname-directory", "pathname-host",
|
||||
"pathname-match-p", "pathname-name", "pathnamep", "pathname-type",
|
||||
"pathname-version", "peek-char", "phase", "plusp", "position",
|
||||
"position-if", "position-if-not", "pprint", "pprint-dispatch",
|
||||
"pprint-fill", "pprint-indent", "pprint-linear", "pprint-newline",
|
||||
"pprint-tab", "pprint-tabular", "prin1", "prin1-to-string", "princ",
|
||||
"princ-to-string", "print", "print-object", "probe-file", "proclaim",
|
||||
"provide", "random", "random-state-p", "rassoc", "rassoc-if",
|
||||
"rassoc-if-not", "rational", "rationalize", "rationalp", "read",
|
||||
"read-byte", "read-char", "read-char-no-hang", "read-delimited-list",
|
||||
"read-from-string", "read-line", "read-preserving-whitespace",
|
||||
"read-sequence", "readtable-case", "readtablep", "realp", "realpart",
|
||||
"reduce", "reinitialize-instance", "rem", "remhash", "remove",
|
||||
"remove-duplicates", "remove-if", "remove-if-not", "remove-method",
|
||||
"remprop", "rename-file", "rename-package", "replace", "require",
|
||||
"rest", "restart-name", "revappend", "reverse", "room", "round",
|
||||
"row-major-aref", "rplaca", "rplacd", "sbit", "scale-float", "schar",
|
||||
"search", "second", "set", "set-difference",
|
||||
"set-dispatch-macro-character", "set-exclusive-or",
|
||||
"set-macro-character", "set-pprint-dispatch", "set-syntax-from-char",
|
||||
"seventh", "shadow", "shadowing-import", "shared-initialize",
|
||||
"short-site-name", "signal", "signum", "simple-bit-vector-p",
|
||||
"simple-condition-format-arguments", "simple-condition-format-control",
|
||||
"simple-string-p", "simple-vector-p", "sin", "sinh", "sixth", "sleep",
|
||||
"slot-boundp", "slot-exists-p", "slot-makunbound", "slot-missing",
|
||||
"slot-unbound", "slot-value", "software-type", "software-version",
|
||||
"some", "sort", "special-operator-p", "sqrt", "stable-sort",
|
||||
"standard-char-p", "store-value", "stream-element-type",
|
||||
"stream-error-stream", "stream-external-format", "streamp", "string",
|
||||
"string<", "string<=", "string=", "string>", "string>=", "string/=",
|
||||
"string-capitalize", "string-downcase", "string-equal",
|
||||
"string-greaterp", "string-left-trim", "string-lessp",
|
||||
"string-not-equal", "string-not-greaterp", "string-not-lessp",
|
||||
"stringp", "string-right-trim", "string-trim", "string-upcase",
|
||||
"sublis", "subseq", "subsetp", "subst", "subst-if", "subst-if-not",
|
||||
"substitute", "substitute-if", "substitute-if-not", "subtypep", "svref",
|
||||
"sxhash", "symbol-function", "symbol-name", "symbolp", "symbol-package",
|
||||
"symbol-plist", "symbol-value", "synonym-stream-symbol", "syntax:",
|
||||
"tailp", "tan", "tanh", "tenth", "terpri", "third",
|
||||
"translate-logical-pathname", "translate-pathname", "tree-equal",
|
||||
"truename", "truncate", "two-way-stream-input-stream",
|
||||
"two-way-stream-output-stream", "type-error-datum",
|
||||
"type-error-expected-type", "type-of", "typep", "unbound-slot-instance",
|
||||
"unexport", "unintern", "union", "unread-char", "unuse-package",
|
||||
"update-instance-for-different-class",
|
||||
"update-instance-for-redefined-class", "upgraded-array-element-type",
|
||||
"upgraded-complex-part-type", "upper-case-p", "use-package",
|
||||
"user-homedir-pathname", "use-value", "values", "values-list", "vector",
|
||||
"vectorp", "vector-pop", "vector-push", "vector-push-extend", "warn",
|
||||
"wild-pathname-p", "write", "write-byte", "write-char", "write-line",
|
||||
"write-sequence", "write-string", "write-to-string", "yes-or-no-p",
|
||||
"y-or-n-p", "zerop",
|
||||
}
|
||||
|
||||
clSpecialForms = []string{
|
||||
"block", "catch", "declare", "eval-when", "flet", "function", "go", "if",
|
||||
"labels", "lambda", "let", "let*", "load-time-value", "locally", "macrolet",
|
||||
"multiple-value-call", "multiple-value-prog1", "progn", "progv", "quote",
|
||||
"return-from", "setq", "symbol-macrolet", "tagbody", "the", "throw",
|
||||
"unwind-protect",
|
||||
}
|
||||
|
||||
clMacros = []string{
|
||||
"and", "assert", "call-method", "case", "ccase", "check-type", "cond",
|
||||
"ctypecase", "decf", "declaim", "defclass", "defconstant", "defgeneric",
|
||||
"define-compiler-macro", "define-condition", "define-method-combination",
|
||||
"define-modify-macro", "define-setf-expander", "define-symbol-macro",
|
||||
"defmacro", "defmethod", "defpackage", "defparameter", "defsetf",
|
||||
"defstruct", "deftype", "defun", "defvar", "destructuring-bind", "do",
|
||||
"do*", "do-all-symbols", "do-external-symbols", "dolist", "do-symbols",
|
||||
"dotimes", "ecase", "etypecase", "formatter", "handler-bind",
|
||||
"handler-case", "ignore-errors", "incf", "in-package", "lambda", "loop",
|
||||
"loop-finish", "make-method", "multiple-value-bind", "multiple-value-list",
|
||||
"multiple-value-setq", "nth-value", "or", "pop",
|
||||
"pprint-exit-if-list-exhausted", "pprint-logical-block", "pprint-pop",
|
||||
"print-unreadable-object", "prog", "prog*", "prog1", "prog2", "psetf",
|
||||
"psetq", "push", "pushnew", "remf", "restart-bind", "restart-case",
|
||||
"return", "rotatef", "setf", "shiftf", "step", "time", "trace", "typecase",
|
||||
"unless", "untrace", "when", "with-accessors", "with-compilation-unit",
|
||||
"with-condition-restarts", "with-hash-table-iterator",
|
||||
"with-input-from-string", "with-open-file", "with-open-stream",
|
||||
"with-output-to-string", "with-package-iterator", "with-simple-restart",
|
||||
"with-slots", "with-standard-io-syntax",
|
||||
}
|
||||
|
||||
clLambdaListKeywords = []string{
|
||||
"&allow-other-keys", "&aux", "&body", "&environment", "&key", "&optional",
|
||||
"&rest", "&whole",
|
||||
}
|
||||
|
||||
clDeclarations = []string{
|
||||
"dynamic-extent", "ignore", "optimize", "ftype", "inline", "special",
|
||||
"ignorable", "notinline", "type",
|
||||
}
|
||||
|
||||
clBuiltinTypes = []string{
|
||||
"atom", "boolean", "base-char", "base-string", "bignum", "bit",
|
||||
"compiled-function", "extended-char", "fixnum", "keyword", "nil",
|
||||
"signed-byte", "short-float", "single-float", "double-float", "long-float",
|
||||
"simple-array", "simple-base-string", "simple-bit-vector", "simple-string",
|
||||
"simple-vector", "standard-char", "unsigned-byte",
|
||||
|
||||
// Condition Types
|
||||
"arithmetic-error", "cell-error", "condition", "control-error",
|
||||
"division-by-zero", "end-of-file", "error", "file-error",
|
||||
"floating-point-inexact", "floating-point-overflow",
|
||||
"floating-point-underflow", "floating-point-invalid-operation",
|
||||
"parse-error", "package-error", "print-not-readable", "program-error",
|
||||
"reader-error", "serious-condition", "simple-condition", "simple-error",
|
||||
"simple-type-error", "simple-warning", "stream-error", "storage-condition",
|
||||
"style-warning", "type-error", "unbound-variable", "unbound-slot",
|
||||
"undefined-function", "warning",
|
||||
}
|
||||
|
||||
clBuiltinClasses = []string{
|
||||
"array", "broadcast-stream", "bit-vector", "built-in-class", "character",
|
||||
"class", "complex", "concatenated-stream", "cons", "echo-stream",
|
||||
"file-stream", "float", "function", "generic-function", "hash-table",
|
||||
"integer", "list", "logical-pathname", "method-combination", "method",
|
||||
"null", "number", "package", "pathname", "ratio", "rational", "readtable",
|
||||
"real", "random-state", "restart", "sequence", "standard-class",
|
||||
"standard-generic-function", "standard-method", "standard-object",
|
||||
"string-stream", "stream", "string", "structure-class", "structure-object",
|
||||
"symbol", "synonym-stream", "t", "two-way-stream", "vector",
|
||||
}
|
||||
)
|
||||
|
||||
// Common Lisp lexer.
|
||||
var CommonLisp = Register(TypeRemappingLexer(MustNewXMLLexer(
|
||||
embedded,
|
||||
"embedded/common_lisp.xml",
|
||||
), TypeMapping{
|
||||
{NameVariable, NameFunction, clBuiltinFunctions},
|
||||
{NameVariable, Keyword, clSpecialForms},
|
||||
{NameVariable, NameBuiltin, clMacros},
|
||||
{NameVariable, Keyword, clLambdaListKeywords},
|
||||
{NameVariable, Keyword, clDeclarations},
|
||||
{NameVariable, KeywordType, clBuiltinTypes},
|
||||
{NameVariable, NameClass, clBuiltinClasses},
|
||||
}))
|
||||
17
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/dns.go
generated
vendored
Normal file
17
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/dns.go
generated
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package lexers
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// TODO(moorereason): can this be factored away?
|
||||
var zoneAnalyserRe = regexp.MustCompile(`(?m)^@\s+IN\s+SOA\s+`)
|
||||
|
||||
func init() { // nolint: gochecknoinits
|
||||
Get("dns").SetAnalyser(func(text string) float32 {
|
||||
if zoneAnalyserRe.FindString(text) != "" {
|
||||
return 1.0
|
||||
}
|
||||
return 0.0
|
||||
})
|
||||
}
|
||||
533
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/emacs.go
generated
vendored
Normal file
533
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/emacs.go
generated
vendored
Normal file
|
|
@ -0,0 +1,533 @@
|
|||
package lexers
|
||||
|
||||
import (
|
||||
. "github.com/alecthomas/chroma/v2" // nolint
|
||||
)
|
||||
|
||||
var (
|
||||
emacsMacros = []string{
|
||||
"atomic-change-group", "case", "block", "cl-block", "cl-callf", "cl-callf2",
|
||||
"cl-case", "cl-decf", "cl-declaim", "cl-declare",
|
||||
"cl-define-compiler-macro", "cl-defmacro", "cl-defstruct",
|
||||
"cl-defsubst", "cl-deftype", "cl-defun", "cl-destructuring-bind",
|
||||
"cl-do", "cl-do*", "cl-do-all-symbols", "cl-do-symbols", "cl-dolist",
|
||||
"cl-dotimes", "cl-ecase", "cl-etypecase", "eval-when", "cl-eval-when", "cl-flet",
|
||||
"cl-flet*", "cl-function", "cl-incf", "cl-labels", "cl-letf",
|
||||
"cl-letf*", "cl-load-time-value", "cl-locally", "cl-loop",
|
||||
"cl-macrolet", "cl-multiple-value-bind", "cl-multiple-value-setq",
|
||||
"cl-progv", "cl-psetf", "cl-psetq", "cl-pushnew", "cl-remf",
|
||||
"cl-return", "cl-return-from", "cl-rotatef", "cl-shiftf",
|
||||
"cl-symbol-macrolet", "cl-tagbody", "cl-the", "cl-typecase",
|
||||
"combine-after-change-calls", "condition-case-unless-debug", "decf",
|
||||
"declaim", "declare", "declare-function", "def-edebug-spec",
|
||||
"defadvice", "defclass", "defcustom", "defface", "defgeneric",
|
||||
"defgroup", "define-advice", "define-alternatives",
|
||||
"define-compiler-macro", "define-derived-mode", "define-generic-mode",
|
||||
"define-global-minor-mode", "define-globalized-minor-mode",
|
||||
"define-minor-mode", "define-modify-macro",
|
||||
"define-obsolete-face-alias", "define-obsolete-function-alias",
|
||||
"define-obsolete-variable-alias", "define-setf-expander",
|
||||
"define-skeleton", "defmacro", "defmethod", "defsetf", "defstruct",
|
||||
"defsubst", "deftheme", "deftype", "defun", "defvar-local",
|
||||
"delay-mode-hooks", "destructuring-bind", "do", "do*",
|
||||
"do-all-symbols", "do-symbols", "dolist", "dont-compile", "dotimes",
|
||||
"dotimes-with-progress-reporter", "ecase", "ert-deftest", "etypecase",
|
||||
"eval-and-compile", "eval-when-compile", "flet", "ignore-errors",
|
||||
"incf", "labels", "lambda", "letrec", "lexical-let", "lexical-let*",
|
||||
"loop", "multiple-value-bind", "multiple-value-setq", "noreturn",
|
||||
"oref", "oref-default", "oset", "oset-default", "pcase",
|
||||
"pcase-defmacro", "pcase-dolist", "pcase-exhaustive", "pcase-let",
|
||||
"pcase-let*", "pop", "psetf", "psetq", "push", "pushnew", "remf",
|
||||
"return", "rotatef", "rx", "save-match-data", "save-selected-window",
|
||||
"save-window-excursion", "setf", "setq-local", "shiftf",
|
||||
"track-mouse", "typecase", "unless", "use-package", "when",
|
||||
"while-no-input", "with-case-table", "with-category-table",
|
||||
"with-coding-priority", "with-current-buffer", "with-demoted-errors",
|
||||
"with-eval-after-load", "with-file-modes", "with-local-quit",
|
||||
"with-output-to-string", "with-output-to-temp-buffer",
|
||||
"with-parsed-tramp-file-name", "with-selected-frame",
|
||||
"with-selected-window", "with-silent-modifications", "with-slots",
|
||||
"with-syntax-table", "with-temp-buffer", "with-temp-file",
|
||||
"with-temp-message", "with-timeout", "with-tramp-connection-property",
|
||||
"with-tramp-file-property", "with-tramp-progress-reporter",
|
||||
"with-wrapper-hook", "load-time-value", "locally", "macrolet", "progv",
|
||||
"return-from",
|
||||
}
|
||||
|
||||
emacsSpecialForms = []string{
|
||||
"and", "catch", "cond", "condition-case", "defconst", "defvar",
|
||||
"function", "if", "interactive", "let", "let*", "or", "prog1",
|
||||
"prog2", "progn", "quote", "save-current-buffer", "save-excursion",
|
||||
"save-restriction", "setq", "setq-default", "subr-arity",
|
||||
"unwind-protect", "while",
|
||||
}
|
||||
|
||||
emacsBuiltinFunction = []string{
|
||||
"%", "*", "+", "-", "/", "/=", "1+", "1-", "<", "<=", "=", ">", ">=",
|
||||
"Snarf-documentation", "abort-recursive-edit", "abs",
|
||||
"accept-process-output", "access-file", "accessible-keymaps", "acos",
|
||||
"active-minibuffer-window", "add-face-text-property",
|
||||
"add-name-to-file", "add-text-properties", "all-completions",
|
||||
"append", "apply", "apropos-internal", "aref", "arrayp", "aset",
|
||||
"ash", "asin", "assoc", "assoc-string", "assq", "atan", "atom",
|
||||
"autoload", "autoload-do-load", "backtrace", "backtrace--locals",
|
||||
"backtrace-debug", "backtrace-eval", "backtrace-frame",
|
||||
"backward-char", "backward-prefix-chars", "barf-if-buffer-read-only",
|
||||
"base64-decode-region", "base64-decode-string",
|
||||
"base64-encode-region", "base64-encode-string", "beginning-of-line",
|
||||
"bidi-find-overridden-directionality", "bidi-resolved-levels",
|
||||
"bitmap-spec-p", "bobp", "bolp", "bool-vector",
|
||||
"bool-vector-count-consecutive", "bool-vector-count-population",
|
||||
"bool-vector-exclusive-or", "bool-vector-intersection",
|
||||
"bool-vector-not", "bool-vector-p", "bool-vector-set-difference",
|
||||
"bool-vector-subsetp", "bool-vector-union", "boundp",
|
||||
"buffer-base-buffer", "buffer-chars-modified-tick",
|
||||
"buffer-enable-undo", "buffer-file-name", "buffer-has-markers-at",
|
||||
"buffer-list", "buffer-live-p", "buffer-local-value",
|
||||
"buffer-local-variables", "buffer-modified-p", "buffer-modified-tick",
|
||||
"buffer-name", "buffer-size", "buffer-string", "buffer-substring",
|
||||
"buffer-substring-no-properties", "buffer-swap-text", "bufferp",
|
||||
"bury-buffer-internal", "byte-code", "byte-code-function-p",
|
||||
"byte-to-position", "byte-to-string", "byteorder",
|
||||
"call-interactively", "call-last-kbd-macro", "call-process",
|
||||
"call-process-region", "cancel-kbd-macro-events", "capitalize",
|
||||
"capitalize-region", "capitalize-word", "car", "car-less-than-car",
|
||||
"car-safe", "case-table-p", "category-docstring",
|
||||
"category-set-mnemonics", "category-table", "category-table-p",
|
||||
"ccl-execute", "ccl-execute-on-string", "ccl-program-p", "cdr",
|
||||
"cdr-safe", "ceiling", "char-after", "char-before",
|
||||
"char-category-set", "char-charset", "char-equal", "char-or-string-p",
|
||||
"char-resolve-modifiers", "char-syntax", "char-table-extra-slot",
|
||||
"char-table-p", "char-table-parent", "char-table-range",
|
||||
"char-table-subtype", "char-to-string", "char-width", "characterp",
|
||||
"charset-after", "charset-id-internal", "charset-plist",
|
||||
"charset-priority-list", "charsetp", "check-coding-system",
|
||||
"check-coding-systems-region", "clear-buffer-auto-save-failure",
|
||||
"clear-charset-maps", "clear-face-cache", "clear-font-cache",
|
||||
"clear-image-cache", "clear-string", "clear-this-command-keys",
|
||||
"close-font", "clrhash", "coding-system-aliases",
|
||||
"coding-system-base", "coding-system-eol-type", "coding-system-p",
|
||||
"coding-system-plist", "coding-system-priority-list",
|
||||
"coding-system-put", "color-distance", "color-gray-p",
|
||||
"color-supported-p", "combine-after-change-execute",
|
||||
"command-error-default-function", "command-remapping", "commandp",
|
||||
"compare-buffer-substrings", "compare-strings",
|
||||
"compare-window-configurations", "completing-read",
|
||||
"compose-region-internal", "compose-string-internal",
|
||||
"composition-get-gstring", "compute-motion", "concat", "cons",
|
||||
"consp", "constrain-to-field", "continue-process",
|
||||
"controlling-tty-p", "coordinates-in-window-p", "copy-alist",
|
||||
"copy-category-table", "copy-file", "copy-hash-table", "copy-keymap",
|
||||
"copy-marker", "copy-sequence", "copy-syntax-table", "copysign",
|
||||
"cos", "current-active-maps", "current-bidi-paragraph-direction",
|
||||
"current-buffer", "current-case-table", "current-column",
|
||||
"current-global-map", "current-idle-time", "current-indentation",
|
||||
"current-input-mode", "current-local-map", "current-message",
|
||||
"current-minor-mode-maps", "current-time", "current-time-string",
|
||||
"current-time-zone", "current-window-configuration",
|
||||
"cygwin-convert-file-name-from-windows",
|
||||
"cygwin-convert-file-name-to-windows", "daemon-initialized",
|
||||
"daemonp", "dbus--init-bus", "dbus-get-unique-name",
|
||||
"dbus-message-internal", "debug-timer-check", "declare-equiv-charset",
|
||||
"decode-big5-char", "decode-char", "decode-coding-region",
|
||||
"decode-coding-string", "decode-sjis-char", "decode-time",
|
||||
"default-boundp", "default-file-modes", "default-printer-name",
|
||||
"default-toplevel-value", "default-value", "define-category",
|
||||
"define-charset-alias", "define-charset-internal",
|
||||
"define-coding-system-alias", "define-coding-system-internal",
|
||||
"define-fringe-bitmap", "define-hash-table-test", "define-key",
|
||||
"define-prefix-command", "delete",
|
||||
"delete-all-overlays", "delete-and-extract-region", "delete-char",
|
||||
"delete-directory-internal", "delete-field", "delete-file",
|
||||
"delete-frame", "delete-other-windows-internal", "delete-overlay",
|
||||
"delete-process", "delete-region", "delete-terminal",
|
||||
"delete-window-internal", "delq", "describe-buffer-bindings",
|
||||
"describe-vector", "destroy-fringe-bitmap", "detect-coding-region",
|
||||
"detect-coding-string", "ding", "directory-file-name",
|
||||
"directory-files", "directory-files-and-attributes", "discard-input",
|
||||
"display-supports-face-attributes-p", "do-auto-save", "documentation",
|
||||
"documentation-property", "downcase", "downcase-region",
|
||||
"downcase-word", "draw-string", "dump-colors", "dump-emacs",
|
||||
"dump-face", "dump-frame-glyph-matrix", "dump-glyph-matrix",
|
||||
"dump-glyph-row", "dump-redisplay-history", "dump-tool-bar-row",
|
||||
"elt", "emacs-pid", "encode-big5-char", "encode-char",
|
||||
"encode-coding-region", "encode-coding-string", "encode-sjis-char",
|
||||
"encode-time", "end-kbd-macro", "end-of-line", "eobp", "eolp", "eq",
|
||||
"eql", "equal", "equal-including-properties", "erase-buffer",
|
||||
"error-message-string", "eval", "eval-buffer", "eval-region",
|
||||
"event-convert-list", "execute-kbd-macro", "exit-recursive-edit",
|
||||
"exp", "expand-file-name", "expt", "external-debugging-output",
|
||||
"face-attribute-relative-p", "face-attributes-as-vector", "face-font",
|
||||
"fboundp", "fceiling", "fetch-bytecode", "ffloor",
|
||||
"field-beginning", "field-end", "field-string",
|
||||
"field-string-no-properties", "file-accessible-directory-p",
|
||||
"file-acl", "file-attributes", "file-attributes-lessp",
|
||||
"file-directory-p", "file-executable-p", "file-exists-p",
|
||||
"file-locked-p", "file-modes", "file-name-absolute-p",
|
||||
"file-name-all-completions", "file-name-as-directory",
|
||||
"file-name-completion", "file-name-directory",
|
||||
"file-name-nondirectory", "file-newer-than-file-p", "file-readable-p",
|
||||
"file-regular-p", "file-selinux-context", "file-symlink-p",
|
||||
"file-system-info", "file-system-info", "file-writable-p",
|
||||
"fillarray", "find-charset-region", "find-charset-string",
|
||||
"find-coding-systems-region-internal", "find-composition-internal",
|
||||
"find-file-name-handler", "find-font", "find-operation-coding-system",
|
||||
"float", "float-time", "floatp", "floor", "fmakunbound",
|
||||
"following-char", "font-at", "font-drive-otf", "font-face-attributes",
|
||||
"font-family-list", "font-get", "font-get-glyphs",
|
||||
"font-get-system-font", "font-get-system-normal-font", "font-info",
|
||||
"font-match-p", "font-otf-alternates", "font-put",
|
||||
"font-shape-gstring", "font-spec", "font-variation-glyphs",
|
||||
"font-xlfd-name", "fontp", "fontset-font", "fontset-info",
|
||||
"fontset-list", "fontset-list-all", "force-mode-line-update",
|
||||
"force-window-update", "format", "format-mode-line",
|
||||
"format-network-address", "format-time-string", "forward-char",
|
||||
"forward-comment", "forward-line", "forward-word",
|
||||
"frame-border-width", "frame-bottom-divider-width",
|
||||
"frame-can-run-window-configuration-change-hook", "frame-char-height",
|
||||
"frame-char-width", "frame-face-alist", "frame-first-window",
|
||||
"frame-focus", "frame-font-cache", "frame-fringe-width", "frame-list",
|
||||
"frame-live-p", "frame-or-buffer-changed-p", "frame-parameter",
|
||||
"frame-parameters", "frame-pixel-height", "frame-pixel-width",
|
||||
"frame-pointer-visible-p", "frame-right-divider-width",
|
||||
"frame-root-window", "frame-scroll-bar-height",
|
||||
"frame-scroll-bar-width", "frame-selected-window", "frame-terminal",
|
||||
"frame-text-cols", "frame-text-height", "frame-text-lines",
|
||||
"frame-text-width", "frame-total-cols", "frame-total-lines",
|
||||
"frame-visible-p", "framep", "frexp", "fringe-bitmaps-at-pos",
|
||||
"fround", "fset", "ftruncate", "funcall", "funcall-interactively",
|
||||
"function-equal", "functionp", "gap-position", "gap-size",
|
||||
"garbage-collect", "gc-status", "generate-new-buffer-name", "get",
|
||||
"get-buffer", "get-buffer-create", "get-buffer-process",
|
||||
"get-buffer-window", "get-byte", "get-char-property",
|
||||
"get-char-property-and-overlay", "get-file-buffer", "get-file-char",
|
||||
"get-internal-run-time", "get-load-suffixes", "get-pos-property",
|
||||
"get-process", "get-screen-color", "get-text-property",
|
||||
"get-unicode-property-internal", "get-unused-category",
|
||||
"get-unused-iso-final-char", "getenv-internal", "gethash",
|
||||
"gfile-add-watch", "gfile-rm-watch", "global-key-binding",
|
||||
"gnutls-available-p", "gnutls-boot", "gnutls-bye", "gnutls-deinit",
|
||||
"gnutls-error-fatalp", "gnutls-error-string", "gnutls-errorp",
|
||||
"gnutls-get-initstage", "gnutls-peer-status",
|
||||
"gnutls-peer-status-warning-describe", "goto-char", "gpm-mouse-start",
|
||||
"gpm-mouse-stop", "group-gid", "group-real-gid",
|
||||
"handle-save-session", "handle-switch-frame", "hash-table-count",
|
||||
"hash-table-p", "hash-table-rehash-size",
|
||||
"hash-table-rehash-threshold", "hash-table-size", "hash-table-test",
|
||||
"hash-table-weakness", "iconify-frame", "identity", "image-flush",
|
||||
"image-mask-p", "image-metadata", "image-size", "imagemagick-types",
|
||||
"imagep", "indent-to", "indirect-function", "indirect-variable",
|
||||
"init-image-library", "inotify-add-watch", "inotify-rm-watch",
|
||||
"input-pending-p", "insert", "insert-and-inherit",
|
||||
"insert-before-markers", "insert-before-markers-and-inherit",
|
||||
"insert-buffer-substring", "insert-byte", "insert-char",
|
||||
"insert-file-contents", "insert-startup-screen", "int86",
|
||||
"integer-or-marker-p", "integerp", "interactive-form", "intern",
|
||||
"intern-soft", "internal--track-mouse", "internal-char-font",
|
||||
"internal-complete-buffer", "internal-copy-lisp-face",
|
||||
"internal-default-process-filter",
|
||||
"internal-default-process-sentinel", "internal-describe-syntax-value",
|
||||
"internal-event-symbol-parse-modifiers",
|
||||
"internal-face-x-get-resource", "internal-get-lisp-face-attribute",
|
||||
"internal-lisp-face-attribute-values", "internal-lisp-face-empty-p",
|
||||
"internal-lisp-face-equal-p", "internal-lisp-face-p",
|
||||
"internal-make-lisp-face", "internal-make-var-non-special",
|
||||
"internal-merge-in-global-face",
|
||||
"internal-set-alternative-font-family-alist",
|
||||
"internal-set-alternative-font-registry-alist",
|
||||
"internal-set-font-selection-order",
|
||||
"internal-set-lisp-face-attribute",
|
||||
"internal-set-lisp-face-attribute-from-resource",
|
||||
"internal-show-cursor", "internal-show-cursor-p", "interrupt-process",
|
||||
"invisible-p", "invocation-directory", "invocation-name", "isnan",
|
||||
"iso-charset", "key-binding", "key-description",
|
||||
"keyboard-coding-system", "keymap-parent", "keymap-prompt", "keymapp",
|
||||
"keywordp", "kill-all-local-variables", "kill-buffer", "kill-emacs",
|
||||
"kill-local-variable", "kill-process", "last-nonminibuffer-frame",
|
||||
"lax-plist-get", "lax-plist-put", "ldexp", "length",
|
||||
"libxml-parse-html-region", "libxml-parse-xml-region",
|
||||
"line-beginning-position", "line-end-position", "line-pixel-height",
|
||||
"list", "list-fonts", "list-system-processes", "listp", "load",
|
||||
"load-average", "local-key-binding", "local-variable-if-set-p",
|
||||
"local-variable-p", "locale-info", "locate-file-internal",
|
||||
"lock-buffer", "log", "logand", "logb", "logior", "lognot", "logxor",
|
||||
"looking-at", "lookup-image", "lookup-image-map", "lookup-key",
|
||||
"lower-frame", "lsh", "macroexpand", "make-bool-vector",
|
||||
"make-byte-code", "make-category-set", "make-category-table",
|
||||
"make-char", "make-char-table", "make-directory-internal",
|
||||
"make-frame-invisible", "make-frame-visible", "make-hash-table",
|
||||
"make-indirect-buffer", "make-keymap", "make-list",
|
||||
"make-local-variable", "make-marker", "make-network-process",
|
||||
"make-overlay", "make-serial-process", "make-sparse-keymap",
|
||||
"make-string", "make-symbol", "make-symbolic-link", "make-temp-name",
|
||||
"make-terminal-frame", "make-variable-buffer-local",
|
||||
"make-variable-frame-local", "make-vector", "makunbound",
|
||||
"map-char-table", "map-charset-chars", "map-keymap",
|
||||
"map-keymap-internal", "mapatoms", "mapc", "mapcar", "mapconcat",
|
||||
"maphash", "mark-marker", "marker-buffer", "marker-insertion-type",
|
||||
"marker-position", "markerp", "match-beginning", "match-data",
|
||||
"match-end", "matching-paren", "max", "max-char", "md5", "member",
|
||||
"memory-info", "memory-limit", "memory-use-counts", "memq", "memql",
|
||||
"menu-bar-menu-at-x-y", "menu-or-popup-active-p",
|
||||
"menu-or-popup-active-p", "merge-face-attribute", "message",
|
||||
"message-box", "message-or-box", "min",
|
||||
"minibuffer-completion-contents", "minibuffer-contents",
|
||||
"minibuffer-contents-no-properties", "minibuffer-depth",
|
||||
"minibuffer-prompt", "minibuffer-prompt-end",
|
||||
"minibuffer-selected-window", "minibuffer-window", "minibufferp",
|
||||
"minor-mode-key-binding", "mod", "modify-category-entry",
|
||||
"modify-frame-parameters", "modify-syntax-entry",
|
||||
"mouse-pixel-position", "mouse-position", "move-overlay",
|
||||
"move-point-visually", "move-to-column", "move-to-window-line",
|
||||
"msdos-downcase-filename", "msdos-long-file-names", "msdos-memget",
|
||||
"msdos-memput", "msdos-mouse-disable", "msdos-mouse-enable",
|
||||
"msdos-mouse-init", "msdos-mouse-p", "msdos-remember-default-colors",
|
||||
"msdos-set-keyboard", "msdos-set-mouse-buttons",
|
||||
"multibyte-char-to-unibyte", "multibyte-string-p", "narrow-to-region",
|
||||
"natnump", "nconc", "network-interface-info",
|
||||
"network-interface-list", "new-fontset", "newline-cache-check",
|
||||
"next-char-property-change", "next-frame", "next-overlay-change",
|
||||
"next-property-change", "next-read-file-uses-dialog-p",
|
||||
"next-single-char-property-change", "next-single-property-change",
|
||||
"next-window", "nlistp", "nreverse", "nth", "nthcdr", "null",
|
||||
"number-or-marker-p", "number-to-string", "numberp",
|
||||
"open-dribble-file", "open-font", "open-termscript",
|
||||
"optimize-char-table", "other-buffer", "other-window-for-scrolling",
|
||||
"overlay-buffer", "overlay-end", "overlay-get", "overlay-lists",
|
||||
"overlay-properties", "overlay-put", "overlay-recenter",
|
||||
"overlay-start", "overlayp", "overlays-at", "overlays-in",
|
||||
"parse-partial-sexp", "play-sound-internal", "plist-get",
|
||||
"plist-member", "plist-put", "point", "point-marker", "point-max",
|
||||
"point-max-marker", "point-min", "point-min-marker",
|
||||
"pos-visible-in-window-p", "position-bytes", "posix-looking-at",
|
||||
"posix-search-backward", "posix-search-forward", "posix-string-match",
|
||||
"posn-at-point", "posn-at-x-y", "preceding-char",
|
||||
"prefix-numeric-value", "previous-char-property-change",
|
||||
"previous-frame", "previous-overlay-change",
|
||||
"previous-property-change", "previous-single-char-property-change",
|
||||
"previous-single-property-change", "previous-window", "prin1",
|
||||
"prin1-to-string", "princ", "print", "process-attributes",
|
||||
"process-buffer", "process-coding-system", "process-command",
|
||||
"process-connection", "process-contact", "process-datagram-address",
|
||||
"process-exit-status", "process-filter", "process-filter-multibyte-p",
|
||||
"process-id", "process-inherit-coding-system-flag", "process-list",
|
||||
"process-mark", "process-name", "process-plist",
|
||||
"process-query-on-exit-flag", "process-running-child-p",
|
||||
"process-send-eof", "process-send-region", "process-send-string",
|
||||
"process-sentinel", "process-status", "process-tty-name",
|
||||
"process-type", "processp", "profiler-cpu-log",
|
||||
"profiler-cpu-running-p", "profiler-cpu-start", "profiler-cpu-stop",
|
||||
"profiler-memory-log", "profiler-memory-running-p",
|
||||
"profiler-memory-start", "profiler-memory-stop", "propertize",
|
||||
"purecopy", "put", "put-text-property",
|
||||
"put-unicode-property-internal", "puthash", "query-font",
|
||||
"query-fontset", "quit-process", "raise-frame", "random", "rassoc",
|
||||
"rassq", "re-search-backward", "re-search-forward", "read",
|
||||
"read-buffer", "read-char", "read-char-exclusive",
|
||||
"read-coding-system", "read-command", "read-event",
|
||||
"read-from-minibuffer", "read-from-string", "read-function",
|
||||
"read-key-sequence", "read-key-sequence-vector",
|
||||
"read-no-blanks-input", "read-non-nil-coding-system", "read-string",
|
||||
"read-variable", "recent-auto-save-p", "recent-doskeys",
|
||||
"recent-keys", "recenter", "recursion-depth", "recursive-edit",
|
||||
"redirect-debugging-output", "redirect-frame-focus", "redisplay",
|
||||
"redraw-display", "redraw-frame", "regexp-quote", "region-beginning",
|
||||
"region-end", "register-ccl-program", "register-code-conversion-map",
|
||||
"remhash", "remove-list-of-text-properties", "remove-text-properties",
|
||||
"rename-buffer", "rename-file", "replace-match",
|
||||
"reset-this-command-lengths", "resize-mini-window-internal",
|
||||
"restore-buffer-modified-p", "resume-tty", "reverse", "round",
|
||||
"run-hook-with-args", "run-hook-with-args-until-failure",
|
||||
"run-hook-with-args-until-success", "run-hook-wrapped", "run-hooks",
|
||||
"run-window-configuration-change-hook", "run-window-scroll-functions",
|
||||
"safe-length", "scan-lists", "scan-sexps", "scroll-down",
|
||||
"scroll-left", "scroll-other-window", "scroll-right", "scroll-up",
|
||||
"search-backward", "search-forward", "secure-hash", "select-frame",
|
||||
"select-window", "selected-frame", "selected-window",
|
||||
"self-insert-command", "send-string-to-terminal", "sequencep",
|
||||
"serial-process-configure", "set", "set-buffer",
|
||||
"set-buffer-auto-saved", "set-buffer-major-mode",
|
||||
"set-buffer-modified-p", "set-buffer-multibyte", "set-case-table",
|
||||
"set-category-table", "set-char-table-extra-slot",
|
||||
"set-char-table-parent", "set-char-table-range", "set-charset-plist",
|
||||
"set-charset-priority", "set-coding-system-priority",
|
||||
"set-cursor-size", "set-default", "set-default-file-modes",
|
||||
"set-default-toplevel-value", "set-file-acl", "set-file-modes",
|
||||
"set-file-selinux-context", "set-file-times", "set-fontset-font",
|
||||
"set-frame-height", "set-frame-position", "set-frame-selected-window",
|
||||
"set-frame-size", "set-frame-width", "set-fringe-bitmap-face",
|
||||
"set-input-interrupt-mode", "set-input-meta-mode", "set-input-mode",
|
||||
"set-keyboard-coding-system-internal", "set-keymap-parent",
|
||||
"set-marker", "set-marker-insertion-type", "set-match-data",
|
||||
"set-message-beep", "set-minibuffer-window",
|
||||
"set-mouse-pixel-position", "set-mouse-position",
|
||||
"set-network-process-option", "set-output-flow-control",
|
||||
"set-process-buffer", "set-process-coding-system",
|
||||
"set-process-datagram-address", "set-process-filter",
|
||||
"set-process-filter-multibyte",
|
||||
"set-process-inherit-coding-system-flag", "set-process-plist",
|
||||
"set-process-query-on-exit-flag", "set-process-sentinel",
|
||||
"set-process-window-size", "set-quit-char",
|
||||
"set-safe-terminal-coding-system-internal", "set-screen-color",
|
||||
"set-standard-case-table", "set-syntax-table",
|
||||
"set-terminal-coding-system-internal", "set-terminal-local-value",
|
||||
"set-terminal-parameter", "set-text-properties", "set-time-zone-rule",
|
||||
"set-visited-file-modtime", "set-window-buffer",
|
||||
"set-window-combination-limit", "set-window-configuration",
|
||||
"set-window-dedicated-p", "set-window-display-table",
|
||||
"set-window-fringes", "set-window-hscroll", "set-window-margins",
|
||||
"set-window-new-normal", "set-window-new-pixel",
|
||||
"set-window-new-total", "set-window-next-buffers",
|
||||
"set-window-parameter", "set-window-point", "set-window-prev-buffers",
|
||||
"set-window-redisplay-end-trigger", "set-window-scroll-bars",
|
||||
"set-window-start", "set-window-vscroll", "setcar", "setcdr",
|
||||
"setplist", "show-face-resources", "signal", "signal-process", "sin",
|
||||
"single-key-description", "skip-chars-backward", "skip-chars-forward",
|
||||
"skip-syntax-backward", "skip-syntax-forward", "sleep-for", "sort",
|
||||
"sort-charsets", "special-variable-p", "split-char",
|
||||
"split-window-internal", "sqrt", "standard-case-table",
|
||||
"standard-category-table", "standard-syntax-table", "start-kbd-macro",
|
||||
"start-process", "stop-process", "store-kbd-macro-event", "string",
|
||||
"string-as-multibyte", "string-as-unibyte", "string-bytes",
|
||||
"string-collate-equalp", "string-collate-lessp", "string-equal",
|
||||
"string-lessp", "string-make-multibyte", "string-make-unibyte",
|
||||
"string-match", "string-to-char", "string-to-multibyte",
|
||||
"string-to-number", "string-to-syntax", "string-to-unibyte",
|
||||
"string-width", "stringp", "subr-name", "subrp",
|
||||
"subst-char-in-region", "substitute-command-keys",
|
||||
"substitute-in-file-name", "substring", "substring-no-properties",
|
||||
"suspend-emacs", "suspend-tty", "suspicious-object", "sxhash",
|
||||
"symbol-function", "symbol-name", "symbol-plist", "symbol-value",
|
||||
"symbolp", "syntax-table", "syntax-table-p", "system-groups",
|
||||
"system-move-file-to-trash", "system-name", "system-users", "tan",
|
||||
"terminal-coding-system", "terminal-list", "terminal-live-p",
|
||||
"terminal-local-value", "terminal-name", "terminal-parameter",
|
||||
"terminal-parameters", "terpri", "test-completion",
|
||||
"text-char-description", "text-properties-at", "text-property-any",
|
||||
"text-property-not-all", "this-command-keys",
|
||||
"this-command-keys-vector", "this-single-command-keys",
|
||||
"this-single-command-raw-keys", "time-add", "time-less-p",
|
||||
"time-subtract", "tool-bar-get-system-style", "tool-bar-height",
|
||||
"tool-bar-pixel-width", "top-level", "trace-redisplay",
|
||||
"trace-to-stderr", "translate-region-internal", "transpose-regions",
|
||||
"truncate", "try-completion", "tty-display-color-cells",
|
||||
"tty-display-color-p", "tty-no-underline",
|
||||
"tty-suppress-bold-inverse-default-colors", "tty-top-frame",
|
||||
"tty-type", "type-of", "undo-boundary", "unencodable-char-position",
|
||||
"unhandled-file-name-directory", "unibyte-char-to-multibyte",
|
||||
"unibyte-string", "unicode-property-table-internal", "unify-charset",
|
||||
"unintern", "unix-sync", "unlock-buffer", "upcase", "upcase-initials",
|
||||
"upcase-initials-region", "upcase-region", "upcase-word",
|
||||
"use-global-map", "use-local-map", "user-full-name",
|
||||
"user-login-name", "user-real-login-name", "user-real-uid",
|
||||
"user-uid", "variable-binding-locus", "vconcat", "vector",
|
||||
"vector-or-char-table-p", "vectorp", "verify-visited-file-modtime",
|
||||
"vertical-motion", "visible-frame-list", "visited-file-modtime",
|
||||
"w16-get-clipboard-data", "w16-selection-exists-p",
|
||||
"w16-set-clipboard-data", "w32-battery-status",
|
||||
"w32-default-color-map", "w32-define-rgb-color",
|
||||
"w32-display-monitor-attributes-list", "w32-frame-menu-bar-size",
|
||||
"w32-frame-rect", "w32-get-clipboard-data",
|
||||
"w32-get-codepage-charset", "w32-get-console-codepage",
|
||||
"w32-get-console-output-codepage", "w32-get-current-locale-id",
|
||||
"w32-get-default-locale-id", "w32-get-keyboard-layout",
|
||||
"w32-get-locale-info", "w32-get-valid-codepages",
|
||||
"w32-get-valid-keyboard-layouts", "w32-get-valid-locale-ids",
|
||||
"w32-has-winsock", "w32-long-file-name", "w32-reconstruct-hot-key",
|
||||
"w32-register-hot-key", "w32-registered-hot-keys",
|
||||
"w32-selection-exists-p", "w32-send-sys-command",
|
||||
"w32-set-clipboard-data", "w32-set-console-codepage",
|
||||
"w32-set-console-output-codepage", "w32-set-current-locale",
|
||||
"w32-set-keyboard-layout", "w32-set-process-priority",
|
||||
"w32-shell-execute", "w32-short-file-name", "w32-toggle-lock-key",
|
||||
"w32-unload-winsock", "w32-unregister-hot-key", "w32-window-exists-p",
|
||||
"w32notify-add-watch", "w32notify-rm-watch",
|
||||
"waiting-for-user-input-p", "where-is-internal", "widen",
|
||||
"widget-apply", "widget-get", "widget-put",
|
||||
"window-absolute-pixel-edges", "window-at", "window-body-height",
|
||||
"window-body-width", "window-bottom-divider-width", "window-buffer",
|
||||
"window-combination-limit", "window-configuration-frame",
|
||||
"window-configuration-p", "window-dedicated-p",
|
||||
"window-display-table", "window-edges", "window-end", "window-frame",
|
||||
"window-fringes", "window-header-line-height", "window-hscroll",
|
||||
"window-inside-absolute-pixel-edges", "window-inside-edges",
|
||||
"window-inside-pixel-edges", "window-left-child",
|
||||
"window-left-column", "window-line-height", "window-list",
|
||||
"window-list-1", "window-live-p", "window-margins",
|
||||
"window-minibuffer-p", "window-mode-line-height", "window-new-normal",
|
||||
"window-new-pixel", "window-new-total", "window-next-buffers",
|
||||
"window-next-sibling", "window-normal-size", "window-old-point",
|
||||
"window-parameter", "window-parameters", "window-parent",
|
||||
"window-pixel-edges", "window-pixel-height", "window-pixel-left",
|
||||
"window-pixel-top", "window-pixel-width", "window-point",
|
||||
"window-prev-buffers", "window-prev-sibling",
|
||||
"window-redisplay-end-trigger", "window-resize-apply",
|
||||
"window-resize-apply-total", "window-right-divider-width",
|
||||
"window-scroll-bar-height", "window-scroll-bar-width",
|
||||
"window-scroll-bars", "window-start", "window-system",
|
||||
"window-text-height", "window-text-pixel-size", "window-text-width",
|
||||
"window-top-child", "window-top-line", "window-total-height",
|
||||
"window-total-width", "window-use-time", "window-valid-p",
|
||||
"window-vscroll", "windowp", "write-char", "write-region",
|
||||
"x-backspace-delete-keys-p", "x-change-window-property",
|
||||
"x-change-window-property", "x-close-connection",
|
||||
"x-close-connection", "x-create-frame", "x-create-frame",
|
||||
"x-delete-window-property", "x-delete-window-property",
|
||||
"x-disown-selection-internal", "x-display-backing-store",
|
||||
"x-display-backing-store", "x-display-color-cells",
|
||||
"x-display-color-cells", "x-display-grayscale-p",
|
||||
"x-display-grayscale-p", "x-display-list", "x-display-list",
|
||||
"x-display-mm-height", "x-display-mm-height", "x-display-mm-width",
|
||||
"x-display-mm-width", "x-display-monitor-attributes-list",
|
||||
"x-display-pixel-height", "x-display-pixel-height",
|
||||
"x-display-pixel-width", "x-display-pixel-width", "x-display-planes",
|
||||
"x-display-planes", "x-display-save-under", "x-display-save-under",
|
||||
"x-display-screens", "x-display-screens", "x-display-visual-class",
|
||||
"x-display-visual-class", "x-family-fonts", "x-file-dialog",
|
||||
"x-file-dialog", "x-file-dialog", "x-focus-frame", "x-frame-geometry",
|
||||
"x-frame-geometry", "x-get-atom-name", "x-get-resource",
|
||||
"x-get-selection-internal", "x-hide-tip", "x-hide-tip",
|
||||
"x-list-fonts", "x-load-color-file", "x-menu-bar-open-internal",
|
||||
"x-menu-bar-open-internal", "x-open-connection", "x-open-connection",
|
||||
"x-own-selection-internal", "x-parse-geometry", "x-popup-dialog",
|
||||
"x-popup-menu", "x-register-dnd-atom", "x-select-font",
|
||||
"x-select-font", "x-selection-exists-p", "x-selection-owner-p",
|
||||
"x-send-client-message", "x-server-max-request-size",
|
||||
"x-server-max-request-size", "x-server-vendor", "x-server-vendor",
|
||||
"x-server-version", "x-server-version", "x-show-tip", "x-show-tip",
|
||||
"x-synchronize", "x-synchronize", "x-uses-old-gtk-dialog",
|
||||
"x-window-property", "x-window-property", "x-wm-set-size-hint",
|
||||
"xw-color-defined-p", "xw-color-defined-p", "xw-color-values",
|
||||
"xw-color-values", "xw-display-color-p", "xw-display-color-p",
|
||||
"yes-or-no-p", "zlib-available-p", "zlib-decompress-region",
|
||||
"forward-point",
|
||||
}
|
||||
|
||||
emacsBuiltinFunctionHighlighted = []string{
|
||||
"defvaralias", "provide", "require",
|
||||
"with-no-warnings", "define-widget", "with-electric-help",
|
||||
"throw", "defalias", "featurep",
|
||||
}
|
||||
|
||||
emacsLambdaListKeywords = []string{
|
||||
"&allow-other-keys", "&aux", "&body", "&environment", "&key", "&optional",
|
||||
"&rest", "&whole",
|
||||
}
|
||||
|
||||
emacsErrorKeywords = []string{
|
||||
"cl-assert", "cl-check-type", "error", "signal",
|
||||
"user-error", "warn",
|
||||
}
|
||||
)
|
||||
|
||||
// EmacsLisp lexer.
|
||||
var EmacsLisp = Register(TypeRemappingLexer(MustNewXMLLexer(
|
||||
embedded,
|
||||
"embedded/emacslisp.xml",
|
||||
), TypeMapping{
|
||||
{NameVariable, NameFunction, emacsBuiltinFunction},
|
||||
{NameVariable, NameBuiltin, emacsSpecialForms},
|
||||
{NameVariable, NameException, emacsErrorKeywords},
|
||||
{NameVariable, NameBuiltin, append(emacsBuiltinFunctionHighlighted, emacsMacros...)},
|
||||
{NameVariable, KeywordPseudo, emacsLambdaListKeywords},
|
||||
}))
|
||||
154
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/abap.xml
generated
vendored
Normal file
154
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/abap.xml
generated
vendored
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
<lexer>
|
||||
<config>
|
||||
<name>ABAP</name>
|
||||
<alias>abap</alias>
|
||||
<filename>*.abap</filename>
|
||||
<filename>*.ABAP</filename>
|
||||
<mime_type>text/x-abap</mime_type>
|
||||
<case_insensitive>true</case_insensitive>
|
||||
</config>
|
||||
<rules>
|
||||
<state name="common">
|
||||
<rule pattern="\s+">
|
||||
<token type="Text"/>
|
||||
</rule>
|
||||
<rule pattern="^\*.*$">
|
||||
<token type="CommentSingle"/>
|
||||
</rule>
|
||||
<rule pattern="\".*?\n">
|
||||
<token type="CommentSingle"/>
|
||||
</rule>
|
||||
<rule pattern="##\w+">
|
||||
<token type="CommentSpecial"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="variable-names">
|
||||
<rule pattern="<\S+>">
|
||||
<token type="NameVariable"/>
|
||||
</rule>
|
||||
<rule pattern="\w[\w~]*(?:(\[\])|->\*)?">
|
||||
<token type="NameVariable"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="root">
|
||||
<rule>
|
||||
<include state="common"/>
|
||||
</rule>
|
||||
<rule pattern="CALL\s+(?:BADI|CUSTOMER-FUNCTION|FUNCTION)">
|
||||
<token type="Keyword"/>
|
||||
</rule>
|
||||
<rule pattern="(CALL\s+(?:DIALOG|SCREEN|SUBSCREEN|SELECTION-SCREEN|TRANSACTION|TRANSFORMATION))\b">
|
||||
<token type="Keyword"/>
|
||||
</rule>
|
||||
<rule pattern="(FORM|PERFORM)(\s+)(\w+)">
|
||||
<bygroups>
|
||||
<token type="Keyword"/>
|
||||
<token type="Text"/>
|
||||
<token type="NameFunction"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="(PERFORM)(\s+)(\()(\w+)(\))">
|
||||
<bygroups>
|
||||
<token type="Keyword"/>
|
||||
<token type="Text"/>
|
||||
<token type="Punctuation"/>
|
||||
<token type="NameVariable"/>
|
||||
<token type="Punctuation"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="(MODULE)(\s+)(\S+)(\s+)(INPUT|OUTPUT)">
|
||||
<bygroups>
|
||||
<token type="Keyword"/>
|
||||
<token type="Text"/>
|
||||
<token type="NameFunction"/>
|
||||
<token type="Text"/>
|
||||
<token type="Keyword"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="(METHOD)(\s+)([\w~]+)">
|
||||
<bygroups>
|
||||
<token type="Keyword"/>
|
||||
<token type="Text"/>
|
||||
<token type="NameFunction"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="(\s+)([\w\-]+)([=\-]>)([\w\-~]+)">
|
||||
<bygroups>
|
||||
<token type="Text"/>
|
||||
<token type="NameVariable"/>
|
||||
<token type="Operator"/>
|
||||
<token type="NameFunction"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="(?<=(=|-)>)([\w\-~]+)(?=\()">
|
||||
<token type="NameFunction"/>
|
||||
</rule>
|
||||
<rule pattern="(TEXT)(-)(\d{3})">
|
||||
<bygroups>
|
||||
<token type="Keyword"/>
|
||||
<token type="Punctuation"/>
|
||||
<token type="LiteralNumberInteger"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="(TEXT)(-)(\w{3})">
|
||||
<bygroups>
|
||||
<token type="Keyword"/>
|
||||
<token type="Punctuation"/>
|
||||
<token type="NameVariable"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="(ADD-CORRESPONDING|AUTHORITY-CHECK|CLASS-DATA|CLASS-EVENTS|CLASS-METHODS|CLASS-POOL|DELETE-ADJACENT|DIVIDE-CORRESPONDING|EDITOR-CALL|ENHANCEMENT-POINT|ENHANCEMENT-SECTION|EXIT-COMMAND|FIELD-GROUPS|FIELD-SYMBOLS|FUNCTION-POOL|INTERFACE-POOL|INVERTED-DATE|LOAD-OF-PROGRAM|LOG-POINT|MESSAGE-ID|MOVE-CORRESPONDING|MULTIPLY-CORRESPONDING|NEW-LINE|NEW-PAGE|NEW-SECTION|NO-EXTENSION|OUTPUT-LENGTH|PRINT-CONTROL|SELECT-OPTIONS|START-OF-SELECTION|SUBTRACT-CORRESPONDING|SYNTAX-CHECK|SYSTEM-EXCEPTIONS|TYPE-POOL|TYPE-POOLS|NO-DISPLAY)\b">
|
||||
<token type="Keyword"/>
|
||||
</rule>
|
||||
<rule pattern="(?<![-\>])(CREATE\s+(PUBLIC|PRIVATE|DATA|OBJECT)|(PUBLIC|PRIVATE|PROTECTED)\s+SECTION|(TYPE|LIKE)\s+((LINE\s+OF|REF\s+TO|(SORTED|STANDARD|HASHED)\s+TABLE\s+OF))?|FROM\s+(DATABASE|MEMORY)|CALL\s+METHOD|(GROUP|ORDER) BY|HAVING|SEPARATED BY|GET\s+(BADI|BIT|CURSOR|DATASET|LOCALE|PARAMETER|PF-STATUS|(PROPERTY|REFERENCE)\s+OF|RUN\s+TIME|TIME\s+(STAMP)?)?|SET\s+(BIT|BLANK\s+LINES|COUNTRY|CURSOR|DATASET|EXTENDED\s+CHECK|HANDLER|HOLD\s+DATA|LANGUAGE|LEFT\s+SCROLL-BOUNDARY|LOCALE|MARGIN|PARAMETER|PF-STATUS|PROPERTY\s+OF|RUN\s+TIME\s+(ANALYZER|CLOCK\s+RESOLUTION)|SCREEN|TITLEBAR|UPADTE\s+TASK\s+LOCAL|USER-COMMAND)|CONVERT\s+((INVERTED-)?DATE|TIME|TIME\s+STAMP|TEXT)|(CLOSE|OPEN)\s+(DATASET|CURSOR)|(TO|FROM)\s+(DATA BUFFER|INTERNAL TABLE|MEMORY ID|DATABASE|SHARED\s+(MEMORY|BUFFER))|DESCRIBE\s+(DISTANCE\s+BETWEEN|FIELD|LIST|TABLE)|FREE\s(MEMORY|OBJECT)?|PROCESS\s+(BEFORE\s+OUTPUT|AFTER\s+INPUT|ON\s+(VALUE-REQUEST|HELP-REQUEST))|AT\s+(LINE-SELECTION|USER-COMMAND|END\s+OF|NEW)|AT\s+SELECTION-SCREEN(\s+(ON(\s+(BLOCK|(HELP|VALUE)-REQUEST\s+FOR|END\s+OF|RADIOBUTTON\s+GROUP))?|OUTPUT))?|SELECTION-SCREEN:?\s+((BEGIN|END)\s+OF\s+((TABBED\s+)?BLOCK|LINE|SCREEN)|COMMENT|FUNCTION\s+KEY|INCLUDE\s+BLOCKS|POSITION|PUSHBUTTON|SKIP|ULINE)|LEAVE\s+(LIST-PROCESSING|PROGRAM|SCREEN|TO LIST-PROCESSING|TO TRANSACTION)(ENDING|STARTING)\s+AT|FORMAT\s+(COLOR|INTENSIFIED|INVERSE|HOTSPOT|INPUT|FRAMES|RESET)|AS\s+(CHECKBOX|SUBSCREEN|WINDOW)|WITH\s+(((NON-)?UNIQUE)?\s+KEY|FRAME)|(BEGIN|END)\s+OF|DELETE(\s+ADJACENT\s+DUPLICATES\sFROM)?|COMPARING(\s+ALL\s+FIELDS)?|(INSERT|APPEND)(\s+INITIAL\s+LINE\s+(IN)?TO|\s+LINES\s+OF)?|IN\s+((BYTE|CHARACTER)\s+MODE|PROGRAM)|END-OF-(DEFINITION|PAGE|SELECTION)|WITH\s+FRAME(\s+TITLE)|(REPLACE|FIND)\s+((FIRST|ALL)\s+OCCURRENCES?\s+OF\s+)?(SUBSTRING|REGEX)?|MATCH\s+(LENGTH|COUNT|LINE|OFFSET)|(RESPECTING|IGNORING)\s+CASE|IN\s+UPDATE\s+TASK|(SOURCE|RESULT)\s+(XML)?|REFERENCE\s+INTO|AND\s+(MARK|RETURN)|CLIENT\s+SPECIFIED|CORRESPONDING\s+FIELDS\s+OF|IF\s+FOUND|FOR\s+EVENT|INHERITING\s+FROM|LEAVE\s+TO\s+SCREEN|LOOP\s+AT\s+(SCREEN)?|LOWER\s+CASE|MATCHCODE\s+OBJECT|MODIF\s+ID|MODIFY\s+SCREEN|NESTING\s+LEVEL|NO\s+INTERVALS|OF\s+STRUCTURE|RADIOBUTTON\s+GROUP|RANGE\s+OF|REF\s+TO|SUPPRESS DIALOG|TABLE\s+OF|UPPER\s+CASE|TRANSPORTING\s+NO\s+FIELDS|VALUE\s+CHECK|VISIBLE\s+LENGTH|HEADER\s+LINE|COMMON\s+PART)\b">
|
||||
<token type="Keyword"/>
|
||||
</rule>
|
||||
<rule pattern="(^|(?<=(\s|\.)))(ABBREVIATED|ABSTRACT|ADD|ALIASES|ALIGN|ALPHA|ASSERT|AS|ASSIGN(ING)?|AT(\s+FIRST)?|BACK|BLOCK|BREAK-POINT|CASE|CATCH|CHANGING|CHECK|CLASS|CLEAR|COLLECT|COLOR|COMMIT|CREATE|COMMUNICATION|COMPONENTS?|COMPUTE|CONCATENATE|CONDENSE|CONSTANTS|CONTEXTS|CONTINUE|CONTROLS|COUNTRY|CURRENCY|DATA|DATE|DECIMALS|DEFAULT|DEFINE|DEFINITION|DEFERRED|DEMAND|DETAIL|DIRECTORY|DIVIDE|DO|DUMMY|ELSE(IF)?|ENDAT|ENDCASE|ENDCATCH|ENDCLASS|ENDDO|ENDFORM|ENDFUNCTION|ENDIF|ENDINTERFACE|ENDLOOP|ENDMETHOD|ENDMODULE|ENDSELECT|ENDTRY|ENDWHILE|ENHANCEMENT|EVENTS|EXACT|EXCEPTIONS?|EXIT|EXPONENT|EXPORT|EXPORTING|EXTRACT|FETCH|FIELDS?|FOR|FORM|FORMAT|FREE|FROM|FUNCTION|HIDE|ID|IF|IMPORT|IMPLEMENTATION|IMPORTING|IN|INCLUDE|INCLUDING|INDEX|INFOTYPES|INITIALIZATION|INTERFACE|INTERFACES|INTO|LANGUAGE|LEAVE|LENGTH|LINES|LOAD|LOCAL|JOIN|KEY|NEXT|MAXIMUM|MESSAGE|METHOD[S]?|MINIMUM|MODULE|MODIFIER|MODIFY|MOVE|MULTIPLY|NODES|NUMBER|OBLIGATORY|OBJECT|OF|OFF|ON|OTHERS|OVERLAY|PACK|PAD|PARAMETERS|PERCENTAGE|POSITION|PROGRAM|PROVIDE|PUBLIC|PUT|PF\d\d|RAISE|RAISING|RANGES?|READ|RECEIVE|REDEFINITION|REFRESH|REJECT|REPORT|RESERVE|RESUME|RETRY|RETURN|RETURNING|RIGHT|ROLLBACK|REPLACE|SCROLL|SEARCH|SELECT|SHIFT|SIGN|SINGLE|SIZE|SKIP|SORT|SPLIT|STATICS|STOP|STYLE|SUBMATCHES|SUBMIT|SUBTRACT|SUM(?!\()|SUMMARY|SUMMING|SUPPLY|TABLE|TABLES|TIMESTAMP|TIMES?|TIMEZONE|TITLE|\??TO|TOP-OF-PAGE|TRANSFER|TRANSLATE|TRY|TYPES|ULINE|UNDER|UNPACK|UPDATE|USING|VALUE|VALUES|VIA|VARYING|VARY|WAIT|WHEN|WHERE|WIDTH|WHILE|WITH|WINDOW|WRITE|XSD|ZERO)\b">
|
||||
<token type="Keyword"/>
|
||||
</rule>
|
||||
<rule pattern="(abs|acos|asin|atan|boolc|boolx|bit_set|char_off|charlen|ceil|cmax|cmin|condense|contains|contains_any_of|contains_any_not_of|concat_lines_of|cos|cosh|count|count_any_of|count_any_not_of|dbmaxlen|distance|escape|exp|find|find_end|find_any_of|find_any_not_of|floor|frac|from_mixed|insert|lines|log|log10|match|matches|nmax|nmin|numofchar|repeat|replace|rescale|reverse|round|segment|shift_left|shift_right|sign|sin|sinh|sqrt|strlen|substring|substring_after|substring_from|substring_before|substring_to|tan|tanh|to_upper|to_lower|to_mixed|translate|trunc|xstrlen)(\()\b">
|
||||
<bygroups>
|
||||
<token type="NameBuiltin"/>
|
||||
<token type="Punctuation"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="&[0-9]">
|
||||
<token type="Name"/>
|
||||
</rule>
|
||||
<rule pattern="[0-9]+">
|
||||
<token type="LiteralNumberInteger"/>
|
||||
</rule>
|
||||
<rule pattern="(?<=(\s|.))(AND|OR|EQ|NE|GT|LT|GE|LE|CO|CN|CA|NA|CS|NOT|NS|CP|NP|BYTE-CO|BYTE-CN|BYTE-CA|BYTE-NA|BYTE-CS|BYTE-NS|IS\s+(NOT\s+)?(INITIAL|ASSIGNED|REQUESTED|BOUND))\b">
|
||||
<token type="OperatorWord"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="variable-names"/>
|
||||
</rule>
|
||||
<rule pattern="[?*<>=\-+&]">
|
||||
<token type="Operator"/>
|
||||
</rule>
|
||||
<rule pattern="'(''|[^'])*'">
|
||||
<token type="LiteralStringSingle"/>
|
||||
</rule>
|
||||
<rule pattern="`([^`])*`">
|
||||
<token type="LiteralStringSingle"/>
|
||||
</rule>
|
||||
<rule pattern="([|}])([^{}|]*?)([|{])">
|
||||
<bygroups>
|
||||
<token type="Punctuation"/>
|
||||
<token type="LiteralStringSingle"/>
|
||||
<token type="Punctuation"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="[/;:()\[\],.]">
|
||||
<token type="Punctuation"/>
|
||||
</rule>
|
||||
<rule pattern="(!)(\w+)">
|
||||
<bygroups>
|
||||
<token type="Operator"/>
|
||||
<token type="Name"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
</state>
|
||||
</rules>
|
||||
</lexer>
|
||||
66
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/abnf.xml
generated
vendored
Normal file
66
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/abnf.xml
generated
vendored
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
<lexer>
|
||||
<config>
|
||||
<name>ABNF</name>
|
||||
<alias>abnf</alias>
|
||||
<filename>*.abnf</filename>
|
||||
<mime_type>text/x-abnf</mime_type>
|
||||
</config>
|
||||
<rules>
|
||||
<state name="root">
|
||||
<rule pattern=";.*$">
|
||||
<token type="CommentSingle"/>
|
||||
</rule>
|
||||
<rule pattern="(%[si])?"[^"]*"">
|
||||
<token type="Literal"/>
|
||||
</rule>
|
||||
<rule pattern="%b[01]+\-[01]+\b">
|
||||
<token type="Literal"/>
|
||||
</rule>
|
||||
<rule pattern="%b[01]+(\.[01]+)*\b">
|
||||
<token type="Literal"/>
|
||||
</rule>
|
||||
<rule pattern="%d[0-9]+\-[0-9]+\b">
|
||||
<token type="Literal"/>
|
||||
</rule>
|
||||
<rule pattern="%d[0-9]+(\.[0-9]+)*\b">
|
||||
<token type="Literal"/>
|
||||
</rule>
|
||||
<rule pattern="%x[0-9a-fA-F]+\-[0-9a-fA-F]+\b">
|
||||
<token type="Literal"/>
|
||||
</rule>
|
||||
<rule pattern="%x[0-9a-fA-F]+(\.[0-9a-fA-F]+)*\b">
|
||||
<token type="Literal"/>
|
||||
</rule>
|
||||
<rule pattern="\b[0-9]+\*[0-9]+">
|
||||
<token type="Operator"/>
|
||||
</rule>
|
||||
<rule pattern="\b[0-9]+\*">
|
||||
<token type="Operator"/>
|
||||
</rule>
|
||||
<rule pattern="\b[0-9]+">
|
||||
<token type="Operator"/>
|
||||
</rule>
|
||||
<rule pattern="\*">
|
||||
<token type="Operator"/>
|
||||
</rule>
|
||||
<rule pattern="(HEXDIG|DQUOTE|DIGIT|VCHAR|OCTET|ALPHA|CHAR|CRLF|HTAB|LWSP|BIT|CTL|WSP|LF|SP|CR)\b">
|
||||
<token type="Keyword"/>
|
||||
</rule>
|
||||
<rule pattern="[a-zA-Z][a-zA-Z0-9-]+\b">
|
||||
<token type="NameClass"/>
|
||||
</rule>
|
||||
<rule pattern="(=/|=|/)">
|
||||
<token type="Operator"/>
|
||||
</rule>
|
||||
<rule pattern="[\[\]()]">
|
||||
<token type="Punctuation"/>
|
||||
</rule>
|
||||
<rule pattern="\s+">
|
||||
<token type="Text"/>
|
||||
</rule>
|
||||
<rule pattern=".">
|
||||
<token type="Text"/>
|
||||
</rule>
|
||||
</state>
|
||||
</rules>
|
||||
</lexer>
|
||||
68
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/actionscript.xml
generated
vendored
Normal file
68
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/actionscript.xml
generated
vendored
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
<lexer>
|
||||
<config>
|
||||
<name>ActionScript</name>
|
||||
<alias>as</alias>
|
||||
<alias>actionscript</alias>
|
||||
<filename>*.as</filename>
|
||||
<mime_type>application/x-actionscript</mime_type>
|
||||
<mime_type>text/x-actionscript</mime_type>
|
||||
<mime_type>text/actionscript</mime_type>
|
||||
<dot_all>true</dot_all>
|
||||
<not_multiline>true</not_multiline>
|
||||
</config>
|
||||
<rules>
|
||||
<state name="root">
|
||||
<rule pattern="\s+">
|
||||
<token type="Text"/>
|
||||
</rule>
|
||||
<rule pattern="//.*?\n">
|
||||
<token type="CommentSingle"/>
|
||||
</rule>
|
||||
<rule pattern="/\*.*?\*/">
|
||||
<token type="CommentMultiline"/>
|
||||
</rule>
|
||||
<rule pattern="/(\\\\|\\/|[^/\n])*/[gim]*">
|
||||
<token type="LiteralStringRegex"/>
|
||||
</rule>
|
||||
<rule pattern="[~^*!%&<>|+=:;,/?\\-]+">
|
||||
<token type="Operator"/>
|
||||
</rule>
|
||||
<rule pattern="[{}\[\]();.]+">
|
||||
<token type="Punctuation"/>
|
||||
</rule>
|
||||
<rule pattern="(instanceof|arguments|continue|default|typeof|switch|return|catch|break|while|throw|each|this|with|else|case|var|new|for|try|if|do|in)\b">
|
||||
<token type="Keyword"/>
|
||||
</rule>
|
||||
<rule pattern="(implements|protected|namespace|interface|intrinsic|override|function|internal|private|package|extends|dynamic|import|native|return|public|static|class|const|super|final|get|set)\b">
|
||||
<token type="KeywordDeclaration"/>
|
||||
</rule>
|
||||
<rule pattern="(true|false|null|NaN|Infinity|-Infinity|undefined|Void)\b">
|
||||
<token type="KeywordConstant"/>
|
||||
</rule>
|
||||
<rule pattern="(IDynamicPropertyOutputIDynamicPropertyWriter|DisplacmentMapFilterMode|AccessibilityProperties|ContextMenuBuiltInItems|SharedObjectFlushStatus|DisplayObjectContainer|IllegalOperationError|DisplacmentMapFilter|InterpolationMethod|URLLoaderDataFormat|PrintJobOrientation|ActionScriptVersion|BitmapFilterQuality|GradientBevelFilter|GradientGlowFilter|DeleteObjectSample|StackOverflowError|SoundLoaderContext|ScriptTimeoutError|SecurityErrorEvent|InteractiveObject|StageDisplayState|FileReferenceList|TextFieldAutoSize|ApplicationDomain|BitmapDataChannel|ColorMatrixFilter|ExternalInterface|IMEConversionMode|DropShadowFilter|URLRequestHeader|ContextMenuEvent|ConvultionFilter|URLRequestMethod|BitmapFilterType|IEventDispatcher|ContextMenuItem|LocalConnection|InvalidSWFError|AsyncErrorEvent|MovieClipLoader|IBitmapDrawable|PrintJobOptions|EventDispatcher|NewObjectSample|HTTPStatusEvent|TextFormatAlign|IExternalizable|FullScreenEvent|DefinitionError|TextLineMetrics|NetStatusEvent|ColorTransform|ObjectEncoding|SecurityDomain|StageScaleMode|FocusDirection|ReferenceError|SoundTransform|KeyboardEvent|DisplayObject|PixelSnapping|LoaderContext|NetConnection|SecurityPanel|SecurityError|FileReference|AsBroadcaster|LineScaleMode|AntiAliasType|Accessibility|TextFieldType|URLVariabeles|ActivityEvent|ProgressEvent|TextColorType|StageQuality|TextSnapshot|Capabilities|BitmapFilter|SpreadMethod|GradientType|TextRenderer|SoundChannel|SharedObject|IOErrorEvent|SimpleButton|ContextMenu|InvokeEvent|CSMSettings|SyntaxError|StatusEvent|KeyLocation|IDataOutput|VerifyError|XMLDocument|XMLNodeType|MemoryError|GridFitType|BevelFilter|ErrorEvent|FrameLabel|GlowFilter|LoaderInfo|Microphone|MorphShape|BlurFilter|MouseEvent|FocusEvent|SoundMixer|FileFilter|TimerEvent|JointStyle|EventPhase|StageAlign|Dictionary|URLRequest|StyleSheet|SWFVersion|IDataInput|StaticText|RangeError|BitmapData|TextFormat|StackFrame|Namespace|SyncEvent|Rectangle|URLLoader|TypeError|Responder|NetStream|BlendMode|CapsStyle|DataEvent|ByteArray|MovieClip|Transform|TextField|Selection|AVM1Movie|XMLSocket|URLStream|FontStyle|EvalError|FontType|LoadVars|Graphics|Security|IMEEvent|URIError|Keyboard|Function|EOFError|PrintJob|IOError|XMLList|Boolean|ID3Info|XMLNode|Bitmap|String|RegExp|Sample|Object|Sprite|System|Endian|Matrix|Camera|Locale|Number|Loader|Socket|QName|Class|Timer|Sound|Shape|XMLUI|Mouse|Scene|Stage|Color|Point|Video|Error|Event|Proxy|Array|Date|uint|Math|Font|int|Key|IME|XML)\b">
|
||||
<token type="NameBuiltin"/>
|
||||
</rule>
|
||||
<rule pattern="(decodeURIComponent|updateAfterEvent|clearInterval|setInterval|getVersion|parseFloat|fscommand|isXMLName|encodeURI|decodeURI|getTimer|unescape|isFinite|parseInt|getURL|escape|trace|isNaN|eval)\b">
|
||||
<token type="NameFunction"/>
|
||||
</rule>
|
||||
<rule pattern="[$a-zA-Z_]\w*">
|
||||
<token type="NameOther"/>
|
||||
</rule>
|
||||
<rule pattern="[0-9][0-9]*\.[0-9]+([eE][0-9]+)?[fd]?">
|
||||
<token type="LiteralNumberFloat"/>
|
||||
</rule>
|
||||
<rule pattern="0x[0-9a-f]+">
|
||||
<token type="LiteralNumberHex"/>
|
||||
</rule>
|
||||
<rule pattern="[0-9]+">
|
||||
<token type="LiteralNumberInteger"/>
|
||||
</rule>
|
||||
<rule pattern=""(\\\\|\\"|[^"])*"">
|
||||
<token type="LiteralStringDouble"/>
|
||||
</rule>
|
||||
<rule pattern="'(\\\\|\\'|[^'])*'">
|
||||
<token type="LiteralStringSingle"/>
|
||||
</rule>
|
||||
</state>
|
||||
</rules>
|
||||
</lexer>
|
||||
163
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/actionscript_3.xml
generated
vendored
Normal file
163
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/actionscript_3.xml
generated
vendored
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
<lexer>
|
||||
<config>
|
||||
<name>ActionScript 3</name>
|
||||
<alias>as3</alias>
|
||||
<alias>actionscript3</alias>
|
||||
<filename>*.as</filename>
|
||||
<mime_type>application/x-actionscript3</mime_type>
|
||||
<mime_type>text/x-actionscript3</mime_type>
|
||||
<mime_type>text/actionscript3</mime_type>
|
||||
<dot_all>true</dot_all>
|
||||
</config>
|
||||
<rules>
|
||||
<state name="funcparams">
|
||||
<rule pattern="\s+">
|
||||
<token type="Text"/>
|
||||
</rule>
|
||||
<rule pattern="(\s*)(\.\.\.)?([$a-zA-Z_]\w*)(\s*)(:)(\s*)([$a-zA-Z_]\w*(?:\.<\w+>)?|\*)(\s*)">
|
||||
<bygroups>
|
||||
<token type="Text"/>
|
||||
<token type="Punctuation"/>
|
||||
<token type="Name"/>
|
||||
<token type="Text"/>
|
||||
<token type="Operator"/>
|
||||
<token type="Text"/>
|
||||
<token type="KeywordType"/>
|
||||
<token type="Text"/>
|
||||
</bygroups>
|
||||
<push state="defval"/>
|
||||
</rule>
|
||||
<rule pattern="\)">
|
||||
<token type="Operator"/>
|
||||
<push state="type"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="type">
|
||||
<rule pattern="(\s*)(:)(\s*)([$a-zA-Z_]\w*(?:\.<\w+>)?|\*)">
|
||||
<bygroups>
|
||||
<token type="Text"/>
|
||||
<token type="Operator"/>
|
||||
<token type="Text"/>
|
||||
<token type="KeywordType"/>
|
||||
</bygroups>
|
||||
<pop depth="2"/>
|
||||
</rule>
|
||||
<rule pattern="\s+">
|
||||
<token type="Text"/>
|
||||
<pop depth="2"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<pop depth="2"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="defval">
|
||||
<rule pattern="(=)(\s*)([^(),]+)(\s*)(,?)">
|
||||
<bygroups>
|
||||
<token type="Operator"/>
|
||||
<token type="Text"/>
|
||||
<usingself state="root"/>
|
||||
<token type="Text"/>
|
||||
<token type="Operator"/>
|
||||
</bygroups>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule pattern=",">
|
||||
<token type="Operator"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="root">
|
||||
<rule pattern="\s+">
|
||||
<token type="Text"/>
|
||||
</rule>
|
||||
<rule pattern="(function\s+)([$a-zA-Z_]\w*)(\s*)(\()">
|
||||
<bygroups>
|
||||
<token type="KeywordDeclaration"/>
|
||||
<token type="NameFunction"/>
|
||||
<token type="Text"/>
|
||||
<token type="Operator"/>
|
||||
</bygroups>
|
||||
<push state="funcparams"/>
|
||||
</rule>
|
||||
<rule pattern="(var|const)(\s+)([$a-zA-Z_]\w*)(\s*)(:)(\s*)([$a-zA-Z_]\w*(?:\.<\w+>)?)">
|
||||
<bygroups>
|
||||
<token type="KeywordDeclaration"/>
|
||||
<token type="Text"/>
|
||||
<token type="Name"/>
|
||||
<token type="Text"/>
|
||||
<token type="Punctuation"/>
|
||||
<token type="Text"/>
|
||||
<token type="KeywordType"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="(import|package)(\s+)((?:[$a-zA-Z_]\w*|\.)+)(\s*)">
|
||||
<bygroups>
|
||||
<token type="Keyword"/>
|
||||
<token type="Text"/>
|
||||
<token type="NameNamespace"/>
|
||||
<token type="Text"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="(new)(\s+)([$a-zA-Z_]\w*(?:\.<\w+>)?)(\s*)(\()">
|
||||
<bygroups>
|
||||
<token type="Keyword"/>
|
||||
<token type="Text"/>
|
||||
<token type="KeywordType"/>
|
||||
<token type="Text"/>
|
||||
<token type="Operator"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="//.*?\n">
|
||||
<token type="CommentSingle"/>
|
||||
</rule>
|
||||
<rule pattern="/\*.*?\*/">
|
||||
<token type="CommentMultiline"/>
|
||||
</rule>
|
||||
<rule pattern="/(\\\\|\\/|[^\n])*/[gisx]*">
|
||||
<token type="LiteralStringRegex"/>
|
||||
</rule>
|
||||
<rule pattern="(\.)([$a-zA-Z_]\w*)">
|
||||
<bygroups>
|
||||
<token type="Operator"/>
|
||||
<token type="NameAttribute"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="(case|default|for|each|in|while|do|break|return|continue|if|else|throw|try|catch|with|new|typeof|arguments|instanceof|this|switch|import|include|as|is)\b">
|
||||
<token type="Keyword"/>
|
||||
</rule>
|
||||
<rule pattern="(class|public|final|internal|native|override|private|protected|static|import|extends|implements|interface|intrinsic|return|super|dynamic|function|const|get|namespace|package|set)\b">
|
||||
<token type="KeywordDeclaration"/>
|
||||
</rule>
|
||||
<rule pattern="(true|false|null|NaN|Infinity|-Infinity|undefined|void)\b">
|
||||
<token type="KeywordConstant"/>
|
||||
</rule>
|
||||
<rule pattern="(decodeURI|decodeURIComponent|encodeURI|escape|eval|isFinite|isNaN|isXMLName|clearInterval|fscommand|getTimer|getURL|getVersion|isFinite|parseFloat|parseInt|setInterval|trace|updateAfterEvent|unescape)\b">
|
||||
<token type="NameFunction"/>
|
||||
</rule>
|
||||
<rule pattern="[$a-zA-Z_]\w*">
|
||||
<token type="Name"/>
|
||||
</rule>
|
||||
<rule pattern="[0-9][0-9]*\.[0-9]+([eE][0-9]+)?[fd]?">
|
||||
<token type="LiteralNumberFloat"/>
|
||||
</rule>
|
||||
<rule pattern="0x[0-9a-f]+">
|
||||
<token type="LiteralNumberHex"/>
|
||||
</rule>
|
||||
<rule pattern="[0-9]+">
|
||||
<token type="LiteralNumberInteger"/>
|
||||
</rule>
|
||||
<rule pattern=""(\\\\|\\"|[^"])*"">
|
||||
<token type="LiteralStringDouble"/>
|
||||
</rule>
|
||||
<rule pattern="'(\\\\|\\'|[^'])*'">
|
||||
<token type="LiteralStringSingle"/>
|
||||
</rule>
|
||||
<rule pattern="[~^*!%&<>|+=:;,/?\\{}\[\]().-]+">
|
||||
<token type="Operator"/>
|
||||
</rule>
|
||||
</state>
|
||||
</rules>
|
||||
</lexer>
|
||||
321
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/ada.xml
generated
vendored
Normal file
321
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/ada.xml
generated
vendored
Normal file
|
|
@ -0,0 +1,321 @@
|
|||
<lexer>
|
||||
<config>
|
||||
<name>Ada</name>
|
||||
<alias>ada</alias>
|
||||
<alias>ada95</alias>
|
||||
<alias>ada2005</alias>
|
||||
<filename>*.adb</filename>
|
||||
<filename>*.ads</filename>
|
||||
<filename>*.ada</filename>
|
||||
<mime_type>text/x-ada</mime_type>
|
||||
<case_insensitive>true</case_insensitive>
|
||||
</config>
|
||||
<rules>
|
||||
<state name="end">
|
||||
<rule pattern="(if|case|record|loop|select)">
|
||||
<token type="KeywordReserved"/>
|
||||
</rule>
|
||||
<rule pattern=""[^"]+"|[\w.]+">
|
||||
<token type="NameFunction"/>
|
||||
</rule>
|
||||
<rule pattern="\s+">
|
||||
<token type="Text"/>
|
||||
</rule>
|
||||
<rule pattern=";">
|
||||
<token type="Punctuation"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="array_def">
|
||||
<rule pattern=";">
|
||||
<token type="Punctuation"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule pattern="(\w+)(\s+)(range)">
|
||||
<bygroups>
|
||||
<token type="KeywordType"/>
|
||||
<token type="Text"/>
|
||||
<token type="KeywordReserved"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="root"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="package_instantiation">
|
||||
<rule pattern="("[^"]+"|\w+)(\s+)(=>)">
|
||||
<bygroups>
|
||||
<token type="NameVariable"/>
|
||||
<token type="Text"/>
|
||||
<token type="Punctuation"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="[\w.\'"]">
|
||||
<token type="Text"/>
|
||||
</rule>
|
||||
<rule pattern="\)">
|
||||
<token type="Punctuation"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="root"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="subprogram">
|
||||
<rule pattern="\(">
|
||||
<token type="Punctuation"/>
|
||||
<push state="#pop" state="formal_part"/>
|
||||
</rule>
|
||||
<rule pattern=";">
|
||||
<token type="Punctuation"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule pattern="is\b">
|
||||
<token type="KeywordReserved"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule pattern=""[^"]+"|\w+">
|
||||
<token type="NameFunction"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="root"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="type_def">
|
||||
<rule pattern=";">
|
||||
<token type="Punctuation"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule pattern="\(">
|
||||
<token type="Punctuation"/>
|
||||
<push state="formal_part"/>
|
||||
</rule>
|
||||
<rule pattern="with|and|use">
|
||||
<token type="KeywordReserved"/>
|
||||
</rule>
|
||||
<rule pattern="array\b">
|
||||
<token type="KeywordReserved"/>
|
||||
<push state="#pop" state="array_def"/>
|
||||
</rule>
|
||||
<rule pattern="record\b">
|
||||
<token type="KeywordReserved"/>
|
||||
<push state="record_def"/>
|
||||
</rule>
|
||||
<rule pattern="(null record)(;)">
|
||||
<bygroups>
|
||||
<token type="KeywordReserved"/>
|
||||
<token type="Punctuation"/>
|
||||
</bygroups>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="root"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="import">
|
||||
<rule pattern="[\w.]+">
|
||||
<token type="NameNamespace"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="formal_part">
|
||||
<rule pattern="\)">
|
||||
<token type="Punctuation"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule pattern="\w+">
|
||||
<token type="NameVariable"/>
|
||||
</rule>
|
||||
<rule pattern=",|:[^=]">
|
||||
<token type="Punctuation"/>
|
||||
</rule>
|
||||
<rule pattern="(in|not|null|out|access)\b">
|
||||
<token type="KeywordReserved"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="root"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="package">
|
||||
<rule pattern="body">
|
||||
<token type="KeywordDeclaration"/>
|
||||
</rule>
|
||||
<rule pattern="is\s+new|renames">
|
||||
<token type="KeywordReserved"/>
|
||||
</rule>
|
||||
<rule pattern="is">
|
||||
<token type="KeywordReserved"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule pattern=";">
|
||||
<token type="Punctuation"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule pattern="\(">
|
||||
<token type="Punctuation"/>
|
||||
<push state="package_instantiation"/>
|
||||
</rule>
|
||||
<rule pattern="([\w.]+)">
|
||||
<token type="NameClass"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="root"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="attribute">
|
||||
<rule pattern="(')(\w+)">
|
||||
<bygroups>
|
||||
<token type="Punctuation"/>
|
||||
<token type="NameAttribute"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="record_def">
|
||||
<rule pattern="end record">
|
||||
<token type="KeywordReserved"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="root"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="root">
|
||||
<rule pattern="[^\S\n]+">
|
||||
<token type="Text"/>
|
||||
</rule>
|
||||
<rule pattern="--.*?\n">
|
||||
<token type="CommentSingle"/>
|
||||
</rule>
|
||||
<rule pattern="[^\S\n]+">
|
||||
<token type="Text"/>
|
||||
</rule>
|
||||
<rule pattern="function|procedure|entry">
|
||||
<token type="KeywordDeclaration"/>
|
||||
<push state="subprogram"/>
|
||||
</rule>
|
||||
<rule pattern="(subtype|type)(\s+)(\w+)">
|
||||
<bygroups>
|
||||
<token type="KeywordDeclaration"/>
|
||||
<token type="Text"/>
|
||||
<token type="KeywordType"/>
|
||||
</bygroups>
|
||||
<push state="type_def"/>
|
||||
</rule>
|
||||
<rule pattern="task|protected">
|
||||
<token type="KeywordDeclaration"/>
|
||||
</rule>
|
||||
<rule pattern="(subtype)(\s+)">
|
||||
<bygroups>
|
||||
<token type="KeywordDeclaration"/>
|
||||
<token type="Text"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="(end)(\s+)">
|
||||
<bygroups>
|
||||
<token type="KeywordReserved"/>
|
||||
<token type="Text"/>
|
||||
</bygroups>
|
||||
<push state="end"/>
|
||||
</rule>
|
||||
<rule pattern="(pragma)(\s+)(\w+)">
|
||||
<bygroups>
|
||||
<token type="KeywordReserved"/>
|
||||
<token type="Text"/>
|
||||
<token type="CommentPreproc"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="(true|false|null)\b">
|
||||
<token type="KeywordConstant"/>
|
||||
</rule>
|
||||
<rule pattern="(Short_Short_Integer|Short_Short_Float|Long_Long_Integer|Long_Long_Float|Wide_Character|Reference_Type|Short_Integer|Long_Integer|Wide_String|Short_Float|Controlled|Long_Float|Character|Generator|File_Type|File_Mode|Positive|Duration|Boolean|Natural|Integer|Address|Cursor|String|Count|Float|Byte)\b">
|
||||
<token type="KeywordType"/>
|
||||
</rule>
|
||||
<rule pattern="(and(\s+then)?|in|mod|not|or(\s+else)|rem)\b">
|
||||
<token type="OperatorWord"/>
|
||||
</rule>
|
||||
<rule pattern="generic|private">
|
||||
<token type="KeywordDeclaration"/>
|
||||
</rule>
|
||||
<rule pattern="package">
|
||||
<token type="KeywordDeclaration"/>
|
||||
<push state="package"/>
|
||||
</rule>
|
||||
<rule pattern="array\b">
|
||||
<token type="KeywordReserved"/>
|
||||
<push state="array_def"/>
|
||||
</rule>
|
||||
<rule pattern="(with|use)(\s+)">
|
||||
<bygroups>
|
||||
<token type="KeywordNamespace"/>
|
||||
<token type="Text"/>
|
||||
</bygroups>
|
||||
<push state="import"/>
|
||||
</rule>
|
||||
<rule pattern="(\w+)(\s*)(:)(\s*)(constant)">
|
||||
<bygroups>
|
||||
<token type="NameConstant"/>
|
||||
<token type="Text"/>
|
||||
<token type="Punctuation"/>
|
||||
<token type="Text"/>
|
||||
<token type="KeywordReserved"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="<<\w+>>">
|
||||
<token type="NameLabel"/>
|
||||
</rule>
|
||||
<rule pattern="(\w+)(\s*)(:)(\s*)(declare|begin|loop|for|while)">
|
||||
<bygroups>
|
||||
<token type="NameLabel"/>
|
||||
<token type="Text"/>
|
||||
<token type="Punctuation"/>
|
||||
<token type="Text"/>
|
||||
<token type="KeywordReserved"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="\b(synchronized|overriding|terminate|interface|exception|protected|separate|constant|abstract|renames|reverse|subtype|aliased|declare|requeue|limited|return|tagged|access|record|select|accept|digits|others|pragma|entry|elsif|delta|delay|array|until|range|raise|while|begin|abort|else|loop|when|type|null|then|body|task|goto|case|exit|end|for|abs|xor|all|new|out|is|of|if|or|do|at)\b">
|
||||
<token type="KeywordReserved"/>
|
||||
</rule>
|
||||
<rule pattern=""[^"]*"">
|
||||
<token type="LiteralString"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="attribute"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="numbers"/>
|
||||
</rule>
|
||||
<rule pattern="'[^']'">
|
||||
<token type="LiteralStringChar"/>
|
||||
</rule>
|
||||
<rule pattern="(\w+)(\s*|[(,])">
|
||||
<bygroups>
|
||||
<token type="Name"/>
|
||||
<usingself state="root"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="(<>|=>|:=|[()|:;,.'])">
|
||||
<token type="Punctuation"/>
|
||||
</rule>
|
||||
<rule pattern="[*<>+=/&-]">
|
||||
<token type="Operator"/>
|
||||
</rule>
|
||||
<rule pattern="\n+">
|
||||
<token type="Text"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="numbers">
|
||||
<rule pattern="[0-9_]+#[0-9a-f]+#">
|
||||
<token type="LiteralNumberHex"/>
|
||||
</rule>
|
||||
<rule pattern="[0-9_]+\.[0-9_]*">
|
||||
<token type="LiteralNumberFloat"/>
|
||||
</rule>
|
||||
<rule pattern="[0-9_]+">
|
||||
<token type="LiteralNumberInteger"/>
|
||||
</rule>
|
||||
</state>
|
||||
</rules>
|
||||
</lexer>
|
||||
66
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/agda.xml
generated
vendored
Normal file
66
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/agda.xml
generated
vendored
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
<lexer>
|
||||
<config>
|
||||
<name>Agda</name>
|
||||
<alias>agda</alias>
|
||||
<filename>*.agda</filename>
|
||||
<mime_type>text/x-agda</mime_type>
|
||||
</config>
|
||||
<rules>
|
||||
<state name="root">
|
||||
<rule pattern="^(\s*)([^\s(){}]+)(\s*)(:)(\s*)"><bygroups><token type="TextWhitespace"/><token type="NameFunction"/><token type="TextWhitespace"/><token type="OperatorWord"/><token type="TextWhitespace"/></bygroups></rule>
|
||||
<rule pattern="--(?![!#$%&*+./<=>?@^|_~:\\]).*?$"><token type="CommentSingle"/></rule>
|
||||
<rule pattern="\{-"><token type="CommentMultiline"/><push state="comment"/></rule>
|
||||
<rule pattern="\{!"><token type="CommentMultiline"/><push state="hole"/></rule>
|
||||
<rule pattern="\b(abstract|codata|coinductive|constructor|data|do|eta-equality|field|forall|hiding|in|inductive|infix|infixl|infixr|instance|interleaved|let|macro|mutual|no-eta-equality|open|overlap|pattern|postulate|primitive|private|quote|quoteTerm|record|renaming|rewrite|syntax|tactic|unquote|unquoteDecl|unquoteDef|using|variable|where|with)(?!\')\b"><token type="KeywordReserved"/></rule>
|
||||
<rule pattern="(import|module)(\s+)"><bygroups><token type="KeywordReserved"/><token type="TextWhitespace"/></bygroups><push state="module"/></rule>
|
||||
<rule pattern="\b(Set|Prop)[\u2080-\u2089]*\b"><token type="KeywordType"/></rule>
|
||||
<rule pattern="(\(|\)|\{|\})"><token type="Operator"/></rule>
|
||||
<rule pattern="(\.{1,3}|\||\u03BB|\u2200|\u2192|:|=|->)"><token type="OperatorWord"/></rule>
|
||||
<rule pattern="\d+[eE][+-]?\d+"><token type="LiteralNumberFloat"/></rule>
|
||||
<rule pattern="\d+\.\d+([eE][+-]?\d+)?"><token type="LiteralNumberFloat"/></rule>
|
||||
<rule pattern="0[xX][\da-fA-F]+"><token type="LiteralNumberHex"/></rule>
|
||||
<rule pattern="\d+"><token type="LiteralNumberInteger"/></rule>
|
||||
<rule pattern="'"><token type="LiteralStringChar"/><push state="character"/></rule>
|
||||
<rule pattern="""><token type="LiteralString"/><push state="string"/></rule>
|
||||
<rule pattern="[^\s(){}]+"><token type="Text"/></rule>
|
||||
<rule pattern="\s+?"><token type="TextWhitespace"/></rule>
|
||||
</state>
|
||||
<state name="hole">
|
||||
<rule pattern="[^!{}]+"><token type="CommentMultiline"/></rule>
|
||||
<rule pattern="\{!"><token type="CommentMultiline"/><push/></rule>
|
||||
<rule pattern="!\}"><token type="CommentMultiline"/><pop depth="1"/></rule>
|
||||
<rule pattern="[!{}]"><token type="CommentMultiline"/></rule>
|
||||
</state>
|
||||
<state name="module">
|
||||
<rule pattern="\{-"><token type="CommentMultiline"/><push state="comment"/></rule>
|
||||
<rule pattern="[a-zA-Z][\w.\']*"><token type="Name"/><pop depth="1"/></rule>
|
||||
<rule pattern="[\W0-9_]+"><token type="Text"/></rule>
|
||||
</state>
|
||||
<state name="comment">
|
||||
<rule pattern="[^-{}]+"><token type="CommentMultiline"/></rule>
|
||||
<rule pattern="\{-"><token type="CommentMultiline"/><push/></rule>
|
||||
<rule pattern="-\}"><token type="CommentMultiline"/><pop depth="1"/></rule>
|
||||
<rule pattern="[-{}]"><token type="CommentMultiline"/></rule>
|
||||
</state>
|
||||
<state name="character">
|
||||
<rule pattern="[^\\']'"><token type="LiteralStringChar"/><pop depth="1"/></rule>
|
||||
<rule pattern="\\"><token type="LiteralStringEscape"/><push state="escape"/></rule>
|
||||
<rule pattern="'"><token type="LiteralStringChar"/><pop depth="1"/></rule>
|
||||
</state>
|
||||
<state name="string">
|
||||
<rule pattern="[^\\"]+"><token type="LiteralString"/></rule>
|
||||
<rule pattern="\\"><token type="LiteralStringEscape"/><push state="escape"/></rule>
|
||||
<rule pattern="""><token type="LiteralString"/><pop depth="1"/></rule>
|
||||
</state>
|
||||
<state name="escape">
|
||||
<rule pattern="[abfnrtv"\'&\\]"><token type="LiteralStringEscape"/><pop depth="1"/></rule>
|
||||
<rule pattern="\^[][A-ZÀ-ÖØ-ÞĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮİIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŸ-ŹŻŽƁ-ƂƄƆ-ƇƉ-ƋƎ-ƑƓ-ƔƖ-ƘƜ-ƝƟ-ƠƢƤƦ-ƧƩƬƮ-ƯƱ-ƳƵƷ-ƸƼDŽLJNJǍǏǑǓǕǗǙǛǞǠǢǤǦǨǪǬǮDZǴǶ-ǸǺǼǾȀȂȄȆȈȊȌȎȐȒȔȖȘȚȜȞȠȢȤȦȨȪȬȮȰȲȺ-ȻȽ-ȾɁɃ-ɆɈɊɌɎͰͲͶͿΆΈ-ΊΌΎ-ΏΑ-ΡΣ-ΫϏϒ-ϔϘϚϜϞϠϢϤϦϨϪϬϮϴϷϹ-ϺϽ-ЯѠѢѤѦѨѪѬѮѰѲѴѶѸѺѼѾҀҊҌҎҐҒҔҖҘҚҜҞҠҢҤҦҨҪҬҮҰҲҴҶҸҺҼҾӀ-ӁӃӅӇӉӋӍӐӒӔӖӘӚӜӞӠӢӤӦӨӪӬӮӰӲӴӶӸӺӼӾԀԂԄԆԈԊԌԎԐԒԔԖԘԚԜԞԠԢԤԦԨԪԬԮԱ-ՖႠ-ჅჇჍᎠ-ᏵᲐ-ᲺᲽ-ᲿḀḂḄḆḈḊḌḎḐḒḔḖḘḚḜḞḠḢḤḦḨḪḬḮḰḲḴḶḸḺḼḾṀṂṄṆṈṊṌṎṐṒṔṖṘṚṜṞṠṢṤṦṨṪṬṮṰṲṴṶṸṺṼṾẀẂẄẆẈẊẌẎẐẒẔẞẠẢẤẦẨẪẬẮẰẲẴẶẸẺẼẾỀỂỄỆỈỊỌỎỐỒỔỖỘỚỜỞỠỢỤỦỨỪỬỮỰỲỴỶỸỺỼỾἈ-ἏἘ-ἝἨ-ἯἸ-ἿὈ-ὍὙὛὝὟὨ-ὯᾸ-ΆῈ-ΉῘ-ΊῨ-ῬῸ-Ώℂℇℋ-ℍℐ-ℒℕℙ-ℝℤΩℨK-ℭℰ-ℳℾ-ℿⅅↃⰀ-ⰮⱠⱢ-ⱤⱧⱩⱫⱭ-ⱰⱲⱵⱾ-ⲀⲂⲄⲆⲈⲊⲌⲎⲐⲒⲔⲖⲘⲚⲜⲞⲠⲢⲤⲦⲨⲪⲬⲮⲰⲲⲴⲶⲸⲺⲼⲾⳀⳂⳄⳆⳈⳊⳌⳎⳐⳒⳔⳖⳘⳚⳜⳞⳠⳢⳫⳭⳲꙀꙂꙄꙆꙈꙊꙌꙎꙐꙒꙔꙖꙘꙚꙜꙞꙠꙢꙤꙦꙨꙪꙬꚀꚂꚄꚆꚈꚊꚌꚎꚐꚒꚔꚖꚘꚚꜢꜤꜦꜨꜪꜬꜮꜲꜴꜶꜸꜺꜼꜾꝀꝂꝄꝆꝈꝊꝌꝎꝐꝒꝔꝖꝘꝚꝜꝞꝠꝢꝤꝦꝨꝪꝬꝮꝹꝻꝽ-ꝾꞀꞂꞄꞆꞋꞍꞐꞒꞖꞘꞚꞜꞞꞠꞢꞤꞦꞨꞪ-ꞮꞰ-ꞴꞶꞸA-Z𐐀-𐐧𐒰-𐓓𐲀-𐲲𑢠-𑢿𖹀-𖹟𝐀-𝐙𝐴-𝑍𝑨-𝒁𝒜𝒞-𝒟𝒢𝒥-𝒦𝒩-𝒬𝒮-𝒵𝓐-𝓩𝔄-𝔅𝔇-𝔊𝔍-𝔔𝔖-𝔜𝔸-𝔹𝔻-𝔾𝕀-𝕄𝕆𝕊-𝕐𝕬-𝖅𝖠-𝖹𝗔-𝗭𝘈-𝘡𝘼-𝙕𝙰-𝚉𝚨-𝛀𝛢-𝛺𝜜-𝜴𝝖-𝝮𝞐-𝞨𝟊𞤀-𞤡@^_]"><token type="LiteralStringEscape"/><pop depth="1"/></rule>
|
||||
<rule pattern="NUL|SOH|[SE]TX|EOT|ENQ|ACK|BEL|BS|HT|LF|VT|FF|CR|S[OI]|DLE|DC[1-4]|NAK|SYN|ETB|CAN|EM|SUB|ESC|[FGRU]S|SP|DEL"><token type="LiteralStringEscape"/><pop depth="1"/></rule>
|
||||
<rule pattern="o[0-7]+"><token type="LiteralStringEscape"/><pop depth="1"/></rule>
|
||||
<rule pattern="x[\da-fA-F]+"><token type="LiteralStringEscape"/><pop depth="1"/></rule>
|
||||
<rule pattern="\d+"><token type="LiteralStringEscape"/><pop depth="1"/></rule>
|
||||
<rule pattern="(\s+)(\\)"><bygroups><token type="TextWhitespace"/><token type="LiteralStringEscape"/></bygroups><pop depth="1"/></rule>
|
||||
</state>
|
||||
</rules>
|
||||
</lexer>
|
||||
|
||||
75
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/al.xml
generated
vendored
Normal file
75
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/al.xml
generated
vendored
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
<lexer>
|
||||
<config>
|
||||
<name>AL</name>
|
||||
<alias>al</alias>
|
||||
<filename>*.al</filename>
|
||||
<filename>*.dal</filename>
|
||||
<mime_type>text/x-al</mime_type>
|
||||
<case_insensitive>true</case_insensitive>
|
||||
<dot_all>true</dot_all>
|
||||
</config>
|
||||
<rules>
|
||||
<state name="root">
|
||||
<rule pattern="\s+">
|
||||
<token type="TextWhitespace"/>
|
||||
</rule>
|
||||
<rule pattern="(?s)\/\*.*?\\*\*\/">
|
||||
<token type="CommentMultiline"/>
|
||||
</rule>
|
||||
<rule pattern="(?s)//.*?\n">
|
||||
<token type="CommentSingle"/>
|
||||
</rule>
|
||||
<rule pattern="\"([^\"])*\"">
|
||||
<token type="Text"/>
|
||||
</rule>
|
||||
<rule pattern="'([^'])*'">
|
||||
<token type="LiteralString"/>
|
||||
</rule>
|
||||
<rule pattern="\b(?i:(ARRAY|ASSERTERROR|BEGIN|BREAK|CASE|DO|DOWNTO|ELSE|END|EVENT|EXIT|FOR|FOREACH|FUNCTION|IF|IMPLEMENTS|IN|INDATASET|INTERFACE|INTERNAL|LOCAL|OF|PROCEDURE|PROGRAM|PROTECTED|REPEAT|RUNONCLIENT|SECURITYFILTERING|SUPPRESSDISPOSE|TEMPORARY|THEN|TO|TRIGGER|UNTIL|VAR|WHILE|WITH|WITHEVENTS))\b">
|
||||
<token type="Keyword"/>
|
||||
</rule>
|
||||
<rule pattern="\b(?i:(AND|DIV|MOD|NOT|OR|XOR))\b">
|
||||
<token type="OperatorWord"/>
|
||||
</rule>
|
||||
<rule pattern="\b(?i:(AVERAGE|CONST|COUNT|EXIST|FIELD|FILTER|LOOKUP|MAX|MIN|ORDER|SORTING|SUM|TABLEDATA|UPPERLIMIT|WHERE|ASCENDING|DESCENDING))\b">
|
||||
<token type="Keyword"/>
|
||||
</rule>
|
||||
<rule pattern="\b(?i:(CODEUNIT|PAGE|PAGEEXTENSION|PAGECUSTOMIZATION|DOTNET|ENUM|ENUMEXTENSION|VALUE|QUERY|REPORT|TABLE|TABLEEXTENSION|XMLPORT|PROFILE|CONTROLADDIN|REPORTEXTENSION|INTERFACE|PERMISSIONSET|PERMISSIONSETEXTENSION|ENTITLEMENT))\b">
|
||||
<token type="Keyword"/>
|
||||
</rule>
|
||||
<rule pattern="\b(?i:(Action|Array|Automation|BigInteger|BigText|Blob|Boolean|Byte|Char|ClientType|Code|Codeunit|CompletionTriggerErrorLevel|ConnectionType|Database|DataClassification|DataScope|Date|DateFormula|DateTime|Decimal|DefaultLayout|Dialog|Dictionary|DotNet|DotNetAssembly|DotNetTypeDeclaration|Duration|Enum|ErrorInfo|ErrorType|ExecutionContext|ExecutionMode|FieldClass|FieldRef|FieldType|File|FilterPageBuilder|Guid|InStream|Integer|Joker|KeyRef|List|ModuleDependencyInfo|ModuleInfo|None|Notification|NotificationScope|ObjectType|Option|OutStream|Page|PageResult|Query|Record|RecordId|RecordRef|Report|ReportFormat|SecurityFilter|SecurityFiltering|Table|TableConnectionType|TableFilter|TestAction|TestField|TestFilterField|TestPage|TestPermissions|TestRequestPage|Text|TextBuilder|TextConst|TextEncoding|Time|TransactionModel|TransactionType|Variant|Verbosity|Version|XmlPort|HttpContent|HttpHeaders|HttpClient|HttpRequestMessage|HttpResponseMessage|JsonToken|JsonValue|JsonArray|JsonObject|View|Views|XmlAttribute|XmlAttributeCollection|XmlComment|XmlCData|XmlDeclaration|XmlDocument|XmlDocumentType|XmlElement|XmlNamespaceManager|XmlNameTable|XmlNode|XmlNodeList|XmlProcessingInstruction|XmlReadOptions|XmlText|XmlWriteOptions|WebServiceActionContext|WebServiceActionResultCode|SessionSettings))\b">
|
||||
<token type="Keyword"/>
|
||||
</rule>
|
||||
<rule pattern="\b([<>]=|<>|<|>)\b?">
|
||||
<token type="Operator"/>
|
||||
</rule>
|
||||
<rule pattern="\b(\-|\+|\/|\*)\b">
|
||||
<token type="Operator"/>
|
||||
</rule>
|
||||
<rule pattern="\s*(\:=|\+=|-=|\/=|\*=)\s*?">
|
||||
<token type="Operator"/>
|
||||
</rule>
|
||||
<rule pattern="\b(?i:(ADD|ADDFIRST|ADDLAST|ADDAFTER|ADDBEFORE|ACTION|ACTIONS|AREA|ASSEMBLY|CHARTPART|CUEGROUP|CUSTOMIZES|COLUMN|DATAITEM|DATASET|ELEMENTS|EXTENDS|FIELD|FIELDGROUP|FIELDATTRIBUTE|FIELDELEMENT|FIELDGROUPS|FIELDS|FILTER|FIXED|GRID|GROUP|MOVEAFTER|MOVEBEFORE|KEY|KEYS|LABEL|LABELS|LAYOUT|MODIFY|MOVEFIRST|MOVELAST|MOVEBEFORE|MOVEAFTER|PART|REPEATER|USERCONTROL|REQUESTPAGE|SCHEMA|SEPARATOR|SYSTEMPART|TABLEELEMENT|TEXTATTRIBUTE|TEXTELEMENT|TYPE))\b">
|
||||
<token type="Keyword"/>
|
||||
</rule>
|
||||
<rule pattern="\s*[(\.\.)&\|]\s*">
|
||||
<token type="Operator"/>
|
||||
</rule>
|
||||
<rule pattern="\b((0(x|X)[0-9a-fA-F]*)|(([0-9]+\.?[0-9]*)|(\.[0-9]+))((e|E)(\+|-)?[0-9]+)?)(L|l|UL|ul|u|U|F|f|ll|LL|ull|ULL)?\b">
|
||||
<token type="LiteralNumber"/>
|
||||
</rule>
|
||||
<rule pattern="[;:,]">
|
||||
<token type="Punctuation"/>
|
||||
</rule>
|
||||
<rule pattern="#[ \t]*(if|else|elif|endif|define|undef|region|endregion|pragma)\b.*?\n">
|
||||
<token type="CommentPreproc"/>
|
||||
</rule>
|
||||
<rule pattern="\w+">
|
||||
<token type="Text"/>
|
||||
</rule>
|
||||
<rule pattern=".">
|
||||
<token type="Text"/>
|
||||
</rule>
|
||||
</state>
|
||||
</rules>
|
||||
</lexer>
|
||||
58
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/alloy.xml
generated
vendored
Normal file
58
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/alloy.xml
generated
vendored
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
|
||||
<lexer>
|
||||
<config>
|
||||
<name>Alloy</name>
|
||||
<alias>alloy</alias>
|
||||
<filename>*.als</filename>
|
||||
<mime_type>text/x-alloy</mime_type>
|
||||
<dot_all>true</dot_all>
|
||||
</config>
|
||||
<rules>
|
||||
<state name="sig">
|
||||
<rule pattern="(extends)\b"><token type="Keyword"/><pop depth="1"/></rule>
|
||||
<rule pattern="[a-zA-Z_][\w]*"*"><token type="Name"/></rule>
|
||||
<rule pattern="[^\S\n]+"><token type="TextWhitespace"/></rule>
|
||||
<rule pattern=","><token type="Punctuation"/></rule>
|
||||
<rule pattern="\{"><token type="Operator"/><pop depth="1"/></rule>
|
||||
</state>
|
||||
<state name="module">
|
||||
<rule pattern="[^\S\n]+"><token type="TextWhitespace"/></rule>
|
||||
<rule pattern="[a-zA-Z_][\w]*"*"><token type="Name"/><pop depth="1"/></rule>
|
||||
</state>
|
||||
<state name="fun">
|
||||
<rule pattern="[^\S\n]+"><token type="TextWhitespace"/></rule>
|
||||
<rule pattern="\{"><token type="Operator"/><pop depth="1"/></rule>
|
||||
<rule pattern="[a-zA-Z_][\w]*"*"><token type="Name"/><pop depth="1"/></rule>
|
||||
</state>
|
||||
<state name="fact">
|
||||
<rule><include state="fun"/></rule>
|
||||
<rule pattern=""\b(\\\\|\\[^\\]|[^"\\])*""><token type="LiteralString"/><pop depth="1"/></rule>
|
||||
</state>
|
||||
<state name="root">
|
||||
<rule pattern="--.*?$"><token type="CommentSingle"/></rule>
|
||||
<rule pattern="//.*?$"><token type="CommentSingle"/></rule>
|
||||
<rule pattern="/\*.*?\*/"><token type="CommentMultiline"/></rule>
|
||||
<rule pattern="[^\S\n]+"><token type="TextWhitespace"/></rule>
|
||||
<rule pattern="(module|open)(\s+)"><bygroups><token type="KeywordNamespace"/><token type="TextWhitespace"/></bygroups><push state="module"/></rule>
|
||||
<rule pattern="(sig|enum)(\s+)"><bygroups><token type="KeywordDeclaration"/><token type="TextWhitespace"/></bygroups><push state="sig"/></rule>
|
||||
<rule pattern="(iden|univ|none)\b"><token type="KeywordConstant"/></rule>
|
||||
<rule pattern="(int|Int)\b"><token type="KeywordType"/></rule>
|
||||
<rule pattern="(var|this|abstract|extends|set|seq|one|lone|let)\b"><token type="Keyword"/></rule>
|
||||
<rule pattern="(all|some|no|sum|disj|when|else)\b"><token type="Keyword"/></rule>
|
||||
<rule pattern="(run|check|for|but|exactly|expect|as|steps)\b"><token type="Keyword"/></rule>
|
||||
<rule pattern="(always|after|eventually|until|release)\b"><token type="Keyword"/></rule>
|
||||
<rule pattern="(historically|before|once|since|triggered)\b"><token type="Keyword"/></rule>
|
||||
<rule pattern="(and|or|implies|iff|in)\b"><token type="OperatorWord"/></rule>
|
||||
<rule pattern="(fun|pred|assert)(\s+)"><bygroups><token type="Keyword"/><token type="TextWhitespace"/></bygroups><push state="fun"/></rule>
|
||||
<rule pattern="(fact)(\s+)"><bygroups><token type="Keyword"/><token type="TextWhitespace"/></bygroups><push state="fact"/></rule>
|
||||
<rule pattern="!|#|&&|\+\+|<<|>>|>=|<=>|<=|\.\.|\.|->"><token type="Operator"/></rule>
|
||||
<rule pattern="[-+/*%=<>&!^|~{}\[\]().\';]"><token type="Operator"/></rule>
|
||||
<rule pattern="[a-zA-Z_][\w]*"*"><token type="Name"/></rule>
|
||||
<rule pattern="[:,]"><token type="Punctuation"/></rule>
|
||||
<rule pattern="[0-9]+"><token type="LiteralNumberInteger"/></rule>
|
||||
<rule pattern=""\b(\\\\|\\[^\\]|[^"\\])*""><token type="LiteralString"/></rule>
|
||||
<rule pattern="\n"><token type="TextWhitespace"/></rule>
|
||||
</state>
|
||||
</rules>
|
||||
</lexer>
|
||||
|
||||
109
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/angular2.xml
generated
vendored
Normal file
109
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/angular2.xml
generated
vendored
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
<lexer>
|
||||
<config>
|
||||
<name>Angular2</name>
|
||||
<alias>ng2</alias>
|
||||
</config>
|
||||
<rules>
|
||||
<state name="attr">
|
||||
<rule pattern="".*?"">
|
||||
<token type="LiteralString" />
|
||||
<pop depth="1" />
|
||||
</rule>
|
||||
<rule pattern="'.*?'">
|
||||
<token type="LiteralString" />
|
||||
<pop depth="1" />
|
||||
</rule>
|
||||
<rule pattern="[^\s>]+">
|
||||
<token type="LiteralString" />
|
||||
<pop depth="1" />
|
||||
</rule>
|
||||
</state>
|
||||
<state name="root">
|
||||
<rule pattern="[^{([*#]+">
|
||||
<token type="Other" />
|
||||
</rule>
|
||||
<rule pattern="(\{\{)(\s*)">
|
||||
<bygroups>
|
||||
<token type="CommentPreproc" />
|
||||
<token type="Text" />
|
||||
</bygroups>
|
||||
<push state="ngExpression" />
|
||||
</rule>
|
||||
<rule pattern="([([]+)([\w:.-]+)([\])]+)(\s*)(=)(\s*)">
|
||||
<bygroups>
|
||||
<token type="Punctuation" />
|
||||
<token type="NameAttribute" />
|
||||
<token type="Punctuation" />
|
||||
<token type="Text" />
|
||||
<token type="Operator" />
|
||||
<token type="Text" />
|
||||
</bygroups>
|
||||
<push state="attr" />
|
||||
</rule>
|
||||
<rule pattern="([([]+)([\w:.-]+)([\])]+)(\s*)">
|
||||
<bygroups>
|
||||
<token type="Punctuation" />
|
||||
<token type="NameAttribute" />
|
||||
<token type="Punctuation" />
|
||||
<token type="TextWhitespace" />
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="([*#])([\w:.-]+)(\s*)(=)(\s*)">
|
||||
<bygroups>
|
||||
<token type="Punctuation" />
|
||||
<token type="NameAttribute" />
|
||||
<token type="Punctuation" />
|
||||
<token type="Operator" />
|
||||
<token type="TextWhitespace" />
|
||||
</bygroups>
|
||||
<push state="attr" />
|
||||
</rule>
|
||||
<rule pattern="([*#])([\w:.-]+)(\s*)">
|
||||
<bygroups>
|
||||
<token type="Punctuation" />
|
||||
<token type="NameAttribute" />
|
||||
<token type="Punctuation" />
|
||||
</bygroups>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="ngExpression">
|
||||
<rule pattern="\s+(\|\s+)?">
|
||||
<token type="Text" />
|
||||
</rule>
|
||||
<rule pattern="\}\}">
|
||||
<token type="CommentPreproc" />
|
||||
<pop depth="1" />
|
||||
</rule>
|
||||
<rule pattern=":?(true|false)">
|
||||
<token type="LiteralStringBoolean" />
|
||||
</rule>
|
||||
<rule pattern=":?"(\\\\|\\"|[^"])*"">
|
||||
<token type="LiteralStringDouble" />
|
||||
</rule>
|
||||
<rule pattern=":?'(\\\\|\\'|[^'])*'">
|
||||
<token type="LiteralStringSingle" />
|
||||
</rule>
|
||||
<rule pattern="[0-9](\.[0-9]*)?(eE[+-][0-9])?[flFLdD]?|0[xX][0-9a-fA-F]+[Ll]?">
|
||||
<token type="LiteralNumber" />
|
||||
</rule>
|
||||
<rule pattern="[a-zA-Z][\w-]*(\(.*\))?">
|
||||
<token type="NameVariable" />
|
||||
</rule>
|
||||
<rule pattern="\.[\w-]+(\(.*\))?">
|
||||
<token type="NameVariable" />
|
||||
</rule>
|
||||
<rule pattern="(\?)(\s*)([^}\s]+)(\s*)(:)(\s*)([^}\s]+)(\s*)">
|
||||
<bygroups>
|
||||
<token type="Operator" />
|
||||
<token type="Text" />
|
||||
<token type="LiteralString" />
|
||||
<token type="Text" />
|
||||
<token type="Operator" />
|
||||
<token type="Text" />
|
||||
<token type="LiteralString" />
|
||||
<token type="Text" />
|
||||
</bygroups>
|
||||
</rule>
|
||||
</state>
|
||||
</rules>
|
||||
</lexer>
|
||||
317
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/antlr.xml
generated
vendored
Normal file
317
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/antlr.xml
generated
vendored
Normal file
|
|
@ -0,0 +1,317 @@
|
|||
<lexer>
|
||||
<config>
|
||||
<name>ANTLR</name>
|
||||
<alias>antlr</alias>
|
||||
</config>
|
||||
<rules>
|
||||
<state name="nested-arg-action">
|
||||
<rule pattern="([^$\[\]\'"/]+|"(\\\\|\\"|[^"])*"|'(\\\\|\\'|[^'])*'|//.*$\n?|/\*(.|\n)*?\*/|/(?!\*)(\\\\|\\/|[^/])*/|/)+">
|
||||
<token type="Other"/>
|
||||
</rule>
|
||||
<rule pattern="\[">
|
||||
<token type="Punctuation"/>
|
||||
<push/>
|
||||
</rule>
|
||||
<rule pattern="\]">
|
||||
<token type="Punctuation"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule pattern="(\$[a-zA-Z]+)(\.?)(text|value)?">
|
||||
<bygroups>
|
||||
<token type="NameVariable"/>
|
||||
<token type="Punctuation"/>
|
||||
<token type="NameProperty"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="(\\\\|\\\]|\\\[|[^\[\]])+">
|
||||
<token type="Other"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="exception">
|
||||
<rule pattern="\n">
|
||||
<token type="TextWhitespace"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule pattern="\s">
|
||||
<token type="TextWhitespace"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="comments"/>
|
||||
</rule>
|
||||
<rule pattern="\[">
|
||||
<token type="Punctuation"/>
|
||||
<push state="nested-arg-action"/>
|
||||
</rule>
|
||||
<rule pattern="\{">
|
||||
<token type="Punctuation"/>
|
||||
<push state="action"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="whitespace">
|
||||
<rule pattern="\s+">
|
||||
<token type="TextWhitespace"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="root">
|
||||
<rule>
|
||||
<include state="whitespace"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="comments"/>
|
||||
</rule>
|
||||
<rule pattern="(lexer|parser|tree)?(\s*)(grammar\b)(\s*)([A-Za-z]\w*)(;)">
|
||||
<bygroups>
|
||||
<token type="Keyword"/>
|
||||
<token type="TextWhitespace"/>
|
||||
<token type="Keyword"/>
|
||||
<token type="TextWhitespace"/>
|
||||
<token type="NameClass"/>
|
||||
<token type="Punctuation"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="options\b">
|
||||
<token type="Keyword"/>
|
||||
<push state="options"/>
|
||||
</rule>
|
||||
<rule pattern="tokens\b">
|
||||
<token type="Keyword"/>
|
||||
<push state="tokens"/>
|
||||
</rule>
|
||||
<rule pattern="(scope)(\s*)([A-Za-z]\w*)(\s*)(\{)">
|
||||
<bygroups>
|
||||
<token type="Keyword"/>
|
||||
<token type="TextWhitespace"/>
|
||||
<token type="NameVariable"/>
|
||||
<token type="TextWhitespace"/>
|
||||
<token type="Punctuation"/>
|
||||
</bygroups>
|
||||
<push state="action"/>
|
||||
</rule>
|
||||
<rule pattern="(catch|finally)\b">
|
||||
<token type="Keyword"/>
|
||||
<push state="exception"/>
|
||||
</rule>
|
||||
<rule pattern="(@[A-Za-z]\w*)(\s*)(::)?(\s*)([A-Za-z]\w*)(\s*)(\{)">
|
||||
<bygroups>
|
||||
<token type="NameLabel"/>
|
||||
<token type="TextWhitespace"/>
|
||||
<token type="Punctuation"/>
|
||||
<token type="TextWhitespace"/>
|
||||
<token type="NameLabel"/>
|
||||
<token type="TextWhitespace"/>
|
||||
<token type="Punctuation"/>
|
||||
</bygroups>
|
||||
<push state="action"/>
|
||||
</rule>
|
||||
<rule pattern="((?:protected|private|public|fragment)\b)?(\s*)([A-Za-z]\w*)(!)?">
|
||||
<bygroups>
|
||||
<token type="Keyword"/>
|
||||
<token type="TextWhitespace"/>
|
||||
<token type="NameLabel"/>
|
||||
<token type="Punctuation"/>
|
||||
</bygroups>
|
||||
<push state="rule-alts" state="rule-prelims"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="tokens">
|
||||
<rule>
|
||||
<include state="whitespace"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="comments"/>
|
||||
</rule>
|
||||
<rule pattern="\{">
|
||||
<token type="Punctuation"/>
|
||||
</rule>
|
||||
<rule pattern="([A-Z]\w*)(\s*)(=)?(\s*)(\'(?:\\\\|\\\'|[^\']*)\')?(\s*)(;)">
|
||||
<bygroups>
|
||||
<token type="NameLabel"/>
|
||||
<token type="TextWhitespace"/>
|
||||
<token type="Punctuation"/>
|
||||
<token type="TextWhitespace"/>
|
||||
<token type="LiteralString"/>
|
||||
<token type="TextWhitespace"/>
|
||||
<token type="Punctuation"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="\}">
|
||||
<token type="Punctuation"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="options">
|
||||
<rule>
|
||||
<include state="whitespace"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="comments"/>
|
||||
</rule>
|
||||
<rule pattern="\{">
|
||||
<token type="Punctuation"/>
|
||||
</rule>
|
||||
<rule pattern="([A-Za-z]\w*)(\s*)(=)(\s*)([A-Za-z]\w*|\'(?:\\\\|\\\'|[^\']*)\'|[0-9]+|\*)(\s*)(;)">
|
||||
<bygroups>
|
||||
<token type="NameVariable"/>
|
||||
<token type="TextWhitespace"/>
|
||||
<token type="Punctuation"/>
|
||||
<token type="TextWhitespace"/>
|
||||
<token type="Text"/>
|
||||
<token type="TextWhitespace"/>
|
||||
<token type="Punctuation"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="\}">
|
||||
<token type="Punctuation"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="rule-alts">
|
||||
<rule>
|
||||
<include state="whitespace"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="comments"/>
|
||||
</rule>
|
||||
<rule pattern="options\b">
|
||||
<token type="Keyword"/>
|
||||
<push state="options"/>
|
||||
</rule>
|
||||
<rule pattern=":">
|
||||
<token type="Punctuation"/>
|
||||
</rule>
|
||||
<rule pattern="'(\\\\|\\'|[^'])*'">
|
||||
<token type="LiteralString"/>
|
||||
</rule>
|
||||
<rule pattern=""(\\\\|\\"|[^"])*"">
|
||||
<token type="LiteralString"/>
|
||||
</rule>
|
||||
<rule pattern="<<([^>]|>[^>])>>">
|
||||
<token type="LiteralString"/>
|
||||
</rule>
|
||||
<rule pattern="\$?[A-Z_]\w*">
|
||||
<token type="NameConstant"/>
|
||||
</rule>
|
||||
<rule pattern="\$?[a-z_]\w*">
|
||||
<token type="NameVariable"/>
|
||||
</rule>
|
||||
<rule pattern="(\+|\||->|=>|=|\(|\)|\.\.|\.|\?|\*|\^|!|\#|~)">
|
||||
<token type="Operator"/>
|
||||
</rule>
|
||||
<rule pattern=",">
|
||||
<token type="Punctuation"/>
|
||||
</rule>
|
||||
<rule pattern="\[">
|
||||
<token type="Punctuation"/>
|
||||
<push state="nested-arg-action"/>
|
||||
</rule>
|
||||
<rule pattern="\{">
|
||||
<token type="Punctuation"/>
|
||||
<push state="action"/>
|
||||
</rule>
|
||||
<rule pattern=";">
|
||||
<token type="Punctuation"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="rule-prelims">
|
||||
<rule>
|
||||
<include state="whitespace"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="comments"/>
|
||||
</rule>
|
||||
<rule pattern="returns\b">
|
||||
<token type="Keyword"/>
|
||||
</rule>
|
||||
<rule pattern="\[">
|
||||
<token type="Punctuation"/>
|
||||
<push state="nested-arg-action"/>
|
||||
</rule>
|
||||
<rule pattern="\{">
|
||||
<token type="Punctuation"/>
|
||||
<push state="action"/>
|
||||
</rule>
|
||||
<rule pattern="(throws)(\s+)([A-Za-z]\w*)">
|
||||
<bygroups>
|
||||
<token type="Keyword"/>
|
||||
<token type="TextWhitespace"/>
|
||||
<token type="NameLabel"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="(,)(\s*)([A-Za-z]\w*)">
|
||||
<bygroups>
|
||||
<token type="Punctuation"/>
|
||||
<token type="TextWhitespace"/>
|
||||
<token type="NameLabel"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="options\b">
|
||||
<token type="Keyword"/>
|
||||
<push state="options"/>
|
||||
</rule>
|
||||
<rule pattern="(scope)(\s+)(\{)">
|
||||
<bygroups>
|
||||
<token type="Keyword"/>
|
||||
<token type="TextWhitespace"/>
|
||||
<token type="Punctuation"/>
|
||||
</bygroups>
|
||||
<push state="action"/>
|
||||
</rule>
|
||||
<rule pattern="(scope)(\s+)([A-Za-z]\w*)(\s*)(;)">
|
||||
<bygroups>
|
||||
<token type="Keyword"/>
|
||||
<token type="TextWhitespace"/>
|
||||
<token type="NameLabel"/>
|
||||
<token type="TextWhitespace"/>
|
||||
<token type="Punctuation"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="(@[A-Za-z]\w*)(\s*)(\{)">
|
||||
<bygroups>
|
||||
<token type="NameLabel"/>
|
||||
<token type="TextWhitespace"/>
|
||||
<token type="Punctuation"/>
|
||||
</bygroups>
|
||||
<push state="action"/>
|
||||
</rule>
|
||||
<rule pattern=":">
|
||||
<token type="Punctuation"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="action">
|
||||
<rule pattern="([^${}\'"/\\]+|"(\\\\|\\"|[^"])*"|'(\\\\|\\'|[^'])*'|//.*$\n?|/\*(.|\n)*?\*/|/(?!\*)(\\\\|\\/|[^/])*/|\\(?!%)|/)+">
|
||||
<token type="Other"/>
|
||||
</rule>
|
||||
<rule pattern="(\\)(%)">
|
||||
<bygroups>
|
||||
<token type="Punctuation"/>
|
||||
<token type="Other"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="(\$[a-zA-Z]+)(\.?)(text|value)?">
|
||||
<bygroups>
|
||||
<token type="NameVariable"/>
|
||||
<token type="Punctuation"/>
|
||||
<token type="NameProperty"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="\{">
|
||||
<token type="Punctuation"/>
|
||||
<push/>
|
||||
</rule>
|
||||
<rule pattern="\}">
|
||||
<token type="Punctuation"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="comments">
|
||||
<rule pattern="//.*$">
|
||||
<token type="Comment"/>
|
||||
</rule>
|
||||
<rule pattern="/\*(.|\n)*?\*/">
|
||||
<token type="Comment"/>
|
||||
</rule>
|
||||
</state>
|
||||
</rules>
|
||||
</lexer>
|
||||
74
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/apacheconf.xml
generated
vendored
Normal file
74
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/apacheconf.xml
generated
vendored
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
<lexer>
|
||||
<config>
|
||||
<name>ApacheConf</name>
|
||||
<alias>apacheconf</alias>
|
||||
<alias>aconf</alias>
|
||||
<alias>apache</alias>
|
||||
<filename>.htaccess</filename>
|
||||
<filename>apache.conf</filename>
|
||||
<filename>apache2.conf</filename>
|
||||
<mime_type>text/x-apacheconf</mime_type>
|
||||
<case_insensitive>true</case_insensitive>
|
||||
</config>
|
||||
<rules>
|
||||
<state name="root">
|
||||
<rule pattern="\s+">
|
||||
<token type="Text"/>
|
||||
</rule>
|
||||
<rule pattern="(#.*?)$">
|
||||
<token type="Comment"/>
|
||||
</rule>
|
||||
<rule pattern="(<[^\s>]+)(?:(\s+)(.*?))?(>)">
|
||||
<bygroups>
|
||||
<token type="NameTag"/>
|
||||
<token type="Text"/>
|
||||
<token type="LiteralString"/>
|
||||
<token type="NameTag"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="([a-z]\w*)(\s+)">
|
||||
<bygroups>
|
||||
<token type="NameBuiltin"/>
|
||||
<token type="Text"/>
|
||||
</bygroups>
|
||||
<push state="value"/>
|
||||
</rule>
|
||||
<rule pattern="\.+">
|
||||
<token type="Text"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="value">
|
||||
<rule pattern="\\\n">
|
||||
<token type="Text"/>
|
||||
</rule>
|
||||
<rule pattern="$">
|
||||
<token type="Text"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule pattern="\\">
|
||||
<token type="Text"/>
|
||||
</rule>
|
||||
<rule pattern="[^\S\n]+">
|
||||
<token type="Text"/>
|
||||
</rule>
|
||||
<rule pattern="\d+\.\d+\.\d+\.\d+(?:/\d+)?">
|
||||
<token type="LiteralNumber"/>
|
||||
</rule>
|
||||
<rule pattern="\d+">
|
||||
<token type="LiteralNumber"/>
|
||||
</rule>
|
||||
<rule pattern="/([a-z0-9][\w./-]+)">
|
||||
<token type="LiteralStringOther"/>
|
||||
</rule>
|
||||
<rule pattern="(on|off|none|any|all|double|email|dns|min|minimal|os|productonly|full|emerg|alert|crit|error|warn|notice|info|debug|registry|script|inetd|standalone|user|group)\b">
|
||||
<token type="Keyword"/>
|
||||
</rule>
|
||||
<rule pattern=""([^"\\]*(?:\\.[^"\\]*)*)"">
|
||||
<token type="LiteralStringDouble"/>
|
||||
</rule>
|
||||
<rule pattern="[^\s"\\]+">
|
||||
<token type="Text"/>
|
||||
</rule>
|
||||
</state>
|
||||
</rules>
|
||||
</lexer>
|
||||
59
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/apl.xml
generated
vendored
Normal file
59
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/apl.xml
generated
vendored
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
<lexer>
|
||||
<config>
|
||||
<name>APL</name>
|
||||
<alias>apl</alias>
|
||||
<filename>*.apl</filename>
|
||||
</config>
|
||||
<rules>
|
||||
<state name="root">
|
||||
<rule pattern="\s+">
|
||||
<token type="Text"/>
|
||||
</rule>
|
||||
<rule pattern="[⍝#].*$">
|
||||
<token type="CommentSingle"/>
|
||||
</rule>
|
||||
<rule pattern="\'((\'\')|[^\'])*\'">
|
||||
<token type="LiteralStringSingle"/>
|
||||
</rule>
|
||||
<rule pattern=""(("")|[^"])*"">
|
||||
<token type="LiteralStringDouble"/>
|
||||
</rule>
|
||||
<rule pattern="[⋄◇()]">
|
||||
<token type="Punctuation"/>
|
||||
</rule>
|
||||
<rule pattern="[\[\];]">
|
||||
<token type="LiteralStringRegex"/>
|
||||
</rule>
|
||||
<rule pattern="⎕[A-Za-zΔ∆⍙][A-Za-zΔ∆⍙_¯0-9]*">
|
||||
<token type="NameFunction"/>
|
||||
</rule>
|
||||
<rule pattern="[A-Za-zΔ∆⍙_][A-Za-zΔ∆⍙_¯0-9]*">
|
||||
<token type="NameVariable"/>
|
||||
</rule>
|
||||
<rule pattern="¯?(0[Xx][0-9A-Fa-f]+|[0-9]*\.?[0-9]+([Ee][+¯]?[0-9]+)?|¯|∞)([Jj]¯?(0[Xx][0-9A-Fa-f]+|[0-9]*\.?[0-9]+([Ee][+¯]?[0-9]+)?|¯|∞))?">
|
||||
<token type="LiteralNumber"/>
|
||||
</rule>
|
||||
<rule pattern="[\.\\/⌿⍀¨⍣⍨⍠⍤∘⍥@⌺⌶⍢]">
|
||||
<token type="NameAttribute"/>
|
||||
</rule>
|
||||
<rule pattern="[+\-×÷⌈⌊∣|⍳?*⍟○!⌹<≤=>≥≠≡≢∊⍷∪∩~∨∧⍱⍲⍴,⍪⌽⊖⍉↑↓⊂⊃⌷⍋⍒⊤⊥⍕⍎⊣⊢⍁⍂≈⌸⍯↗⊆⍸]">
|
||||
<token type="Operator"/>
|
||||
</rule>
|
||||
<rule pattern="⍬">
|
||||
<token type="NameConstant"/>
|
||||
</rule>
|
||||
<rule pattern="[⎕⍞]">
|
||||
<token type="NameVariableGlobal"/>
|
||||
</rule>
|
||||
<rule pattern="[←→]">
|
||||
<token type="KeywordDeclaration"/>
|
||||
</rule>
|
||||
<rule pattern="[⍺⍵⍶⍹∇:]">
|
||||
<token type="NameBuiltinPseudo"/>
|
||||
</rule>
|
||||
<rule pattern="[{}]">
|
||||
<token type="KeywordType"/>
|
||||
</rule>
|
||||
</state>
|
||||
</rules>
|
||||
</lexer>
|
||||
151
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/applescript.xml
generated
vendored
Normal file
151
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/applescript.xml
generated
vendored
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
<lexer>
|
||||
<config>
|
||||
<name>AppleScript</name>
|
||||
<alias>applescript</alias>
|
||||
<filename>*.applescript</filename>
|
||||
<dot_all>true</dot_all>
|
||||
</config>
|
||||
<rules>
|
||||
<state name="root">
|
||||
<rule pattern="\s+">
|
||||
<token type="Text" />
|
||||
</rule>
|
||||
<rule pattern="¬\n">
|
||||
<token type="LiteralStringEscape" />
|
||||
</rule>
|
||||
<rule pattern="'s\s+">
|
||||
<token type="Text" />
|
||||
</rule>
|
||||
<rule pattern="(--|#).*?$">
|
||||
<token type="Comment" />
|
||||
</rule>
|
||||
<rule pattern="\(\*">
|
||||
<token type="CommentMultiline" />
|
||||
<push state="comment" />
|
||||
</rule>
|
||||
<rule pattern="[(){}!,.:]">
|
||||
<token type="Punctuation" />
|
||||
</rule>
|
||||
<rule pattern="(«)([^»]+)(»)">
|
||||
<bygroups>
|
||||
<token type="Text" />
|
||||
<token type="NameBuiltin" />
|
||||
<token type="Text" />
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule
|
||||
pattern="\b((?:considering|ignoring)\s*)(application responses|case|diacriticals|hyphens|numeric strings|punctuation|white space)"
|
||||
>
|
||||
<bygroups>
|
||||
<token type="Keyword" />
|
||||
<token type="NameBuiltin" />
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="(-|\*|\+|&|≠|>=?|<=?|=|≥|≤|/|÷|\^)">
|
||||
<token type="Operator" />
|
||||
</rule>
|
||||
<rule
|
||||
pattern="\b(and|or|is equal|equals|(is )?equal to|is not|isn't|isn't equal( to)?|is not equal( to)?|doesn't equal|does not equal|(is )?greater than|comes after|is not less than or equal( to)?|isn't less than or equal( to)?|(is )?less than|comes before|is not greater than or equal( to)?|isn't greater than or equal( to)?|(is )?greater than or equal( to)?|is not less than|isn't less than|does not come before|doesn't come before|(is )?less than or equal( to)?|is not greater than|isn't greater than|does not come after|doesn't come after|starts? with|begins? with|ends? with|contains?|does not contain|doesn't contain|is in|is contained by|is not in|is not contained by|isn't contained by|div|mod|not|(a )?(ref( to)?|reference to)|is|does)\b"
|
||||
>
|
||||
<token type="OperatorWord" />
|
||||
</rule>
|
||||
<rule
|
||||
pattern="^(\s*(?:on|end)\s+)(zoomed|write to file|will zoom|will show|will select tab view item|will resize( sub views)?|will resign active|will quit|will pop up|will open|will move|will miniaturize|will hide|will finish launching|will display outline cell|will display item cell|will display cell|will display browser cell|will dismiss|will close|will become active|was miniaturized|was hidden|update toolbar item|update parameters|update menu item|shown|should zoom|should selection change|should select tab view item|should select row|should select item|should select column|should quit( after last window closed)?|should open( untitled)?|should expand item|should end editing|should collapse item|should close|should begin editing|selection changing|selection changed|selected tab view item|scroll wheel|rows changed|right mouse up|right mouse dragged|right mouse down|resized( sub views)?|resigned main|resigned key|resigned active|read from file|prepare table drop|prepare table drag|prepare outline drop|prepare outline drag|prepare drop|plugin loaded|parameters updated|panel ended|opened|open untitled|number of rows|number of items|number of browser rows|moved|mouse up|mouse moved|mouse exited|mouse entered|mouse dragged|mouse down|miniaturized|load data representation|launched|keyboard up|keyboard down|items changed|item value changed|item value|item expandable|idle|exposed|end editing|drop|drag( (entered|exited|updated))?|double clicked|document nib name|dialog ended|deminiaturized|data representation|conclude drop|column resized|column moved|column clicked|closed|clicked toolbar item|clicked|choose menu item|child of item|changed|change item value|change cell value|cell value changed|cell value|bounds changed|begin editing|became main|became key|awake from nib|alert ended|activated|action|accept table drop|accept outline drop)"
|
||||
>
|
||||
<token type="Keyword" />
|
||||
</rule>
|
||||
<rule pattern="^(\s*)(in|on|script|to)(\s+)">
|
||||
<bygroups>
|
||||
<token type="Text" />
|
||||
<token type="Keyword" />
|
||||
<token type="Text" />
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule
|
||||
pattern="\b(as )(alias |application |boolean |class |constant |date |file |integer |list |number |POSIX file |real |record |reference |RGB color |script |text |unit types|(?:Unicode )?text|string)\b"
|
||||
>
|
||||
<bygroups>
|
||||
<token type="Keyword" />
|
||||
<token type="NameClass" />
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule
|
||||
pattern="\b(AppleScript|current application|false|linefeed|missing value|pi|quote|result|return|space|tab|text item delimiters|true|version)\b"
|
||||
>
|
||||
<token type="NameConstant" />
|
||||
</rule>
|
||||
<rule
|
||||
pattern="\b(ASCII (character|number)|activate|beep|choose URL|choose application|choose color|choose file( name)?|choose folder|choose from list|choose remote application|clipboard info|close( access)?|copy|count|current date|delay|delete|display (alert|dialog)|do shell script|duplicate|exists|get eof|get volume settings|info for|launch|list (disks|folder)|load script|log|make|mount volume|new|offset|open( (for access|location))?|path to|print|quit|random number|read|round|run( script)?|say|scripting components|set (eof|the clipboard to|volume)|store script|summarize|system attribute|system info|the clipboard|time to GMT|write|quoted form)\b"
|
||||
>
|
||||
<token type="NameBuiltin" />
|
||||
</rule>
|
||||
<rule
|
||||
pattern="\b(considering|else|error|exit|from|if|ignoring|in|repeat|tell|then|times|to|try|until|using terms from|while|with|with timeout( of)?|with transaction|by|continue|end|its?|me|my|return|of|as)\b"
|
||||
>
|
||||
<token type="Keyword" />
|
||||
</rule>
|
||||
<rule pattern="\b(global|local|prop(erty)?|set|get)\b">
|
||||
<token type="Keyword" />
|
||||
</rule>
|
||||
<rule pattern="\b(but|put|returning|the)\b">
|
||||
<token type="NameBuiltin" />
|
||||
</rule>
|
||||
<rule pattern="\b(attachment|attribute run|character|day|month|paragraph|word|year)s?\b">
|
||||
<token type="NameBuiltin" />
|
||||
</rule>
|
||||
<rule
|
||||
pattern="\b(about|above|against|apart from|around|aside from|at|below|beneath|beside|between|for|given|instead of|on|onto|out of|over|since)\b"
|
||||
>
|
||||
<token type="NameBuiltin" />
|
||||
</rule>
|
||||
<rule
|
||||
pattern="\b(accepts arrow key|action method|active|alignment|allowed identifiers|allows branch selection|allows column reordering|allows column resizing|allows column selection|allows customization|allows editing text attributes|allows empty selection|allows mixed state|allows multiple selection|allows reordering|allows undo|alpha( value)?|alternate image|alternate increment value|alternate title|animation delay|associated file name|associated object|auto completes|auto display|auto enables items|auto repeat|auto resizes( outline column)?|auto save expanded items|auto save name|auto save table columns|auto saves configuration|auto scroll|auto sizes all columns to fit|auto sizes cells|background color|bezel state|bezel style|bezeled|border rect|border type|bordered|bounds( rotation)?|box type|button returned|button type|can choose directories|can choose files|can draw|can hide|cell( (background color|size|type))?|characters|class|click count|clicked( data)? column|clicked data item|clicked( data)? row|closeable|collating|color( (mode|panel))|command key down|configuration|content(s| (size|view( margins)?))?|context|continuous|control key down|control size|control tint|control view|controller visible|coordinate system|copies( on scroll)?|corner view|current cell|current column|current( field)? editor|current( menu)? item|current row|current tab view item|data source|default identifiers|delta (x|y|z)|destination window|directory|display mode|displayed cell|document( (edited|rect|view))?|double value|dragged column|dragged distance|dragged items|draws( cell)? background|draws grid|dynamically scrolls|echos bullets|edge|editable|edited( data)? column|edited data item|edited( data)? row|enabled|enclosing scroll view|ending page|error handling|event number|event type|excluded from windows menu|executable path|expanded|fax number|field editor|file kind|file name|file type|first responder|first visible column|flipped|floating|font( panel)?|formatter|frameworks path|frontmost|gave up|grid color|has data items|has horizontal ruler|has horizontal scroller|has parent data item|has resize indicator|has shadow|has sub menu|has vertical ruler|has vertical scroller|header cell|header view|hidden|hides when deactivated|highlights by|horizontal line scroll|horizontal page scroll|horizontal ruler view|horizontally resizable|icon image|id|identifier|ignores multiple clicks|image( (alignment|dims when disabled|frame style|scaling))?|imports graphics|increment value|indentation per level|indeterminate|index|integer value|intercell spacing|item height|key( (code|equivalent( modifier)?|window))?|knob thickness|label|last( visible)? column|leading offset|leaf|level|line scroll|loaded|localized sort|location|loop mode|main( (bunde|menu|window))?|marker follows cell|matrix mode|maximum( content)? size|maximum visible columns|menu( form representation)?|miniaturizable|miniaturized|minimized image|minimized title|minimum column width|minimum( content)? size|modal|modified|mouse down state|movie( (controller|file|rect))?|muted|name|needs display|next state|next text|number of tick marks|only tick mark values|opaque|open panel|option key down|outline table column|page scroll|pages across|pages down|palette label|pane splitter|parent data item|parent window|pasteboard|path( (names|separator))?|playing|plays every frame|plays selection only|position|preferred edge|preferred type|pressure|previous text|prompt|properties|prototype cell|pulls down|rate|released when closed|repeated|requested print time|required file type|resizable|resized column|resource path|returns records|reuses columns|rich text|roll over|row height|rulers visible|save panel|scripts path|scrollable|selectable( identifiers)?|selected cell|selected( data)? columns?|selected data items?|selected( data)? rows?|selected item identifier|selection by rect|send action on arrow key|sends action when done editing|separates columns|separator item|sequence number|services menu|shared frameworks path|shared support path|sheet|shift key down|shows alpha|shows state by|size( mode)?|smart insert delete enabled|sort case sensitivity|sort column|sort order|sort type|sorted( data rows)?|sound|source( mask)?|spell checking enabled|starting page|state|string value|sub menu|super menu|super view|tab key traverses cells|tab state|tab type|tab view|table view|tag|target( printer)?|text color|text container insert|text container origin|text returned|tick mark position|time stamp|title(d| (cell|font|height|position|rect))?|tool tip|toolbar|trailing offset|transparent|treat packages as directories|truncated labels|types|unmodified characters|update views|use sort indicator|user defaults|uses data source|uses ruler|uses threaded animation|uses title from previous column|value wraps|version|vertical( (line scroll|page scroll|ruler view))?|vertically resizable|view|visible( document rect)?|volume|width|window|windows menu|wraps|zoomable|zoomed)\b"
|
||||
>
|
||||
<token type="NameAttribute" />
|
||||
</rule>
|
||||
<rule
|
||||
pattern="\b(action cell|alert reply|application|box|browser( cell)?|bundle|button( cell)?|cell|clip view|color well|color-panel|combo box( item)?|control|data( (cell|column|item|row|source))?|default entry|dialog reply|document|drag info|drawer|event|font(-panel)?|formatter|image( (cell|view))?|matrix|menu( item)?|item|movie( view)?|open-panel|outline view|panel|pasteboard|plugin|popup button|progress indicator|responder|save-panel|scroll view|secure text field( cell)?|slider|sound|split view|stepper|tab view( item)?|table( (column|header cell|header view|view))|text( (field( cell)?|view))?|toolbar( item)?|user-defaults|view|window)s?\b"
|
||||
>
|
||||
<token type="NameBuiltin" />
|
||||
</rule>
|
||||
<rule
|
||||
pattern="\b(animate|append|call method|center|close drawer|close panel|display|display alert|display dialog|display panel|go|hide|highlight|increment|item for|load image|load movie|load nib|load panel|load sound|localized string|lock focus|log|open drawer|path for|pause|perform action|play|register|resume|scroll|select( all)?|show|size to fit|start|step back|step forward|stop|synchronize|unlock focus|update)\b"
|
||||
>
|
||||
<token type="NameBuiltin" />
|
||||
</rule>
|
||||
<rule
|
||||
pattern="\b((in )?back of|(in )?front of|[0-9]+(st|nd|rd|th)|first|second|third|fourth|fifth|sixth|seventh|eighth|ninth|tenth|after|back|before|behind|every|front|index|last|middle|some|that|through|thru|where|whose)\b"
|
||||
>
|
||||
<token type="NameBuiltin" />
|
||||
</rule>
|
||||
<rule pattern=""(\\\\|\\"|[^"])*"">
|
||||
<token type="LiteralStringDouble" />
|
||||
</rule>
|
||||
<rule pattern="\b([a-zA-Z]\w*)\b">
|
||||
<token type="NameVariable" />
|
||||
</rule>
|
||||
<rule pattern="[-+]?(\d+\.\d*|\d*\.\d+)(E[-+][0-9]+)?">
|
||||
<token type="LiteralNumberFloat" />
|
||||
</rule>
|
||||
<rule pattern="[-+]?\d+">
|
||||
<token type="LiteralNumberInteger" />
|
||||
</rule>
|
||||
</state>
|
||||
<state name="comment">
|
||||
<rule pattern="\(\*">
|
||||
<token type="CommentMultiline" />
|
||||
<push />
|
||||
</rule>
|
||||
<rule pattern="\*\)">
|
||||
<token type="CommentMultiline" />
|
||||
<pop depth="1" />
|
||||
</rule>
|
||||
<rule pattern="[^*(]+">
|
||||
<token type="CommentMultiline" />
|
||||
</rule>
|
||||
<rule pattern="[*(]">
|
||||
<token type="CommentMultiline" />
|
||||
</rule>
|
||||
</state>
|
||||
</rules>
|
||||
</lexer>
|
||||
174
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/arangodb_aql.xml
generated
vendored
Normal file
174
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/arangodb_aql.xml
generated
vendored
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
<lexer>
|
||||
<config>
|
||||
<name>ArangoDB AQL</name>
|
||||
<alias>aql</alias>
|
||||
<filename>*.aql</filename>
|
||||
<mime_type>text/x-aql</mime_type>
|
||||
<case_insensitive>true</case_insensitive>
|
||||
<dot_all>true</dot_all>
|
||||
<ensure_nl>true</ensure_nl>
|
||||
</config>
|
||||
<rules>
|
||||
<state name="comments-and-whitespace">
|
||||
<rule pattern="\s+">
|
||||
<token type="Text"/>
|
||||
</rule>
|
||||
<rule pattern="//.*?\n">
|
||||
<token type="CommentSingle"/>
|
||||
</rule>
|
||||
<rule pattern="/\*">
|
||||
<token type="CommentMultiline"/>
|
||||
<push state="multiline-comment"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="multiline-comment">
|
||||
<rule pattern="[^*]+">
|
||||
<token type="CommentMultiline"/>
|
||||
</rule>
|
||||
<rule pattern="\*/">
|
||||
<token type="CommentMultiline"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule pattern="\*">
|
||||
<token type="CommentMultiline"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="double-quote">
|
||||
<rule pattern="\\.">
|
||||
<token type="LiteralStringDouble"/>
|
||||
</rule>
|
||||
<rule pattern="[^"\\]+">
|
||||
<token type="LiteralStringDouble"/>
|
||||
</rule>
|
||||
<rule pattern=""">
|
||||
<token type="LiteralStringDouble"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="single-quote">
|
||||
<rule pattern="\\.">
|
||||
<token type="LiteralStringSingle"/>
|
||||
</rule>
|
||||
<rule pattern="[^'\\]+">
|
||||
<token type="LiteralStringSingle"/>
|
||||
</rule>
|
||||
<rule pattern="'">
|
||||
<token type="LiteralStringSingle"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="backtick">
|
||||
<rule pattern="\\.">
|
||||
<token type="Name"/>
|
||||
</rule>
|
||||
<rule pattern="[^`\\]+">
|
||||
<token type="Name"/>
|
||||
</rule>
|
||||
<rule pattern="`">
|
||||
<token type="Name"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="forwardtick">
|
||||
<rule pattern="\\.">
|
||||
<token type="Name"/>
|
||||
</rule>
|
||||
<rule pattern="[^´\\]+">
|
||||
<token type="Name"/>
|
||||
</rule>
|
||||
<rule pattern="´">
|
||||
<token type="Name"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="identifier">
|
||||
<rule pattern="(?:\$?|_+)[a-z]+[_a-z0-9]*">
|
||||
<token type="Name"/>
|
||||
</rule>
|
||||
<rule pattern="`">
|
||||
<token type="Name"/>
|
||||
<push state="backtick"/>
|
||||
</rule>
|
||||
<rule pattern="´">
|
||||
<token type="Name"/>
|
||||
<push state="forwardtick"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="root">
|
||||
<rule>
|
||||
<include state="comments-and-whitespace"/>
|
||||
</rule>
|
||||
<rule pattern="0b[01]+">
|
||||
<token type="LiteralNumberBin"/>
|
||||
</rule>
|
||||
<rule pattern="0x[0-9a-f]+">
|
||||
<token type="LiteralNumberHex"/>
|
||||
</rule>
|
||||
<rule pattern="(?:0|[1-9][0-9]*)(?![\.e])">
|
||||
<token type="LiteralNumberInteger"/>
|
||||
</rule>
|
||||
<rule pattern="(?:(?:0|[1-9][0-9]*)(?:\.[0-9]+)?|\.[0-9]+)(?:e[\-\+]?[0-9]+)?">
|
||||
<token type="LiteralNumberFloat"/>
|
||||
</rule>
|
||||
<rule pattern="@@(?:_+[a-z0-9]+[a-z0-9_]*|[a-z0-9][a-z0-9_]*)">
|
||||
<token type="NameVariableGlobal"/>
|
||||
</rule>
|
||||
<rule pattern="@(?:_+[a-z0-9]+[a-z0-9_]*|[a-z0-9][a-z0-9_]*)">
|
||||
<token type="NameVariable"/>
|
||||
</rule>
|
||||
<rule pattern="=~|!~|[=!<>]=?|[%?:/*+-]|\.\.|&&|\|\|">
|
||||
<token type="Operator"/>
|
||||
</rule>
|
||||
<rule pattern="[.,(){}\[\]]">
|
||||
<token type="Punctuation"/>
|
||||
</rule>
|
||||
<rule pattern="[a-zA-Z0-9][a-zA-Z0-9_]*(?:::[a-zA-Z0-9_]+)+(?=\s*\()">
|
||||
<token type="NameFunction"/>
|
||||
</rule>
|
||||
<rule pattern="(WITH)(\s+)(COUNT)(\s+)(INTO)\b">
|
||||
<bygroups>
|
||||
<token type="KeywordReserved"/>
|
||||
<token type="Text"/>
|
||||
<token type="KeywordPseudo"/>
|
||||
<token type="Text"/>
|
||||
<token type="KeywordReserved"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="(?:KEEP|PRUNE|SEARCH|TO)\b">
|
||||
<token type="KeywordPseudo"/>
|
||||
</rule>
|
||||
<rule pattern="OPTIONS(?=\s*\{)">
|
||||
<token type="KeywordPseudo"/>
|
||||
</rule>
|
||||
<rule pattern="(?:AGGREGATE|ALL|ALL_SHORTEST_PATHS|AND|ANY|ASC|AT LEAST|COLLECT|DESC|DISTINCT|FILTER|FOR|GRAPH|IN|INBOUND|INSERT|INTO|K_PATHS|K_SHORTEST_PATHS|LIKE|LIMIT|NONE|NOT|OR|OUTBOUND|REMOVE|REPLACE|RETURN|SHORTEST_PATH|SORT|UPDATE|UPSERT|WITH|WINDOW)\b">
|
||||
<token type="KeywordReserved"/>
|
||||
</rule>
|
||||
<rule pattern="LET\b">
|
||||
<token type="KeywordDeclaration"/>
|
||||
</rule>
|
||||
<rule pattern="(?:true|false|null)\b">
|
||||
<token type="KeywordConstant"/>
|
||||
</rule>
|
||||
<rule pattern="(?-i)(?:CURRENT|NEW|OLD)\b">
|
||||
<token type="NameBuiltinPseudo"/>
|
||||
</rule>
|
||||
<rule pattern="(?:to_bool|to_number|to_char|to_string|to_array|to_list|is_null|is_bool|is_number|is_string|is_array|is_list|is_object|is_document|is_datestring|typename|json_stringify|json_parse|concat|concat_separator|char_length|lower|upper|substring|substring_bytes|left|right|trim|reverse|repeat|contains|log|log2|log10|exp|exp2|sin|cos|tan|asin|acos|atan|atan2|radians|degrees|pi|regex_test|regex_replace|like|floor|ceil|round|abs|rand|random|sqrt|pow|length|count|min|max|average|avg|sum|product|median|variance_population|variance_sample|variance|percentile|bit_and|bit_or|bit_xor|bit_negate|bit_test|bit_popcount|bit_shift_left|bit_shift_right|bit_construct|bit_deconstruct|bit_to_string|bit_from_string|first|last|unique|outersection|interleave|in_range|jaccard|matches|merge|merge_recursive|has|attributes|keys|values|entries|unset|unset_recursive|keep|keep_recursive|near|within|within_rectangle|is_in_polygon|distance|fulltext|stddev_sample|stddev_population|stddev|slice|nth|position|contains_array|translate|zip|call|apply|push|append|pop|shift|unshift|remove_value|remove_values|remove_nth|replace_nth|date_now|date_timestamp|date_iso8601|date_dayofweek|date_year|date_month|date_day|date_hour|date_minute|date_second|date_millisecond|date_dayofyear|date_isoweek|date_isoweekyear|date_leapyear|date_quarter|date_days_in_month|date_trunc|date_round|date_add|date_subtract|date_diff|date_compare|date_format|date_utctolocal|date_localtoutc|date_timezone|date_timezones|fail|passthru|v8|sleep|schema_get|schema_validate|shard_id|version|noopt|noeval|not_null|first_list|first_document|parse_identifier|parse_collection|parse_key|current_user|current_database|collection_count|pregel_result|collections|document|decode_rev|range|union|union_distinct|minus|intersection|flatten|is_same_collection|check_document|ltrim|rtrim|find_first|find_last|split|substitute|ipv4_to_number|ipv4_from_number|is_ipv4|md5|sha1|sha256|sha512|crc32|fnv64|hash|random_token|to_base64|to_hex|encode_uri_component|soundex|assert|warn|is_key|sorted|sorted_unique|count_distinct|count_unique|levenshtein_distance|levenshtein_match|regex_matches|regex_split|ngram_match|ngram_similarity|ngram_positional_similarity|uuid|tokens|exists|starts_with|phrase|min_match|bm25|tfidf|boost|analyzer|offset_info|value|cosine_similarity|decay_exp|decay_gauss|decay_linear|l1_distance|l2_distance|minhash|minhash_count|minhash_error|minhash_match|geo_point|geo_multipoint|geo_polygon|geo_multipolygon|geo_linestring|geo_multilinestring|geo_contains|geo_intersects|geo_equals|geo_distance|geo_area|geo_in_range)(?=\s*\()">
|
||||
<token type="NameFunction"/>
|
||||
</rule>
|
||||
<rule pattern=""">
|
||||
<token type="LiteralStringDouble"/>
|
||||
<push state="double-quote"/>
|
||||
</rule>
|
||||
<rule pattern="'">
|
||||
<token type="LiteralStringSingle"/>
|
||||
<push state="single-quote"/>
|
||||
</rule>
|
||||
<rule pattern="#\d+\b">
|
||||
<token type="NameLabel"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="identifier"/>
|
||||
</rule>
|
||||
</state>
|
||||
</rules>
|
||||
</lexer>
|
||||
322
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/arduino.xml
generated
vendored
Normal file
322
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/arduino.xml
generated
vendored
Normal file
|
|
@ -0,0 +1,322 @@
|
|||
<lexer>
|
||||
<config>
|
||||
<name>Arduino</name>
|
||||
<alias>arduino</alias>
|
||||
<filename>*.ino</filename>
|
||||
<mime_type>text/x-arduino</mime_type>
|
||||
<ensure_nl>true</ensure_nl>
|
||||
</config>
|
||||
<rules>
|
||||
<state name="whitespace">
|
||||
<rule pattern="^#if\s+0">
|
||||
<token type="CommentPreproc" />
|
||||
<push state="if0" />
|
||||
</rule>
|
||||
<rule pattern="^#">
|
||||
<token type="CommentPreproc" />
|
||||
<push state="macro" />
|
||||
</rule>
|
||||
<rule pattern="^(\s*(?:/[*].*?[*]/\s*)?)(#if\s+0)">
|
||||
<bygroups>
|
||||
<usingself state="root" />
|
||||
<token type="CommentPreproc" />
|
||||
</bygroups>
|
||||
<push state="if0" />
|
||||
</rule>
|
||||
<rule pattern="^(\s*(?:/[*].*?[*]/\s*)?)(#)">
|
||||
<bygroups>
|
||||
<usingself state="root" />
|
||||
<token type="CommentPreproc" />
|
||||
</bygroups>
|
||||
<push state="macro" />
|
||||
</rule>
|
||||
<rule pattern="\n">
|
||||
<token type="Text" />
|
||||
</rule>
|
||||
<rule pattern="\s+">
|
||||
<token type="Text" />
|
||||
</rule>
|
||||
<rule pattern="\\\n">
|
||||
<token type="Text" />
|
||||
</rule>
|
||||
<rule pattern="//(\n|[\w\W]*?[^\\]\n)">
|
||||
<token type="CommentSingle" />
|
||||
</rule>
|
||||
<rule pattern="/(\\\n)?[*][\w\W]*?[*](\\\n)?/">
|
||||
<token type="CommentMultiline" />
|
||||
</rule>
|
||||
<rule pattern="/(\\\n)?[*][\w\W]*">
|
||||
<token type="CommentMultiline" />
|
||||
</rule>
|
||||
</state>
|
||||
<state name="string">
|
||||
<rule pattern=""">
|
||||
<token type="LiteralString" />
|
||||
<pop depth="1" />
|
||||
</rule>
|
||||
<rule pattern="\\([\\abfnrtv"\']|x[a-fA-F0-9]{2,4}|u[a-fA-F0-9]{4}|U[a-fA-F0-9]{8}|[0-7]{1,3})">
|
||||
<token type="LiteralStringEscape" />
|
||||
</rule>
|
||||
<rule pattern="[^\\"\n]+">
|
||||
<token type="LiteralString" />
|
||||
</rule>
|
||||
<rule pattern="\\\n">
|
||||
<token type="LiteralString" />
|
||||
</rule>
|
||||
<rule pattern="\\">
|
||||
<token type="LiteralString" />
|
||||
</rule>
|
||||
</state>
|
||||
<state name="macro">
|
||||
<rule pattern="(include)(\s*(?:/[*].*?[*]/\s*)?)([^\n]+)">
|
||||
<bygroups>
|
||||
<token type="CommentPreproc" />
|
||||
<token type="Text" />
|
||||
<token type="CommentPreprocFile" />
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="[^/\n]+">
|
||||
<token type="CommentPreproc" />
|
||||
</rule>
|
||||
<rule pattern="/[*](.|\n)*?[*]/">
|
||||
<token type="CommentMultiline" />
|
||||
</rule>
|
||||
<rule pattern="//.*?\n">
|
||||
<token type="CommentSingle" />
|
||||
<pop depth="1" />
|
||||
</rule>
|
||||
<rule pattern="/">
|
||||
<token type="CommentPreproc" />
|
||||
</rule>
|
||||
<rule pattern="(?<=\\)\n">
|
||||
<token type="CommentPreproc" />
|
||||
</rule>
|
||||
<rule pattern="\n">
|
||||
<token type="CommentPreproc" />
|
||||
<pop depth="1" />
|
||||
</rule>
|
||||
</state>
|
||||
<state name="statements">
|
||||
<rule
|
||||
pattern="(reinterpret_cast|static_assert|dynamic_cast|thread_local|static_cast|const_cast|protected|constexpr|namespace|restrict|noexcept|override|operator|typename|template|explicit|decltype|nullptr|private|alignof|virtual|mutable|alignas|typeid|friend|throws|export|public|delete|final|using|throw|catch|this|try|new)\b"
|
||||
>
|
||||
<token type="Keyword" />
|
||||
</rule>
|
||||
<rule pattern="char(16_t|32_t)\b">
|
||||
<token type="KeywordType" />
|
||||
</rule>
|
||||
<rule pattern="(class)\b">
|
||||
<bygroups>
|
||||
<token type="Keyword" />
|
||||
</bygroups>
|
||||
<push state="classname" />
|
||||
</rule>
|
||||
<rule pattern="(R)(")([^\\()\s]{,16})(\()((?:.|\n)*?)(\)\3)(")">
|
||||
<bygroups>
|
||||
<token type="LiteralStringAffix" />
|
||||
<token type="LiteralString" />
|
||||
<token type="LiteralStringDelimiter" />
|
||||
<token type="LiteralStringDelimiter" />
|
||||
<token type="LiteralString" />
|
||||
<token type="LiteralStringDelimiter" />
|
||||
<token type="LiteralString" />
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="(u8|u|U)(")">
|
||||
<bygroups>
|
||||
<token type="LiteralStringAffix" />
|
||||
<token type="LiteralString" />
|
||||
</bygroups>
|
||||
<push state="string" />
|
||||
</rule>
|
||||
<rule pattern="(L?)(")">
|
||||
<bygroups>
|
||||
<token type="LiteralStringAffix" />
|
||||
<token type="LiteralString" />
|
||||
</bygroups>
|
||||
<push state="string" />
|
||||
</rule>
|
||||
<rule pattern="(L?)(')(\\.|\\[0-7]{1,3}|\\x[a-fA-F0-9]{1,2}|[^\\\'\n])(')">
|
||||
<bygroups>
|
||||
<token type="LiteralStringAffix" />
|
||||
<token type="LiteralStringChar" />
|
||||
<token type="LiteralStringChar" />
|
||||
<token type="LiteralStringChar" />
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="(\d+\.\d*|\.\d+|\d+)[eE][+-]?\d+[LlUu]*">
|
||||
<token type="LiteralNumberFloat" />
|
||||
</rule>
|
||||
<rule pattern="(\d+\.\d*|\.\d+|\d+[fF])[fF]?">
|
||||
<token type="LiteralNumberFloat" />
|
||||
</rule>
|
||||
<rule pattern="0x[0-9a-fA-F]+[LlUu]*">
|
||||
<token type="LiteralNumberHex" />
|
||||
</rule>
|
||||
<rule pattern="0[0-7]+[LlUu]*">
|
||||
<token type="LiteralNumberOct" />
|
||||
</rule>
|
||||
<rule pattern="\d+[LlUu]*">
|
||||
<token type="LiteralNumberInteger" />
|
||||
</rule>
|
||||
<rule pattern="\*/">
|
||||
<token type="Error" />
|
||||
</rule>
|
||||
<rule pattern="[~!%^&*+=|?:<>/-]">
|
||||
<token type="Operator" />
|
||||
</rule>
|
||||
<rule pattern="[()\[\],.]">
|
||||
<token type="Punctuation" />
|
||||
</rule>
|
||||
<rule
|
||||
pattern="(restricted|volatile|continue|register|default|typedef|struct|extern|switch|sizeof|static|return|union|while|const|break|goto|enum|else|case|auto|for|asm|if|do)\b"
|
||||
>
|
||||
<token type="Keyword" />
|
||||
</rule>
|
||||
<rule
|
||||
pattern="(_Bool|_Complex|_Imaginary|array|atomic_bool|atomic_char|atomic_int|atomic_llong|atomic_long|atomic_schar|atomic_short|atomic_uchar|atomic_uint|atomic_ullong|atomic_ulong|atomic_ushort|auto|bool|boolean|BooleanVariables|Byte|byte|Char|char|char16_t|char32_t|class|complex|Const|const|const_cast|delete|double|dynamic_cast|enum|explicit|extern|Float|float|friend|inline|Int|int|int16_t|int32_t|int64_t|int8_t|Long|long|new|NULL|null|operator|private|PROGMEM|protected|public|register|reinterpret_cast|short|signed|sizeof|Static|static|static_cast|String|struct|typedef|uint16_t|uint32_t|uint64_t|uint8_t|union|unsigned|virtual|Void|void|Volatile|volatile|word)\b"
|
||||
>
|
||||
<token type="KeywordType" />
|
||||
</rule>
|
||||
<rule pattern="(and|final|If|Loop|loop|not|or|override|setup|Setup|throw|try|xor)\b">
|
||||
<token type="Keyword" />
|
||||
</rule>
|
||||
<rule
|
||||
pattern="(ANALOG_MESSAGE|BIN|CHANGE|DEC|DEFAULT|DIGITAL_MESSAGE|EXTERNAL|FALLING|FIRMATA_STRING|HALF_PI|HEX|HIGH|INPUT|INPUT_PULLUP|INTERNAL|INTERNAL1V1|INTERNAL1V1|INTERNAL2V56|INTERNAL2V56|LED_BUILTIN|LED_BUILTIN_RX|LED_BUILTIN_TX|LOW|LSBFIRST|MSBFIRST|OCT|OUTPUT|PI|REPORT_ANALOG|REPORT_DIGITAL|RISING|SET_PIN_MODE|SYSEX_START|SYSTEM_RESET|TWO_PI)\b"
|
||||
>
|
||||
<token type="KeywordConstant" />
|
||||
</rule>
|
||||
<rule pattern="(boolean|const|byte|word|string|String|array)\b">
|
||||
<token type="NameVariable" />
|
||||
</rule>
|
||||
<rule
|
||||
pattern="(Keyboard|KeyboardController|MouseController|SoftwareSerial|EthernetServer|EthernetClient|LiquidCrystal|RobotControl|GSMVoiceCall|EthernetUDP|EsploraTFT|HttpClient|RobotMotor|WiFiClient|GSMScanner|FileSystem|Scheduler|GSMServer|YunClient|YunServer|IPAddress|GSMClient|GSMModem|Keyboard|Ethernet|Console|GSMBand|Esplora|Stepper|Process|WiFiUDP|GSM_SMS|Mailbox|USBHost|Firmata|PImage|Client|Server|GSMPIN|FileIO|Bridge|Serial|EEPROM|Stream|Mouse|Audio|Servo|File|Task|GPRS|WiFi|Wire|TFT|GSM|SPI|SD)\b"
|
||||
>
|
||||
<token type="NameClass" />
|
||||
</rule>
|
||||
<rule
|
||||
pattern="(abs|Abs|accept|ACos|acos|acosf|addParameter|analogRead|AnalogRead|analogReadResolution|AnalogReadResolution|analogReference|AnalogReference|analogWrite|AnalogWrite|analogWriteResolution|AnalogWriteResolution|answerCall|asin|ASin|asinf|atan|ATan|atan2|ATan2|atan2f|atanf|attach|attached|attachGPRS|attachInterrupt|AttachInterrupt|autoscroll|available|availableForWrite|background|beep|begin|beginPacket|beginSD|beginSMS|beginSpeaker|beginTFT|beginTransmission|beginWrite|bit|Bit|BitClear|bitClear|bitRead|BitRead|bitSet|BitSet|BitWrite|bitWrite|blink|blinkVersion|BSSID|buffer|byte|cbrt|cbrtf|Ceil|ceil|ceilf|changePIN|char|charAt|checkPIN|checkPUK|checkReg|circle|cityNameRead|cityNameWrite|clear|clearScreen|click|close|compareTo|compassRead|concat|config|connect|connected|constrain|Constrain|copysign|copysignf|cos|Cos|cosf|cosh|coshf|countryNameRead|countryNameWrite|createChar|cursor|debugPrint|degrees|Delay|delay|DelayMicroseconds|delayMicroseconds|detach|DetachInterrupt|detachInterrupt|DigitalPinToInterrupt|digitalPinToInterrupt|DigitalRead|digitalRead|DigitalWrite|digitalWrite|disconnect|display|displayLogos|drawBMP|drawCompass|encryptionType|end|endPacket|endSMS|endsWith|endTransmission|endWrite|equals|equalsIgnoreCase|exists|exitValue|Exp|exp|expf|fabs|fabsf|fdim|fdimf|fill|find|findUntil|float|floor|Floor|floorf|flush|fma|fmaf|fmax|fmaxf|fmin|fminf|fmod|fmodf|gatewayIP|get|getAsynchronously|getBand|getButton|getBytes|getCurrentCarrier|getIMEI|getKey|getModifiers|getOemKey|getPINUsed|getResult|getSignalStrength|getSocket|getVoiceCallStatus|getXChange|getYChange|hangCall|height|highByte|HighByte|home|hypot|hypotf|image|indexOf|int|interrupts|IPAddress|IRread|isActionDone|isAlpha|isAlphaNumeric|isAscii|isControl|isDigit|isDirectory|isfinite|isGraph|isHexadecimalDigit|isinf|isListening|isLowerCase|isnan|isPIN|isPressed|isPrintable|isPunct|isSpace|isUpperCase|isValid|isWhitespace|keyboardRead|keyPressed|keyReleased|knobRead|lastIndexOf|ldexp|ldexpf|leftToRight|length|line|lineFollowConfig|listen|listenOnLocalhost|loadImage|localIP|log|Log|log10|log10f|logf|long|lowByte|LowByte|lrint|lrintf|lround|lroundf|macAddress|maintain|map|Map|Max|max|messageAvailable|Micros|micros|millis|Millis|Min|min|mkdir|motorsStop|motorsWrite|mouseDragged|mouseMoved|mousePressed|mouseReleased|move|noAutoscroll|noBlink|noBuffer|noCursor|noDisplay|noFill|noInterrupts|NoInterrupts|noListenOnLocalhost|noStroke|noTone|NoTone|onReceive|onRequest|open|openNextFile|overflow|parseCommand|parseFloat|parseInt|parsePacket|pauseMode|peek|PinMode|pinMode|playFile|playMelody|point|pointTo|position|Pow|pow|powf|prepare|press|print|printFirmwareVersion|println|printVersion|process|processInput|PulseIn|pulseIn|pulseInLong|PulseInLong|put|radians|random|Random|randomSeed|RandomSeed|read|readAccelerometer|readBlue|readButton|readBytes|readBytesUntil|readGreen|readJoystickButton|readJoystickSwitch|readJoystickX|readJoystickY|readLightSensor|readMessage|readMicrophone|readNetworks|readRed|readSlider|readString|readStringUntil|readTemperature|ready|rect|release|releaseAll|remoteIP|remoteNumber|remotePort|remove|replace|requestFrom|retrieveCallingNumber|rewindDirectory|rightToLeft|rmdir|robotNameRead|robotNameWrite|round|roundf|RSSI|run|runAsynchronously|running|runShellCommand|runShellCommandAsynchronously|scanNetworks|scrollDisplayLeft|scrollDisplayRight|seek|sendAnalog|sendDigitalPortPair|sendDigitalPorts|sendString|sendSysex|Serial_Available|Serial_Begin|Serial_End|Serial_Flush|Serial_Peek|Serial_Print|Serial_Println|Serial_Read|serialEvent|setBand|setBitOrder|setCharAt|setClockDivider|setCursor|setDataMode|setDNS|setFirmwareVersion|setMode|setPINUsed|setSpeed|setTextSize|setTimeout|ShiftIn|shiftIn|ShiftOut|shiftOut|shutdown|signbit|sin|Sin|sinf|sinh|sinhf|size|sizeof|Sq|sq|Sqrt|sqrt|sqrtf|SSID|startLoop|startsWith|step|stop|stroke|subnetMask|substring|switchPIN|tan|Tan|tanf|tanh|tanhf|tempoWrite|text|toCharArray|toInt|toLowerCase|tone|Tone|toUpperCase|transfer|trim|trunc|truncf|tuneWrite|turn|updateIR|userNameRead|userNameWrite|voiceCall|waitContinue|width|WiFiServer|word|write|writeBlue|writeGreen|writeJSON|writeMessage|writeMicroseconds|writeRed|writeRGB|yield|Yield)\b"
|
||||
>
|
||||
<token type="NameFunction" />
|
||||
</rule>
|
||||
<rule pattern="(typename|__inline|restrict|_inline|thread|inline|naked)\b">
|
||||
<token type="KeywordReserved" />
|
||||
</rule>
|
||||
<rule pattern="(__m(128i|128d|128|64))\b">
|
||||
<token type="KeywordReserved" />
|
||||
</rule>
|
||||
<rule
|
||||
pattern="__(forceinline|identifier|unaligned|declspec|fastcall|finally|stdcall|wchar_t|assume|except|int32|cdecl|int16|leave|based|raise|int64|noop|int8|w64|try|asm)\b"
|
||||
>
|
||||
<token type="KeywordReserved" />
|
||||
</rule>
|
||||
<rule pattern="(true|false|NULL)\b">
|
||||
<token type="NameBuiltin" />
|
||||
</rule>
|
||||
<rule pattern="([a-zA-Z_]\w*)(\s*)(:)(?!:)">
|
||||
<bygroups>
|
||||
<token type="NameLabel" />
|
||||
<token type="Text" />
|
||||
<token type="Punctuation" />
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="[a-zA-Z_]\w*">
|
||||
<token type="Name" />
|
||||
</rule>
|
||||
</state>
|
||||
<state name="function">
|
||||
<rule>
|
||||
<include state="whitespace" />
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="statements" />
|
||||
</rule>
|
||||
<rule pattern=";">
|
||||
<token type="Punctuation" />
|
||||
</rule>
|
||||
<rule pattern="\{">
|
||||
<token type="Punctuation" />
|
||||
<push />
|
||||
</rule>
|
||||
<rule pattern="\}">
|
||||
<token type="Punctuation" />
|
||||
<pop depth="1" />
|
||||
</rule>
|
||||
</state>
|
||||
<state name="if0">
|
||||
<rule pattern="^\s*#if.*?(?<!\\)\n">
|
||||
<token type="CommentPreproc" />
|
||||
<push />
|
||||
</rule>
|
||||
<rule pattern="^\s*#el(?:se|if).*\n">
|
||||
<token type="CommentPreproc" />
|
||||
<pop depth="1" />
|
||||
</rule>
|
||||
<rule pattern="^\s*#endif.*?(?<!\\)\n">
|
||||
<token type="CommentPreproc" />
|
||||
<pop depth="1" />
|
||||
</rule>
|
||||
<rule pattern=".*?\n">
|
||||
<token type="Comment" />
|
||||
</rule>
|
||||
</state>
|
||||
<state name="classname">
|
||||
<rule pattern="[a-zA-Z_]\w*">
|
||||
<token type="NameClass" />
|
||||
<pop depth="1" />
|
||||
</rule>
|
||||
<rule pattern="\s*(?=>)">
|
||||
<token type="Text" />
|
||||
<pop depth="1" />
|
||||
</rule>
|
||||
</state>
|
||||
<state name="statement">
|
||||
<rule>
|
||||
<include state="whitespace" />
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="statements" />
|
||||
</rule>
|
||||
<rule pattern="[{}]">
|
||||
<token type="Punctuation" />
|
||||
</rule>
|
||||
<rule pattern=";">
|
||||
<token type="Punctuation" />
|
||||
<pop depth="1" />
|
||||
</rule>
|
||||
</state>
|
||||
<state name="root">
|
||||
<rule>
|
||||
<include state="whitespace" />
|
||||
</rule>
|
||||
<rule pattern="((?:[\w*\s])+?(?:\s|[*]))([a-zA-Z_]\w*)(\s*\([^;]*?\))([^;{]*)(\{)">
|
||||
<bygroups>
|
||||
<usingself state="root" />
|
||||
<token type="NameFunction" />
|
||||
<usingself state="root" />
|
||||
<usingself state="root" />
|
||||
<token type="Punctuation" />
|
||||
</bygroups>
|
||||
<push state="function" />
|
||||
</rule>
|
||||
<rule pattern="((?:[\w*\s])+?(?:\s|[*]))([a-zA-Z_]\w*)(\s*\([^;]*?\))([^;]*)(;)">
|
||||
<bygroups>
|
||||
<usingself state="root" />
|
||||
<token type="NameFunction" />
|
||||
<usingself state="root" />
|
||||
<usingself state="root" />
|
||||
<token type="Punctuation" />
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule>
|
||||
<push state="statement" />
|
||||
</rule>
|
||||
<rule pattern="__(multiple_inheritance|virtual_inheritance|single_inheritance|interface|uuidof|super|event)\b">
|
||||
<token type="KeywordReserved" />
|
||||
</rule>
|
||||
<rule pattern="__(offload|blockingoffload|outer)\b">
|
||||
<token type="KeywordPseudo" />
|
||||
</rule>
|
||||
</state>
|
||||
</rules>
|
||||
</lexer>
|
||||
126
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/armasm.xml
generated
vendored
Normal file
126
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/armasm.xml
generated
vendored
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
<lexer>
|
||||
<config>
|
||||
<name>ArmAsm</name>
|
||||
<alias>armasm</alias>
|
||||
<filename>*.s</filename>
|
||||
<filename>*.S</filename>
|
||||
<mime_type>text/x-armasm</mime_type>
|
||||
<mime_type>text/x-asm</mime_type>
|
||||
<ensure_nl>true</ensure_nl>
|
||||
</config>
|
||||
<rules>
|
||||
<state name="root">
|
||||
<rule>
|
||||
<include state="commentsandwhitespace" />
|
||||
</rule>
|
||||
<rule pattern="(\.\w+)([ \t]+\w+\s+?)?">
|
||||
<bygroups>
|
||||
<token type="KeywordNamespace" />
|
||||
<token type="NameLabel" />
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="(\w+)(:)(\s+\.\w+\s+)">
|
||||
<bygroups>
|
||||
<token type="NameLabel" />
|
||||
<token type="Punctuation" />
|
||||
<token type="KeywordNamespace" />
|
||||
</bygroups>
|
||||
<push state="literal" />
|
||||
</rule>
|
||||
<rule pattern="(\w+)(:)">
|
||||
<bygroups>
|
||||
<token type="NameLabel" />
|
||||
<token type="Punctuation" />
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="svc\s+\w+">
|
||||
<token type="NameNamespace" />
|
||||
</rule>
|
||||
<rule pattern="[a-zA-Z]+">
|
||||
<token type="Text" />
|
||||
<push state="opcode" />
|
||||
</rule>
|
||||
</state>
|
||||
<state name="commentsandwhitespace">
|
||||
<rule pattern="\s+">
|
||||
<token type="Text" />
|
||||
</rule>
|
||||
<rule pattern="[@;].*?\n">
|
||||
<token type="CommentSingle" />
|
||||
</rule>
|
||||
<rule pattern="/\*.*?\*/">
|
||||
<token type="CommentMultiline" />
|
||||
</rule>
|
||||
</state>
|
||||
<state name="literal">
|
||||
<rule pattern="0b[01]+">
|
||||
<token type="LiteralNumberBin" />
|
||||
<pop depth="1" />
|
||||
</rule>
|
||||
<rule pattern="0x\w{1,8}">
|
||||
<token type="LiteralNumberHex" />
|
||||
<pop depth="1" />
|
||||
</rule>
|
||||
<rule pattern="0\d+">
|
||||
<token type="LiteralNumberOct" />
|
||||
<pop depth="1" />
|
||||
</rule>
|
||||
<rule pattern="\d+?\.\d+?">
|
||||
<token type="LiteralNumberFloat" />
|
||||
<pop depth="1" />
|
||||
</rule>
|
||||
<rule pattern="\d+">
|
||||
<token type="LiteralNumberInteger" />
|
||||
<pop depth="1" />
|
||||
</rule>
|
||||
<rule pattern="(")(.+)(")">
|
||||
<bygroups>
|
||||
<token type="Punctuation" />
|
||||
<token type="LiteralStringDouble" />
|
||||
<token type="Punctuation" />
|
||||
</bygroups>
|
||||
<pop depth="1" />
|
||||
</rule>
|
||||
<rule pattern="(')(.{1}|\\.{1})(')">
|
||||
<bygroups>
|
||||
<token type="Punctuation" />
|
||||
<token type="LiteralStringChar" />
|
||||
<token type="Punctuation" />
|
||||
</bygroups>
|
||||
<pop depth="1" />
|
||||
</rule>
|
||||
</state>
|
||||
<state name="opcode">
|
||||
<rule pattern="\n">
|
||||
<token type="Text" />
|
||||
<pop depth="1" />
|
||||
</rule>
|
||||
<rule pattern="(@|;).*\n">
|
||||
<token type="CommentSingle" />
|
||||
<pop depth="1" />
|
||||
</rule>
|
||||
<rule pattern="(\s+|,)">
|
||||
<token type="Text" />
|
||||
</rule>
|
||||
<rule pattern="[rapcfxwbhsdqv]\d{1,2}">
|
||||
<token type="NameClass" />
|
||||
</rule>
|
||||
<rule pattern="(=)(0x\w+)">
|
||||
<bygroups>
|
||||
<token type="Text" />
|
||||
<token type="NameLabel" />
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="(=)(\w+)">
|
||||
<bygroups>
|
||||
<token type="Text" />
|
||||
<token type="NameLabel" />
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="#">
|
||||
<token type="Text" />
|
||||
<push state="literal" />
|
||||
</rule>
|
||||
</state>
|
||||
</rules>
|
||||
</lexer>
|
||||
165
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/atl.xml
generated
vendored
Normal file
165
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/atl.xml
generated
vendored
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
<lexer>
|
||||
<config>
|
||||
<name>ATL</name>
|
||||
<alias>atl</alias>
|
||||
<filename>*.atl</filename>
|
||||
<mime_type>text/x-atl</mime_type>
|
||||
<dot_all>true</dot_all>
|
||||
</config>
|
||||
<rules>
|
||||
<state name="root">
|
||||
<rule pattern="(--.*?)(\n)">
|
||||
<bygroups>
|
||||
<token type="CommentSingle" />
|
||||
<token type="TextWhitespace" />
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="(and|distinct|endif|else|for|foreach|if|implies|in|let|not|or|self|super|then|thisModule|xor)\b">
|
||||
<token type="Keyword" />
|
||||
</rule>
|
||||
<rule pattern="(OclUndefined|true|false|#\w+)\b">
|
||||
<token type="KeywordConstant" />
|
||||
</rule>
|
||||
<rule pattern="(module|query|library|create|from|to|uses)\b">
|
||||
<token type="KeywordNamespace" />
|
||||
</rule>
|
||||
<rule pattern="(do)(\s*)({)">
|
||||
<bygroups>
|
||||
<token type="KeywordNamespace" />
|
||||
<token type="TextWhitespace" />
|
||||
<token type="Punctuation" />
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="(abstract|endpoint|entrypoint|lazy|unique)(\s+)">
|
||||
<bygroups>
|
||||
<token type="KeywordDeclaration" />
|
||||
<token type="TextWhitespace" />
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="(rule)(\s+)">
|
||||
<bygroups>
|
||||
<token type="KeywordNamespace" />
|
||||
<token type="TextWhitespace" />
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="(helper)(\s+)">
|
||||
<bygroups>
|
||||
<token type="KeywordNamespace" />
|
||||
<token type="TextWhitespace" />
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="(context)(\s+)">
|
||||
<bygroups>
|
||||
<token type="KeywordNamespace" />
|
||||
<token type="TextWhitespace" />
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="(def)(\s*)(:)(\s*)">
|
||||
<bygroups>
|
||||
<token type="KeywordNamespace" />
|
||||
<token type="TextWhitespace" />
|
||||
<token type="Punctuation" />
|
||||
<token type="TextWhitespace" />
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="(Bag|Boolean|Integer|OrderedSet|Real|Sequence|Set|String|Tuple)">
|
||||
<token type="KeywordType" />
|
||||
</rule>
|
||||
<rule pattern="(\w+)(\s*)(<-|<:=)">
|
||||
<bygroups>
|
||||
<token type="NameNamespace" />
|
||||
<token type="TextWhitespace" />
|
||||
<token type="Punctuation" />
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="#"">
|
||||
<token type="KeywordConstant" />
|
||||
<push state="quotedenumliteral" />
|
||||
</rule>
|
||||
<rule pattern=""">
|
||||
<token type="NameNamespace" />
|
||||
<push state="quotedname" />
|
||||
</rule>
|
||||
<rule pattern="[^\S\n]+">
|
||||
<token type="TextWhitespace" />
|
||||
</rule>
|
||||
<rule pattern="'">
|
||||
<token type="LiteralString" />
|
||||
<push state="string" />
|
||||
</rule>
|
||||
<rule
|
||||
pattern="[0-9]*\.[0-9]+">
|
||||
<token type="LiteralNumberFloat" />
|
||||
</rule>
|
||||
<rule pattern="0|[1-9][0-9]*">
|
||||
<token type="LiteralNumberInteger" />
|
||||
</rule>
|
||||
<rule pattern="[*<>+=/-]">
|
||||
<token type="Operator" />
|
||||
</rule>
|
||||
<rule pattern="([{}();:.,!|]|->)">
|
||||
<token type="Punctuation" />
|
||||
</rule>
|
||||
<rule pattern="\n">
|
||||
<token type="TextWhitespace" />
|
||||
</rule>
|
||||
<rule pattern="\w+">
|
||||
<token type="NameNamespace" />
|
||||
</rule>
|
||||
</state>
|
||||
<state name="string">
|
||||
<rule pattern="[^\\']+">
|
||||
<token type="LiteralString" />
|
||||
</rule>
|
||||
<rule pattern="\\\\">
|
||||
<token type="LiteralString" />
|
||||
</rule>
|
||||
<rule pattern="\\'">
|
||||
<token type="LiteralString" />
|
||||
</rule>
|
||||
<rule pattern="\\">
|
||||
<token type="LiteralString" />
|
||||
</rule>
|
||||
<rule pattern="'">
|
||||
<token type="LiteralString" />
|
||||
<pop depth="1" />
|
||||
</rule>
|
||||
</state>
|
||||
<state name="quotedname">
|
||||
<rule pattern="[^\\"]+">
|
||||
<token type="NameNamespace" />
|
||||
</rule>
|
||||
<rule pattern="\\\\">
|
||||
<token type="NameNamespace" />
|
||||
</rule>
|
||||
<rule pattern="\\"">
|
||||
<token type="NameNamespace" />
|
||||
</rule>
|
||||
<rule pattern="\\">
|
||||
<token type="NameNamespace" />
|
||||
</rule>
|
||||
<rule pattern=""">
|
||||
<token type="NameNamespace" />
|
||||
<pop depth="1" />
|
||||
</rule>
|
||||
</state>
|
||||
<state name="quotedenumliteral">
|
||||
<rule pattern="[^\\"]+">
|
||||
<token type="KeywordConstant" />
|
||||
</rule>
|
||||
<rule pattern="\\\\">
|
||||
<token type="KeywordConstant" />
|
||||
</rule>
|
||||
<rule pattern="\\"">
|
||||
<token type="KeywordConstant" />
|
||||
</rule>
|
||||
<rule pattern="\\">
|
||||
<token type="KeywordConstant" />
|
||||
</rule>
|
||||
<rule pattern=""">
|
||||
<token type="KeywordConstant" />
|
||||
<pop depth="1" />
|
||||
</rule>
|
||||
</state>
|
||||
</rules>
|
||||
</lexer>
|
||||
78
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/autohotkey.xml
generated
vendored
Normal file
78
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/autohotkey.xml
generated
vendored
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
|
||||
<lexer>
|
||||
<config>
|
||||
<name>AutoHotkey</name>
|
||||
<alias>autohotkey</alias>
|
||||
<alias>ahk</alias>
|
||||
<filename>*.ahk</filename>
|
||||
<filename>*.ahkl</filename>
|
||||
<mime_type>text/x-autohotkey</mime_type>
|
||||
</config>
|
||||
<rules>
|
||||
<state name="root">
|
||||
<rule pattern="^(\s*)(/\*)"><bygroups><token type="Text"/><token type="CommentMultiline"/></bygroups><push state="incomment"/></rule>
|
||||
<rule pattern="^(\s*)(\()"><bygroups><token type="Text"/><token type="Generic"/></bygroups><push state="incontinuation"/></rule>
|
||||
<rule pattern="\s+;.*?$"><token type="CommentSingle"/></rule>
|
||||
<rule pattern="^;.*?$"><token type="CommentSingle"/></rule>
|
||||
<rule pattern="[]{}(),;[]"><token type="Punctuation"/></rule>
|
||||
<rule pattern="(in|is|and|or|not)\b"><token type="OperatorWord"/></rule>
|
||||
<rule pattern="\%[a-zA-Z_#@$][\w#@$]*\%"><token type="NameVariable"/></rule>
|
||||
<rule pattern="!=|==|:=|\.=|<<|>>|[-~+/*%=<>&^|?:!.]"><token type="Operator"/></rule>
|
||||
<rule><include state="commands"/></rule>
|
||||
<rule><include state="labels"/></rule>
|
||||
<rule><include state="builtInFunctions"/></rule>
|
||||
<rule><include state="builtInVariables"/></rule>
|
||||
<rule pattern="""><token type="LiteralString"/><combined state="stringescape" state="dqs"/></rule>
|
||||
<rule><include state="numbers"/></rule>
|
||||
<rule pattern="[a-zA-Z_#@$][\w#@$]*"><token type="Name"/></rule>
|
||||
<rule pattern="\\|\'"><token type="Text"/></rule>
|
||||
<rule pattern="\`([,%`abfnrtv\-+;])"><token type="LiteralStringEscape"/></rule>
|
||||
<rule><include state="garbage"/></rule>
|
||||
</state>
|
||||
<state name="incomment">
|
||||
<rule pattern="^\s*\*/"><token type="CommentMultiline"/><pop depth="1"/></rule>
|
||||
<rule pattern="[^*]+"><token type="CommentMultiline"/></rule>
|
||||
<rule pattern="\*"><token type="CommentMultiline"/></rule>
|
||||
</state>
|
||||
<state name="incontinuation">
|
||||
<rule pattern="^\s*\)"><token type="Generic"/><pop depth="1"/></rule>
|
||||
<rule pattern="[^)]"><token type="Generic"/></rule>
|
||||
<rule pattern="[)]"><token type="Generic"/></rule>
|
||||
</state>
|
||||
<state name="commands">
|
||||
<rule pattern="(?i)^(\s*)(global|local|static|#AllowSameLineComments|#ClipboardTimeout|#CommentFlag|#ErrorStdOut|#EscapeChar|#HotkeyInterval|#HotkeyModifierTimeout|#Hotstring|#IfWinActive|#IfWinExist|#IfWinNotActive|#IfWinNotExist|#IncludeAgain|#Include|#InstallKeybdHook|#InstallMouseHook|#KeyHistory|#LTrim|#MaxHotkeysPerInterval|#MaxMem|#MaxThreads|#MaxThreadsBuffer|#MaxThreadsPerHotkey|#NoEnv|#NoTrayIcon|#Persistent|#SingleInstance|#UseHook|#WinActivateForce|AutoTrim|BlockInput|Break|Click|ClipWait|Continue|Control|ControlClick|ControlFocus|ControlGetFocus|ControlGetPos|ControlGetText|ControlGet|ControlMove|ControlSend|ControlSendRaw|ControlSetText|CoordMode|Critical|DetectHiddenText|DetectHiddenWindows|Drive|DriveGet|DriveSpaceFree|Edit|Else|EnvAdd|EnvDiv|EnvGet|EnvMult|EnvSet|EnvSub|EnvUpdate|Exit|ExitApp|FileAppend|FileCopy|FileCopyDir|FileCreateDir|FileCreateShortcut|FileDelete|FileGetAttrib|FileGetShortcut|FileGetSize|FileGetTime|FileGetVersion|FileInstall|FileMove|FileMoveDir|FileRead|FileReadLine|FileRecycle|FileRecycleEmpty|FileRemoveDir|FileSelectFile|FileSelectFolder|FileSetAttrib|FileSetTime|FormatTime|GetKeyState|Gosub|Goto|GroupActivate|GroupAdd|GroupClose|GroupDeactivate|Gui|GuiControl|GuiControlGet|Hotkey|IfEqual|IfExist|IfGreaterOrEqual|IfGreater|IfInString|IfLess|IfLessOrEqual|IfMsgBox|IfNotEqual|IfNotExist|IfNotInString|IfWinActive|IfWinExist|IfWinNotActive|IfWinNotExist|If |ImageSearch|IniDelete|IniRead|IniWrite|InputBox|Input|KeyHistory|KeyWait|ListHotkeys|ListLines|ListVars|Loop|Menu|MouseClickDrag|MouseClick|MouseGetPos|MouseMove|MsgBox|OnExit|OutputDebug|Pause|PixelGetColor|PixelSearch|PostMessage|Process|Progress|Random|RegDelete|RegRead|RegWrite|Reload|Repeat|Return|RunAs|RunWait|Run|SendEvent|SendInput|SendMessage|SendMode|SendPlay|SendRaw|Send|SetBatchLines|SetCapslockState|SetControlDelay|SetDefaultMouseSpeed|SetEnv|SetFormat|SetKeyDelay|SetMouseDelay|SetNumlockState|SetScrollLockState|SetStoreCapslockMode|SetTimer|SetTitleMatchMode|SetWinDelay|SetWorkingDir|Shutdown|Sleep|Sort|SoundBeep|SoundGet|SoundGetWaveVolume|SoundPlay|SoundSet|SoundSetWaveVolume|SplashImage|SplashTextOff|SplashTextOn|SplitPath|StatusBarGetText|StatusBarWait|StringCaseSense|StringGetPos|StringLeft|StringLen|StringLower|StringMid|StringReplace|StringRight|StringSplit|StringTrimLeft|StringTrimRight|StringUpper|Suspend|SysGet|Thread|ToolTip|Transform|TrayTip|URLDownloadToFile|While|WinActivate|WinActivateBottom|WinClose|WinGetActiveStats|WinGetActiveTitle|WinGetClass|WinGetPos|WinGetText|WinGetTitle|WinGet|WinHide|WinKill|WinMaximize|WinMenuSelectItem|WinMinimizeAllUndo|WinMinimizeAll|WinMinimize|WinMove|WinRestore|WinSetTitle|WinSet|WinShow|WinWaitActive|WinWaitClose|WinWaitNotActive|WinWait)\b"><bygroups><token type="Text"/><token type="NameBuiltin"/></bygroups></rule>
|
||||
</state>
|
||||
<state name="builtInFunctions">
|
||||
<rule pattern="(?i)(Abs|ACos|Asc|ASin|ATan|Ceil|Chr|Cos|DllCall|Exp|FileExist|Floor|GetKeyState|IL_Add|IL_Create|IL_Destroy|InStr|IsFunc|IsLabel|Ln|Log|LV_Add|LV_Delete|LV_DeleteCol|LV_GetCount|LV_GetNext|LV_GetText|LV_Insert|LV_InsertCol|LV_Modify|LV_ModifyCol|LV_SetImageList|Mod|NumGet|NumPut|OnMessage|RegExMatch|RegExReplace|RegisterCallback|Round|SB_SetIcon|SB_SetParts|SB_SetText|Sin|Sqrt|StrLen|SubStr|Tan|TV_Add|TV_Delete|TV_GetChild|TV_GetCount|TV_GetNext|TV_Get|TV_GetParent|TV_GetPrev|TV_GetSelection|TV_GetText|TV_Modify|VarSetCapacity|WinActive|WinExist|Object|ComObjActive|ComObjArray|ComObjEnwrap|ComObjUnwrap|ComObjParameter|ComObjType|ComObjConnect|ComObjCreate|ComObjGet|ComObjError|ComObjValue|Insert|MinIndex|MaxIndex|Remove|SetCapacity|GetCapacity|GetAddress|_NewEnum|FileOpen|Read|Write|ReadLine|WriteLine|ReadNumType|WriteNumType|RawRead|RawWrite|Seek|Tell|Close|Next|IsObject|StrPut|StrGet|Trim|LTrim|RTrim)\b"><token type="NameFunction"/></rule>
|
||||
</state>
|
||||
<state name="builtInVariables">
|
||||
<rule pattern="(?i)(A_AhkPath|A_AhkVersion|A_AppData|A_AppDataCommon|A_AutoTrim|A_BatchLines|A_CaretX|A_CaretY|A_ComputerName|A_ControlDelay|A_Cursor|A_DDDD|A_DDD|A_DD|A_DefaultMouseSpeed|A_Desktop|A_DesktopCommon|A_DetectHiddenText|A_DetectHiddenWindows|A_EndChar|A_EventInfo|A_ExitReason|A_FormatFloat|A_FormatInteger|A_Gui|A_GuiEvent|A_GuiControl|A_GuiControlEvent|A_GuiHeight|A_GuiWidth|A_GuiX|A_GuiY|A_Hour|A_IconFile|A_IconHidden|A_IconNumber|A_IconTip|A_Index|A_IPAddress1|A_IPAddress2|A_IPAddress3|A_IPAddress4|A_ISAdmin|A_IsCompiled|A_IsCritical|A_IsPaused|A_IsSuspended|A_KeyDelay|A_Language|A_LastError|A_LineFile|A_LineNumber|A_LoopField|A_LoopFileAttrib|A_LoopFileDir|A_LoopFileExt|A_LoopFileFullPath|A_LoopFileLongPath|A_LoopFileName|A_LoopFileShortName|A_LoopFileShortPath|A_LoopFileSize|A_LoopFileSizeKB|A_LoopFileSizeMB|A_LoopFileTimeAccessed|A_LoopFileTimeCreated|A_LoopFileTimeModified|A_LoopReadLine|A_LoopRegKey|A_LoopRegName|A_LoopRegSubkey|A_LoopRegTimeModified|A_LoopRegType|A_MDAY|A_Min|A_MM|A_MMM|A_MMMM|A_Mon|A_MouseDelay|A_MSec|A_MyDocuments|A_Now|A_NowUTC|A_NumBatchLines|A_OSType|A_OSVersion|A_PriorHotkey|A_ProgramFiles|A_Programs|A_ProgramsCommon|A_ScreenHeight|A_ScreenWidth|A_ScriptDir|A_ScriptFullPath|A_ScriptName|A_Sec|A_Space|A_StartMenu|A_StartMenuCommon|A_Startup|A_StartupCommon|A_StringCaseSense|A_Tab|A_Temp|A_ThisFunc|A_ThisHotkey|A_ThisLabel|A_ThisMenu|A_ThisMenuItem|A_ThisMenuItemPos|A_TickCount|A_TimeIdle|A_TimeIdlePhysical|A_TimeSincePriorHotkey|A_TimeSinceThisHotkey|A_TitleMatchMode|A_TitleMatchModeSpeed|A_UserName|A_WDay|A_WinDelay|A_WinDir|A_WorkingDir|A_YDay|A_YEAR|A_YWeek|A_YYYY|Clipboard|ClipboardAll|ComSpec|ErrorLevel|ProgramFiles|True|False|A_IsUnicode|A_FileEncoding|A_OSVersion|A_PtrSize)\b"><token type="NameVariable"/></rule>
|
||||
</state>
|
||||
<state name="labels">
|
||||
<rule pattern="(^\s*)([^:\s("]+?:{1,2})"><bygroups><token type="Text"/><token type="NameLabel"/></bygroups></rule>
|
||||
<rule pattern="(^\s*)(::[^:\s]+?::)"><bygroups><token type="Text"/><token type="NameLabel"/></bygroups></rule>
|
||||
</state>
|
||||
<state name="numbers">
|
||||
<rule pattern="(\d+\.\d*|\d*\.\d+)([eE][+-]?[0-9]+)?"><token type="LiteralNumberFloat"/></rule>
|
||||
<rule pattern="\d+[eE][+-]?[0-9]+"><token type="LiteralNumberFloat"/></rule>
|
||||
<rule pattern="0\d+"><token type="LiteralNumberOct"/></rule>
|
||||
<rule pattern="0[xX][a-fA-F0-9]+"><token type="LiteralNumberHex"/></rule>
|
||||
<rule pattern="\d+L"><token type="LiteralNumberIntegerLong"/></rule>
|
||||
<rule pattern="\d+"><token type="LiteralNumberInteger"/></rule>
|
||||
</state>
|
||||
<state name="stringescape">
|
||||
<rule pattern="\"\"|\`([,%`abfnrtv])"><token type="LiteralStringEscape"/></rule>
|
||||
</state>
|
||||
<state name="strings">
|
||||
<rule pattern="[^"\n]+"><token type="LiteralString"/></rule>
|
||||
</state>
|
||||
<state name="dqs">
|
||||
<rule pattern="""><token type="LiteralString"/><pop depth="1"/></rule>
|
||||
<rule><include state="strings"/></rule>
|
||||
</state>
|
||||
<state name="garbage">
|
||||
<rule pattern="[^\S\n]"><token type="Text"/></rule>
|
||||
</state>
|
||||
</rules>
|
||||
</lexer>
|
||||
|
||||
70
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/autoit.xml
generated
vendored
Normal file
70
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/autoit.xml
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
95
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/awk.xml
generated
vendored
Normal file
95
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/awk.xml
generated
vendored
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
<lexer>
|
||||
<config>
|
||||
<name>Awk</name>
|
||||
<alias>awk</alias>
|
||||
<alias>gawk</alias>
|
||||
<alias>mawk</alias>
|
||||
<alias>nawk</alias>
|
||||
<filename>*.awk</filename>
|
||||
<mime_type>application/x-awk</mime_type>
|
||||
</config>
|
||||
<rules>
|
||||
<state name="root">
|
||||
<rule pattern="^(?=\s|/)">
|
||||
<token type="Text"/>
|
||||
<push state="slashstartsregex"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="commentsandwhitespace"/>
|
||||
</rule>
|
||||
<rule pattern="\+\+|--|\|\||&&|in\b|\$|!?~|\|&|(\*\*|[-<>+*%\^/!=|])=?">
|
||||
<token type="Operator"/>
|
||||
<push state="slashstartsregex"/>
|
||||
</rule>
|
||||
<rule pattern="[{(\[;,]">
|
||||
<token type="Punctuation"/>
|
||||
<push state="slashstartsregex"/>
|
||||
</rule>
|
||||
<rule pattern="[})\].]">
|
||||
<token type="Punctuation"/>
|
||||
</rule>
|
||||
<rule pattern="(break|continue|do|while|exit|for|if|else|return|switch|case|default)\b">
|
||||
<token type="Keyword"/>
|
||||
<push state="slashstartsregex"/>
|
||||
</rule>
|
||||
<rule pattern="function\b">
|
||||
<token type="KeywordDeclaration"/>
|
||||
<push state="slashstartsregex"/>
|
||||
</rule>
|
||||
<rule pattern="(atan2|cos|exp|int|log|rand|sin|sqrt|srand|gensub|gsub|index|length|match|split|patsplit|sprintf|sub|substr|tolower|toupper|close|fflush|getline|next(file)|print|printf|strftime|systime|mktime|delete|system|strtonum|and|compl|lshift|or|rshift|asorti?|isarray|bindtextdomain|dcn?gettext|@(include|load|namespace))\b">
|
||||
<token type="KeywordReserved"/>
|
||||
</rule>
|
||||
<rule pattern="(ARGC|ARGIND|ARGV|BEGIN(FILE)?|BINMODE|CONVFMT|ENVIRON|END(FILE)?|ERRNO|FIELDWIDTHS|FILENAME|FNR|FPAT|FS|IGNORECASE|LINT|NF|NR|OFMT|OFS|ORS|PROCINFO|RLENGTH|RS|RSTART|RT|SUBSEP|TEXTDOMAIN)\b">
|
||||
<token type="NameBuiltin"/>
|
||||
</rule>
|
||||
<rule pattern="[@$a-zA-Z_]\w*">
|
||||
<token type="NameOther"/>
|
||||
</rule>
|
||||
<rule pattern="[0-9][0-9]*\.[0-9]+([eE][0-9]+)?[fd]?">
|
||||
<token type="LiteralNumberFloat"/>
|
||||
</rule>
|
||||
<rule pattern="0x[0-9a-fA-F]+">
|
||||
<token type="LiteralNumberHex"/>
|
||||
</rule>
|
||||
<rule pattern="[0-9]+">
|
||||
<token type="LiteralNumberInteger"/>
|
||||
</rule>
|
||||
<rule pattern=""(\\\\|\\"|[^"])*"">
|
||||
<token type="LiteralStringDouble"/>
|
||||
</rule>
|
||||
<rule pattern="'(\\\\|\\'|[^'])*'">
|
||||
<token type="LiteralStringSingle"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="commentsandwhitespace">
|
||||
<rule pattern="\s+">
|
||||
<token type="Text"/>
|
||||
</rule>
|
||||
<rule pattern="#.*$">
|
||||
<token type="CommentSingle"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="slashstartsregex">
|
||||
<rule>
|
||||
<include state="commentsandwhitespace"/>
|
||||
</rule>
|
||||
<rule pattern="/(\\.|[^[/\\\n]|\[(\\.|[^\]\\\n])*])+/\B">
|
||||
<token type="LiteralStringRegex"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule pattern="(?=/)">
|
||||
<token type="Text"/>
|
||||
<push state="#pop" state="badregex"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="badregex">
|
||||
<rule pattern="\n">
|
||||
<token type="Text"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
</state>
|
||||
</rules>
|
||||
</lexer>
|
||||
97
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/ballerina.xml
generated
vendored
Normal file
97
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/ballerina.xml
generated
vendored
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
<lexer>
|
||||
<config>
|
||||
<name>Ballerina</name>
|
||||
<alias>ballerina</alias>
|
||||
<filename>*.bal</filename>
|
||||
<mime_type>text/x-ballerina</mime_type>
|
||||
<dot_all>true</dot_all>
|
||||
</config>
|
||||
<rules>
|
||||
<state name="root">
|
||||
<rule pattern="[^\S\n]+">
|
||||
<token type="Text"/>
|
||||
</rule>
|
||||
<rule pattern="//.*?\n">
|
||||
<token type="CommentSingle"/>
|
||||
</rule>
|
||||
<rule pattern="/\*.*?\*/">
|
||||
<token type="CommentMultiline"/>
|
||||
</rule>
|
||||
<rule pattern="(break|catch|continue|done|else|finally|foreach|forever|fork|if|lock|match|return|throw|transaction|try|while)\b">
|
||||
<token type="Keyword"/>
|
||||
</rule>
|
||||
<rule pattern="((?:(?:[^\W\d]|\$)[\w.\[\]$<>]*\s+)+?)((?:[^\W\d]|\$)[\w$]*)(\s*)(\()">
|
||||
<bygroups>
|
||||
<usingself state="root"/>
|
||||
<token type="NameFunction"/>
|
||||
<token type="Text"/>
|
||||
<token type="Operator"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="@[^\W\d][\w.]*">
|
||||
<token type="NameDecorator"/>
|
||||
</rule>
|
||||
<rule pattern="(annotation|bind|but|endpoint|error|function|object|private|public|returns|service|type|var|with|worker)\b">
|
||||
<token type="KeywordDeclaration"/>
|
||||
</rule>
|
||||
<rule pattern="(boolean|byte|decimal|float|int|json|map|nil|record|string|table|xml)\b">
|
||||
<token type="KeywordType"/>
|
||||
</rule>
|
||||
<rule pattern="(true|false|null)\b">
|
||||
<token type="KeywordConstant"/>
|
||||
</rule>
|
||||
<rule pattern="(import)(\s+)">
|
||||
<bygroups>
|
||||
<token type="KeywordNamespace"/>
|
||||
<token type="Text"/>
|
||||
</bygroups>
|
||||
<push state="import"/>
|
||||
</rule>
|
||||
<rule pattern=""(\\\\|\\"|[^"])*"">
|
||||
<token type="LiteralString"/>
|
||||
</rule>
|
||||
<rule pattern="'\\.'|'[^\\]'|'\\u[0-9a-fA-F]{4}'">
|
||||
<token type="LiteralStringChar"/>
|
||||
</rule>
|
||||
<rule pattern="(\.)((?:[^\W\d]|\$)[\w$]*)">
|
||||
<bygroups>
|
||||
<token type="Operator"/>
|
||||
<token type="NameAttribute"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="^\s*([^\W\d]|\$)[\w$]*:">
|
||||
<token type="NameLabel"/>
|
||||
</rule>
|
||||
<rule pattern="([^\W\d]|\$)[\w$]*">
|
||||
<token type="Name"/>
|
||||
</rule>
|
||||
<rule pattern="([0-9][0-9_]*\.([0-9][0-9_]*)?|\.[0-9][0-9_]*)([eE][+\-]?[0-9][0-9_]*)?[fFdD]?|[0-9][eE][+\-]?[0-9][0-9_]*[fFdD]?|[0-9]([eE][+\-]?[0-9][0-9_]*)?[fFdD]|0[xX]([0-9a-fA-F][0-9a-fA-F_]*\.?|([0-9a-fA-F][0-9a-fA-F_]*)?\.[0-9a-fA-F][0-9a-fA-F_]*)[pP][+\-]?[0-9][0-9_]*[fFdD]?">
|
||||
<token type="LiteralNumberFloat"/>
|
||||
</rule>
|
||||
<rule pattern="0[xX][0-9a-fA-F][0-9a-fA-F_]*[lL]?">
|
||||
<token type="LiteralNumberHex"/>
|
||||
</rule>
|
||||
<rule pattern="0[bB][01][01_]*[lL]?">
|
||||
<token type="LiteralNumberBin"/>
|
||||
</rule>
|
||||
<rule pattern="0[0-7_]+[lL]?">
|
||||
<token type="LiteralNumberOct"/>
|
||||
</rule>
|
||||
<rule pattern="0|[1-9][0-9_]*[lL]?">
|
||||
<token type="LiteralNumberInteger"/>
|
||||
</rule>
|
||||
<rule pattern="[~^*!%&\[\](){}<>|+=:;,./?-]">
|
||||
<token type="Operator"/>
|
||||
</rule>
|
||||
<rule pattern="\n">
|
||||
<token type="Text"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="import">
|
||||
<rule pattern="[\w.]+">
|
||||
<token type="NameNamespace"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
</state>
|
||||
</rules>
|
||||
</lexer>
|
||||
222
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/bash.xml
generated
vendored
Normal file
222
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/bash.xml
generated
vendored
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
<lexer>
|
||||
<config>
|
||||
<name>Bash</name>
|
||||
<alias>bash</alias>
|
||||
<alias>sh</alias>
|
||||
<alias>ksh</alias>
|
||||
<alias>zsh</alias>
|
||||
<alias>shell</alias>
|
||||
<filename>*.sh</filename>
|
||||
<filename>*.ksh</filename>
|
||||
<filename>*.bash</filename>
|
||||
<filename>*.ebuild</filename>
|
||||
<filename>*.eclass</filename>
|
||||
<filename>.env</filename>
|
||||
<filename>.env.*</filename>
|
||||
<filename>*.env</filename>
|
||||
<filename>*.exheres-0</filename>
|
||||
<filename>*.exlib</filename>
|
||||
<filename>*.zsh</filename>
|
||||
<filename>*.zshrc</filename>
|
||||
<filename>.bashrc</filename>
|
||||
<filename>bashrc</filename>
|
||||
<filename>.bash_*</filename>
|
||||
<filename>bash_*</filename>
|
||||
<filename>zshrc</filename>
|
||||
<filename>.zshrc</filename>
|
||||
<filename>APKBUILD</filename>
|
||||
<filename>PKGBUILD</filename>
|
||||
<mime_type>application/x-sh</mime_type>
|
||||
<mime_type>application/x-shellscript</mime_type>
|
||||
<analyse first="true" >
|
||||
<regex pattern="(?m)^#!.*/bin/(?:env |)(?:bash|zsh|sh|ksh)" score="1.0" />
|
||||
</analyse>
|
||||
</config>
|
||||
<rules>
|
||||
<state name="data">
|
||||
<rule pattern="(?s)\$?"(\\\\|\\[0-7]+|\\.|[^"\\$])*"">
|
||||
<token type="LiteralStringDouble"/>
|
||||
</rule>
|
||||
<rule pattern=""">
|
||||
<token type="LiteralStringDouble"/>
|
||||
<push state="string"/>
|
||||
</rule>
|
||||
<rule pattern="(?s)\$'(\\\\|\\[0-7]+|\\.|[^'\\])*'">
|
||||
<token type="LiteralStringSingle"/>
|
||||
</rule>
|
||||
<rule pattern="(?s)'.*?'">
|
||||
<token type="LiteralStringSingle"/>
|
||||
</rule>
|
||||
<rule pattern=";">
|
||||
<token type="Punctuation"/>
|
||||
</rule>
|
||||
<rule pattern="&">
|
||||
<token type="Punctuation"/>
|
||||
</rule>
|
||||
<rule pattern="\|">
|
||||
<token type="Punctuation"/>
|
||||
</rule>
|
||||
<rule pattern="\s+">
|
||||
<token type="Text"/>
|
||||
</rule>
|
||||
<rule pattern="\d+(?= |$)">
|
||||
<token type="LiteralNumber"/>
|
||||
</rule>
|
||||
<rule pattern="[^=\s\[\]{}()$"\'`\\<&|;]+">
|
||||
<token type="Text"/>
|
||||
</rule>
|
||||
<rule pattern="<">
|
||||
<token type="Text"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="string">
|
||||
<rule pattern=""">
|
||||
<token type="LiteralStringDouble"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule pattern="(?s)(\\\\|\\[0-7]+|\\.|[^"\\$])+">
|
||||
<token type="LiteralStringDouble"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="interp"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="interp">
|
||||
<rule pattern="\$\(\(">
|
||||
<token type="Keyword"/>
|
||||
<push state="math"/>
|
||||
</rule>
|
||||
<rule pattern="\$\(">
|
||||
<token type="Keyword"/>
|
||||
<push state="paren"/>
|
||||
</rule>
|
||||
<rule pattern="\$\{#?">
|
||||
<token type="LiteralStringInterpol"/>
|
||||
<push state="curly"/>
|
||||
</rule>
|
||||
<rule pattern="\$[a-zA-Z_]\w*">
|
||||
<token type="NameVariable"/>
|
||||
</rule>
|
||||
<rule pattern="\$(?:\d+|[#$?!_*@-])">
|
||||
<token type="NameVariable"/>
|
||||
</rule>
|
||||
<rule pattern="\$">
|
||||
<token type="Text"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="paren">
|
||||
<rule pattern="\)">
|
||||
<token type="Keyword"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="root"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="math">
|
||||
<rule pattern="\)\)">
|
||||
<token type="Keyword"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule pattern="[-+*/%^|&]|\*\*|\|\|">
|
||||
<token type="Operator"/>
|
||||
</rule>
|
||||
<rule pattern="\d+#\d+">
|
||||
<token type="LiteralNumber"/>
|
||||
</rule>
|
||||
<rule pattern="\d+#(?! )">
|
||||
<token type="LiteralNumber"/>
|
||||
</rule>
|
||||
<rule pattern="\d+">
|
||||
<token type="LiteralNumber"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="root"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="backticks">
|
||||
<rule pattern="`">
|
||||
<token type="LiteralStringBacktick"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="root"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="root">
|
||||
<rule>
|
||||
<include state="basic"/>
|
||||
</rule>
|
||||
<rule pattern="`">
|
||||
<token type="LiteralStringBacktick"/>
|
||||
<push state="backticks"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="data"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="interp"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="basic">
|
||||
<rule pattern="\b(if|fi|else|while|do|done|for|then|return|function|case|select|continue|until|esac|elif)(\s*)\b">
|
||||
<bygroups>
|
||||
<token type="Keyword"/>
|
||||
<token type="Text"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="\b(alias|bg|bind|break|builtin|caller|cd|command|compgen|complete|declare|dirs|disown|echo|enable|eval|exec|exit|export|false|fc|fg|getopts|hash|help|history|jobs|kill|let|local|logout|popd|printf|pushd|pwd|read|readonly|set|shift|shopt|source|suspend|test|time|times|trap|true|type|typeset|ulimit|umask|unalias|unset|wait)(?=[\s)`])">
|
||||
<token type="NameBuiltin"/>
|
||||
</rule>
|
||||
<rule pattern="\A#!.+\n">
|
||||
<token type="CommentPreproc"/>
|
||||
</rule>
|
||||
<rule pattern="#.*(\S|$)">
|
||||
<token type="CommentSingle"/>
|
||||
</rule>
|
||||
<rule pattern="\\[\w\W]">
|
||||
<token type="LiteralStringEscape"/>
|
||||
</rule>
|
||||
<rule pattern="(\b\w+)(\s*)(\+?=)">
|
||||
<bygroups>
|
||||
<token type="NameVariable"/>
|
||||
<token type="Text"/>
|
||||
<token type="Operator"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="[\[\]{}()=]">
|
||||
<token type="Operator"/>
|
||||
</rule>
|
||||
<rule pattern="<<<">
|
||||
<token type="Operator"/>
|
||||
</rule>
|
||||
<rule pattern="<<-?\s*(\'?)\\?(\w+)[\w\W]+?\2">
|
||||
<token type="LiteralString"/>
|
||||
</rule>
|
||||
<rule pattern="&&|\|\|">
|
||||
<token type="Operator"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="curly">
|
||||
<rule pattern="\}">
|
||||
<token type="LiteralStringInterpol"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule pattern=":-">
|
||||
<token type="Keyword"/>
|
||||
</rule>
|
||||
<rule pattern="\w+">
|
||||
<token type="NameVariable"/>
|
||||
</rule>
|
||||
<rule pattern="[^}:"\'`$\\]+">
|
||||
<token type="Punctuation"/>
|
||||
</rule>
|
||||
<rule pattern=":">
|
||||
<token type="Punctuation"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="root"/>
|
||||
</rule>
|
||||
</state>
|
||||
</rules>
|
||||
</lexer>
|
||||
25
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/bash_session.xml
generated
vendored
Normal file
25
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/bash_session.xml
generated
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<lexer>
|
||||
<config>
|
||||
<name>Bash Session</name>
|
||||
<alias>bash-session</alias>
|
||||
<alias>console</alias>
|
||||
<alias>shell-session</alias>
|
||||
<filename>*.sh-session</filename>
|
||||
<mime_type>text/x-sh</mime_type>
|
||||
<ensure_nl>true</ensure_nl>
|
||||
</config>
|
||||
<rules>
|
||||
<state name="root">
|
||||
<rule pattern="^((?:\[[^]]+@[^]]+\]\s?)?[#$%>])(\s*)(.*\n?)">
|
||||
<bygroups>
|
||||
<token type="GenericPrompt"/>
|
||||
<token type="Text"/>
|
||||
<using lexer="bash"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="^.+\n?">
|
||||
<token type="GenericOutput"/>
|
||||
</rule>
|
||||
</state>
|
||||
</rules>
|
||||
</lexer>
|
||||
660
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/batchfile.xml
generated
vendored
Normal file
660
src/vcom-0.2.5/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/batchfile.xml
generated
vendored
Normal file
|
|
@ -0,0 +1,660 @@
|
|||
<lexer>
|
||||
<config>
|
||||
<name>Batchfile</name>
|
||||
<alias>bat</alias>
|
||||
<alias>batch</alias>
|
||||
<alias>dosbatch</alias>
|
||||
<alias>winbatch</alias>
|
||||
<filename>*.bat</filename>
|
||||
<filename>*.cmd</filename>
|
||||
<mime_type>application/x-dos-batch</mime_type>
|
||||
<case_insensitive>true</case_insensitive>
|
||||
</config>
|
||||
<rules>
|
||||
<state name="arithmetic">
|
||||
<rule pattern="0[0-7]+">
|
||||
<token type="LiteralNumberOct"/>
|
||||
</rule>
|
||||
<rule pattern="0x[\da-f]+">
|
||||
<token type="LiteralNumberHex"/>
|
||||
</rule>
|
||||
<rule pattern="\d+">
|
||||
<token type="LiteralNumberInteger"/>
|
||||
</rule>
|
||||
<rule pattern="[(),]+">
|
||||
<token type="Punctuation"/>
|
||||
</rule>
|
||||
<rule pattern="([=+\-*/!~]|%|\^\^)+">
|
||||
<token type="Operator"/>
|
||||
</rule>
|
||||
<rule pattern="((?:"[^\n\x1a"]*(?:"|(?=[\n\x1a])))|(?:(?:%(?:\*|(?:~[a-z]*(?:\$[^:]+:)?)?\d|[^%:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^%\n\x1a^]|\^[^%\n\x1a])[^=\n\x1a]*=(?:[^%\n\x1a^]|\^[^%\n\x1a])*)?)?%))|(?:\^?![^!:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^!\n\x1a^]|\^[^!\n\x1a])[^=\n\x1a]*=(?:[^!\n\x1a^]|\^[^!\n\x1a])*)?)?\^?!))|(\^[\n\x1a]?)?[^()=+\-*/!~%^"\n\x1a&<>|\t\v\f\r ,;=\xa0]|\^[\n\x1a\t\v\f\r ,;=\xa0]?[\w\W])+">
|
||||
<usingself state="variable"/>
|
||||
</rule>
|
||||
<rule pattern="(?=[\x00|&])">
|
||||
<token type="Text"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="follow"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="else?">
|
||||
<rule pattern="(?:(?:(?:\^[\n\x1a])?[\t\v\f\r ,;=\xa0])+)">
|
||||
<usingself state="text"/>
|
||||
</rule>
|
||||
<rule pattern="else(?=\^?[\t\v\f\r ,;=\xa0]|[&<>|\n\x1a])">
|
||||
<token type="Keyword"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="sqstring">
|
||||
<rule>
|
||||
<include state="variable-or-escape"/>
|
||||
</rule>
|
||||
<rule pattern="[^%]+|%">
|
||||
<token type="LiteralStringSingle"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="root">
|
||||
<rule pattern="\)((?=\()|(?=\^?[\t\v\f\r ,;=\xa0]|[&<>|\n\x1a]))(?:(?:[^\n\x1a^]|\^[\n\x1a]?[\w\W])*)">
|
||||
<token type="CommentSingle"/>
|
||||
</rule>
|
||||
<rule pattern="(?=((?:(?<=^[^:])|^[^:]?)[\t\v\f\r ,;=\xa0]*)(:))">
|
||||
<token type="Text"/>
|
||||
<push state="follow"/>
|
||||
</rule>
|
||||
<rule pattern="(?:(?:(?:\^[\n\x1a])?[\t\v\f\r ,;=\xa0])+)">
|
||||
<usingself state="text"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="redirect"/>
|
||||
</rule>
|
||||
<rule pattern="[\n\x1a]+">
|
||||
<token type="Text"/>
|
||||
</rule>
|
||||
<rule pattern="\(">
|
||||
<token type="Punctuation"/>
|
||||
<push state="root/compound"/>
|
||||
</rule>
|
||||
<rule pattern="@+">
|
||||
<token type="Punctuation"/>
|
||||
</rule>
|
||||
<rule pattern="((?:for|if|rem)(?:(?=(?:\^[\n\x1a]?)?/)|(?:(?!\^)|(?<=m))(?:(?=\()|(?=\^?[\t\v\f\r ,;=\xa0]|[&<>|\n\x1a]))))((?:(?:(?:\^[\n\x1a])?[\t\v\f\r ,;=\xa0])+)?(?:(?:(?:\^[\n\x1a]?)?[^"\n\x1a&<>|\t\v\f\r ,;=\xa0])+)?(?:\^[\n\x1a]?)?/(?:\^[\n\x1a]?)?\?)">
|
||||
<bygroups>
|
||||
<token type="Keyword"/>
|
||||
<usingself state="text"/>
|
||||
</bygroups>
|
||||
<push state="follow"/>
|
||||
</rule>
|
||||
<rule pattern="(goto(?=(?:\^[\n\x1a]?)?[\t\v\f\r ,;=\xa0+./:[\\\]]|[\n\x1a&<>|(]))((?:(?:"[^\n\x1a"]*(?:"|(?=[\n\x1a])))|(?:(?:%(?:\*|(?:~[a-z]*(?:\$[^:]+:)?)?\d|[^%:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^%\n\x1a^]|\^[^%\n\x1a])[^=\n\x1a]*=(?:[^%\n\x1a^]|\^[^%\n\x1a])*)?)?%))|(?:\^?![^!:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^!\n\x1a^]|\^[^!\n\x1a])[^=\n\x1a]*=(?:[^!\n\x1a^]|\^[^!\n\x1a])*)?)?\^?!))|[^"%\n\x1a&<>|])*(?:\^[\n\x1a]?)?/(?:\^[\n\x1a]?)?\?(?:(?:"[^\n\x1a"]*(?:"|(?=[\n\x1a])))|(?:(?:%(?:\*|(?:~[a-z]*(?:\$[^:]+:)?)?\d|[^%:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^%\n\x1a^]|\^[^%\n\x1a])[^=\n\x1a]*=(?:[^%\n\x1a^]|\^[^%\n\x1a])*)?)?%))|(?:\^?![^!:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^!\n\x1a^]|\^[^!\n\x1a])[^=\n\x1a]*=(?:[^!\n\x1a^]|\^[^!\n\x1a])*)?)?\^?!))|[^"%\n\x1a&<>|])*)">
|
||||
<bygroups>
|
||||
<token type="Keyword"/>
|
||||
<usingself state="text"/>
|
||||
</bygroups>
|
||||
<push state="follow"/>
|
||||
</rule>
|
||||
<rule pattern="(setlocal|endlocal|prompt|verify|rename|mklink|rmdir|shift|start|color|dpath|title|chdir|erase|pushd|ftype|break|pause|mkdir|assoc|date|path|time|popd|keys|exit|type|copy|echo|move|dir|del|ren|ver|cls|vol|rd|md|cd)(?=(?:\^[\n\x1a]?)?[\t\v\f\r ,;=\xa0+./:[\\\]]|[\n\x1a&<>|(])">
|
||||
<token type="Keyword"/>
|
||||
<push state="follow"/>
|
||||
</rule>
|
||||
<rule pattern="(call)((?:(?:(?:\^[\n\x1a])?[\t\v\f\r ,;=\xa0])+)?)(:)">
|
||||
<bygroups>
|
||||
<token type="Keyword"/>
|
||||
<usingself state="text"/>
|
||||
<token type="Punctuation"/>
|
||||
</bygroups>
|
||||
<push state="call"/>
|
||||
</rule>
|
||||
<rule pattern="call(?=(?:\^[\n\x1a]?)?[\t\v\f\r ,;=\xa0+./:[\\\]]|[\n\x1a&<>|(])">
|
||||
<token type="Keyword"/>
|
||||
</rule>
|
||||
<rule pattern="(for(?=\^?[\t\v\f\r ,;=\xa0]|[&<>|\n\x1a])(?!\^))((?:(?:(?:\^[\n\x1a])?[\t\v\f\r ,;=\xa0])+))(/f(?=\^?[\t\v\f\r ,;=\xa0]|[&<>|\n\x1a]))">
|
||||
<bygroups>
|
||||
<token type="Keyword"/>
|
||||
<usingself state="text"/>
|
||||
<token type="Keyword"/>
|
||||
</bygroups>
|
||||
<push state="for/f" state="for"/>
|
||||
</rule>
|
||||
<rule pattern="(for(?=\^?[\t\v\f\r ,;=\xa0]|[&<>|\n\x1a])(?!\^))((?:(?:(?:\^[\n\x1a])?[\t\v\f\r ,;=\xa0])+))(/l(?=\^?[\t\v\f\r ,;=\xa0]|[&<>|\n\x1a]))">
|
||||
<bygroups>
|
||||
<token type="Keyword"/>
|
||||
<usingself state="text"/>
|
||||
<token type="Keyword"/>
|
||||
</bygroups>
|
||||
<push state="for/l" state="for"/>
|
||||
</rule>
|
||||
<rule pattern="for(?=\^?[\t\v\f\r ,;=\xa0]|[&<>|\n\x1a])(?!\^)">
|
||||
<token type="Keyword"/>
|
||||
<push state="for2" state="for"/>
|
||||
</rule>
|
||||
<rule pattern="(goto(?=(?:\^[\n\x1a]?)?[\t\v\f\r ,;=\xa0+./:[\\\]]|[\n\x1a&<>|(]))((?:(?:(?:\^[\n\x1a])?[\t\v\f\r ,;=\xa0])+)?)(:?)">
|
||||
<bygroups>
|
||||
<token type="Keyword"/>
|
||||
<usingself state="text"/>
|
||||
<token type="Punctuation"/>
|
||||
</bygroups>
|
||||
<push state="label"/>
|
||||
</rule>
|
||||
<rule pattern="(if(?:(?=\()|(?=\^?[\t\v\f\r ,;=\xa0]|[&<>|\n\x1a]))(?!\^))((?:(?:(?:\^[\n\x1a])?[\t\v\f\r ,;=\xa0])+)?)((?:/i(?=\^?[\t\v\f\r ,;=\xa0]|[&<>|\n\x1a]))?)((?:(?:(?:\^[\n\x1a])?[\t\v\f\r ,;=\xa0])+)?)((?:not(?=\^?[\t\v\f\r ,;=\xa0]|[&<>|\n\x1a]))?)((?:(?:(?:\^[\n\x1a])?[\t\v\f\r ,;=\xa0])+)?)">
|
||||
<bygroups>
|
||||
<token type="Keyword"/>
|
||||
<usingself state="text"/>
|
||||
<token type="Keyword"/>
|
||||
<usingself state="text"/>
|
||||
<token type="Keyword"/>
|
||||
<usingself state="text"/>
|
||||
</bygroups>
|
||||
<push state="(?" state="if"/>
|
||||
</rule>
|
||||
<rule pattern="rem(((?=\()|(?=\^?[\t\v\f\r ,;=\xa0]|[&<>|\n\x1a]))(?:(?:(?:\^[\n\x1a])?[\t\v\f\r ,;=\xa0])+)?(?:[&<>|]+|(?:(?:"[^\n\x1a"]*(?:"|(?=[\n\x1a])))|(?:(?:%(?:\*|(?:~[a-z]*(?:\$[^:]+:)?)?\d|[^%:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^%\n\x1a^]|\^[^%\n\x1a])[^=\n\x1a]*=(?:[^%\n\x1a^]|\^[^%\n\x1a])*)?)?%))|(?:\^?![^!:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^!\n\x1a^]|\^[^!\n\x1a])[^=\n\x1a]*=(?:[^!\n\x1a^]|\^[^!\n\x1a])*)?)?\^?!))|(?:(?:(?:\^[\n\x1a]?)?[^"\n\x1a&<>|\t\v\f\r ,;=\xa0])+))+)?.*|(?=(?:\^[\n\x1a]?)?[\t\v\f\r ,;=\xa0+./:[\\\]]|[\n\x1a&<>|(])(?:(?:[^\n\x1a^]|\^[\n\x1a]?[\w\W])*))">
|
||||
<token type="CommentSingle"/>
|
||||
<push state="follow"/>
|
||||
</rule>
|
||||
<rule pattern="(set(?=(?:\^[\n\x1a]?)?[\t\v\f\r ,;=\xa0+./:[\\\]]|[\n\x1a&<>|(]))((?:(?:\^[\n\x1a]?)?[^\S\n])*)(/a)">
|
||||
<bygroups>
|
||||
<token type="Keyword"/>
|
||||
<usingself state="text"/>
|
||||
<token type="Keyword"/>
|
||||
</bygroups>
|
||||
<push state="arithmetic"/>
|
||||
</rule>
|
||||
<rule pattern="(set(?=(?:\^[\n\x1a]?)?[\t\v\f\r ,;=\xa0+./:[\\\]]|[\n\x1a&<>|(]))((?:(?:\^[\n\x1a]?)?[^\S\n])*)((?:/p)?)((?:(?:\^[\n\x1a]?)?[^\S\n])*)((?:(?:(?:\^[\n\x1a]?)?[^"\n\x1a&<>|^=]|\^[\n\x1a]?[^"=])+)?)((?:(?:\^[\n\x1a]?)?=)?)">
|
||||
<bygroups>
|
||||
<token type="Keyword"/>
|
||||
<usingself state="text"/>
|
||||
<token type="Keyword"/>
|
||||
<usingself state="text"/>
|
||||
<usingself state="variable"/>
|
||||
<token type="Punctuation"/>
|
||||
</bygroups>
|
||||
<push state="follow"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<push state="follow"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="follow">
|
||||
<rule pattern="((?:(?<=^[^:])|^[^:]?)[\t\v\f\r ,;=\xa0]*)(:)([\t\v\f\r ,;=\xa0]*)((?:(?:[^\n\x1a&<>|\t\v\f\r ,;=\xa0+:^]|\^[\n\x1a]?[\w\W])*))(.*)">
|
||||
<bygroups>
|
||||
<token type="Text"/>
|
||||
<token type="Punctuation"/>
|
||||
<token type="Text"/>
|
||||
<token type="NameLabel"/>
|
||||
<token type="CommentSingle"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="redirect"/>
|
||||
</rule>
|
||||
<rule pattern="(?=[\n\x1a])">
|
||||
<token type="Text"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule pattern="\|\|?|&&?">
|
||||
<token type="Punctuation"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="text"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="bqstring">
|
||||
<rule>
|
||||
<include state="variable-or-escape"/>
|
||||
</rule>
|
||||
<rule pattern="[^%]+|%">
|
||||
<token type="LiteralStringBacktick"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="for2">
|
||||
<rule pattern="\)">
|
||||
<token type="Punctuation"/>
|
||||
</rule>
|
||||
<rule pattern="((?:(?:(?:\^[\n\x1a])?[\t\v\f\r ,;=\xa0])+))(do(?=\^?[\t\v\f\r ,;=\xa0]|[&<>|\n\x1a]))">
|
||||
<bygroups>
|
||||
<usingself state="text"/>
|
||||
<token type="Keyword"/>
|
||||
</bygroups>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule pattern="[\n\x1a]+">
|
||||
<token type="Text"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="follow"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="label/compound">
|
||||
<rule pattern="(?=\))">
|
||||
<token type="Text"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule pattern="((?:(?:[^\n\x1a&<>|\t\v\f\r ,;=\xa0+:^)]|\^[\n\x1a]?[^)])*)?)((?:(?:"[^\n\x1a"]*(?:"|(?=[\n\x1a])))|(?:(?:%(?:\*|(?:~[a-z]*(?:\$[^:]+:)?)?\d|[^%:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^%\n\x1a^]|\^[^%\n\x1a])[^=\n\x1a]*=(?:[^%\n\x1a^]|\^[^%\n\x1a])*)?)?%))|(?:\^?![^!:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^!\n\x1a^]|\^[^!\n\x1a])[^=\n\x1a]*=(?:[^!\n\x1a^]|\^[^!\n\x1a])*)?)?\^?!))|\^[\n\x1a]?[^)]|[^"%^\n\x1a&<>|)])*)">
|
||||
<bygroups>
|
||||
<token type="NameLabel"/>
|
||||
<token type="CommentSingle"/>
|
||||
</bygroups>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="for">
|
||||
<rule pattern="((?:(?:(?:\^[\n\x1a])?[\t\v\f\r ,;=\xa0])+))(in)((?:(?:(?:\^[\n\x1a])?[\t\v\f\r ,;=\xa0])+))(\()">
|
||||
<bygroups>
|
||||
<usingself state="text"/>
|
||||
<token type="Keyword"/>
|
||||
<usingself state="text"/>
|
||||
<token type="Punctuation"/>
|
||||
</bygroups>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="follow"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="redirect/compound">
|
||||
<rule pattern="((?:(?<=[\n\x1a\t\v\f\r ,;=\xa0])\d)?)(>>?&|<&)([\n\x1a\t\v\f\r ,;=\xa0]*)(\d)">
|
||||
<bygroups>
|
||||
<token type="LiteralNumberInteger"/>
|
||||
<token type="Punctuation"/>
|
||||
<token type="Text"/>
|
||||
<token type="LiteralNumberInteger"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="((?:(?<=[\n\x1a\t\v\f\r ,;=\xa0])(?<!\^[\n\x1a])\d)?)(>>?|<)((?:(?:(?:\^[\n\x1a])?[\t\v\f\r ,;=\xa0])+)?(?:[&<>|]+|(?:(?:"[^\n\x1a"]*(?:"|(?=[\n\x1a])))|(?:(?:%(?:\*|(?:~[a-z]*(?:\$[^:]+:)?)?\d|[^%:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^%\n\x1a^]|\^[^%\n\x1a])[^=\n\x1a]*=(?:[^%\n\x1a^]|\^[^%\n\x1a])*)?)?%))|(?:\^?![^!:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^!\n\x1a^]|\^[^!\n\x1a])[^=\n\x1a]*=(?:[^!\n\x1a^]|\^[^!\n\x1a])*)?)?\^?!))|(?:(?:(?:\^[\n\x1a]?)?[^"\n\x1a&<>|\t\v\f\r ,;=\xa0)])+))+))">
|
||||
<bygroups>
|
||||
<token type="LiteralNumberInteger"/>
|
||||
<token type="Punctuation"/>
|
||||
<usingself state="text"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="if">
|
||||
<rule pattern="((?:cmdextversion|errorlevel)(?=\^?[\t\v\f\r ,;=\xa0]|[&<>|\n\x1a]))((?:(?:(?:\^[\n\x1a])?[\t\v\f\r ,;=\xa0])+))(\d+)">
|
||||
<bygroups>
|
||||
<token type="Keyword"/>
|
||||
<usingself state="text"/>
|
||||
<token type="LiteralNumberInteger"/>
|
||||
</bygroups>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule pattern="(defined(?=\^?[\t\v\f\r ,;=\xa0]|[&<>|\n\x1a]))((?:(?:(?:\^[\n\x1a])?[\t\v\f\r ,;=\xa0])+))((?:[&<>|]+|(?:(?:"[^\n\x1a"]*(?:"|(?=[\n\x1a])))|(?:(?:%(?:\*|(?:~[a-z]*(?:\$[^:]+:)?)?\d|[^%:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^%\n\x1a^]|\^[^%\n\x1a])[^=\n\x1a]*=(?:[^%\n\x1a^]|\^[^%\n\x1a])*)?)?%))|(?:\^?![^!:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^!\n\x1a^]|\^[^!\n\x1a])[^=\n\x1a]*=(?:[^!\n\x1a^]|\^[^!\n\x1a])*)?)?\^?!))|(?:(?:(?:\^[\n\x1a]?)?[^"\n\x1a&<>|\t\v\f\r ,;=\xa0])+))+))">
|
||||
<bygroups>
|
||||
<token type="Keyword"/>
|
||||
<usingself state="text"/>
|
||||
<usingself state="variable"/>
|
||||
</bygroups>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule pattern="(exist(?=\^?[\t\v\f\r ,;=\xa0]|[&<>|\n\x1a]))((?:(?:(?:\^[\n\x1a])?[\t\v\f\r ,;=\xa0])+)(?:[&<>|]+|(?:(?:"[^\n\x1a"]*(?:"|(?=[\n\x1a])))|(?:(?:%(?:\*|(?:~[a-z]*(?:\$[^:]+:)?)?\d|[^%:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^%\n\x1a^]|\^[^%\n\x1a])[^=\n\x1a]*=(?:[^%\n\x1a^]|\^[^%\n\x1a])*)?)?%))|(?:\^?![^!:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^!\n\x1a^]|\^[^!\n\x1a])[^=\n\x1a]*=(?:[^!\n\x1a^]|\^[^!\n\x1a])*)?)?\^?!))|(?:(?:(?:\^[\n\x1a]?)?[^"\n\x1a&<>|\t\v\f\r ,;=\xa0])+))+))">
|
||||
<bygroups>
|
||||
<token type="Keyword"/>
|
||||
<usingself state="text"/>
|
||||
</bygroups>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule pattern="((?:-?(?:0[0-7]+|0x[\da-f]+|\d+)(?=\^?[\t\v\f\r ,;=\xa0]|[&<>|\n\x1a]))(?:(?:(?:\^[\n\x1a])?[\t\v\f\r ,;=\xa0])+))((?:equ|geq|gtr|leq|lss|neq))((?:(?:(?:\^[\n\x1a])?[\t\v\f\r ,;=\xa0])+)(?:-?(?:0[0-7]+|0x[\da-f]+|\d+)(?=\^?[\t\v\f\r ,;=\xa0]|[&<>|\n\x1a])))">
|
||||
<bygroups>
|
||||
<usingself state="arithmetic"/>
|
||||
<token type="OperatorWord"/>
|
||||
<usingself state="arithmetic"/>
|
||||
</bygroups>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule pattern="(?:[&<>|]+|(?:(?:"[^\n\x1a"]*(?:"|(?=[\n\x1a])))|(?:(?:%(?:\*|(?:~[a-z]*(?:\$[^:]+:)?)?\d|[^%:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^%\n\x1a^]|\^[^%\n\x1a])[^=\n\x1a]*=(?:[^%\n\x1a^]|\^[^%\n\x1a])*)?)?%))|(?:\^?![^!:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^!\n\x1a^]|\^[^!\n\x1a])[^=\n\x1a]*=(?:[^!\n\x1a^]|\^[^!\n\x1a])*)?)?\^?!))|(?:(?:(?:\^[\n\x1a]?)?[^"\n\x1a&<>|\t\v\f\r ,;=\xa0])+))+)">
|
||||
<usingself state="text"/>
|
||||
<push state="#pop" state="if2"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="root/compound">
|
||||
<rule pattern="\)">
|
||||
<token type="Punctuation"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule pattern="(?=((?:(?<=^[^:])|^[^:]?)[\t\v\f\r ,;=\xa0]*)(:))">
|
||||
<token type="Text"/>
|
||||
<push state="follow/compound"/>
|
||||
</rule>
|
||||
<rule pattern="(?:(?:(?:\^[\n\x1a])?[\t\v\f\r ,;=\xa0])+)">
|
||||
<usingself state="text"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="redirect/compound"/>
|
||||
</rule>
|
||||
<rule pattern="[\n\x1a]+">
|
||||
<token type="Text"/>
|
||||
</rule>
|
||||
<rule pattern="\(">
|
||||
<token type="Punctuation"/>
|
||||
<push state="root/compound"/>
|
||||
</rule>
|
||||
<rule pattern="@+">
|
||||
<token type="Punctuation"/>
|
||||
</rule>
|
||||
<rule pattern="((?:for|if|rem)(?:(?=(?:\^[\n\x1a]?)?/)|(?:(?!\^)|(?<=m))(?:(?=\()|(?:(?=\))|(?=\^?[\t\v\f\r ,;=\xa0]|[&<>|\n\x1a])))))((?:(?:(?:\^[\n\x1a])?[\t\v\f\r ,;=\xa0])+)?(?:(?:(?:\^[\n\x1a]?)?[^"\n\x1a&<>|\t\v\f\r ,;=\xa0)])+)?(?:\^[\n\x1a]?)?/(?:\^[\n\x1a]?)?\?)">
|
||||
<bygroups>
|
||||
<token type="Keyword"/>
|
||||
<usingself state="text"/>
|
||||
</bygroups>
|
||||
<push state="follow/compound"/>
|
||||
</rule>
|
||||
<rule pattern="(goto(?:(?=\))|(?=(?:\^[\n\x1a]?)?[\t\v\f\r ,;=\xa0+./:[\\\]]|[\n\x1a&<>|(])))((?:(?:"[^\n\x1a"]*(?:"|(?=[\n\x1a])))|(?:(?:%(?:\*|(?:~[a-z]*(?:\$[^:]+:)?)?\d|[^%:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^%\n\x1a^]|\^[^%\n\x1a])[^=\n\x1a]*=(?:[^%\n\x1a^]|\^[^%\n\x1a])*)?)?%))|(?:\^?![^!:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^!\n\x1a^]|\^[^!\n\x1a])[^=\n\x1a]*=(?:[^!\n\x1a^]|\^[^!\n\x1a])*)?)?\^?!))|[^"%\n\x1a&<>|)])*(?:\^[\n\x1a]?)?/(?:\^[\n\x1a]?)?\?(?:(?:"[^\n\x1a"]*(?:"|(?=[\n\x1a])))|(?:(?:%(?:\*|(?:~[a-z]*(?:\$[^:]+:)?)?\d|[^%:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^%\n\x1a^]|\^[^%\n\x1a])[^=\n\x1a]*=(?:[^%\n\x1a^]|\^[^%\n\x1a])*)?)?%))|(?:\^?![^!:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^!\n\x1a^]|\^[^!\n\x1a])[^=\n\x1a]*=(?:[^!\n\x1a^]|\^[^!\n\x1a])*)?)?\^?!))|[^"%\n\x1a&<>|)])*)">
|
||||
<bygroups>
|
||||
<token type="Keyword"/>
|
||||
<usingself state="text"/>
|
||||
</bygroups>
|
||||
<push state="follow/compound"/>
|
||||
</rule>
|
||||
<rule pattern="(setlocal|endlocal|prompt|verify|rename|mklink|rmdir|shift|start|color|dpath|title|chdir|erase|pushd|ftype|break|pause|mkdir|assoc|date|path|time|popd|keys|exit|type|copy|echo|move|dir|del|ren|ver|cls|vol|rd|md|cd)(?:(?=\))|(?=(?:\^[\n\x1a]?)?[\t\v\f\r ,;=\xa0+./:[\\\]]|[\n\x1a&<>|(]))">
|
||||
<token type="Keyword"/>
|
||||
<push state="follow/compound"/>
|
||||
</rule>
|
||||
<rule pattern="(call)((?:(?:(?:\^[\n\x1a])?[\t\v\f\r ,;=\xa0])+)?)(:)">
|
||||
<bygroups>
|
||||
<token type="Keyword"/>
|
||||
<usingself state="text"/>
|
||||
<token type="Punctuation"/>
|
||||
</bygroups>
|
||||
<push state="call/compound"/>
|
||||
</rule>
|
||||
<rule pattern="call(?:(?=\))|(?=(?:\^[\n\x1a]?)?[\t\v\f\r ,;=\xa0+./:[\\\]]|[\n\x1a&<>|(]))">
|
||||
<token type="Keyword"/>
|
||||
</rule>
|
||||
<rule pattern="(for(?:(?=\))|(?=\^?[\t\v\f\r ,;=\xa0]|[&<>|\n\x1a]))(?!\^))((?:(?:(?:\^[\n\x1a])?[\t\v\f\r ,;=\xa0])+))(/f(?:(?=\))|(?=\^?[\t\v\f\r ,;=\xa0]|[&<>|\n\x1a])))">
|
||||
<bygroups>
|
||||
<token type="Keyword"/>
|
||||
<usingself state="text"/>
|
||||
<token type="Keyword"/>
|
||||
</bygroups>
|
||||
<push state="for/f" state="for"/>
|
||||
</rule>
|
||||
<rule pattern="(for(?:(?=\))|(?=\^?[\t\v\f\r ,;=\xa0]|[&<>|\n\x1a]))(?!\^))((?:(?:(?:\^[\n\x1a])?[\t\v\f\r ,;=\xa0])+))(/l(?:(?=\))|(?=\^?[\t\v\f\r ,;=\xa0]|[&<>|\n\x1a])))">
|
||||
<bygroups>
|
||||
<token type="Keyword"/>
|
||||
<usingself state="text"/>
|
||||
<token type="Keyword"/>
|
||||
</bygroups>
|
||||
<push state="for/l" state="for"/>
|
||||
</rule>
|
||||
<rule pattern="for(?:(?=\))|(?=\^?[\t\v\f\r ,;=\xa0]|[&<>|\n\x1a]))(?!\^)">
|
||||
<token type="Keyword"/>
|
||||
<push state="for2" state="for"/>
|
||||
</rule>
|
||||
<rule pattern="(goto(?:(?=\))|(?=(?:\^[\n\x1a]?)?[\t\v\f\r ,;=\xa0+./:[\\\]]|[\n\x1a&<>|(])))((?:(?:(?:\^[\n\x1a])?[\t\v\f\r ,;=\xa0])+)?)(:?)">
|
||||
<bygroups>
|
||||
<token type="Keyword"/>
|
||||
<usingself state="text"/>
|
||||
<token type="Punctuation"/>
|
||||
</bygroups>
|
||||
<push state="label/compound"/>
|
||||
</rule>
|
||||
<rule pattern="(if(?:(?=\()|(?:(?=\))|(?=\^?[\t\v\f\r ,;=\xa0]|[&<>|\n\x1a])))(?!\^))((?:(?:(?:\^[\n\x1a])?[\t\v\f\r ,;=\xa0])+)?)((?:/i(?:(?=\))|(?=\^?[\t\v\f\r ,;=\xa0]|[&<>|\n\x1a])))?)((?:(?:(?:\^[\n\x1a])?[\t\v\f\r ,;=\xa0])+)?)((?:not(?:(?=\))|(?=\^?[\t\v\f\r ,;=\xa0]|[&<>|\n\x1a])))?)((?:(?:(?:\^[\n\x1a])?[\t\v\f\r ,;=\xa0])+)?)">
|
||||
<bygroups>
|
||||
<token type="Keyword"/>
|
||||
<usingself state="text"/>
|
||||
<token type="Keyword"/>
|
||||
<usingself state="text"/>
|
||||
<token type="Keyword"/>
|
||||
<usingself state="text"/>
|
||||
</bygroups>
|
||||
<push state="(?" state="if"/>
|
||||
</rule>
|
||||
<rule pattern="rem(((?=\()|(?:(?=\))|(?=\^?[\t\v\f\r ,;=\xa0]|[&<>|\n\x1a])))(?:(?:(?:\^[\n\x1a])?[\t\v\f\r ,;=\xa0])+)?(?:[&<>|]+|(?:(?:"[^\n\x1a"]*(?:"|(?=[\n\x1a])))|(?:(?:%(?:\*|(?:~[a-z]*(?:\$[^:]+:)?)?\d|[^%:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^%\n\x1a^]|\^[^%\n\x1a])[^=\n\x1a]*=(?:[^%\n\x1a^]|\^[^%\n\x1a])*)?)?%))|(?:\^?![^!:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^!\n\x1a^]|\^[^!\n\x1a])[^=\n\x1a]*=(?:[^!\n\x1a^]|\^[^!\n\x1a])*)?)?\^?!))|(?:(?:(?:\^[\n\x1a]?)?[^"\n\x1a&<>|\t\v\f\r ,;=\xa0])+))+)?.*|(?:(?=\))|(?=(?:\^[\n\x1a]?)?[\t\v\f\r ,;=\xa0+./:[\\\]]|[\n\x1a&<>|(]))(?:(?:[^\n\x1a^)]|\^[\n\x1a]?[^)])*))">
|
||||
<token type="CommentSingle"/>
|
||||
<push state="follow/compound"/>
|
||||
</rule>
|
||||
<rule pattern="(set(?:(?=\))|(?=(?:\^[\n\x1a]?)?[\t\v\f\r ,;=\xa0+./:[\\\]]|[\n\x1a&<>|(])))((?:(?:\^[\n\x1a]?)?[^\S\n])*)(/a)">
|
||||
<bygroups>
|
||||
<token type="Keyword"/>
|
||||
<usingself state="text"/>
|
||||
<token type="Keyword"/>
|
||||
</bygroups>
|
||||
<push state="arithmetic/compound"/>
|
||||
</rule>
|
||||
<rule pattern="(set(?:(?=\))|(?=(?:\^[\n\x1a]?)?[\t\v\f\r ,;=\xa0+./:[\\\]]|[\n\x1a&<>|(])))((?:(?:\^[\n\x1a]?)?[^\S\n])*)((?:/p)?)((?:(?:\^[\n\x1a]?)?[^\S\n])*)((?:(?:(?:\^[\n\x1a]?)?[^"\n\x1a&<>|^=)]|\^[\n\x1a]?[^"=])+)?)((?:(?:\^[\n\x1a]?)?=)?)">
|
||||
<bygroups>
|
||||
<token type="Keyword"/>
|
||||
<usingself state="text"/>
|
||||
<token type="Keyword"/>
|
||||
<usingself state="text"/>
|
||||
<usingself state="variable"/>
|
||||
<token type="Punctuation"/>
|
||||
</bygroups>
|
||||
<push state="follow/compound"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<push state="follow/compound"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="follow/compound">
|
||||
<rule pattern="(?=\))">
|
||||
<token type="Text"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule pattern="((?:(?<=^[^:])|^[^:]?)[\t\v\f\r ,;=\xa0]*)(:)([\t\v\f\r ,;=\xa0]*)((?:(?:[^\n\x1a&<>|\t\v\f\r ,;=\xa0+:^)]|\^[\n\x1a]?[^)])*))(.*)">
|
||||
<bygroups>
|
||||
<token type="Text"/>
|
||||
<token type="Punctuation"/>
|
||||
<token type="Text"/>
|
||||
<token type="NameLabel"/>
|
||||
<token type="CommentSingle"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="redirect/compound"/>
|
||||
</rule>
|
||||
<rule pattern="(?=[\n\x1a])">
|
||||
<token type="Text"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule pattern="\|\|?|&&?">
|
||||
<token type="Punctuation"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="text"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="text">
|
||||
<rule pattern=""">
|
||||
<token type="LiteralStringDouble"/>
|
||||
<push state="string"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="variable-or-escape"/>
|
||||
</rule>
|
||||
<rule pattern="[^"%^\n\x1a&<>|\t\v\f\r ,;=\xa0\d)]+|.">
|
||||
<token type="Text"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="redirect">
|
||||
<rule pattern="((?:(?<=[\n\x1a\t\v\f\r ,;=\xa0])\d)?)(>>?&|<&)([\n\x1a\t\v\f\r ,;=\xa0]*)(\d)">
|
||||
<bygroups>
|
||||
<token type="LiteralNumberInteger"/>
|
||||
<token type="Punctuation"/>
|
||||
<token type="Text"/>
|
||||
<token type="LiteralNumberInteger"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="((?:(?<=[\n\x1a\t\v\f\r ,;=\xa0])(?<!\^[\n\x1a])\d)?)(>>?|<)((?:(?:(?:\^[\n\x1a])?[\t\v\f\r ,;=\xa0])+)?(?:[&<>|]+|(?:(?:"[^\n\x1a"]*(?:"|(?=[\n\x1a])))|(?:(?:%(?:\*|(?:~[a-z]*(?:\$[^:]+:)?)?\d|[^%:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^%\n\x1a^]|\^[^%\n\x1a])[^=\n\x1a]*=(?:[^%\n\x1a^]|\^[^%\n\x1a])*)?)?%))|(?:\^?![^!:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^!\n\x1a^]|\^[^!\n\x1a])[^=\n\x1a]*=(?:[^!\n\x1a^]|\^[^!\n\x1a])*)?)?\^?!))|(?:(?:(?:\^[\n\x1a]?)?[^"\n\x1a&<>|\t\v\f\r ,;=\xa0])+))+))">
|
||||
<bygroups>
|
||||
<token type="LiteralNumberInteger"/>
|
||||
<token type="Punctuation"/>
|
||||
<usingself state="text"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="label">
|
||||
<rule pattern="((?:(?:[^\n\x1a&<>|\t\v\f\r ,;=\xa0+:^]|\^[\n\x1a]?[\w\W])*)?)((?:(?:"[^\n\x1a"]*(?:"|(?=[\n\x1a])))|(?:(?:%(?:\*|(?:~[a-z]*(?:\$[^:]+:)?)?\d|[^%:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^%\n\x1a^]|\^[^%\n\x1a])[^=\n\x1a]*=(?:[^%\n\x1a^]|\^[^%\n\x1a])*)?)?%))|(?:\^?![^!:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^!\n\x1a^]|\^[^!\n\x1a])[^=\n\x1a]*=(?:[^!\n\x1a^]|\^[^!\n\x1a])*)?)?\^?!))|\^[\n\x1a]?[\w\W]|[^"%^\n\x1a&<>|])*)">
|
||||
<bygroups>
|
||||
<token type="NameLabel"/>
|
||||
<token type="CommentSingle"/>
|
||||
</bygroups>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="arithmetic/compound">
|
||||
<rule pattern="(?=\))">
|
||||
<token type="Text"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule pattern="0[0-7]+">
|
||||
<token type="LiteralNumberOct"/>
|
||||
</rule>
|
||||
<rule pattern="0x[\da-f]+">
|
||||
<token type="LiteralNumberHex"/>
|
||||
</rule>
|
||||
<rule pattern="\d+">
|
||||
<token type="LiteralNumberInteger"/>
|
||||
</rule>
|
||||
<rule pattern="[(),]+">
|
||||
<token type="Punctuation"/>
|
||||
</rule>
|
||||
<rule pattern="([=+\-*/!~]|%|\^\^)+">
|
||||
<token type="Operator"/>
|
||||
</rule>
|
||||
<rule pattern="((?:"[^\n\x1a"]*(?:"|(?=[\n\x1a])))|(?:(?:%(?:\*|(?:~[a-z]*(?:\$[^:]+:)?)?\d|[^%:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^%\n\x1a^]|\^[^%\n\x1a])[^=\n\x1a]*=(?:[^%\n\x1a^]|\^[^%\n\x1a])*)?)?%))|(?:\^?![^!:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^!\n\x1a^]|\^[^!\n\x1a])[^=\n\x1a]*=(?:[^!\n\x1a^]|\^[^!\n\x1a])*)?)?\^?!))|(\^[\n\x1a]?)?[^()=+\-*/!~%^"\n\x1a&<>|\t\v\f\r ,;=\xa0]|\^[\n\x1a\t\v\f\r ,;=\xa0]?[^)])+">
|
||||
<usingself state="variable"/>
|
||||
</rule>
|
||||
<rule pattern="(?=[\x00|&])">
|
||||
<token type="Text"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="follow"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="string">
|
||||
<rule pattern=""">
|
||||
<token type="LiteralStringDouble"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule pattern="(?:(?:%(?:\*|(?:~[a-z]*(?:\$[^:]+:)?)?\d|[^%:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^%\n\x1a^]|\^[^%\n\x1a])[^=\n\x1a]*=(?:[^%\n\x1a^]|\^[^%\n\x1a])*)?)?%))|(?:\^?![^!:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^!\n\x1a^]|\^[^!\n\x1a])[^=\n\x1a]*=(?:[^!\n\x1a^]|\^[^!\n\x1a])*)?)?\^?!))">
|
||||
<token type="NameVariable"/>
|
||||
</rule>
|
||||
<rule pattern="\^!|%%">
|
||||
<token type="LiteralStringEscape"/>
|
||||
</rule>
|
||||
<rule pattern="[^"%^\n\x1a]+|[%^]">
|
||||
<token type="LiteralStringDouble"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="variable">
|
||||
<rule pattern=""">
|
||||
<token type="LiteralStringDouble"/>
|
||||
<push state="string"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="variable-or-escape"/>
|
||||
</rule>
|
||||
<rule pattern="[^"%^\n\x1a]+|.">
|
||||
<token type="NameVariable"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="call/compound">
|
||||
<rule pattern="(?=\))">
|
||||
<token type="Text"/>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule pattern="(:?)((?:(?:[^\n\x1a&<>|\t\v\f\r ,;=\xa0+:^)]|\^[\n\x1a]?[^)])*))">
|
||||
<bygroups>
|
||||
<token type="Punctuation"/>
|
||||
<token type="NameLabel"/>
|
||||
</bygroups>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="for/f">
|
||||
<rule pattern="(")((?:(?:(?:%(?:\*|(?:~[a-z]*(?:\$[^:]+:)?)?\d|[^%:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^%\n\x1a^]|\^[^%\n\x1a])[^=\n\x1a]*=(?:[^%\n\x1a^]|\^[^%\n\x1a])*)?)?%))|(?:\^?![^!:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^!\n\x1a^]|\^[^!\n\x1a])[^=\n\x1a]*=(?:[^!\n\x1a^]|\^[^!\n\x1a])*)?)?\^?!))|[^"])*?")([\n\x1a\t\v\f\r ,;=\xa0]*)(\))">
|
||||
<bygroups>
|
||||
<token type="LiteralStringDouble"/>
|
||||
<usingself state="string"/>
|
||||
<token type="Text"/>
|
||||
<token type="Punctuation"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern=""">
|
||||
<token type="LiteralStringDouble"/>
|
||||
<push state="#pop" state="for2" state="string"/>
|
||||
</rule>
|
||||
<rule pattern="('(?:%%|(?:(?:%(?:\*|(?:~[a-z]*(?:\$[^:]+:)?)?\d|[^%:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^%\n\x1a^]|\^[^%\n\x1a])[^=\n\x1a]*=(?:[^%\n\x1a^]|\^[^%\n\x1a])*)?)?%))|(?:\^?![^!:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^!\n\x1a^]|\^[^!\n\x1a])[^=\n\x1a]*=(?:[^!\n\x1a^]|\^[^!\n\x1a])*)?)?\^?!))|[\w\W])*?')([\n\x1a\t\v\f\r ,;=\xa0]*)(\))">
|
||||
<bygroups>
|
||||
<usingself state="sqstring"/>
|
||||
<token type="Text"/>
|
||||
<token type="Punctuation"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule pattern="(`(?:%%|(?:(?:%(?:\*|(?:~[a-z]*(?:\$[^:]+:)?)?\d|[^%:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^%\n\x1a^]|\^[^%\n\x1a])[^=\n\x1a]*=(?:[^%\n\x1a^]|\^[^%\n\x1a])*)?)?%))|(?:\^?![^!:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^!\n\x1a^]|\^[^!\n\x1a])[^=\n\x1a]*=(?:[^!\n\x1a^]|\^[^!\n\x1a])*)?)?\^?!))|[\w\W])*?`)([\n\x1a\t\v\f\r ,;=\xa0]*)(\))">
|
||||
<bygroups>
|
||||
<usingself state="bqstring"/>
|
||||
<token type="Text"/>
|
||||
<token type="Punctuation"/>
|
||||
</bygroups>
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="for2"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="for/l">
|
||||
<rule pattern="-?\d+">
|
||||
<token type="LiteralNumberInteger"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<include state="for2"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="if2">
|
||||
<rule pattern="((?:(?:(?:\^[\n\x1a])?[\t\v\f\r ,;=\xa0])+)?)(==)((?:(?:(?:\^[\n\x1a])?[\t\v\f\r ,;=\xa0])+)?(?:[&<>|]+|(?:(?:"[^\n\x1a"]*(?:"|(?=[\n\x1a])))|(?:(?:%(?:\*|(?:~[a-z]*(?:\$[^:]+:)?)?\d|[^%:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^%\n\x1a^]|\^[^%\n\x1a])[^=\n\x1a]*=(?:[^%\n\x1a^]|\^[^%\n\x1a])*)?)?%))|(?:\^?![^!:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^!\n\x1a^]|\^[^!\n\x1a])[^=\n\x1a]*=(?:[^!\n\x1a^]|\^[^!\n\x1a])*)?)?\^?!))|(?:(?:(?:\^[\n\x1a]?)?[^"\n\x1a&<>|\t\v\f\r ,;=\xa0])+))+))">
|
||||
<bygroups>
|
||||
<usingself state="text"/>
|
||||
<token type="Operator"/>
|
||||
<usingself state="text"/>
|
||||
</bygroups>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
<rule pattern="((?:(?:(?:\^[\n\x1a])?[\t\v\f\r ,;=\xa0])+))((?:equ|geq|gtr|leq|lss|neq))((?:(?:(?:\^[\n\x1a])?[\t\v\f\r ,;=\xa0])+)(?:[&<>|]+|(?:(?:"[^\n\x1a"]*(?:"|(?=[\n\x1a])))|(?:(?:%(?:\*|(?:~[a-z]*(?:\$[^:]+:)?)?\d|[^%:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^%\n\x1a^]|\^[^%\n\x1a])[^=\n\x1a]*=(?:[^%\n\x1a^]|\^[^%\n\x1a])*)?)?%))|(?:\^?![^!:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^!\n\x1a^]|\^[^!\n\x1a])[^=\n\x1a]*=(?:[^!\n\x1a^]|\^[^!\n\x1a])*)?)?\^?!))|(?:(?:(?:\^[\n\x1a]?)?[^"\n\x1a&<>|\t\v\f\r ,;=\xa0])+))+))">
|
||||
<bygroups>
|
||||
<usingself state="text"/>
|
||||
<token type="OperatorWord"/>
|
||||
<usingself state="text"/>
|
||||
</bygroups>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="(?">
|
||||
<rule pattern="(?:(?:(?:\^[\n\x1a])?[\t\v\f\r ,;=\xa0])+)">
|
||||
<usingself state="text"/>
|
||||
</rule>
|
||||
<rule pattern="\(">
|
||||
<token type="Punctuation"/>
|
||||
<push state="#pop" state="else?" state="root/compound"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="call">
|
||||
<rule pattern="(:?)((?:(?:[^\n\x1a&<>|\t\v\f\r ,;=\xa0+:^]|\^[\n\x1a]?[\w\W])*))">
|
||||
<bygroups>
|
||||
<token type="Punctuation"/>
|
||||
<token type="NameLabel"/>
|
||||
</bygroups>
|
||||
<pop depth="1"/>
|
||||
</rule>
|
||||
</state>
|
||||
<state name="variable-or-escape">
|
||||
<rule pattern="(?:(?:%(?:\*|(?:~[a-z]*(?:\$[^:]+:)?)?\d|[^%:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^%\n\x1a^]|\^[^%\n\x1a])[^=\n\x1a]*=(?:[^%\n\x1a^]|\^[^%\n\x1a])*)?)?%))|(?:\^?![^!:\n\x1a]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^!\n\x1a^]|\^[^!\n\x1a])[^=\n\x1a]*=(?:[^!\n\x1a^]|\^[^!\n\x1a])*)?)?\^?!))">
|
||||
<token type="NameVariable"/>
|
||||
</rule>
|
||||
<rule pattern="%%|\^[\n\x1a]?(\^!|[\w\W])">
|
||||
<token type="LiteralStringEscape"/>
|
||||
</rule>
|
||||
</state>
|
||||
</rules>
|
||||
</lexer>
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue