Compare commits

...

159 Commits
3.2.0 ... 4.2.5

Author SHA1 Message Date
14f321b0e6 chore: bump version to 4.2.5 2022-07-04 22:00:35 -04:00
d3250fda79 chore(deps): upgrade vendored dependencies 2022-07-04 22:00:35 -04:00
c482488c41 fix: replace Parse with ParseArgs
Parse() is deprecated
2022-07-04 22:00:35 -04:00
fe8f39013e Merge pull request #673 from chrisallenlane/win-compat
fix: Windows compatibility
2022-07-04 17:03:11 -04:00
1016b20ef2 chore: bump version to 4.2.4
Bump version to `4.2.4`. This version contains numerous Windows fixes
and improvements.
2022-07-04 16:58:58 -04:00
def8985dcd fix: Windows support
Fix an issue whereby the installer installed cheatsheets into the wrong
directory on Windows. This occurred because previously `path.Join` was
used where `path/filepath.Join` should have been used.

This matters, because the former always uses `/` as the path separator,
whereas the latter will use `/` or `\` as is appropriate for the
runtime environment.

This should resolve bullet point 4 in #665.
2022-07-04 16:55:57 -04:00
e6f12147df fix: config fixes for Windows
- Update the default config file to use `more` instead of `less` as the
  default pager, in order to support Windows out-of-the-box. (#655, #665).

- Use `terminal` Chroma formatter (rather than `terminal16m`) in order
  to accommodate less capable terminal emulators like `cmd.exe` by
  default. Similarly, default to `colorize: false` in configs (changed
  from `true`) (#665).

- Comment out default `style` in order to avoid printing ANSI color
  codes into terminals without color support (#665)

- Attempt to intelligently choose a default editor, rather than rely on
  a hard-coded `vim` in the configs. This should make it easier to use
  `cheat` immediately without needing to specify configs. It should also
  improve `cheat`'s Windows compatibility. (#665)
2022-07-04 16:06:37 -04:00
a8c2c396ed feat(build): crate docker-run target
Create a `docker-run` `make` target for opening a shell in an Alpine
container for development.
2022-07-04 13:13:27 -04:00
35262df4f2 fix(build): Windows executable packaging
Fix an issue whereby the Windows zip release contained an extraneous
(and annoying) `dist` parent directory.
2022-07-04 12:34:06 -04:00
12ffa4cb5c Merge pull request #644 from cheat/develop
Windows fixes, Android support
2021-10-09 12:13:01 -04:00
d9c602f9e1 Merge pull request #643 from chrisallenlane/android
fix(Paths): Android support
2021-10-09 11:30:18 -04:00
b67ff8b6a8 fix(Paths): Android support
Add `"android"` to the explicit whitelist of supported operating
systems.  This may resolve incompatibilities with certain Android
environments.
2021-10-09 11:27:38 -04:00
a500a621a1 chore: bump version
Bump version to 4.2.3.
2021-10-09 10:59:02 -04:00
23b6928874 Merge pull request #639 from mattn/fix-windows
Fix Windows
2021-10-09 10:10:39 -04:00
9de39fb12b Merge pull request #634 from cheat/dependabot/go_modules/github.com/mattn/go-isatty-0.0.14
chore(deps): bump github.com/mattn/go-isatty from 0.0.13 to 0.0.14
2021-10-09 09:51:49 -04:00
ad501c4cbe Merge pull request #641 from OmgImAlexis/patch-1
chore: fix typo in comment
2021-10-09 09:39:41 -04:00
f17de401e5 docs(CONTRIBUTING): pr against develop
Add a note to `CONTRIBUTING.md` requesting that contributors open
pull-requests against the `develop` branch.
2021-10-09 09:34:23 -04:00
2c097adeda chore: fix typo in comment 2021-09-30 07:30:20 +09:30
b825e0f535 Fix Windows 2021-09-29 01:33:59 +09:00
8385277b28 chore(deps): bump github.com/mattn/go-isatty from 0.0.13 to 0.0.14
Bumps [github.com/mattn/go-isatty](https://github.com/mattn/go-isatty) from 0.0.13 to 0.0.14.
- [Release notes](https://github.com/mattn/go-isatty/releases)
- [Commits](https://github.com/mattn/go-isatty/compare/v0.0.13...v0.0.14)

---
updated-dependencies:
- dependency-name: github.com/mattn/go-isatty
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-07 22:10:46 +00:00
768d55e5d4 chore: bump version
Bump version to `4.2.2`.
2021-06-08 21:02:03 -04:00
6aedc5c116 chore: whitespace edit on Makefile 2021-06-08 20:59:57 -04:00
e881bb1f97 chore: update go.sum 2021-06-08 20:59:57 -04:00
501f9c66ad deps: upgrade dependencies 2021-06-08 20:59:57 -04:00
a2aa82d9f3 Add ARM64/ARMv8 build 2021-06-08 20:59:57 -04:00
018bce7ad5 Fix ZSH autocompletion 2021-06-07 11:42:47 +02:00
17acefdd9b Merge pull request #617 from bernermic/master
Adds some git helper scripts
2021-05-14 12:21:26 -04:00
37918e09a4 Adds some git helper scripts 2021-05-07 20:53:54 +02:00
86967873a8 Merge pull request #623 from cheat/github-actions
chore: migrate into Github Actions
2021-05-03 17:02:27 -04:00
d237d98c15 chore: migrate into Github Actions
Replace Travis CI integration in favor of Github Actions.
2021-05-03 16:43:27 -04:00
eb9b3e7798 Merge pull request #624 from cheat/dependabot/add-v2-config-file
chore: upgrade to GitHub-native Dependabot
2021-05-03 14:32:08 -04:00
b0a351033d Upgrade to GitHub-native Dependabot 2021-04-29 20:40:56 +00:00
1eb44e8809 Merge pull request #621 from chrisallenlane/v4.2.1
Squashed commit of the following:
2021-04-28 12:55:17 -04:00
55b18b4897 Squashed commit of the following:
commit 95479c8ad744db48386a5c78e54ef8da80e9120b
Author: Chris Lane <chris@chris-allen-lane.com>
Date:   Wed Apr 28 12:26:32 2021 -0400

    chore(version): bump version to 4.2.1

commit 6956f51cae
Author: Chris Lane <chris@chris-allen-lane.com>
Date:   Wed Apr 28 12:24:21 2021 -0400

    fix(Makefile): `vendor-update`

    Update the `vendor-update` build target to run `go mod vendor` after
    updating dependencies.

commit 0aca411279
Author: Chris Lane <chris@chris-allen-lane.com>
Date:   Wed Apr 28 12:23:24 2021 -0400

    chore(deps): update dependencies

commit e847956b02
Author: Chris Lane <chris@chris-allen-lane.com>
Date:   Wed Apr 28 08:26:51 2021 -0400

    chore(deps): build updates

    - Upgrade `go` to `1.16.3`

    - Attempt to fix build errors regarding dependencies
2021-04-28 12:35:32 -04:00
883a17092f Merge pull request #606 from chrisallenlane/4.2.0
4.2.0
2020-11-28 11:27:09 -05:00
4f2a57fce8 fix(view): whitespace corrections
- Fix bug whereby `--all` flag would conflict with pager

- Fix whitespace inconsistencies among view and search outputs
2020-11-28 11:18:16 -05:00
ecc96c64f9 refactor(installer): externalize installer
Move installation-related code out of `main.go` and into a new
`installer.Run` method.
2020-11-28 10:32:37 -05:00
a81dd96ff4 fix: rename display.go
Rename `display.go` to `write.go`. (I forgot to do this previously.)
2020-11-27 23:05:02 -05:00
fb538baba5 chore(version): bump to 4.2.0 2020-11-27 22:57:25 -05:00
1a7b5c6127 feat(display): make Faint respect Colorize
Make `display.Faint` respect the `Colorize` config value.
2020-11-27 22:50:55 -05:00
cdddfbb516 chore: rename display.Display
Rename `display.Display` to `display.Write` for clarity and to reduce
"stutter".
2020-11-27 22:35:24 -05:00
4ef4c35d8c feat(search): search all cheatpaths
Update the search function. It now searches all cheatpaths all the time,
as if `--all` were implicitly passed.
2020-11-27 22:31:16 -05:00
a58294859e chore: spelling
`s/pathSheets/pathsheets/g` in `cmd_list` for consistency elsewhere.
2020-11-27 22:26:14 -05:00
606092e288 feat(search): improve search output formatting
Improve the search output formatting.
2020-11-27 17:06:02 -05:00
233a9de1aa feat: implement --all flag
Implement an `--all` flag that can be used to view cheatsheets on all
chaetpaths. (Resolves #548)
2020-11-27 16:39:34 -05:00
aa16f68620 feat(display): add methods to display
- Add `indent`, `faint`, and `underline` methods to `display`
- Add tests for the above
2020-11-27 16:14:33 -05:00
367673d5d9 chore(dependencies): update dependencies
Run `make vendor-update`.
2020-11-27 09:51:39 -05:00
08fb9e11a9 feat(Makefile): add vendor-update
Add `vendor-update` target to `Makefile`, which updates all dependencies
to their newest versions.
2020-11-27 09:50:11 -05:00
3f4d4bddb2 feat(tests): add unit-tests
Add unit-tests for `sheets.Load`.
2020-11-11 19:33:31 -05:00
6c6753b35c Merge pull request #599 from chrisallenlane/issue-597
fix: update installation instructions in README
2020-11-07 18:56:37 -05:00
0718b606e1 fix(README): clarify installation verbiage
Update the installation verbiage in the `README` for clarity
(issue #597).
2020-11-07 18:48:24 -05:00
857119b443 feat(Docker): create development Docker image
- Create Docker image to be used for experimentation during development
- Create targets in `Makefile` pertaining to the above
2020-11-07 18:47:24 -05:00
f421483eea Merge pull request #596 from chrisallenlane/v4.1.1
v4.1.1
2020-11-03 18:32:25 -05:00
4adddbf504 chore: bump version to v4.1.1 2020-11-03 18:05:46 -05:00
b9c86b6975 chore(dependencies): update dependencies 2020-11-03 17:59:56 -05:00
0b21ccf6f8 feat(tests): improve test coverage 2020-11-03 17:29:49 -05:00
a3ad8c5101 Merge pull request #595 from chrisallenlane/codeql
feat: integrate CodeQL build action
2020-11-01 10:51:38 -05:00
bacb74929a feat: integrate CodeQL build action 2020-11-01 10:47:25 -05:00
82e1c27494 Merge pull request #588 from chrisallenlane/bare-tag
feat: implement `cheat -t` shorthand
2020-09-05 09:05:09 -04:00
45beeb2edb chore: bump version to 4.1.0 2020-09-05 08:56:51 -04:00
c2c479b36c feat: support -t shorthand
Make `cheat -t <tag>` function as a shorthand for `cheat -l -t <tag>`.
2020-09-02 17:17:44 -04:00
cb0243e7fc Merge pull request #580 from ryaanwells/patch-1
Fixing "cheetsheet" typo in README.md
2020-08-23 15:25:55 -04:00
e5d04d41ea Fixing "cheetsheet" typo in tags_test.go 2020-08-21 15:45:49 +01:00
2474ea4fb1 Fixing "cheetsheet" typo in README.md 2020-08-18 14:53:15 +01:00
7467c9fbc0 Merge pull request #578 from chrisallenlane/v4.0.3
chore: update dependencies
2020-08-08 13:45:10 -04:00
dfba3da003 chore: update dependencies 2020-08-08 10:29:29 -04:00
ad7ad64a75 Merge pull request #573 from chrisallenlane/linux-conf
fix(config): add /etc/cheat config path
2020-07-11 08:12:04 -04:00
c4dcfd5da0 fix(config): add /etc/cheat config path
Add `/etc/cheat/conf.yml` to default config paths. See #568 for context.
2020-07-09 18:28:10 -04:00
278a5d9154 Merge pull request #570 from cheat/v4.0.1
fix(search): fix pagination error
2020-06-30 07:26:30 -04:00
9fa0c466fd fix(search): fix pagination error
Fix the paginator when used in combination with the `-s` (search)
subcommand. Previously, it would not behave as intended, because `cheat`
was writing to `stdout` at inappropriate times.
2020-06-30 07:21:21 -04:00
4e9b2928b3 Merge pull request #569 from chrisallenlane/dev
v4.0.0
2020-06-25 19:05:37 -04:00
fa5eb44be8 chore: bump version to 4.0.0 2020-06-25 18:53:27 -04:00
49afd7c16b feat: modify return codes
Modify exit codes. `cheat` now returns an exit code value `2` on errors
pertaining to a cheatsheet not being found.

BREAKING CHANGE
2020-06-25 18:38:03 -04:00
59d5c96c24 feat(pagination): implement paginated output
Implement a `pager` config option. If configured, `cheat` will
automatically pipe output through the configured pager (where
appropriate).
2020-06-25 18:21:51 -04:00
8e602b0e93 Merge pull request #563 from chrisallenlane/3.10.1
feat(makefile): support 32-bit systems
2020-05-14 20:11:14 -04:00
fb04cb1fcd feat(makefile): support 32-bit systems
- Update the `Makefile` to additionally output a 386 binary (#562)
- Update the dependencies (#561)
- Bump version
2020-05-14 20:02:35 -04:00
d42726101e Merge pull request #560 from cheat/3.10.0
3.10.0
2020-05-11 20:20:09 -04:00
93b3a711f5 chore: bump version to 3.10.0 2020-05-11 20:15:26 -04:00
9c3d41c8bd Merge branch 'syohex-syohex/improve-completion' into 3.10.0 2020-05-11 20:13:58 -04:00
4eeec6c868 chore(deps): resolve dependency conflict 2020-05-11 20:12:28 -04:00
1b17ab1914 chore(deps): bump github.com/alecthomas/chroma from 0.7.1 to 0.7.3
Bumps [github.com/alecthomas/chroma](https://github.com/alecthomas/chroma) from 0.7.1 to 0.7.3.
- [Release notes](https://github.com/alecthomas/chroma/releases)
- [Changelog](https://github.com/alecthomas/chroma/blob/master/.goreleaser.yml)
- [Commits](https://github.com/alecthomas/chroma/compare/v0.7.1...v0.7.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-11 07:20:59 +00:00
477650ee44 Improve zsh completion 2020-04-12 11:03:08 +09:00
c4dd3b52fd Merge pull request #554 from chrisallenlane/master
v3.9.0
2020-04-07 18:59:18 -04:00
e8a0ea0dc3 chore(version): bump version to 3.9.0 2020-04-07 18:50:00 -04:00
992ee66a56 Merge branch 'add-homebrew-release-flow' of https://github.com/chenrui333/cheat into v3.9.0 2020-04-07 18:48:57 -04:00
c9840c2d6f docs(autocompletion): update autocompletion docs
Update the autocompletion documentation (in `README` and `man` page) to
include information about configuring `zsh` autocompletion script.
2020-04-07 18:47:35 -04:00
bd53768f67 chore(zsh): set execute perm on cheat.zsh
Set execute permission on the `zsh` autocompletion script.
2020-04-07 18:41:00 -04:00
8092687956 change taglist and pathlist to local variable. 2020-04-07 14:56:34 +08:00
16ade50672 add complete script for zsh. 2020-04-03 15:34:01 +08:00
62c80d76eb Automate homebrew release flow 2020-03-24 21:21:54 -04:00
3e67eaa3b7 Merge pull request #546 from chrisallenlane/issue-272
docs(man): implement manpage
2020-03-24 20:55:57 -04:00
38b13655fe chore(version): bump version to 3.8.0 2020-03-24 20:54:40 -04:00
749d5c1182 docs(man): implement manpage
- Implement `make man` to generate a manpage

- Change verb tense in `make` help text
2020-03-24 20:46:48 -04:00
521f83377c Merge pull request #545 from chrisallenlane/issue-544
fix(frontmatter): resolve #544
2020-03-11 19:03:06 -04:00
b15ff10537 chore(version): bump version to 3.7.1 2020-03-11 18:55:59 -04:00
5288bd0c1c fix(frontmatter): resolve issue #544
- Fix error in `frontmatter.Parse` which would cause a `panic` when
  encountering malformed frontmatter

- Add a unit-test to cover the above
2020-03-11 18:54:46 -04:00
bddbee4158 fix(logging): improve sheets load logging
Improve logging in `sheets.Load`:

- Make error verbage consistant with verbiage elsewhere

- Add more information to logging statements
2020-03-11 18:51:06 -04:00
ce27cf2cc0 chore(version): bump version to 3.7.0 2020-03-06 20:19:31 -05:00
5733b1d6d4 chore(vendor): update vendor dependencies 2020-03-06 20:18:41 -05:00
2d221050d8 Merge branch '1-14' into 3.7.0 2020-03-06 20:17:54 -05:00
ce37b670c7 feat(installer): implement "installer"
Squashed commit of the following:

commit 5c322e79b7
Author: Chris Lane <chris@chris-allen-lane.com>
Date:   Fri Mar 6 19:56:56 2020 -0500

    docs(README): update the `README`

    Update the `README` to document the improved config-generation
    mechanism.

commit 803e1f014c
Author: Chris Lane <chris@chris-allen-lane.com>
Date:   Fri Mar 6 19:19:49 2020 -0500

    feat(config-init): platform-specific pathing

    Update `--init` subcommand to rely upon the same platform-detection
    intelligence that was previously implemented by the "installer".

    The installer and `--init` should now produce identical config files.

commit 99c48097e2
Author: Chris Lane <chris@chris-allen-lane.com>
Date:   Fri Mar 6 18:26:33 2020 -0500

    feat(installer): platform-correct config templating

    Modify the "installer" to populate cheatpaths with sensible defaults
    based on the detection of the user's operating system and environment.

commit 8e1580ff5a
Author: Chris Lane <chris@chris-allen-lane.com>
Date:   Thu Mar 5 20:19:58 2020 -0500

    fix(tests): fix `config.Paths` tests

    Refactor `config.Paths` (by externalizing a call to `homedir.Dir`) to
    decouple it from filesystem paths, thus facilitating cleaner unit-tests.

commit a08dca70d9
Author: Chris Lane <chris@chris-allen-lane.com>
Date:   Thu Mar 5 18:14:27 2020 -0500

    feat(installer): default path selection

    Modify the installer to improve default config and cheatsheet path
    selection.

commit e15bc6c966
Author: Chris Lane <chris@chris-allen-lane.com>
Date:   Thu Mar 5 17:49:50 2020 -0500

    fix(typo): correct comment typo in `main.go`

commit efd09575df
Author: Chris Lane <chris@chris-allen-lane.com>
Date:   Thu Mar 5 17:46:49 2020 -0500

    feat(config): refactor config path detection

    Previously, failing other checks, on Unix and BSD systems,
    `config.Paths` would attempt to compute the user's home directory by
    reading the `HOME` environment variable.

    This change deprecates that approach with a call to `homedir.Dir`, which
    is used elsewhere throughout the application.

commit ec10244ebe
Author: Chris Lane <chris@chris-allen-lane.com>
Date:   Thu Mar 5 17:15:28 2020 -0500

    chore(installer): delete unused file

    Delete `installer/installer.go`, which (in hindsight) was unnecessary.

commit ebd9ec6287
Author: Chris Lane <chris@chris-allen-lane.com>
Date:   Wed Mar 4 19:31:13 2020 -0500

    wip(installer): stub experimental "installer"

    Stubs out an experimental "installer" that will help new users to
    quickly configure `cheat`.

commit ecac5a0971
Author: Chris Lane <chris@chris-allen-lane.com>
Date:   Wed Mar 4 19:30:12 2020 -0500

    chore(dependencies): updates vendored dependencies
2020-03-06 20:17:26 -05:00
47a9eeb4fd go mod vendor 2020-03-03 18:28:13 -05:00
be56c9cf0c Bump golang to v1.14 2020-03-03 18:27:31 -05:00
7be57cb01c Merge pull request #540 from chrisallenlane/master
fix: README and Makefil
2020-02-26 19:29:04 -05:00
8453af8601 fix(Makefile): build targets
Adds missing dependencies to the `build` and `install` targets.
2020-02-26 19:21:07 -05:00
6e388c3693 fix(README): typo fix
Fixes a bad link to the completion scripts in the `README`.
2020-02-26 19:20:24 -05:00
b13246978a Merge pull request #538 from chrisallenlane/improved-search
feat(search): issue #260
2020-02-15 18:07:40 -05:00
a39d36cd34 chore: bumps version to 3.6.0 2020-02-15 16:13:33 -05:00
87cba04ff2 chore: DRY out colorization code
Creates a `sheet.Colorize` method that DRYs out code that was duplicated
among `cmd_search` and `cmd_view`.
2020-02-15 16:11:15 -05:00
bc623da74b feat: issue #260
Dramatically improves the usefulness of `--search` by outputting
"chunked" results. This removes the need (usually) to search and then
manually open a cheatsheet.
2020-02-15 15:56:25 -05:00
a6c25d4b9c feat: --search improvements
- Deprecates the `Match` struct

- Applies syntax highlighting to search results output in a manner
  consistent with the 'View' output

- Refactors search to move colorization functionality outside of its
  concern
2020-02-15 14:42:06 -05:00
e24ac2b385 chore: deprecates line numbers in search
Removes line numbers from search result output as part of the
refactoring effort.
2020-02-15 10:11:25 -05:00
e0c35a74d4 chore: removes colorization from search
Deprecates `mgutz/ansi` and removes colorization from search as a first
step toward improving the functionality.
2020-02-15 10:01:44 -05:00
3e4c1818a9 feat: single-sheet searching
`--search` will now honor the `<cheatsheet>` argument, making it
possible to constrain a search to a single cheatsheet.
2020-02-15 06:38:57 -05:00
7b4a268ebd fix: resolves #301
Fixes an issue whereby `cheat` would crash when `$EDITOR` (or
equivalent) contained arguments.
2020-02-02 15:56:18 -05:00
f7183aa17a Merge pull request #536 from chrisallenlane/issue-301
fix: issue #301
2020-02-02 15:54:47 -05:00
1ce6c29e6a fix: issue #301
Attempts to resolve issue #301, whereby `cheat` fails if `$EDITOR` (or
equivalent) contains flags.
2020-02-02 15:48:45 -05:00
219db679e1 Merge pull request #535 from chrisallenlane/master
chore: bumps version to 3.5.0
2020-02-02 14:54:46 -05:00
53177cb09d chore: bumps version to 3.5.0
Squashed commit of the following:

commit 8b74d50f1f
Author: Chris Lane <chris@chris-allen-lane.com>
Date:   Sun Feb 2 14:40:23 2020 -0500

    chore: updates README

    Edits the `README` to provid updated information regarding the shell
    autocompletion scripts and `fzf` integration.

commit 9868ba2d68
Author: Chris Lane <chris@chris-allen-lane.com>
Date:   Sun Feb 2 14:39:04 2020 -0500

    chore: modifies envvar check

    Modifies the `CHEAT_USE_FZF` envvar check within the bash autocompletion
    script for clarity.

commit ac1012f743
Author: Chris Lane <chris@chris-allen-lane.com>
Date:   Sun Feb 2 14:25:34 2020 -0500

    chore: renames autocompletion scripts

    Renames autocompletion scripts to conform with the conventions
    established in `scop/bash-completion`.

commit c8747bd91d
Author: Chris Lane <chris@chris-allen-lane.com>
Date:   Sun Feb 2 14:23:03 2020 -0500

    feat: improved bash autocompletions

    - Dramatically improves quality of bash autocompletions

    - Provides optional integration with `fzf`

commit 825bd0139d
Author: Chris Lane <chris@chris-allen-lane.com>
Date:   Sun Feb 2 09:19:46 2020 -0500

    chore: deletes `fzf.bash`

    Deletes `fzf.bash`, which was always intended to be a temporary
    placeholder anticipating future improvements.
2020-02-02 14:50:16 -05:00
ef7a41f9a9 Merge pull request #533 from chrisallenlane/master
chore: creates `make coverage` target
2020-01-31 14:07:20 -05:00
008316d030 chore: creates make coverage target
Creates a `make coverage` target that generates a unit-testing coverage
report.
2020-01-31 14:01:57 -05:00
a59c019642 chore: removes bin directory
Removes the `bin` directory. It has been obsoleted by the `Makefile`,
and no downstream packages appeared to depend on it.
2020-01-31 13:30:21 -05:00
57225442be Merge pull request #532 from chrisallenlane/auto-config-bugfix
fix(config generation): issue #501
2020-01-30 20:12:24 -05:00
2c7ce48859 chore: multi-os builds on travis
Modifies `.travis.yml` to specify that builds be performed on both Linux
and MacOSX. Experimental.
2020-01-30 20:08:11 -05:00
a3fe4f40bb chore: bumps version to 3.4.1 2020-01-30 20:06:06 -05:00
506fb8be15 fix: XDG_CONFIG_HOME mishandling
Attempts to resolve an issue regarding automatic config file generation,
as referenced in #501. This issue is believed occur when
`XDG_CONFIG_HOME` is unset.
2020-01-30 19:59:35 -05:00
408e944eea chore: refactors config.path (small)
Performs a minor refactoring on `config.Paths` to consistently use
`path.Join` when computing config directory paths. Previously, both
`path.Join` and `fmt.Sprintf` were being used, strictly due to an
oversight.
2020-01-30 19:45:02 -05:00
8a313b92ca chore: implements unit-tests for config.Paths 2020-01-30 19:25:53 -05:00
6912771c39 chore: refactors config.Paths
Refactors the reading of multiple envvars out of `config.Paths` in order
to facilitate cleaner unit-testing.
2020-01-30 18:48:36 -05:00
d4c6200702 Merge pull request #531 from chrisallenlane/auto-config
feat(configs): config auto-generation
2020-01-29 14:22:12 -05:00
9251849d23 chore: bumps version to 3.4.0 2020-01-29 14:17:06 -05:00
313b5ebd27 feat: config auto-generation
`cheat` now attempts to auto-generate a config file if one cannot be
found on the filesystem.
2020-01-29 14:08:03 -05:00
ca91b25b02 fix: logging on failed configs
Fixes an issue whereby the error message generated when a config file
could not be loaded (due to a symlink resolution failure) would fail to
print the config file path to `stderr`.
2020-01-29 14:05:27 -05:00
bbf6af50b1 chore: modifies example config
- Generally reduces the complexity demonstrated in the example configs.
  The prior complexity appears to have confused some new users.

- Removes the `dotfiles` references in the example configs. This idiom
  likewise appears to have confused some new users.

- Adds some instruction regarding how to download and configure the
  "community" cheatsheets (`cheat/cheatsheets`).
2020-01-29 14:01:19 -05:00
9f05442bce fix: Makefile
Resolves an error whereby `make build` would fail to call `go generate`
prior to calling `go build`.
2020-01-29 13:59:31 -05:00
3fc4c2f89e chore: Makefile
Makes some adjustments to the behaviors of `make tags` and `make
distclean`.
2020-01-29 09:36:59 -05:00
9e88ff2642 chore: Makefile adjustment
Previously, `build-release` would produce `.gz` files which had the
execute permission set. This modifies the `Makefile` to `chmod -x` the
`.gz` files after compression.
2020-01-29 09:11:06 -05:00
e3764b81e7 3.3.2 (#530)
* chore: trivial Makefile changes

Trivial `Makefile` changes regarding file pathing.

* chore: build flags

- Passes `-trimpath` flag to build tasks

- Creates `make ci` task and modifies `travis.yml` to use it

- Bumps version to `3.3.2`
2020-01-29 08:50:47 -05:00
3786ac96a5 3.3.1 (#529)
* makefile wip

* feat: adds Makefile

Adds a `Makefile` for managing build-related tasks.

* chore: updates dependencies

* chore: updates dependencies

* chore: updates bin scripts

- Removes `build_release.sh`
- Places deprecation notice in `build_devel.sh`, as its purpose has been
  superceded by the `Makefile`.

* chore: updates bin scripts

- Removes `build_release.sh`
- Places deprecation notice in `build_devel.sh`, as its purpose has been
  superceded by the `Makefile`.

* fix: Makefile

Makes several corrections and improvements to the `Makefile`:

- Previously, the `ifeq` rules were not behaving as intended, due to
  false assumptions regarding how `make` fundamentally behaves.
  Malfunctioning imperative-style programming has been replaced with
  declarative rules to repair this issue.

- Previously, all release executables were zipped after compilation. In
  order to spare non-Windows users from (possibly) needing to install a
  package to unzip the executables, all non-Windows binaries are now
  compressed with `gzip`. (Windows executables are still compressed with
  `zip`.)

- Removes a bit of needlessly verbosity in several rules and paths.

* chore: updates dependencies

* chore: bumps version to 3.3.1
2020-01-25 14:44:51 -05:00
4cb7a3b42c add FreeBSD to the supported systems in config.Path (#528) 2020-01-25 14:33:15 -05:00
ff6a866abe chore(deps): bump gopkg.in/yaml.v2 from 2.2.7 to 2.2.8 (#527)
Bumps [gopkg.in/yaml.v2](https://github.com/go-yaml/yaml) from 2.2.7 to 2.2.8.
- [Release notes](https://github.com/go-yaml/yaml/releases)
- [Commits](https://github.com/go-yaml/yaml/compare/v2.2.7...v2.2.8)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-25 14:20:06 -05:00
2e7ccb2a68 chore(deps): bump github.com/mattn/go-isatty from 0.0.11 to 0.0.12 (#526)
Bumps [github.com/mattn/go-isatty](https://github.com/mattn/go-isatty) from 0.0.11 to 0.0.12.
- [Release notes](https://github.com/mattn/go-isatty/releases)
- [Commits](https://github.com/mattn/go-isatty/compare/v0.0.11...v0.0.12)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-25 14:19:57 -05:00
126231db1f feat: makefile release compression
The `Makefile` `build-release` target now zips all executables after
compilation.
2020-01-20 13:54:53 -05:00
91f0d02de2 v3.3.0 (#525)
* feat: directory-scoped cheatpaths

`cheat` now searches for a `.cheat` directory in the current working
directory. If found, that directory is (temporarily) appended to the slice
of cheatpaths.

* makefile wip

* fix: appeases linter

Appeases linter (`go vet`) by adding quotation marks to YAML struct
tags.

* chore: modifies .gitignore

Adds `tag` to `.gitignore`

* feat: adds Makefile

Adds a `Makefile` for managing build-related tasks.

* chore: documents directory-local paths

Adds documentation regarding the new directory-local cheatpath
functionality.

* chore: updates dependencies

* chore: bumps version to 3.3.0

* chore: updates bin scripts

- Removes `build_release.sh`
- Places deprecation notice in `build_devel.sh`, as its purpose has been
  superceded by the `Makefile`.
2020-01-20 12:34:48 -05:00
815e714fb4 Merge pull request #523 from cheat/dependabot/go_modules/github.com/alecthomas/chroma-0.7.1
chore(deps): bump github.com/alecthomas/chroma from 0.7.0 to 0.7.1
2020-01-19 17:05:16 -05:00
bd3986a051 chore(deps): bump github.com/alecthomas/chroma from 0.7.0 to 0.7.1
Bumps [github.com/alecthomas/chroma](https://github.com/alecthomas/chroma) from 0.7.0 to 0.7.1.
- [Release notes](https://github.com/alecthomas/chroma/releases)
- [Changelog](https://github.com/alecthomas/chroma/blob/master/.goreleaser.yml)
- [Commits](https://github.com/alecthomas/chroma/compare/v0.7.0...v0.7.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-09 07:03:22 +00:00
f47b75edc0 Merge pull request #521 from chrisallenlane/master
chore: dependencies and build script
2019-12-28 10:08:32 -05:00
180ee20f77 chore: bumps version to 3.2.2 2019-12-28 09:58:48 -05:00
9b86c583f8 chore: updates chroma dependency 2019-12-28 09:56:19 -05:00
e1f7828869 Merge branch 'master' of github.com:cheat/cheat into issue-520 2019-12-28 09:54:39 -05:00
7f3ae2ab30 feat: ARM support
Modifies `bin/build_release.sh` to compile releases for ARM versions 5,
6, and 7.
2019-12-28 09:53:55 -05:00
bbd03a1bb8 Merge pull request #515 from cheat/dependabot/go_modules/github.com/alecthomas/chroma-0.7.0
chore(deps): bump github.com/alecthomas/chroma from 0.6.9 to 0.7.0
2019-12-28 09:53:22 -05:00
326c54147b chore: updates dependencies 2019-12-28 09:07:37 -05:00
efcedaedec chore(deps): bump github.com/alecthomas/chroma from 0.6.9 to 0.7.0
Bumps [github.com/alecthomas/chroma](https://github.com/alecthomas/chroma) from 0.6.9 to 0.7.0.
- [Release notes](https://github.com/alecthomas/chroma/releases)
- [Changelog](https://github.com/alecthomas/chroma/blob/master/.goreleaser.yml)
- [Commits](https://github.com/alecthomas/chroma/compare/v0.6.9...v0.7.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-28 14:06:13 +00:00
301cbefb0c Merge pull request #519 from cheat/dependabot/go_modules/github.com/mattn/go-isatty-0.0.11
chore(deps): bump github.com/mattn/go-isatty from 0.0.10 to 0.0.11
2019-12-28 09:04:43 -05:00
9a481f7e75 chore(deps): bump github.com/mattn/go-isatty from 0.0.10 to 0.0.11
Bumps [github.com/mattn/go-isatty](https://github.com/mattn/go-isatty) from 0.0.10 to 0.0.11.
- [Release notes](https://github.com/mattn/go-isatty/releases)
- [Commits](https://github.com/mattn/go-isatty/compare/v0.0.10...v0.0.11)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-11 07:07:50 +00:00
e2920bd922 Merge pull request #514 from chrisallenlane/master
fix: colorization errors
2019-11-24 12:17:19 -05:00
973a1f59ea chore: version bump
Bumps version to `3.2.1`.
2019-11-24 12:03:26 -05:00
3afea0972c fix: colorization errors
- Corrects an error with `--search`. Previously, `--search` was not
  aware of whether it was outputted to a TTY, and would apply colorization
  at all times. This resulted in unwanted behavior when, for example,
  piping search results into a paginator.

- Corrects an error with `--color`. Previously, `--color` would be
  ignored if output was being written to a non-TTY. This made it
  impossible, for example, to `cheat tar --color | less -R`, as
  colorization would always be stripped. The behavior of `--color` has
  been modified such that it now behaves similarly to `--color=always` in
  other applications.
2019-11-23 13:47:08 -05:00
678 changed files with 60960 additions and 118201 deletions

11
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,11 @@
version: 2
updates:
- package-ecosystem: gomod
directory: "/"
schedule:
interval: daily
open-pull-requests-limit: 10
ignore:
- dependency-name: github.com/alecthomas/chroma
versions:
- 0.9.1

57
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,57 @@
name: Go
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
# TODO: is it possible to DRY out these jobs? Aside from `runs-on`, they are
# identical.
build-linux:
runs-on: [ ubuntu-latest ]
steps:
- uses: actions/checkout@v2
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.16
- name: Set up Revive (linter)
run: go get -u github.com/boyter/scc github.com/mgechev/revive
env:
GO111MODULE: off
- name: Build
run: make build
- name: Test
run: make test
build-osx:
runs-on: [ macos-latest ]
steps:
- uses: actions/checkout@v2
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.16
- name: Set up Revive (linter)
run: go get -u github.com/boyter/scc github.com/mgechev/revive
env:
GO111MODULE: off
- name: Build
run: make build
- name: Test
run: make test
# TODO: windows

36
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@ -0,0 +1,36 @@
name: CodeQL
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
schedule:
- cron: '45 23 * * 0'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: [ 'go' ]
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
- name: Autobuild
uses: github/codeql-action/autobuild@v1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

17
.github/workflows/homebrew.yml vendored Normal file
View File

@ -0,0 +1,17 @@
name: homebrew
on:
push:
tags: '*'
jobs:
homebrew:
name: Bump Homebrew formula
runs-on: ubuntu-latest
steps:
- uses: mislav/bump-homebrew-formula-action@v1
with:
# A PR will be sent to github.com/Homebrew/homebrew-core to update this formula:
formula-name: cheat
env:
COMMITTER_TOKEN: ${{ secrets.COMMITTER_TOKEN }}

1
.gitignore vendored
View File

@ -1 +1,2 @@
dist
tags

View File

@ -1,4 +0,0 @@
language: go
go:
- 1.13.x

View File

@ -35,6 +35,9 @@ Are you unable to do the above, but still want to contribute? You can help
`cheat` simply by telling others about it. Share it with friends and coworkers
that might benefit from using it.
#### Pull Requests ####
Please open all pull-requests against the `develop` branch.
[cheat]: https://github.com/cheat/cheat
[cheatsheets]: https://github.com/cheat/cheatsheets

8
Dockerfile Normal file
View File

@ -0,0 +1,8 @@
# NB: this image isn't used anywhere in the build pipeline. It exists to
# conveniently facilitate ad-hoc experimentation in a sandboxed environment
# during development.
FROM golang:1.15-alpine
RUN apk add git less make
WORKDIR /app

200
Makefile Normal file
View File

@ -0,0 +1,200 @@
# paths
makefile := $(realpath $(lastword $(MAKEFILE_LIST)))
cmd_dir := ./cmd/cheat
dist_dir := ./dist
# executables
CAT := cat
COLUMN := column
CTAGS := ctags
DOCKER := docker
GO := go
GREP := grep
GZIP := gzip --best
LINT := revive
MAN := man
MKDIR := mkdir -p
PANDOC := pandoc
RM := rm
SCC := scc
SED := sed
SORT := sort
ZIP := zip -m
docker_image := cheat-devel:latest
# build flags
BUILD_FLAGS := -ldflags="-s -w" -mod vendor -trimpath
GOBIN :=
TMPDIR := /tmp
# release binaries
releases := \
$(dist_dir)/cheat-darwin-amd64 \
$(dist_dir)/cheat-linux-386 \
$(dist_dir)/cheat-linux-amd64 \
$(dist_dir)/cheat-linux-arm5 \
$(dist_dir)/cheat-linux-arm6 \
$(dist_dir)/cheat-linux-arm7 \
$(dist_dir)/cheat-linux-arm64 \
$(dist_dir)/cheat-windows-amd64.exe
## build: build an executable for your architecture
.PHONY: build
build: $(dist_dir) clean fmt lint vet vendor generate man
$(GO) build $(BUILD_FLAGS) -o $(dist_dir)/cheat $(cmd_dir)
## build-release: build release executables
.PHONY: build-release
build-release: $(releases)
# cheat-darwin-amd64
$(dist_dir)/cheat-darwin-amd64: prepare
GOARCH=amd64 GOOS=darwin \
$(GO) build $(BUILD_FLAGS) -o $@ $(cmd_dir) && $(GZIP) $@ && chmod -x $@.gz
# cheat-linux-386
$(dist_dir)/cheat-linux-386: prepare
GOARCH=386 GOOS=linux \
$(GO) build $(BUILD_FLAGS) -o $@ $(cmd_dir) && $(GZIP) $@ && chmod -x $@.gz
# cheat-linux-amd64
$(dist_dir)/cheat-linux-amd64: prepare
GOARCH=amd64 GOOS=linux \
$(GO) build $(BUILD_FLAGS) -o $@ $(cmd_dir) && $(GZIP) $@ && chmod -x $@.gz
# cheat-linux-arm5
$(dist_dir)/cheat-linux-arm5: prepare
GOARCH=arm GOOS=linux GOARM=5 \
$(GO) build $(BUILD_FLAGS) -o $@ $(cmd_dir) && $(GZIP) $@ && chmod -x $@.gz
# cheat-linux-arm6
$(dist_dir)/cheat-linux-arm6: prepare
GOARCH=arm GOOS=linux GOARM=6 \
$(GO) build $(BUILD_FLAGS) -o $@ $(cmd_dir) && $(GZIP) $@ && chmod -x $@.gz
# cheat-linux-arm7
$(dist_dir)/cheat-linux-arm7: prepare
GOARCH=arm GOOS=linux GOARM=7 \
$(GO) build $(BUILD_FLAGS) -o $@ $(cmd_dir) && $(GZIP) $@ && chmod -x $@.gz
# cheat-linux-arm64
$(dist_dir)/cheat-linux-arm64: prepare
GOARCH=arm64 GOOS=linux \
$(GO) build $(BUILD_FLAGS) -o $@ $(cmd_dir) && $(GZIP) $@ && chmod -x $@.gz
# cheat-windows-amd64
$(dist_dir)/cheat-windows-amd64.exe: prepare
GOARCH=amd64 GOOS=windows \
$(GO) build $(BUILD_FLAGS) -o $@ $(cmd_dir) && $(ZIP) $@.zip $@ -j
# ./dist
$(dist_dir):
$(MKDIR) $(dist_dir)
.PHONY: generate
generate:
$(GO) generate $(cmd_dir)
## install: build and install cheat on your PATH
.PHONY: install
install: build
$(GO) install $(BUILD_FLAGS) $(GOBIN) $(cmd_dir)
## clean: remove compiled executables
.PHONY: clean
clean: $(dist_dir)
$(RM) -f $(dist_dir)/*
## distclean: remove the tags file
.PHONY: distclean
distclean:
$(RM) -f tags
@$(DOCKER) image rm -f $(docker_image)
## setup: install revive (linter) and scc (sloc tool)
.PHONY: setup
setup:
GO111MODULE=off $(GO) get -u github.com/boyter/scc github.com/mgechev/revive
## sloc: count "semantic lines of code"
.PHONY: sloc
sloc:
$(SCC) --exclude-dir=vendor
## tags: build a tags file
.PHONY: tags
tags:
$(CTAGS) -R --exclude=vendor --languages=go
## man: build a man page
# NB: pandoc may not be installed, so we're ignoring this error on failure
.PHONY: man
man:
-$(PANDOC) -s -t man doc/cheat.1.md -o doc/cheat.1
## vendor: download, tidy, and verify dependencies
.PHONY: vendor
vendor:
$(GO) mod vendor && $(GO) mod tidy && $(GO) mod verify
## vendor-update: update vendored dependencies
vendor-update:
$(GO) get -t -u ./... && $(GO) mod vendor
## fmt: run go fmt
.PHONY: fmt
fmt:
$(GO) fmt ./...
## lint: lint go source files
.PHONY: lint
lint: vendor
$(LINT) -exclude vendor/... ./...
## vet: vet go source files
.PHONY: vet
vet:
$(GO) vet ./...
## test: run unit-tests
.PHONY: test
test:
$(GO) test ./...
## coverage: generate a test coverage report
.PHONY: coverage
coverage:
$(GO) test ./... -coverprofile=$(TMPDIR)/cheat-coverage.out && \
$(GO) tool cover -html=$(TMPDIR)/cheat-coverage.out
## check: format, lint, vet, vendor, and run unit-tests
.PHONY: check
check: | vendor fmt lint vet test
.PHONY: prepare
prepare: | $(dist_dir) clean generate vendor fmt lint vet test
## docker-setup: create a docker image for use during development
.PHONY: docker-setup
docker-setup:
$(DOCKER) build -t $(docker_image) -f Dockerfile .
## docker-run: shell into the development docker container
.PHONY: docker-run
docker-run:
$(DOCKER) run -v `pwd`:/app -ti $(docker_image) sh
## docker-sh: shell into the docker development container
.PHONY: docker-sh
docker-sh:
$(DOCKER) run -v $(shell pwd):/app -ti $(docker_image) /bin/ash
## help: display this help text
.PHONY: help
help:
@$(CAT) $(makefile) | \
$(SORT) | \
$(GREP) "^##" | \
$(SED) 's/## //g' | \
$(COLUMN) -t -s ':'

View File

@ -1,8 +1,9 @@
![Workflow status](https://github.com/cheat/cheat/actions/workflows/build.yml/badge.svg)
cheat
=====
[![Build Status](https://travis-ci.com/cheat/cheat.svg?branch=master)](https://travis-ci.com/cheat/cheat)
`cheat` allows you to create and view interactive cheatsheets on the
command-line. It was designed to help remind \*nix system administrators of
options for commands that they use frequently, but not frequently enough to
@ -47,15 +48,17 @@ Installing
`cheat` has no dependencies. To install it, download the executable from the
[releases][] page and place it on your `PATH`.
Alternatively, if you have [go][] installed, you may install `cheat` using `go
get`:
```sh
go get -u github.com/cheat/cheat/cmd/cheat
```
Configuring
-----------
### conf.yml ###
`cheat` is configured by a YAML file that can be generated with `cheat --init`:
```sh
mkdir -p ~/.config/cheat && cheat --init > ~/.config/cheat/conf.yml
```
`cheat` is configured by a YAML file that will be auto-generated on first run.
By default, the config file is assumed to exist on an XDG-compliant
configuration path like `~/.config/cheat/conf.yml`. If you would like to store
@ -89,13 +92,29 @@ const squares = [1, 2, 3, 4].map(x => x * x);
```
The `cheat` executable includes no cheatsheets, but [community-sourced
cheatsheets are available][cheatsheets].
cheatsheets are available][cheatsheets]. You will be asked if you would like to
install the community-sourced cheatsheets the first time you run `cheat`.
### Script ###
You can manage the cheatsheets via a script `cheatsheets`.
#### Download and install ####
```sh
mkdir -p ~/.local/bin
wget -O ~/.local/bin/cheatsheets https://raw.githubusercontent.com/cheat/cheat/master/scripts/git/cheatsheets
chmod +x ~/.local/bin/cheatsheets
```
#### Pull changes ####
To pull the community and personal cheatsheets call `cheatsheets pull`
#### Push changes ####
To push your personal cheatsheets call `cheatsheets push`
Cheatpaths
----------
Cheatsheets are stored on "cheatpaths", which are directories that contain
cheetsheets. Cheatpaths are specified in the `conf.yml` file.
cheatsheets. Cheatpaths are specified in the `conf.yml` file.
It can be useful to configure `cheat` against multiple cheatpaths. A common
pattern is to store cheatsheets from multiple repositories on individual
@ -125,6 +144,11 @@ If a user attempts to edit a cheatsheet on a read-only cheatpath, `cheat` will
transparently copy that sheet to a writeable directory before opening it for
editing.
### Directory-scoped Cheatpaths ###
At times, it can be useful to closely associate cheatsheets with a directory on
your filesystem. `cheat` facilitates this by searching for a `.cheat` folder in
the current working directory. If found, the `.cheat` directory will
(temporarily) be added to the cheatpaths.
Usage
-----
@ -188,11 +212,19 @@ cheat -p personal -t networking --regex -s '(?:[0-9]{1,3}\.){3}[0-9]{1,3}'
Advanced Usage
--------------
`cheat` may be integrated with [fzf][]. See [fzf.bash][bash] for instructions.
(Support for other shells will be added in future releases.)
Shell autocompletion is currently available for `bash`, `fish`, and `zsh`. Copy
the relevant [completion script][completions] into the appropriate directory on
your filesystem to enable autocompletion. (This directory will vary depending
on operating system and shell specifics.)
Additionally, `cheat` supports enhanced autocompletion via integration with
[fzf][]. To enable `fzf` integration:
1. Ensure that `fzf` is available on your `$PATH`
2. Set an envvar: `export CHEAT_USE_FZF=true`
[Releases]: https://github.com/cheat/cheat/releases
[bash]: https://github.com/cheat/cheat/blob/master/scripts/fzf.bash
[cheatsheets]: https://github.com/cheat/cheatsheets
[completions]: https://github.com/cheat/cheat/tree/master/scripts
[fzf]: https://github.com/junegunn/fzf
[go]: https://golang.org

View File

@ -1,16 +0,0 @@
#!/bin/bash
# locate the cheat project root
BINDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
APPDIR=$(readlink -f "$BINDIR/..")
# update the vendored dependencies
go mod vendor && go mod tidy
# compile the executable
cd "$APPDIR/cmd/cheat"
go clean && go generate && go build -mod vendor
mv "$APPDIR/cmd/cheat/cheat" "$APPDIR/dist/cheat"
# display a build checksum
md5sum "$APPDIR/dist/cheat"

View File

@ -1,22 +0,0 @@
#!/bin/bash
# locate the cheat project root
BINDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
APPDIR=$(readlink -f "$BINDIR/..")
# update the vendored dependencies
go mod vendor && go mod tidy
# build embeds
cd "$APPDIR/cmd/cheat"
go clean && go generate
# compile AMD64 for Linux, OSX, and Windows
env GOOS=darwin GOARCH=amd64 go build -mod vendor -o \
"$APPDIR/dist/cheat-darwin-amd64" "$APPDIR/cmd/cheat"
env GOOS=linux GOARCH=amd64 go build -mod vendor -o \
"$APPDIR/dist/cheat-linux-amd64" "$APPDIR/cmd/cheat"
env GOOS=windows GOARCH=amd64 go build -mod vendor -o \
"$APPDIR/dist/cheat-win-amd64.exe" "$APPDIR/cmd/cheat"

View File

@ -1,3 +1,4 @@
//go:build ignore
// +build ignore
// This script embeds `docopt.txt and `conf.yml` into the binary during at
@ -5,13 +6,11 @@
package main
import (
"fmt"
"io/ioutil"
"log"
"os"
"path"
"path/filepath"
)
@ -52,10 +51,10 @@ func main() {
for _, file := range files {
// delete the outfile
os.Remove(path.Join(root, file.Out))
os.Remove(filepath.Join(root, file.Out))
// read the static template
bytes, err := ioutil.ReadFile(path.Join(root, file.In))
bytes, err := ioutil.ReadFile(filepath.Join(root, file.In))
if err != nil {
log.Fatal(err)
}
@ -64,7 +63,7 @@ func main() {
data := template(file.Method, string(bytes))
// write the file to the specified outpath
spath := path.Join(root, file.Out)
spath := filepath.Join(root, file.Out)
err = ioutil.WriteFile(spath, []byte(data), 0644)
if err != nil {
log.Fatal(err)

View File

@ -1,18 +1,20 @@
package main
import (
"bytes"
"fmt"
"os"
"text/tabwriter"
"github.com/cheat/cheat/internal/config"
"github.com/cheat/cheat/internal/display"
)
// cmdDirectories lists the configured cheatpaths.
func cmdDirectories(opts map[string]interface{}, conf config.Config) {
// initialize a tabwriter to produce cleanly columnized output
w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0)
var out bytes.Buffer
w := tabwriter.NewWriter(&out, 0, 0, 1, ' ', 0)
// generate sorted, columnized output
for _, path := range conf.Cheatpaths {
@ -25,4 +27,5 @@ func cmdDirectories(opts map[string]interface{}, conf config.Config) {
// write columnized output to stdout
w.Flush()
display.Write(out.String(), conf)
}

View File

@ -4,7 +4,7 @@ import (
"fmt"
"os"
"os/exec"
"path"
"path/filepath"
"strings"
"github.com/cheat/cheat/internal/cheatpath"
@ -58,10 +58,10 @@ func cmdEdit(opts map[string]interface{}, conf config.Config) {
}
// compute the new edit path
editpath = path.Join(writepath.Path, sheet.Title)
editpath = filepath.Join(writepath.Path, sheet.Title)
// create any necessary subdirectories
dirs := path.Dir(editpath)
dirs := filepath.Dir(editpath)
if dirs != "." {
if err := os.MkdirAll(dirs, 0755); err != nil {
fmt.Fprintf(os.Stderr, "failed to create directory: %s, %v\n", dirs, err)
@ -87,10 +87,10 @@ func cmdEdit(opts map[string]interface{}, conf config.Config) {
}
// compute the new edit path
editpath = path.Join(writepath.Path, cheatsheet)
editpath = filepath.Join(writepath.Path, cheatsheet)
// create any necessary subdirectories
dirs := path.Dir(editpath)
dirs := filepath.Dir(editpath)
if dirs != "." {
if err := os.MkdirAll(dirs, 0755); err != nil {
fmt.Fprintf(os.Stderr, "failed to create directory: %s, %v\n", dirs, err)
@ -99,8 +99,15 @@ func cmdEdit(opts map[string]interface{}, conf config.Config) {
}
}
// split `conf.Editor` into parts to separate the editor's executable from
// any arguments it may have been passed. If this is not done, the nearby
// call to `exec.Command` will fail.
parts := strings.Fields(conf.Editor)
editor := parts[0]
args := append(parts[1:], editpath)
// edit the cheatsheet
cmd := exec.Command(conf.Editor, editpath)
cmd := exec.Command(editor, args...)
cmd.Stdout = os.Stdout
cmd.Stdin = os.Stdin
cmd.Stderr = os.Stderr

View File

@ -2,9 +2,56 @@ package main
import (
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
"github.com/mitchellh/go-homedir"
"github.com/cheat/cheat/internal/config"
)
// cmdInit displays an example config file.
func cmdInit() {
fmt.Println(configs())
// get the user's home directory
home, err := homedir.Dir()
if err != nil {
fmt.Fprintf(os.Stderr, "failed to get user home directory: %v\n", err)
os.Exit(1)
}
// read the envvars into a map of strings
envvars := map[string]string{}
for _, e := range os.Environ() {
pair := strings.SplitN(e, "=", 2)
envvars[pair[0]] = pair[1]
}
// load the config template
configs := configs()
// identify the os-specifc paths at which configs may be located
confpaths, err := config.Paths(runtime.GOOS, home, envvars)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to read config paths: %v\n", err)
os.Exit(1)
}
// determine the appropriate paths for config data and (optional) community
// cheatsheets based on the user's platform
confpath := confpaths[0]
confdir := filepath.Dir(confpath)
// create paths for community and personal cheatsheets
community := filepath.Join(confdir, "cheatsheets", "community")
personal := filepath.Join(confdir, "cheatsheets", "personal")
// template the above paths into the default configs
configs = strings.Replace(configs, "COMMUNITY_PATH", community, -1)
configs = strings.Replace(configs, "PERSONAL_PATH", personal, -1)
// output the templated configs
fmt.Println(configs)
}

View File

@ -1,6 +1,7 @@
package main
import (
"bytes"
"fmt"
"os"
"regexp"
@ -9,6 +10,7 @@ import (
"text/tabwriter"
"github.com/cheat/cheat/internal/config"
"github.com/cheat/cheat/internal/display"
"github.com/cheat/cheat/internal/sheet"
"github.com/cheat/cheat/internal/sheets"
)
@ -23,7 +25,7 @@ func cmdList(opts map[string]interface{}, conf config.Config) {
os.Exit(1)
}
// filter cheatcheats by tag if --tag was provided
// filter cheatsheets by tag if --tag was provided
if opts["--tag"] != nil {
cheatsheets = sheets.Filter(
cheatsheets,
@ -35,8 +37,8 @@ func cmdList(opts map[string]interface{}, conf config.Config) {
// sheets with local sheets), here we simply want to create a slice
// containing all sheets.
flattened := []sheet.Sheet{}
for _, pathSheets := range cheatsheets {
for _, s := range pathSheets {
for _, pathsheets := range cheatsheets {
for _, s := range pathsheets {
flattened = append(flattened, s)
}
}
@ -79,16 +81,19 @@ func cmdList(opts map[string]interface{}, conf config.Config) {
flattened = filtered
}
// exit early if no cheatsheets are available
// return exit code 2 if no cheatsheets are available
if len(flattened) == 0 {
os.Exit(0)
os.Exit(2)
}
// initialize a tabwriter to produce cleanly columnized output
w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0)
var out bytes.Buffer
w := tabwriter.NewWriter(&out, 0, 0, 1, ' ', 0)
// write a header row
fmt.Fprintln(w, "title:\tfile:\ttags:")
// generate sorted, columnized output
fmt.Fprintln(w, "title:\tfile:\ttags:")
for _, sheet := range flattened {
fmt.Fprintln(w, fmt.Sprintf(
"%s\t%s\t%s",
@ -100,4 +105,5 @@ func cmdList(opts map[string]interface{}, conf config.Config) {
// write columnized output to stdout
w.Flush()
display.Write(out.String(), conf)
}

View File

@ -37,8 +37,8 @@ func cmdRemove(opts map[string]interface{}, conf config.Config) {
// fail early if the requested cheatsheet does not exist
sheet, ok := consolidated[cheatsheet]
if !ok {
fmt.Fprintln(os.Stderr, fmt.Sprintf("no cheatsheet found for '%s'.\n", cheatsheet))
os.Exit(1)
fmt.Fprintln(os.Stderr, fmt.Sprintf("No cheatsheet found for '%s'.\n", cheatsheet))
os.Exit(2)
}
// fail early if the sheet is read-only

View File

@ -7,6 +7,7 @@ import (
"strings"
"github.com/cheat/cheat/internal/config"
"github.com/cheat/cheat/internal/display"
"github.com/cheat/cheat/internal/sheets"
)
@ -30,46 +31,65 @@ func cmdSearch(opts map[string]interface{}, conf config.Config) {
)
}
// consolidate the cheatsheets found on all paths into a single map of
// `title` => `sheet` (ie, allow more local cheatsheets to override less
// local cheatsheets)
consolidated := sheets.Consolidate(cheatsheets)
// iterate over each cheatpath
out := ""
for _, pathcheats := range cheatsheets {
// sort the cheatsheets alphabetically, and search for matches
for _, sheet := range sheets.Sort(consolidated) {
// sort the cheatsheets alphabetically, and search for matches
for _, sheet := range sheets.Sort(pathcheats) {
// colorize output?
colorize := false
if conf.Colorize == true || opts["--colorize"] == true {
colorize = true
}
// assume that we want to perform a case-insensitive search for <phrase>
pattern := "(?i)" + phrase
// unless --regex is provided, in which case we pass the regex unaltered
if opts["--regex"] == true {
pattern = phrase
}
// compile the regex
reg, err := regexp.Compile(pattern)
if err != nil {
fmt.Fprintln(os.Stderr, fmt.Sprintf("failed to compile regexp: %s, %v", pattern, err))
os.Exit(1)
}
// search the sheet
matches := sheet.Search(reg, colorize)
// display the results
if len(matches) > 0 {
fmt.Printf("%s:\n", sheet.Title)
for _, m := range matches {
fmt.Printf(" %d: %s\n", m.Line, m.Text)
// if <cheatsheet> was provided, constrain the search only to
// matching cheatsheets
if opts["<cheatsheet>"] != nil && sheet.Title != opts["<cheatsheet>"] {
continue
}
fmt.Print("\n")
// assume that we want to perform a case-insensitive search for <phrase>
pattern := "(?i)" + phrase
// unless --regex is provided, in which case we pass the regex unaltered
if opts["--regex"] == true {
pattern = phrase
}
// compile the regex
reg, err := regexp.Compile(pattern)
if err != nil {
fmt.Fprintln(os.Stderr, fmt.Sprintf("failed to compile regexp: %s, %v", pattern, err))
os.Exit(1)
}
// `Search` will return text entries that match the search terms. We're
// using it here to overwrite the prior cheatsheet Text, filtering it to
// only what is relevant
sheet.Text = sheet.Search(reg)
// if the sheet did not match the search, ignore it and move on
if sheet.Text == "" {
continue
}
// if colorization was requested, apply it here
if conf.Color(opts) {
sheet.Colorize(conf)
}
// display the cheatsheet title and path
out += fmt.Sprintf("%s %s\n",
display.Underline(sheet.Title),
display.Faint(fmt.Sprintf("(%s)", sheet.CheatPath), conf),
)
// indent each line of content
out += display.Indent(sheet.Text) + "\n"
}
}
// trim superfluous newlines
out = strings.TrimSpace(out)
// display the output
// NB: resist the temptation to call `display.Display` multiple times in
// the loop above. That will not play nicely with the paginator.
display.Write(out, conf)
}

View File

@ -5,6 +5,7 @@ import (
"os"
"github.com/cheat/cheat/internal/config"
"github.com/cheat/cheat/internal/display"
"github.com/cheat/cheat/internal/sheets"
)
@ -18,8 +19,12 @@ func cmdTags(opts map[string]interface{}, conf config.Config) {
os.Exit(1)
}
// write sheet tags to stdout
// assemble the output
out := ""
for _, tag := range sheets.Tags(cheatsheets) {
fmt.Println(tag)
out += fmt.Sprintln(tag)
}
// display the output
display.Write(out, conf)
}

View File

@ -5,10 +5,8 @@ import (
"os"
"strings"
"github.com/alecthomas/chroma/quick"
"github.com/mattn/go-isatty"
"github.com/cheat/cheat/internal/config"
"github.com/cheat/cheat/internal/display"
"github.com/cheat/cheat/internal/sheets"
)
@ -32,54 +30,53 @@ func cmdView(opts map[string]interface{}, conf config.Config) {
)
}
// consolidate the cheatsheets found on all paths into a single map of
// `title` => `sheet` (ie, allow more local cheatsheets to override less
// local cheatsheets)
// if --all was passed, display cheatsheets from all cheatpaths
if opts["--all"].(bool) {
// iterate over the cheatpaths
out := ""
for _, cheatpath := range cheatsheets {
// if the cheatpath contains the specified cheatsheet, display it
if sheet, ok := cheatpath[cheatsheet]; ok {
// identify the matching cheatsheet
out += fmt.Sprintf("%s %s\n",
display.Underline(sheet.Title),
display.Faint(fmt.Sprintf("(%s)", sheet.CheatPath), conf),
)
// apply colorization if requested
if conf.Color(opts) {
sheet.Colorize(conf)
}
// display the cheatsheet
out += display.Indent(sheet.Text) + "\n"
}
}
// display and exit
display.Write(strings.TrimSuffix(out, "\n"), conf)
os.Exit(0)
}
// otherwise, consolidate the cheatsheets found on all paths into a single
// map of `title` => `sheet` (ie, allow more local cheatsheets to override
// less local cheatsheets)
consolidated := sheets.Consolidate(cheatsheets)
// fail early if the requested cheatsheet does not exist
sheet, ok := consolidated[cheatsheet]
if !ok {
fmt.Printf("No cheatsheet found for '%s'.\n", cheatsheet)
os.Exit(0)
os.Exit(2)
}
// apply colorization if so configured ...
colorize := conf.Colorize
// ... or if --colorized were passed ...
if opts["--colorize"] == true {
colorize = true
// apply colorization if requested
if conf.Color(opts) {
sheet.Colorize(conf)
}
// ... unless we're outputting to a non-TTY
if !isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd()) {
colorize = false
}
if !colorize {
fmt.Print(sheet.Text)
os.Exit(0)
}
// otherwise, colorize the output
// if the syntax was not specified, default to bash
lex := sheet.Syntax
if lex == "" {
lex = "bash"
}
// apply syntax highlighting
err = quick.Highlight(
os.Stdout,
sheet.Text,
lex,
conf.Formatter,
conf.Style,
)
// if colorization somehow failed, output non-colorized text
if err != nil {
fmt.Print(sheet.Text)
}
// display the cheatsheet
display.Write(sheet.Text, conf)
}

View File

@ -2,18 +2,19 @@ Usage:
cheat [options] [<cheatsheet>]
Options:
--init Write a default config file to stdout
-c --colorize Colorize output
-d --directories List cheatsheet directories
-e --edit=<sheet> Edit <sheet>
-l --list List cheatsheets
-p --path=<name> Return only sheets found on path <name>
-r --regex Treat search <phrase> as a regex
-s --search=<phrase> Search cheatsheets for <phrase>
-t --tag=<tag> Return only sheets matching <tag>
-T --tags List all tags in use
-v --version Print the version number
--rm=<sheet> Remove (delete) <sheet>
--init Write a default config file to stdout
-a --all Search among all cheatpaths
-c --colorize Colorize output
-d --directories List cheatsheet directories
-e --edit=<cheatsheet> Edit <cheatsheet>
-l --list List cheatsheets
-p --path=<name> Return only sheets found on cheatpath <name>
-r --regex Treat search <phrase> as a regex
-s --search=<phrase> Search cheatsheets for <phrase>
-t --tag=<tag> Return only sheets matching <tag>
-T --tags List all tags in use
-v --version Print the version number
--rm=<cheatsheet> Remove (delete) <cheatsheet>
Examples:

View File

@ -6,19 +6,22 @@ import (
"fmt"
"os"
"runtime"
"strings"
"github.com/docopt/docopt-go"
"github.com/mitchellh/go-homedir"
"github.com/cheat/cheat/internal/cheatpath"
"github.com/cheat/cheat/internal/config"
"github.com/cheat/cheat/internal/installer"
)
const version = "3.2.0"
const version = "4.2.5"
func main() {
// initialize options
opts, err := docopt.Parse(usage(), nil, true, version, false)
opts, err := docopt.ParseArgs(usage(), nil, version)
if err != nil {
// panic here, because this should never happen
panic(fmt.Errorf("docopt failed to parse: %v", err))
@ -31,13 +34,63 @@ func main() {
os.Exit(0)
}
// load the config file
confpath, err := config.Path(runtime.GOOS)
// get the user's home directory
home, err := homedir.Dir()
if err != nil {
fmt.Fprintln(os.Stderr, "could not locate config file")
fmt.Fprintf(os.Stderr, "failed to get user home directory: %v\n", err)
os.Exit(1)
}
// read the envvars into a map of strings
envvars := map[string]string{}
for _, e := range os.Environ() {
pair := strings.SplitN(e, "=", 2)
if runtime.GOOS == "windows" {
pair[0] = strings.ToUpper(pair[0])
}
envvars[pair[0]] = pair[1]
}
// identify the os-specifc paths at which configs may be located
confpaths, err := config.Paths(runtime.GOOS, home, envvars)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to load config: %v\n", err)
os.Exit(1)
}
// search for the config file in the above paths
confpath, err := config.Path(confpaths)
if err != nil {
// prompt the user to create a config file
yes, err := installer.Prompt(
"A config file was not found. Would you like to create one now? [Y/n]",
true,
)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to create config: %v\n", err)
os.Exit(1)
}
// exit early on a negative answer
if !yes {
os.Exit(0)
}
// choose a confpath
confpath = confpaths[0]
// run the installer
if err := installer.Run(configs(), confpath); err != nil {
fmt.Fprintf(os.Stderr, "failed to run installer: %v\n", err)
os.Exit(1)
}
// notify the user and exit
fmt.Printf("Created config file: %s\n", confpath)
fmt.Println("Please read this file for advanced configuration information.")
os.Exit(0)
}
// initialize the configs
conf, err := config.New(opts, confpath, true)
if err != nil {
@ -88,6 +141,9 @@ func main() {
case opts["<cheatsheet>"] != nil:
cmd = cmdView
case opts["--tag"] != nil && opts["--tag"].(string) != "":
cmd = cmdList
default:
fmt.Println(usage())
os.Exit(0)

View File

@ -9,19 +9,23 @@ import (
func configs() string {
return strings.TrimSpace(`---
# The editor to use with 'cheat -e <sheet>'. Defaults to $EDITOR or $VISUAL.
editor: vim
# editor: vim
# Should 'cheat' always colorize output?
colorize: true
colorize: false
# Which 'chroma' colorscheme should be applied to the output?
# Options are available here:
# https://github.com/alecthomas/chroma/tree/master/styles
style: monokai
# style: monokai
# Which 'chroma' "formatter" should be applied?
# One of: "terminal", "terminal256", "terminal16m"
formatter: terminal16m
formatter: terminal
# Through which pager should output be piped? (Unset this key for no pager.)
pager: more
# pager: less -FRX # <- recommended where available
# The paths at which cheatsheets are available. Tags associated with a cheatpath
# are automatically attached to all cheatsheets residing on that path.
@ -41,25 +45,37 @@ cheatpaths:
# thus be overridden by more local cheatsheets. That being the case, you
# should probably list community cheatsheets first.
#
# Note that the paths and tags listed below are just examples. You may freely
# Note that the paths and tags listed below are placeholders. You may freely
# change them to suit your needs.
#
# Community cheatsheets must be installed separately, though you may have
# downloaded them automatically when installing 'cheat'. If not, you may
# download them here:
#
# https://github.com/cheat/cheatsheets
#
# Once downloaded, ensure that 'path' below points to the location at which
# you downloaded the community cheatsheets.
- name: community
path: ~/.dotfiles/cheat/community
path: COMMUNITY_PATH
tags: [ community ]
readonly: true
# Maybe your company or department maintains a repository of cheatsheets as
# well. It's probably sensible to list those second.
- name: work
path: ~/.dotfiles/cheat/work
tags: [ work ]
readonly: false
# If you have personalized cheatsheets, list them last. They will take
# precedence over the more global cheatsheets.
- name: personal
path: ~/.dotfiles/cheat/personal
path: PERSONAL_PATH
tags: [ personal ]
readonly: false
# While it requires no configuration here, it's also worth noting that
# 'cheat' will automatically append directories named '.cheat' within the
# current working directory to the 'cheatpath'. This can be very useful if
# you'd like to closely associate cheatsheets with, for example, a directory
# containing source code.
#
# Such "directory-scoped" cheatsheets will be treated as the most "local"
# cheatsheets, and will override less "local" cheatsheets. Likewise,
# directory-scoped cheatsheets will always be editable ('readonly: false').
`)
}

View File

@ -11,18 +11,19 @@ func usage() string {
cheat [options] [<cheatsheet>]
Options:
--init Write a default config file to stdout
-c --colorize Colorize output
-d --directories List cheatsheet directories
-e --edit=<sheet> Edit <sheet>
-l --list List cheatsheets
-p --path=<name> Return only sheets found on path <name>
-r --regex Treat search <phrase> as a regex
-s --search=<phrase> Search cheatsheets for <phrase>
-t --tag=<tag> Return only sheets matching <tag>
-T --tags List all tags in use
-v --version Print the version number
--rm=<sheet> Remove (delete) <sheet>
--init Write a default config file to stdout
-a --all Search among all cheatpaths
-c --colorize Colorize output
-d --directories List cheatsheet directories
-e --edit=<cheatsheet> Edit <cheatsheet>
-l --list List cheatsheets
-p --path=<name> Return only sheets found on cheatpath <name>
-r --regex Treat search <phrase> as a regex
-s --search=<phrase> Search cheatsheets for <phrase>
-t --tag=<tag> Return only sheets matching <tag>
-T --tags List all tags in use
-v --version Print the version number
--rm=<cheatsheet> Remove (delete) <cheatsheet>
Examples:

View File

@ -1,18 +1,22 @@
---
# The editor to use with 'cheat -e <sheet>'. Defaults to $EDITOR or $VISUAL.
editor: vim
# editor: vim
# Should 'cheat' always colorize output?
colorize: true
colorize: false
# Which 'chroma' colorscheme should be applied to the output?
# Options are available here:
# https://github.com/alecthomas/chroma/tree/master/styles
style: monokai
# style: monokai
# Which 'chroma' "formatter" should be applied?
# One of: "terminal", "terminal256", "terminal16m"
formatter: terminal16m
formatter: terminal
# Through which pager should output be piped? (Unset this key for no pager.)
pager: more
# pager: less -FRX # <- recommended where available
# The paths at which cheatsheets are available. Tags associated with a cheatpath
# are automatically attached to all cheatsheets residing on that path.
@ -32,23 +36,35 @@ cheatpaths:
# thus be overridden by more local cheatsheets. That being the case, you
# should probably list community cheatsheets first.
#
# Note that the paths and tags listed below are just examples. You may freely
# Note that the paths and tags listed below are placeholders. You may freely
# change them to suit your needs.
#
# Community cheatsheets must be installed separately, though you may have
# downloaded them automatically when installing 'cheat'. If not, you may
# download them here:
#
# https://github.com/cheat/cheatsheets
#
# Once downloaded, ensure that 'path' below points to the location at which
# you downloaded the community cheatsheets.
- name: community
path: ~/.dotfiles/cheat/community
path: COMMUNITY_PATH
tags: [ community ]
readonly: true
# Maybe your company or department maintains a repository of cheatsheets as
# well. It's probably sensible to list those second.
- name: work
path: ~/.dotfiles/cheat/work
tags: [ work ]
readonly: false
# If you have personalized cheatsheets, list them last. They will take
# precedence over the more global cheatsheets.
- name: personal
path: ~/.dotfiles/cheat/personal
path: PERSONAL_PATH
tags: [ personal ]
readonly: false
# While it requires no configuration here, it's also worth noting that
# 'cheat' will automatically append directories named '.cheat' within the
# current working directory to the 'cheatpath'. This can be very useful if
# you'd like to closely associate cheatsheets with, for example, a directory
# containing source code.
#
# Such "directory-scoped" cheatsheets will be treated as the most "local"
# cheatsheets, and will override less "local" cheatsheets. Likewise,
# directory-scoped cheatsheets will always be editable ('readonly: false').

221
doc/cheat.1 Normal file
View File

@ -0,0 +1,221 @@
.\" Automatically generated by Pandoc 2.2.1
.\"
.TH "CHEAT" "1" "" "" "General Commands Manual"
.hy
.SH NAME
.PP
\f[B]cheat\f[] \[em] create and view command\-line cheatsheets
.SH SYNOPSIS
.PP
\f[B]cheat\f[] [options] [\f[I]CHEATSHEET\f[]]
.SH DESCRIPTION
.PP
\f[B]cheat\f[] allows you to create and view interactive cheatsheets on
the command\-line.
It was designed to help remind *nix system administrators of options for
commands that they use frequently, but not frequently enough to
remember.
.SH OPTIONS
.TP
.B \[en]init
Print a config file to stdout.
.RS
.RE
.TP
.B \-c, \[en]colorize
Colorize output.
.RS
.RE
.TP
.B \-d, \[en]directories
List cheatsheet directories.
.RS
.RE
.TP
.B \-e, \[en]edit=\f[I]CHEATSHEET\f[]
Open \f[I]CHEATSHEET\f[] for editing.
.RS
.RE
.TP
.B \-l, \[en]list
List available cheatsheets.
.RS
.RE
.TP
.B \-p, \[en]path=\f[I]PATH\f[]
Filter only to sheets found on path \f[I]PATH\f[].
.RS
.RE
.TP
.B \-r, \[en]regex
Treat search \f[I]PHRASE\f[] as a regular expression.
.RS
.RE
.TP
.B \-s, \[en]search=\f[I]PHRASE\f[]
Search cheatsheets for \f[I]PHRASE\f[].
.RS
.RE
.TP
.B \-t, \[en]tag=\f[I]TAG\f[]
Filter only to sheets tagged with \f[I]TAG\f[].
.RS
.RE
.TP
.B \-T, \[en]tags
List all tags in use.
.RS
.RE
.TP
.B \-v, \[en]version
Print the version number.
.RS
.RE
.TP
.B \[en]rm=\f[I]CHEATSHEET\f[]
Remove (deletes) \f[I]CHEATSHEET\f[].
.RS
.RE
.SH EXAMPLES
.TP
.B To view the foo cheatsheet:
cheat \f[I]foo\f[]
.RS
.RE
.TP
.B To edit (or create) the foo cheatsheet:
cheat \-e \f[I]foo\f[]
.RS
.RE
.TP
.B To edit (or create) the foo/bar cheatsheet on the `work' cheatpath:
cheat \-p \f[I]work\f[] \-e \f[I]foo/bar\f[]
.RS
.RE
.TP
.B To view all cheatsheet directories:
cheat \-d
.RS
.RE
.TP
.B To list all available cheatsheets:
cheat \-l
.RS
.RE
.TP
.B To list all cheatsheets whose titles match `apt':
cheat \-l \f[I]apt\f[]
.RS
.RE
.TP
.B To list all tags in use:
cheat \-T
.RS
.RE
.TP
.B To list available cheatsheets that are tagged as `personal':
cheat \-l \-t \f[I]personal\f[]
.RS
.RE
.TP
.B To search for `ssh' among all cheatsheets, and colorize matches:
cheat \-c \-s \f[I]ssh\f[]
.RS
.RE
.TP
.B To search (by regex) for cheatsheets that contain an IP address:
cheat \-c \-r \-s \f[I]`(?:[0\-9]{1,3}.){3}[0\-9]{1,3}'\f[]
.RS
.RE
.TP
.B To remove (delete) the foo/bar cheatsheet:
cheat \[en]rm \f[I]foo/bar\f[]
.RS
.RE
.SH FILES
.SS Configuration
.PP
\f[B]cheat\f[] is configured via a YAML file that is conventionally
named \f[I]conf.yaml\f[].
\f[B]cheat\f[] will search for \f[I]conf.yaml\f[] in varying locations,
depending upon your platform:
.SS Linux, OSX, and other Unixes
.IP "1." 3
\f[B]CHEAT_CONFIG_PATH\f[]
.IP "2." 3
\f[B]XDG_CONFIG_HOME\f[]/cheat/conf.yaml
.IP "3." 3
\f[B]$HOME\f[]/.config/cheat/conf.yml
.IP "4." 3
\f[B]$HOME\f[]/.cheat/conf.yml
.SS Windows
.IP "1." 3
\f[B]CHEAT_CONFIG_PATH\f[]
.IP "2." 3
\f[B]APPDATA\f[]/cheat/conf.yml
.IP "3." 3
\f[B]PROGRAMDATA\f[]/cheat/conf.yml
.PP
\f[B]cheat\f[] will search in the order specified above.
The first \f[I]conf.yaml\f[] encountered will be respected.
.PP
If \f[B]cheat\f[] cannot locate a config file, it will ask if you'd like
to generate one automatically.
Alternatively, you may also generate a config file manually by running
\f[B]cheat \[en]init\f[] and saving its output to the appropriate
location for your platform.
.SS Cheatpaths
.PP
\f[B]cheat\f[] reads its cheatsheets from \[lq]cheatpaths\[rq], which
are the directories in which cheatsheets are stored.
Cheatpaths may be configured in \f[I]conf.yaml\f[], and viewed via
\f[B]cheat \-d\f[].
.PP
For detailed instructions on how to configure cheatpaths, please refer
to the comments in conf.yml.
.SS Autocompletion
.PP
Autocompletion scripts for \f[B]bash\f[], \f[B]zsh\f[], and
\f[B]fish\f[] are available for download:
.IP \[bu] 2
<https://github.com/cheat/cheat/blob/master/scripts/cheat.bash>
.IP \[bu] 2
<https://github.com/cheat/cheat/blob/master/scripts/cheat.fish>
.IP \[bu] 2
<https://github.com/cheat/cheat/blob/master/scripts/cheat.zsh>
.PP
The \f[B]bash\f[] and \f[B]zsh\f[] scripts provide optional integration
with \f[B]fzf\f[], if the latter is available on your \f[B]PATH\f[].
.PP
The installation process will vary per system and shell configuration,
and thus will not be discussed here.
.SH ENVIRONMENT
.TP
.B \f[B]CHEAT_CONFIG_PATH\f[]
The path at which the config file is available.
If \f[B]CHEAT_CONFIG_PATH\f[] is set, all other config paths will be
ignored.
.RS
.RE
.TP
.B \f[B]CHEAT_USE_FZF\f[]
If set, autocompletion scripts will attempt to integrate with
\f[B]fzf\f[].
.RS
.RE
.SH RETURN VALUES
.IP "0." 3
Successful termination
.IP "1." 3
Application error
.IP "2." 3
Cheatsheet(s) not found
.SH BUGS
.PP
See GitHub issues: <https://github.com/cheat/cheat/issues>
.SH AUTHOR
.PP
Christopher Allen Lane <chris@chris-allen-lane.com>
.SH SEE ALSO
.PP
\f[B]fzf(1)\f[]

192
doc/cheat.1.md Normal file
View File

@ -0,0 +1,192 @@
% CHEAT(1) | General Commands Manual
NAME
====
**cheat** — create and view command-line cheatsheets
SYNOPSIS
========
| **cheat** \[options] \[_CHEATSHEET_]
DESCRIPTION
===========
**cheat** allows you to create and view interactive cheatsheets on the
command-line. It was designed to help remind \*nix system administrators of
options for commands that they use frequently, but not frequently enough to
remember.
OPTIONS
=======
--init
: Print a config file to stdout.
-c, --colorize
: Colorize output.
-d, --directories
: List cheatsheet directories.
-e, --edit=_CHEATSHEET_
: Open _CHEATSHEET_ for editing.
-l, --list
: List available cheatsheets.
-p, --path=_PATH_
: Filter only to sheets found on path _PATH_.
-r, --regex
: Treat search _PHRASE_ as a regular expression.
-s, --search=_PHRASE_
: Search cheatsheets for _PHRASE_.
-t, --tag=_TAG_
: Filter only to sheets tagged with _TAG_.
-T, --tags
: List all tags in use.
-v, --version
: Print the version number.
--rm=_CHEATSHEET_
: Remove (deletes) _CHEATSHEET_.
EXAMPLES
========
To view the foo cheatsheet:
: cheat _foo_
To edit (or create) the foo cheatsheet:
: cheat -e _foo_
To edit (or create) the foo/bar cheatsheet on the 'work' cheatpath:
: cheat -p _work_ -e _foo/bar_
To view all cheatsheet directories:
: cheat -d
To list all available cheatsheets:
: cheat -l
To list all cheatsheets whose titles match 'apt':
: cheat -l _apt_
To list all tags in use:
: cheat -T
To list available cheatsheets that are tagged as 'personal':
: cheat -l -t _personal_
To search for 'ssh' among all cheatsheets, and colorize matches:
: cheat -c -s _ssh_
To search (by regex) for cheatsheets that contain an IP address:
: cheat -c -r -s _'(?:[0-9]{1,3}\.){3}[0-9]{1,3}'_
To remove (delete) the foo/bar cheatsheet:
: cheat --rm _foo/bar_
FILES
=====
Configuration
-------------
**cheat** is configured via a YAML file that is conventionally named
_conf.yaml_. **cheat** will search for _conf.yaml_ in varying locations,
depending upon your platform:
### Linux, OSX, and other Unixes ###
1. **CHEAT_CONFIG_PATH**
2. **XDG_CONFIG_HOME**/cheat/conf.yaml
3. **$HOME**/.config/cheat/conf.yml
4. **$HOME**/.cheat/conf.yml
### Windows ###
1. **CHEAT_CONFIG_PATH**
2. **APPDATA**/cheat/conf.yml
3. **PROGRAMDATA**/cheat/conf.yml
**cheat** will search in the order specified above. The first _conf.yaml_
encountered will be respected.
If **cheat** cannot locate a config file, it will ask if you'd like to generate
one automatically. Alternatively, you may also generate a config file manually
by running **cheat --init** and saving its output to the appropriate location
for your platform.
Cheatpaths
----------
**cheat** reads its cheatsheets from "cheatpaths", which are the directories in
which cheatsheets are stored. Cheatpaths may be configured in _conf.yaml_, and
viewed via **cheat -d**.
For detailed instructions on how to configure cheatpaths, please refer to the
comments in conf.yml.
Autocompletion
--------------
Autocompletion scripts for **bash**, **zsh**, and **fish** are available for
download:
- <https://github.com/cheat/cheat/blob/master/scripts/cheat.bash>
- <https://github.com/cheat/cheat/blob/master/scripts/cheat.fish>
- <https://github.com/cheat/cheat/blob/master/scripts/cheat.zsh>
The **bash** and **zsh** scripts provide optional integration with **fzf**, if
the latter is available on your **PATH**.
The installation process will vary per system and shell configuration, and thus
will not be discussed here.
ENVIRONMENT
===========
**CHEAT_CONFIG_PATH**
: The path at which the config file is available. If **CHEAT_CONFIG_PATH** is
set, all other config paths will be ignored.
**CHEAT_USE_FZF**
: If set, autocompletion scripts will attempt to integrate with **fzf**.
RETURN VALUES
=============
0. Successful termination
1. Application error
2. Cheatsheet(s) not found
BUGS
====
See GitHub issues: <https://github.com/cheat/cheat/issues>
AUTHOR
======
Christopher Allen Lane <chris@chris-allen-lane.com>
SEE ALSO
========
**fzf(1)**

13
go.mod
View File

@ -1,14 +1,17 @@
module github.com/cheat/cheat
go 1.13
go 1.14
require (
github.com/alecthomas/chroma v0.6.9
github.com/alecthomas/chroma v0.10.0
github.com/davecgh/go-spew v1.1.1
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815
github.com/mattn/go-isatty v0.0.10
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b
github.com/kr/text v0.2.0 // indirect
github.com/mattn/go-isatty v0.0.14
github.com/mitchellh/go-homedir v1.1.0
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0
gopkg.in/yaml.v2 v2.2.7
gopkg.in/yaml.v2 v2.4.0
)

75
go.sum
View File

@ -1,64 +1,37 @@
github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0=
github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0=
github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U=
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI=
github.com/alecthomas/chroma v0.6.9 h1:afiCdwnNPo6fcyvoqqsXs78t7NbR9TuW4wDB7NJkcag=
github.com/alecthomas/chroma v0.6.9/go.mod h1:1U/PfCsTALWWYHDnsIQkxEBM0+6LLe0v8+RSVMOwxeY=
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 h1:JHZL0hZKJ1VENNfmXvHbgYlbUOvpzYzvy2aZU5gXVeo=
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0=
github.com/alecthomas/kong v0.1.17-0.20190424132513-439c674f7ae0/go.mod h1:+inYUSluD+p4L8KdviBSgzcqEjUQOfC5fQDRFuc36lI=
github.com/alecthomas/kong v0.2.1-0.20190708041108-0548c6b1afae/go.mod h1:+inYUSluD+p4L8KdviBSgzcqEjUQOfC5fQDRFuc36lI=
github.com/alecthomas/kong-hcl v0.1.8-0.20190615233001-b21fea9723c8/go.mod h1:MRgZdU3vrFd05IQ89AxUZ0aYdF39BYoNFa324SodPCA=
github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897 h1:p9Sln00KOTlrYkxI1zYWl1QLnEqAqEARBEYa8FQnQcY=
github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ=
github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E=
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ=
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk=
github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek=
github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dlclark/regexp2 v1.1.6 h1:CqB4MjHw0MFCDj+PHHjiESmHX+N7t0tJzKvC6M97BRg=
github.com/dlclark/regexp2 v1.1.6/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/gorilla/csrf v1.6.0/go.mod h1:7tSf8kmjNYr7IWDCYhd3U8Ck34iQ/Yw5CJu7bAkHEGI=
github.com/gorilla/handlers v1.4.1/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35 h1:YAFjXN64LMvktoUZH9zgY4lGc/msGN7HQfoSuKCgaDU=
golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191008105621-543471e840be h1:QAcqgptGM8IQBC9K/RC4o+O9YmqEm0diQn9QmZw/0mU=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e h1:CsOuNlbOuf0mzxJIefr6Q4uAUetRUwZE4qt7VfzP+xo=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0 h1:POO/ycCATvegFmVuPpQzZFJ+pGZeX22Ufu6fibxDVjU=
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -2,8 +2,8 @@ package cheatpath
// Cheatpath encapsulates cheatsheet path information
type Cheatpath struct {
Name string `yaml:name`
Path string `yaml:path`
ReadOnly bool `yaml:readonly`
Tags []string `yaml:tags`
Name string `yaml:"name"`
Path string `yaml:"path"`
ReadOnly bool `yaml:"readonly"`
Tags []string `yaml:"tags"`
}

26
internal/config/color.go Normal file
View File

@ -0,0 +1,26 @@
package config
import (
"os"
"github.com/mattn/go-isatty"
)
// Color indicates whether colorization should be applied to the output
func (c *Config) Color(opts map[string]interface{}) bool {
// default to the colorization specified in the configs...
colorize := c.Colorize
// ... however, only apply colorization if we're writing to a tty...
if !isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd()) {
colorize = false
}
// ... *unless* the --colorize flag was passed
if opts["--colorize"] == true {
colorize = true
}
return colorize
}

View File

@ -0,0 +1,22 @@
package config
import (
"testing"
)
// TestColor asserts that colorization rules are properly respected
func TestColor(t *testing.T) {
// mock a config
conf := Config{}
opts := map[string]interface{}{"--colorize": false}
if conf.Color(opts) {
t.Errorf("failed to respect --colorize (false)")
}
opts = map[string]interface{}{"--colorize": true}
if !conf.Color(opts) {
t.Errorf("failed to respect --colorize (true)")
}
}

View File

@ -4,7 +4,10 @@ import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
cp "github.com/cheat/cheat/internal/cheatpath"
@ -14,11 +17,12 @@ import (
// Config encapsulates configuration parameters
type Config struct {
Colorize bool `yaml:colorize`
Editor string `yaml:editor`
Cheatpaths []cp.Cheatpath `yaml:cheatpaths`
Style string `yaml:style`
Formatter string `yaml:formatter`
Colorize bool `yaml:"colorize"`
Editor string `yaml:"editor"`
Cheatpaths []cp.Cheatpath `yaml:"cheatpaths"`
Style string `yaml:"style"`
Formatter string `yaml:"formatter"`
Pager string `yaml:"pager"`
}
// New returns a new Config struct
@ -39,6 +43,24 @@ func New(opts map[string]interface{}, confPath string, resolve bool) (Config, er
return Config{}, fmt.Errorf("could not unmarshal yaml: %v", err)
}
// if a .cheat directory exists locally, append it to the cheatpaths
cwd, err := os.Getwd()
if err != nil {
return Config{}, fmt.Errorf("failed to get cwd: %v", err)
}
local := filepath.Join(cwd, ".cheat")
if _, err := os.Stat(local); err == nil {
path := cp.Cheatpath{
Name: "cwd",
Path: local,
ReadOnly: false,
Tags: []string{},
}
conf.Cheatpaths = append(conf.Cheatpaths, path)
}
// process cheatpaths
for i, cheatpath := range conf.Cheatpaths {
@ -57,14 +79,16 @@ func New(opts map[string]interface{}, confPath string, resolve bool) (Config, er
// `resolve` is a switch that allows us to turn off symlink resolution when
// running the config tests.
if resolve {
expanded, err = filepath.EvalSymlinks(expanded)
evaled, err := filepath.EvalSymlinks(expanded)
if err != nil {
return Config{}, fmt.Errorf(
"failed to resolve symlink: %s, %v",
"failed to resolve symlink: %s: %v",
expanded,
err,
)
}
expanded = evaled
}
conf.Cheatpaths[i].Path = expanded
@ -76,8 +100,22 @@ func New(opts map[string]interface{}, confPath string, resolve bool) (Config, er
conf.Editor = os.Getenv("VISUAL")
} else if os.Getenv("EDITOR") != "" {
conf.Editor = os.Getenv("EDITOR")
} else if runtime.GOOS == "windows" {
conf.Editor = "notepad"
} else {
return Config{}, fmt.Errorf("no editor set")
// try to fall back to `nano`
path, err := exec.LookPath("nano")
if err != nil {
return Config{}, fmt.Errorf("failed to locate nano: %s", err)
}
// use `nano` if we found it
if path != "" {
conf.Editor = "nano"
// otherwise, give up
} else {
return Config{}, fmt.Errorf("no editor set")
}
}
}
@ -88,7 +126,13 @@ func New(opts map[string]interface{}, confPath string, resolve bool) (Config, er
// if a chroma formatter was not provided, set a default
if conf.Formatter == "" {
conf.Formatter = "terminal16m"
conf.Formatter = "terminal"
}
// attempt to fall back to `PAGER` if a pager is not specified in configs
conf.Pager = strings.TrimSpace(conf.Pager)
if conf.Pager == "" && os.Getenv("PAGER") != "" {
conf.Pager = os.Getenv("PAGER")
}
return conf, nil

View File

@ -39,17 +39,17 @@ func TestConfigSuccessful(t *testing.T) {
// assert that the cheatpaths are correct
want := []cheatpath.Cheatpath{
cheatpath.Cheatpath{
Path: filepath.Join(home, ".dotfiles/cheat/community"),
Path: filepath.Join(home, ".dotfiles", "cheat", "community"),
ReadOnly: true,
Tags: []string{"community"},
},
cheatpath.Cheatpath{
Path: filepath.Join(home, ".dotfiles/cheat/work"),
Path: filepath.Join(home, ".dotfiles", "cheat", "work"),
ReadOnly: false,
Tags: []string{"work"},
},
cheatpath.Cheatpath{
Path: filepath.Join(home, ".dotfiles/cheat/personal"),
Path: filepath.Join(home, ".dotfiles", "cheat", "personal"),
ReadOnly: false,
Tags: []string{"personal"},
},
@ -85,8 +85,8 @@ func TestEmptyEditor(t *testing.T) {
// initialize a config
conf, err := New(map[string]interface{}{}, mock.Path("conf/empty.yml"), false)
if err == nil {
t.Errorf("failed to return an error on empty editor")
if err != nil {
t.Errorf("failed to initialize test: %v", err)
}
// set editor, and assert that it is respected

24
internal/config/init.go Normal file
View File

@ -0,0 +1,24 @@
package config
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
)
// Init initializes a config file
func Init(confpath string, configs string) error {
// assert that the config directory exists
if err := os.MkdirAll(filepath.Dir(confpath), 0755); err != nil {
return fmt.Errorf("failed to create directory: %v", err)
}
// write the config file
if err := ioutil.WriteFile(confpath, []byte(configs), 0644); err != nil {
return fmt.Errorf("failed to create file: %v", err)
}
return nil
}

View File

@ -0,0 +1,38 @@
package config
import (
"io/ioutil"
"os"
"testing"
)
// TestInit asserts that configs are properly initialized
func TestInit(t *testing.T) {
// initialize a temporary config file
confFile, err := ioutil.TempFile("", "cheat-test")
if err != nil {
t.Errorf("failed to create temp file: %v", err)
}
// clean up the temp file
defer os.Remove(confFile.Name())
// initialize the config file
conf := "mock config data"
if err = Init(confFile.Name(), conf); err != nil {
t.Errorf("failed to init config file: %v", err)
}
// read back the config file contents
bytes, err := ioutil.ReadFile(confFile.Name())
if err != nil {
t.Errorf("failed to read config file: %v", err)
}
// assert that the contents were written correctly
got := string(bytes)
if got != conf {
t.Errorf("failed to write configs: want: %s, got: %s", conf, got)
}
}

View File

@ -3,58 +3,10 @@ package config
import (
"fmt"
"os"
"path"
"github.com/mitchellh/go-homedir"
)
// Path returns the config file path
func Path(sys string) (string, error) {
var paths []string
// if CHEAT_CONFIG_PATH is set, return it
if os.Getenv("CHEAT_CONFIG_PATH") != "" {
// expand ~
expanded, err := homedir.Expand(os.Getenv("CHEAT_CONFIG_PATH"))
if err != nil {
return "", fmt.Errorf("failed to expand ~: %v", err)
}
return expanded, nil
// OSX config paths
} else if sys == "darwin" {
paths = []string{
path.Join(os.Getenv("XDG_CONFIG_HOME"), "/cheat/conf.yml"),
path.Join(os.Getenv("HOME"), ".config/cheat/conf.yml"),
path.Join(os.Getenv("HOME"), ".cheat/conf.yml"),
}
// Linux config paths
} else if sys == "linux" {
paths = []string{
path.Join(os.Getenv("XDG_CONFIG_HOME"), "/cheat/conf.yml"),
path.Join(os.Getenv("HOME"), ".config/cheat/conf.yml"),
path.Join(os.Getenv("HOME"), ".cheat/conf.yml"),
"/etc/cheat/conf.yml",
}
// Windows config paths
} else if sys == "windows" {
paths = []string{
fmt.Sprintf("%s/cheat/conf.yml", os.Getenv("APPDATA")),
fmt.Sprintf("%s/cheat/conf.yml", os.Getenv("PROGRAMDATA")),
}
// Unsupported platforms
} else {
return "", fmt.Errorf("unsupported os: %s", sys)
}
func Path(paths []string) (string, error) {
// check if the config file exists on any paths
for _, p := range paths {

View File

@ -0,0 +1,53 @@
package config
import (
"io/ioutil"
"os"
"testing"
)
// TestPathConfigNotExists asserts that `Path` identifies non-existent config
// files
func TestPathConfigNotExists(t *testing.T) {
// package (invalid) cheatpaths
paths := []string{"/cheat-test-conf-does-not-exist"}
// assert
if _, err := Path(paths); err == nil {
t.Errorf("failed to identify non-existent config file")
}
}
// TestPathConfigExists asserts that `Path` identifies existent config files
func TestPathConfigExists(t *testing.T) {
// initialize a temporary config file
confFile, err := ioutil.TempFile("", "cheat-test")
if err != nil {
t.Errorf("failed to create temp file: %v", err)
}
// clean up the temp file
defer os.Remove(confFile.Name())
// package cheatpaths
paths := []string{
"/cheat-test-conf-does-not-exist",
confFile.Name(),
}
// assert
got, err := Path(paths)
if err != nil {
t.Errorf("failed to identify config file: %v", err)
}
if got != confFile.Name() {
t.Errorf(
"failed to return config path: want: %s, got: %s",
confFile.Name(),
got,
)
}
}

54
internal/config/paths.go Normal file
View File

@ -0,0 +1,54 @@
package config
import (
"fmt"
"path/filepath"
"github.com/mitchellh/go-homedir"
)
// Paths returns config file paths that are appropriate for the operating
// system
func Paths(
sys string,
home string,
envvars map[string]string,
) ([]string, error) {
// if `CHEAT_CONFIG_PATH` is set, expand ~ and return it
if confpath, ok := envvars["CHEAT_CONFIG_PATH"]; ok {
// expand ~
expanded, err := homedir.Expand(confpath)
if err != nil {
return []string{}, fmt.Errorf("failed to expand ~: %v", err)
}
return []string{expanded}, nil
}
switch sys {
case "android", "darwin", "linux", "freebsd":
paths := []string{}
// don't include the `XDG_CONFIG_HOME` path if that envvar is not set
if xdgpath, ok := envvars["XDG_CONFIG_HOME"]; ok {
paths = append(paths, filepath.Join(xdgpath, "cheat", "conf.yml"))
}
paths = append(paths, []string{
filepath.Join(home, ".config", "cheat", "conf.yml"),
filepath.Join(home, ".cheat", "conf.yml"),
"/etc/cheat/conf.yml",
}...)
return paths, nil
case "windows":
return []string{
filepath.Join(envvars["APPDATA"], "cheat", "conf.yml"),
filepath.Join(envvars["PROGRAMDATA"], "cheat", "conf.yml"),
}, nil
default:
return []string{}, fmt.Errorf("unsupported os: %s", sys)
}
}

View File

@ -0,0 +1,176 @@
package config
import (
"reflect"
"testing"
"github.com/davecgh/go-spew/spew"
)
// TestValidatePathsNix asserts that the proper config paths are returned on
// *nix platforms
func TestValidatePathsNix(t *testing.T) {
// mock the user's home directory
home := "/home/foo"
// mock some envvars
envvars := map[string]string{
"XDG_CONFIG_HOME": "/home/bar",
}
// specify the platforms to test
oses := []string{
"android",
"darwin",
"freebsd",
"linux",
}
// test each *nix os
for _, os := range oses {
// get the paths for the platform
paths, err := Paths(os, home, envvars)
if err != nil {
t.Errorf("paths returned an error: %v", err)
}
// specify the expected output
want := []string{
"/home/bar/cheat/conf.yml",
"/home/foo/.config/cheat/conf.yml",
"/home/foo/.cheat/conf.yml",
"/etc/cheat/conf.yml",
}
// assert that output matches expectations
if !reflect.DeepEqual(paths, want) {
t.Errorf(
"failed to return expected paths: want:\n%s, got:\n%s",
spew.Sdump(want),
spew.Sdump(paths),
)
}
}
}
// TestValidatePathsNixNoXDG asserts that the proper config paths are returned
// on *nix platforms when `XDG_CONFIG_HOME is not set
func TestValidatePathsNixNoXDG(t *testing.T) {
// mock the user's home directory
home := "/home/foo"
// mock some envvars
envvars := map[string]string{}
// specify the platforms to test
oses := []string{
"darwin",
"freebsd",
"linux",
}
// test each *nix os
for _, os := range oses {
// get the paths for the platform
paths, err := Paths(os, home, envvars)
if err != nil {
t.Errorf("paths returned an error: %v", err)
}
// specify the expected output
want := []string{
"/home/foo/.config/cheat/conf.yml",
"/home/foo/.cheat/conf.yml",
"/etc/cheat/conf.yml",
}
// assert that output matches expectations
if !reflect.DeepEqual(paths, want) {
t.Errorf(
"failed to return expected paths: want:\n%s, got:\n%s",
spew.Sdump(want),
spew.Sdump(paths),
)
}
}
}
// TestValidatePathsWindows asserts that the proper config paths are returned
// on Windows platforms
func TestValidatePathsWindows(t *testing.T) {
// mock the user's home directory
home := "not-used-on-windows"
// mock some envvars
envvars := map[string]string{
"APPDATA": "/apps",
"PROGRAMDATA": "/programs",
}
// get the paths for the platform
paths, err := Paths("windows", home, envvars)
if err != nil {
t.Errorf("paths returned an error: %v", err)
}
// specify the expected output
want := []string{
"/apps/cheat/conf.yml",
"/programs/cheat/conf.yml",
}
// assert that output matches expectations
if !reflect.DeepEqual(paths, want) {
t.Errorf(
"failed to return expected paths: want:\n%s, got:\n%s",
spew.Sdump(want),
spew.Sdump(paths),
)
}
}
// TestValidatePathsUnsupported asserts that an error is returned on
// unsupported platforms
func TestValidatePathsUnsupported(t *testing.T) {
_, err := Paths("unsupported", "", map[string]string{})
if err == nil {
t.Errorf("failed to return error on unsupported platform")
}
}
// TestValidatePathsCheatConfigPath asserts that the proper config path is
// returned when `CHEAT_CONFIG_PATH` is explicitly specified.
func TestValidatePathsCheatConfigPath(t *testing.T) {
// mock the user's home directory
home := "/home/foo"
// mock some envvars
envvars := map[string]string{
"XDG_CONFIG_HOME": "/home/bar",
"CHEAT_CONFIG_PATH": "/home/baz/conf.yml",
}
// get the paths for the platform
paths, err := Paths("linux", home, envvars)
if err != nil {
t.Errorf("paths returned an error: %v", err)
}
// specify the expected output
want := []string{
"/home/baz/conf.yml",
}
// assert that output matches expectations
if !reflect.DeepEqual(paths, want) {
t.Errorf(
"failed to return expected paths: want:\n%s, got:\n%s",
spew.Sdump(want),
spew.Sdump(paths),
)
}
}

18
internal/display/faint.go Normal file
View File

@ -0,0 +1,18 @@
package display
import (
"fmt"
"github.com/cheat/cheat/internal/config"
)
// Faint returns an faint string
func Faint(str string, conf config.Config) string {
// make `str` faint only if colorization has been requested
if conf.Colorize {
return fmt.Sprintf(fmt.Sprintf("\033[2m%s\033[0m", str))
}
// otherwise, return the string unmodified
return str
}

View File

@ -0,0 +1,27 @@
package display
import (
"testing"
"github.com/cheat/cheat/internal/config"
)
// TestFaint asserts that Faint applies faint formatting
func TestFaint(t *testing.T) {
// case: apply colorization
conf := config.Config{Colorize: true}
want := "\033[2mfoo\033[0m"
got := Faint("foo", conf)
if want != got {
t.Errorf("failed to faint: want: %s, got: %s", want, got)
}
// case: do not apply colorization
conf.Colorize = false
want = "foo"
got = Faint("foo", conf)
if want != got {
t.Errorf("failed to faint: want: %s, got: %s", want, got)
}
}

View File

@ -0,0 +1,21 @@
package display
import (
"fmt"
"strings"
)
// Indent prepends each line of a string with a tab
func Indent(str string) string {
// trim superfluous whitespace
str = strings.TrimSpace(str)
// prepend each line with a tab character
out := ""
for _, line := range strings.Split(str, "\n") {
out += fmt.Sprintf("\t%s\n", line)
}
return out
}

View File

@ -0,0 +1,12 @@
package display
import "testing"
// TestIndent asserts that Indent prepends a tab to each line
func TestIndent(t *testing.T) {
got := Indent("foo\nbar\nbaz")
want := "\tfoo\n\tbar\n\tbaz\n"
if got != want {
t.Errorf("failed to indent: want: %s, got: %s", want, got)
}
}

View File

@ -0,0 +1,8 @@
package display
import "fmt"
// Underline returns an underlined string
func Underline(str string) string {
return fmt.Sprintf(fmt.Sprintf("\033[4m%s\033[0m", str))
}

View File

@ -0,0 +1,14 @@
package display
import (
"testing"
)
// TestUnderline asserts that Underline applies underline formatting
func TestUnderline(t *testing.T) {
want := "\033[4mfoo\033[0m"
got := Underline("foo")
if want != got {
t.Errorf("failed to underline: want: %s, got: %s", want, got)
}
}

37
internal/display/write.go Normal file
View File

@ -0,0 +1,37 @@
package display
import (
"fmt"
"os"
"os/exec"
"strings"
"github.com/cheat/cheat/internal/config"
)
// Write writes output either directly to stdout, or through a pager,
// depending upon configuration.
func Write(out string, conf config.Config) {
// if no pager was configured, print the output to stdout and exit
if conf.Pager == "" {
fmt.Print(out)
os.Exit(0)
}
// otherwise, pipe output through the pager
parts := strings.Split(conf.Pager, " ")
pager := parts[0]
args := parts[1:]
// run the pager
cmd := exec.Command(pager, args...)
cmd.Stdin = strings.NewReader(out)
cmd.Stdout = os.Stdout
// handle errors
err := cmd.Run()
if err != nil {
fmt.Fprintln(os.Stderr, fmt.Sprintf("failed to write to pager: %v", err))
os.Exit(1)
}
}

View File

@ -1,6 +1,7 @@
package frontmatter
import (
"fmt"
"strings"
"gopkg.in/yaml.v1"
@ -28,7 +29,16 @@ func Parse(markdown string) (string, Frontmatter, error) {
// otherwise, split the frontmatter and cheatsheet text
parts := strings.SplitN(markdown, delim, 3)
err := yaml.Unmarshal([]byte(parts[1]), &fm)
return strings.TrimSpace(parts[2]), fm, err
// return an error if the frontmatter parses into the wrong number of parts
if len(parts) != 3 {
return markdown, fm, fmt.Errorf("failed to delimit frontmatter")
}
// return an error if the YAML cannot be unmarshalled
if err := yaml.Unmarshal([]byte(parts[1]), &fm); err != nil {
return markdown, fm, fmt.Errorf("failed to unmarshal frontmatter: %v", err)
}
return strings.TrimSpace(parts[2]), fm, nil
}

View File

@ -69,3 +69,27 @@ func TestHasNoFrontmatter(t *testing.T) {
t.Errorf("failed to parse tags: want: len 0, got: len %d", len(fm.Tags))
}
}
// TestHasInvalidFrontmatter asserts that markdown is properly parsed when it
// contains invalid frontmatter
func TestHasInvalidFrontmatter(t *testing.T) {
// stub our cheatsheet content (with invalid frontmatter)
markdown := `---
syntax: go
tags: [ test ]
To foo the bar: baz`
// parse the frontmatter
text, _, err := Parse(markdown)
// assert that an error was returned
if err == nil {
t.Error("failed to error on invalid frontmatter")
}
// assert that the "raw" markdown was returned
if text != markdown {
t.Errorf("failed to parse text: want: %s, got: %s", markdown, text)
}
}

View File

@ -0,0 +1,24 @@
package installer
import (
"fmt"
"os"
"os/exec"
)
const cloneURL = "https://github.com/cheat/cheatsheets.git"
// clone clones the community cheatsheets
func clone(path string) error {
// perform the clone in a shell
cmd := exec.Command("git", "clone", cloneURL, path)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
return fmt.Errorf("failed to clone cheatsheets: %v", err)
}
return nil
}

View File

@ -0,0 +1,37 @@
package installer
import (
"bufio"
"fmt"
"os"
"strings"
)
// Prompt prompts the user for a answer
func Prompt(prompt string, def bool) (bool, error) {
// initialize a line reader
reader := bufio.NewReader(os.Stdin)
// display the prompt
fmt.Print(fmt.Sprintf("%s: ", prompt))
// read the answer
ans, err := reader.ReadString('\n')
if err != nil {
return false, fmt.Errorf("failed to parse input: %v", err)
}
// normalize the answer
ans = strings.ToLower(strings.TrimSpace(ans))
// return the appropriate response
switch ans {
case "y":
return true, nil
case "":
return def, nil
default:
return false, nil
}
}

57
internal/installer/run.go Normal file
View File

@ -0,0 +1,57 @@
package installer
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/cheat/cheat/internal/config"
)
// Run runs the installer
func Run(configs string, confpath string) error {
// determine the appropriate paths for config data and (optional) community
// cheatsheets based on the user's platform
confdir := filepath.Dir(confpath)
// create paths for community and personal cheatsheets
community := filepath.Join(confdir, "cheatsheets", "community")
personal := filepath.Join(confdir, "cheatsheets", "personal")
// template the above paths into the default configs
configs = strings.Replace(configs, "COMMUNITY_PATH", community, -1)
configs = strings.Replace(configs, "PERSONAL_PATH", personal, -1)
// prompt the user to download the community cheatsheets
yes, err := Prompt(
"Would you like to download the community cheatsheets? [Y/n]",
true,
)
if err != nil {
return fmt.Errorf("failed to prompt: %v", err)
}
// clone the community cheatsheets if so instructed
if yes {
// clone the community cheatsheets
fmt.Printf("Cloning community cheatsheets to %s.\n", community)
if err := clone(community); err != nil {
return fmt.Errorf("failed to clone cheatsheets: %v", err)
}
// also create a directory for personal cheatsheets
fmt.Printf("Cloning personal cheatsheets to %s.\n", personal)
if err := os.MkdirAll(personal, os.ModePerm); err != nil {
return fmt.Errorf("failed to create directory: %v", err)
}
}
// the config file does not exist, so we'll try to create one
if err = config.Init(confpath, configs); err != nil {
return fmt.Errorf("failed to create config file: %v", err)
}
return nil
}

View File

@ -13,7 +13,7 @@ func Path(filename string) string {
// determine the path of this file during runtime
_, thisfile, _, _ := runtime.Caller(0)
// compute the config path
// compute the mock path
file, err := filepath.Abs(
path.Join(
filepath.Dir(thisfile),
@ -22,7 +22,7 @@ func Path(filename string) string {
),
)
if err != nil {
panic(fmt.Errorf("failed to resolve config path: %v", err))
panic(fmt.Errorf("failed to resolve mock path: %v", err))
}
return file

View File

@ -0,0 +1,37 @@
package sheet
import (
"bytes"
"github.com/cheat/cheat/internal/config"
"github.com/alecthomas/chroma/quick"
)
// Colorize applies syntax-highlighting to a cheatsheet's Text.
func (s *Sheet) Colorize(conf config.Config) {
// if the syntax was not specified, default to bash
lex := s.Syntax
if lex == "" {
lex = "bash"
}
// write colorized text into a buffer
var buf bytes.Buffer
err := quick.Highlight(
&buf,
s.Text,
lex,
conf.Formatter,
conf.Style,
)
// if colorization somehow failed, do nothing
if err != nil {
return
}
// otherwise, swap the cheatsheet's Text with its colorized equivalent
s.Text = buf.String()
}

View File

@ -0,0 +1,34 @@
package sheet
import (
"testing"
"github.com/cheat/cheat/internal/config"
)
// TestColorize asserts that syntax-highlighting is correctly applied
func TestColorize(t *testing.T) {
// mock configs
conf := config.Config{
Formatter: "terminal16m",
Style: "solarized-dark",
}
// mock a sheet
s := Sheet{
Text: "echo 'foo'",
}
// colorize the sheet text
s.Colorize(conf)
// initialize expectations
want := "echo"
want += " 'foo'"
// assert
if s.Text != want {
t.Errorf("failed to colorize sheet: want: %s, got: %s", want, s.Text)
}
}

View File

@ -4,7 +4,7 @@ import (
"fmt"
"io"
"os"
"path"
"path/filepath"
)
// Copy copies a cheatsheet to a new location
@ -22,7 +22,7 @@ func (s *Sheet) Copy(dest string) error {
defer infile.Close()
// create any necessary subdirectories
dirs := path.Dir(dest)
dirs := filepath.Dir(dest)
if dirs != "." {
if err := os.MkdirAll(dirs, 0755); err != nil {
return fmt.Errorf("failed to create directory: %s, %v", dirs, err)

View File

@ -25,7 +25,7 @@ func TestCopyFlat(t *testing.T) {
}
// mock a cheatsheet struct
sheet, err := New("foo", src.Name(), []string{}, false)
sheet, err := New("foo", "community", src.Name(), []string{}, false)
if err != nil {
t.Errorf("failed to init cheatsheet: %v", err)
}
@ -72,7 +72,13 @@ func TestCopyDeep(t *testing.T) {
}
// mock a cheatsheet struct
sheet, err := New("/cheat-tests/alpha/bravo/foo", src.Name(), []string{}, false)
sheet, err := New(
"/cheat-tests/alpha/bravo/foo",
"community",
src.Name(),
[]string{},
false,
)
if err != nil {
t.Errorf("failed to init cheatsheet: %v", err)
}

View File

@ -1,7 +0,0 @@
package sheet
// Match encapsulates search matches within cheatsheets
type Match struct {
Line int
Text string
}

View File

@ -3,43 +3,22 @@ package sheet
import (
"regexp"
"strings"
"github.com/mgutz/ansi"
)
// Search searches for regexp matches in a cheatsheet's text, and optionally
// colorizes matching strings.
func (s *Sheet) Search(reg *regexp.Regexp, colorize bool) []Match {
// Search returns lines within a sheet's Text that match the search regex
func (s *Sheet) Search(reg *regexp.Regexp) string {
// record matches
matches := []Match{}
matches := ""
// search through the cheatsheet's text line by line
// TODO: searching line-by-line is surely the "naive" approach. Revisit this
// later with an eye for performance improvements.
for linenum, line := range strings.Split(s.Text, "\n") {
for _, line := range strings.Split(s.Text, "\n\n") {
// exit early if the line doesn't match the regex
if !reg.MatchString(line) {
continue
if reg.MatchString(line) {
matches += line + "\n\n"
}
// init the match
m := Match{
Line: linenum + 1,
Text: strings.TrimSpace(line),
}
// colorize the matching text if so configured
if colorize {
m.Text = reg.ReplaceAllStringFunc(m.Text, func(matched string) string {
return ansi.Color(matched, "red+b")
})
}
// record the match
matches = append(matches, m)
}
return matches
return strings.TrimSpace(matches)
}

View File

@ -4,8 +4,6 @@ import (
"reflect"
"regexp"
"testing"
"github.com/davecgh/go-spew/spew"
)
// TestSearchNoMatch ensures that the expected output is returned when no
@ -24,21 +22,21 @@ func TestSearchNoMatch(t *testing.T) {
}
// search the sheet
matches := sheet.Search(reg, false)
matches := sheet.Search(reg)
// assert that no matches were found
if len(matches) != 0 {
t.Errorf("failure: expected no matches: got: %s", spew.Sdump(matches))
if matches != "" {
t.Errorf("failure: expected no matches: got: %s", matches)
}
}
// TestSearchSingleMatchNoColor asserts that the expected output is returned
// when a single match is returned, and no colorization is applied.
func TestSearchSingleMatchNoColor(t *testing.T) {
// TestSearchSingleMatch asserts that the expected output is returned
// when a single match is returned
func TestSearchSingleMatch(t *testing.T) {
// mock a cheatsheet
sheet := Sheet{
Text: "The quick brown fox\njumped over\nthe lazy dog.",
Text: "The quick brown fox\njumped over\n\nthe lazy dog.",
}
// compile the search regex
@ -48,69 +46,28 @@ func TestSearchSingleMatchNoColor(t *testing.T) {
}
// search the sheet
matches := sheet.Search(reg, false)
matches := sheet.Search(reg)
// specify the expected results
want := []Match{
Match{
Line: 1,
Text: "The quick brown fox",
},
}
want := "The quick brown fox\njumped over"
// assert that the correct matches were returned
if !reflect.DeepEqual(matches, want) {
if matches != want {
t.Errorf(
"failed to return expected matches: want:\n%s, got:\n%s",
spew.Sdump(want),
spew.Sdump(matches),
want,
matches,
)
}
}
// TestSearchSingleMatchColorized asserts that the expected output is returned
// when a single match is returned, and colorization is applied
func TestSearchSingleMatchColorized(t *testing.T) {
// TestSearchMultiMatch asserts that the expected output is returned
// when a multiple matches are returned
func TestSearchMultiMatch(t *testing.T) {
// mock a cheatsheet
sheet := Sheet{
Text: "The quick brown fox\njumped over\nthe lazy dog.",
}
// compile the search regex
reg, err := regexp.Compile("(?i)fox")
if err != nil {
t.Errorf("failed to compile regex: %v", err)
}
// search the sheet
matches := sheet.Search(reg, true)
// specify the expected results
want := []Match{
Match{
Line: 1,
Text: "The quick brown \x1b[1;31mfox\x1b[0m",
},
}
// assert that the correct matches were returned
if !reflect.DeepEqual(matches, want) {
t.Errorf(
"failed to return expected matches: want:\n%s, got:\n%s",
spew.Sdump(want),
spew.Sdump(matches),
)
}
}
// TestSearchMultiMatchNoColor asserts that the expected output is returned
// when a multiple matches are returned, and no colorization is applied
func TestSearchMultiMatchNoColor(t *testing.T) {
// mock a cheatsheet
sheet := Sheet{
Text: "The quick brown fox\njumped over\nthe lazy dog.",
Text: "The quick brown fox\n\njumped over\n\nthe lazy dog.",
}
// compile the search regex
@ -120,66 +77,17 @@ func TestSearchMultiMatchNoColor(t *testing.T) {
}
// search the sheet
matches := sheet.Search(reg, false)
matches := sheet.Search(reg)
// specify the expected results
want := []Match{
Match{
Line: 1,
Text: "The quick brown fox",
},
Match{
Line: 3,
Text: "the lazy dog.",
},
}
want := "The quick brown fox\n\nthe lazy dog."
// assert that the correct matches were returned
if !reflect.DeepEqual(matches, want) {
t.Errorf(
"failed to return expected matches: want:\n%s, got:\n%s",
spew.Sdump(want),
spew.Sdump(matches),
)
}
}
// TestSearchMultiMatchColorized asserts that the expected output is returned
// when a multiple matches are returned, and colorization is applied
func TestSearchMultiMatchColorized(t *testing.T) {
// mock a cheatsheet
sheet := Sheet{
Text: "The quick brown fox\njumped over\nthe lazy dog.",
}
// compile the search regex
reg, err := regexp.Compile("(?i)the")
if err != nil {
t.Errorf("failed to compile regex: %v", err)
}
// search the sheet
matches := sheet.Search(reg, true)
// specify the expected results
want := []Match{
Match{
Line: 1,
Text: "\x1b[1;31mThe\x1b[0m quick brown fox",
},
Match{
Line: 3,
Text: "\x1b[1;31mthe\x1b[0m lazy dog.",
},
}
// assert that the correct matches were returned
if !reflect.DeepEqual(matches, want) {
t.Errorf(
"failed to return expected matches: want:\n%s, got:\n%s",
spew.Sdump(want),
spew.Sdump(matches),
want,
matches,
)
}
}

View File

@ -10,17 +10,19 @@ import (
// Sheet encapsulates sheet information
type Sheet struct {
Title string
Path string
Text string
Tags []string
Syntax string
ReadOnly bool
Title string
CheatPath string
Path string
Text string
Tags []string
Syntax string
ReadOnly bool
}
// New initializes a new Sheet
func New(
title string,
cheatpath string,
path string,
tags []string,
readOnly bool,
@ -46,11 +48,12 @@ func New(
// initialize and return a sheet
return Sheet{
Title: title,
Path: path,
Text: text + "\n",
Tags: tags,
Syntax: fm.Syntax,
ReadOnly: readOnly,
Title: title,
CheatPath: cheatpath,
Path: path,
Text: text + "\n",
Tags: tags,
Syntax: fm.Syntax,
ReadOnly: readOnly,
}, nil
}

View File

@ -13,6 +13,7 @@ func TestSheetSuccess(t *testing.T) {
// initialize a sheet
sheet, err := New(
"foo",
"community",
mock.Path("sheet/foo"),
[]string{"alpha", "bravo"},
false,
@ -61,6 +62,7 @@ func TestSheetFailure(t *testing.T) {
// initialize a sheet
_, err := New(
"foo",
"community",
mock.Path("/does-not-exist"),
[]string{"alpha", "bravo"},
false,
@ -69,3 +71,20 @@ func TestSheetFailure(t *testing.T) {
t.Errorf("failed to return an error on unreadable sheet")
}
}
// TestSheetFrontMatterFailure asserts that an error is returned if the sheet's
// frontmatter cannot be parsed.
func TestSheetFrontMatterFailure(t *testing.T) {
// initialize a sheet
_, err := New(
"foo",
"community",
mock.Path("sheet/bad-fm"),
[]string{"alpha", "bravo"},
false,
)
if err == nil {
t.Errorf("failed to return an error on malformed front-matter")
}
}

View File

@ -33,7 +33,7 @@ func Load(cheatpaths []cp.Cheatpath) ([]map[string]sheet.Sheet, error) {
// fail if an error occurred while walking the directory
if err != nil {
return fmt.Errorf("error walking path: %v", err)
return fmt.Errorf("failed to walk path: %v", err)
}
// don't register directories as cheatsheets
@ -59,9 +59,20 @@ func Load(cheatpaths []cp.Cheatpath) ([]map[string]sheet.Sheet, error) {
}
// parse the cheatsheet file into a `sheet` struct
s, err := sheet.New(title, path, cheatpath.Tags, cheatpath.ReadOnly)
s, err := sheet.New(
title,
cheatpath.Name,
path,
cheatpath.Tags,
cheatpath.ReadOnly,
)
if err != nil {
return fmt.Errorf("could not create sheet: %v", err)
return fmt.Errorf(
"failed to load sheet: %s, path: %s, err: %v",
title,
path,
err,
)
}
// register the cheatsheet on its cheatpath, keyed by its title

View File

@ -1,3 +1,62 @@
package sheets
// TODO
import (
"path"
"testing"
"github.com/cheat/cheat/internal/cheatpath"
"github.com/cheat/cheat/internal/mock"
)
// TestLoad asserts that sheets on valid cheatpaths can be loaded successfully
func TestLoad(t *testing.T) {
// mock cheatpaths
cheatpaths := []cheatpath.Cheatpath{
{
Name: "community",
Path: path.Join(mock.Path("cheatsheets"), "community"),
ReadOnly: true,
},
{
Name: "personal",
Path: path.Join(mock.Path("cheatsheets"), "personal"),
ReadOnly: false,
},
}
// load cheatsheets
sheets, err := Load(cheatpaths)
if err != nil {
t.Errorf("failed to load cheatsheets: %v", err)
}
// assert that the correct number of sheets loaded
// (sheet load details are tested in `sheet_test.go`)
want := 4
if len(sheets) != want {
t.Errorf(
"failed to load correct number of cheatsheets: want: %d, got: %d",
want,
len(sheets),
)
}
}
// TestLoadBadPath asserts that an error is returned if a cheatpath is invalid
func TestLoadBadPath(t *testing.T) {
// mock a bad cheatpath
cheatpaths := []cheatpath.Cheatpath{
{
Name: "badpath",
Path: "/cheat/test/path/does/not/exist",
ReadOnly: true,
},
}
// attempt to load the cheatpath
if _, err := Load(cheatpaths); err == nil {
t.Errorf("failed to reject invalid cheatpath")
}
}

View File

@ -9,7 +9,7 @@ import (
"github.com/cheat/cheat/internal/sheet"
)
// TestTags asserts that cheetsheet tags are properly returned
// TestTags asserts that cheatsheet tags are properly returned
func TestTags(t *testing.T) {
// mock cheatsheets available on multiple cheatpaths

View File

View File

@ -0,0 +1,4 @@
---
tags: [ community ]
---
This is the bar cheatsheet.

View File

@ -0,0 +1,4 @@
---
tags: [ community ]
---
This is the foo cheatsheet.

View File

@ -0,0 +1,4 @@
---
tags: [ personal ]
---
This is the bat cheatsheet.

View File

@ -0,0 +1,4 @@
---
tags: [ personal ]
---
This is the baz cheatsheet.

4
mocks/sheet/bad-fm Normal file
View File

@ -0,0 +1,4 @@
---
syntax: sh
This is malformed frontmatter.

View File

@ -1,9 +0,0 @@
function _cheat_autocomplete {
sheets=$(cheat -l | sed -n '2,$p'|cut -d' ' -f1)
COMPREPLY=()
if [ $COMP_CWORD = 1 ]; then
COMPREPLY=(`compgen -W "$sheets" -- $2`)
fi
}
complete -F _cheat_autocomplete cheat

74
scripts/cheat.bash Executable file
View File

@ -0,0 +1,74 @@
# cheat(1) completion -*- shell-script -*-
# generate cheatsheet completions, optionally using `fzf`
_cheat_complete_cheatsheets()
{
if [[ "$CHEAT_USE_FZF" = true ]]; then
FZF_COMPLETION_TRIGGER='' _fzf_complete "--no-multi" "$@" < <(
cheat -l | tail -n +2 | cut -d' ' -f1
)
else
COMPREPLY=( $(compgen -W "$(cheat -l | tail -n +2 | cut -d' ' -f1)" -- "$cur") )
fi
}
# generate tag completions, optionally using `fzf`
_cheat_complete_tags()
{
if [ "$CHEAT_USE_FZF" = true ]; then
FZF_COMPLETION_TRIGGER='' _fzf_complete "--no-multi" "$@" < <(cheat -T)
else
COMPREPLY=( $(compgen -W "$(cheat -T)" -- "$cur") )
fi
}
# implement the `cheat` autocompletions
_cheat()
{
local cur prev words cword split
_init_completion -s || return
# complete options that are currently being typed: `--col` => `--colorize`
if [[ $cur == -* ]]; then
COMPREPLY=( $(compgen -W '$(_parse_help "$1" | sed "s/=//g")' -- "$cur") )
[[ $COMPREPLY == *= ]] && compopt -o nospace
return
fi
# implement completions
case $prev in
--colorize|-c|\
--directories|-d|\
--init|\
--regex|-r|\
--search|-s|\
--tags|-T|\
--version|-v)
# noop the above, which should implement no completions
;;
--edit|-e)
_cheat_complete_cheatsheets
;;
--list|-l)
_cheat_complete_cheatsheets
;;
--path|-p)
COMPREPLY=( $(compgen -W "$(cheat -d | cut -d':' -f1)" -- "$cur") )
;;
--rm)
_cheat_complete_cheatsheets
;;
--tag|-t)
_cheat_complete_tags
;;
*)
_cheat_complete_cheatsheets
;;
esac
$split && return
} &&
complete -F _cheat cheat
# ex: filetype=sh

65
scripts/cheat.zsh Executable file
View File

@ -0,0 +1,65 @@
#compdef cheat
local cheats taglist pathlist
_cheat_complete_personal_cheatsheets()
{
cheats=("${(f)$(cheat -l -t personal | tail -n +2 | cut -d' ' -f1)}")
_describe -t cheats 'cheats' cheats
}
_cheat_complete_full_cheatsheets()
{
cheats=("${(f)$(cheat -l | tail -n +2 | cut -d' ' -f1)}")
_describe -t cheats 'cheats' cheats
}
_cheat_complete_tags()
{
taglist=("${(f)$(cheat -T)}")
_describe -t taglist 'taglist' taglist
}
_cheat_complete_paths()
{
pathlist=("${(f)$(cheat -d | cut -d':' -f1)}")
_describe -t pathlist 'pathlist' pathlist
}
_cheat() {
_arguments -C \
'(--init)--init[Write a default config file to stdout]: :->none' \
'(-c --colorize)'{-c,--colorize}'[Colorize output]: :->none' \
'(-d --directories)'{-d,--directories}'[List cheatsheet directories]: :->none' \
'(-e --edit)'{-e,--edit}'[Edit <sheet>]: :->personal' \
'(-l --list)'{-l,--list}'[List cheatsheets]: :->full' \
'(-p --path)'{-p,--path}'[Return only sheets found on path <name>]: :->pathlist' \
'(-r --regex)'{-r,--regex}'[Treat search <phrase> as a regex]: :->none' \
'(-s --search)'{-s,--search}'[Search cheatsheets for <phrase>]: :->none' \
'(-t --tag)'{-t,--tag}'[Return only sheets matching <tag>]: :->taglist' \
'(-T --tags)'{-T,--tags}'[List all tags in use]: :->none' \
'(-v --version)'{-v,--version}'[Print the version number]: :->none' \
'(--rm)--rm[Remove (delete) <sheet>]: :->personal'
case $state in
(none)
;;
(full)
_cheat_complete_full_cheatsheets
;;
(personal)
_cheat_complete_personal_cheatsheets
;;
(taglist)
_cheat_complete_tags
;;
(pathlist)
_cheat_complete_paths
;;
(*)
;;
esac
}
compdef _cheat cheat

View File

@ -1,11 +0,0 @@
#!/bin/bash
# This function enables you to choose a cheatsheet to view by selecting output
# from `cheat -l`. `source` it in your shell to enable it. (Consider renaming
# or aliasing it to something convenient.)
# Arguments passed to this function (like --color) will be passed to the second
# invokation of `cheat`.
function cheat-fzf {
eval `cheat -l | tail -n +2 | fzf | awk -v vars="$*" '{ print "cheat " $1 " -t " $3, vars }'`
}

46
scripts/git/cheatsheets Executable file
View File

@ -0,0 +1,46 @@
#!/bin/sh -e
pull() {
for d in `cheat -d | awk '{print $2}'`;
do
echo "Update $d"
cd "$d"
[ -d ".git" ] && git pull || :
done
echo
echo "Finished update"
}
push() {
for d in `cheat -d | grep -v "community" | awk '{print $2}'`;
do
cd "$d"
if [ -d ".git" ]
then
echo "Push modifications $d"
files=$(git ls-files -mo | tr '\n' ' ')
git add -A && git commit -m "Edited files: $files" && git push || :
else
echo "$(pwd) is not a git managed folder"
echo "First connect this to your personal git repository"
fi
done
echo
echo "Finished push operation"
}
if [ "$1" = "pull" ]; then
pull
elif [ "$1" = "push" ]; then
push
else
echo "Usage:
# pull changes
cheatsheets pull
# push changes
cheatsheets push"
fi

View File

@ -18,6 +18,24 @@ linters:
- funlen
- godox
- wsl
- gomnd
- gocognit
- goerr113
- nolintlint
- testpackage
- godot
- nestif
- paralleltest
- nlreturn
- cyclop
- exhaustivestruct
- gci
- gofumpt
- errorlint
- exhaustive
- ifshort
- wrapcheck
- stylecheck
linters-settings:
govet:
@ -29,6 +47,11 @@ linters-settings:
goconst:
min-len: 8
min-occurrences: 3
forbidigo:
forbid:
- (Must)?NewLexer
exclude_godoc_examples: false
issues:
max-per-linter: 0
@ -49,3 +72,5 @@ issues:
- '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`'

View File

@ -3,28 +3,34 @@ release:
github:
owner: alecthomas
name: chroma
brew:
install: bin.install "chroma"
brews:
-
install: bin.install "chroma"
env:
- CGO_ENABLED=0
builds:
- goos:
- linux
- darwin
- windows
- linux
- darwin
- windows
goarch:
- amd64
- "386"
- arm64
- amd64
- "386"
goarm:
- "6"
main: ./cmd/chroma/main.go
- "6"
dir: ./cmd/chroma
main: .
ldflags: -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}
binary: chroma
archive:
format: tar.gz
name_template: '{{ .Binary }}-{{ .Version }}-{{ .Os }}-{{ .Arch }}{{ if .Arm }}v{{
archives:
-
format: tar.gz
name_template: '{{ .Binary }}-{{ .Version }}-{{ .Os }}-{{ .Arch }}{{ if .Arm }}v{{
.Arm }}{{ end }}'
files:
- COPYING
- README*
files:
- COPYING
- README*
snapshot:
name_template: SNAPSHOT-{{ .Commit }}
checksum:

View File

@ -1,10 +0,0 @@
sudo: false
language: go
script:
- go test -v ./...
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s v1.20.0
- ./bin/golangci-lint run
- git clean -fdx .
after_success:
curl -sL https://git.io/goreleaser | bash && goreleaser

19
vendor/github.com/alecthomas/chroma/Makefile generated vendored Normal file
View File

@ -0,0 +1,19 @@
.PHONY: chromad upload all
VERSION ?= $(shell git describe --tags --dirty --always)
all: README.md tokentype_string.go
README.md: lexers/*/*.go
./table.py
tokentype_string.go: types.go
go generate
chromad:
rm -f chromad
(export CGOENABLED=0 GOOS=linux GOARCH=amd64; cd ./cmd/chromad && go build -ldflags="-X 'main.version=$(VERSION)'" -o ../../chromad .)
upload: chromad
scp chromad root@swapoff.org: && \
ssh root@swapoff.org 'install -m755 ./chromad /srv/http/swapoff.org/bin && service chromad restart'

View File

@ -1,4 +1,5 @@
# Chroma — A general purpose syntax highlighter in pure Go [![Golang Documentation](https://godoc.org/github.com/alecthomas/chroma?status.svg)](https://godoc.org/github.com/alecthomas/chroma) [![Build Status](https://travis-ci.org/alecthomas/chroma.svg)](https://travis-ci.org/alecthomas/chroma) [![Gitter chat](https://badges.gitter.im/alecthomas.svg)](https://gitter.im/alecthomas/Lobby)
# Chroma — A general purpose syntax highlighter in pure Go
[![Golang Documentation](https://godoc.org/github.com/alecthomas/chroma?status.svg)](https://godoc.org/github.com/alecthomas/chroma) [![CI](https://github.com/alecthomas/chroma/actions/workflows/ci.yml/badge.svg)](https://github.com/alecthomas/chroma/actions/workflows/ci.yml) [![Slack chat](https://img.shields.io/static/v1?logo=slack&style=flat&label=slack&color=green&message=gophers)](https://invite.slack.golangbridge.org/)
> **NOTE:** As Chroma has just been released, its API is still in flux. That said, the high-level interface should not change significantly.
@ -36,29 +37,30 @@ translators for Pygments lexers and styles.
Prefix | Language
:----: | --------
A | ABAP, ABNF, ActionScript, ActionScript 3, Ada, Angular2, ANTLR, ApacheConf, APL, AppleScript, Arduino, Awk
B | Ballerina, Base Makefile, Bash, Batchfile, BlitzBasic, BNF, Brainfuck
C | C, C#, C++, Cap'n Proto, Cassandra CQL, Ceylon, CFEngine3, cfstatement, ChaiScript, Cheetah, Clojure, CMake, COBOL, CoffeeScript, Common Lisp, Coq, Crystal, CSS, Cython
D | D, Dart, Diff, Django/Jinja, Docker, DTD
B | Ballerina, Base Makefile, Bash, Batchfile, BibTeX, Bicep, BlitzBasic, BNF, Brainfuck
C | C, C#, C++, Caddyfile, Caddyfile Directives, Cap'n Proto, Cassandra CQL, Ceylon, CFEngine3, cfstatement, ChaiScript, Cheetah, Clojure, CMake, COBOL, CoffeeScript, Common Lisp, Coq, Crystal, CSS, Cython
D | D, Dart, Diff, Django/Jinja, Docker, DTD, Dylan
E | EBNF, Elixir, Elm, EmacsLisp, Erlang
F | Factor, Fish, Forth, Fortran, FSharp
G | GAS, GDScript, Genshi, Genshi HTML, Genshi Text, GLSL, Gnuplot, Go, Go HTML Template, Go Text Template, GraphQL, Groovy
H | Handlebars, Haskell, Haxe, HCL, Hexdump, HTML, HTTP, Hy
I | Idris, INI, Io
G | GAS, GDScript, Genshi, Genshi HTML, Genshi Text, Gherkin, GLSL, Gnuplot, Go, Go HTML Template, Go Text Template, GraphQL, Groff, Groovy
H | Handlebars, Haskell, Haxe, HCL, Hexdump, HLB, HTML, HTTP, Hy
I | Idris, Igor, INI, Io
J | J, Java, JavaScript, JSON, Julia, Jungle
K | Kotlin
L | Lighttpd configuration file, LLVM, Lua
M | Mako, markdown, Mason, Mathematica, Matlab, MiniZinc, Modula-2, MonkeyC, MorrowindScript, Myghty, MySQL
M | Mako, markdown, Mason, Mathematica, Matlab, MiniZinc, MLIR, Modula-2, MonkeyC, MorrowindScript, Myghty, MySQL
N | NASM, Newspeak, Nginx configuration file, Nim, Nix
O | Objective-C, OCaml, Octave, OpenSCAD, Org Mode
P | PacmanConf, Perl, PHP, Pig, PkgConfig, PL/pgSQL, plaintext, PostgreSQL SQL dialect, PostScript, POVRay, PowerShell, Prolog, Protocol Buffer, Puppet, Python, Python 3
O | Objective-C, OCaml, Octave, OnesEnterprise, OpenEdge ABL, OpenSCAD, Org Mode
P | PacmanConf, Perl, PHP, PHTML, Pig, PkgConfig, PL/pgSQL, plaintext, Pony, PostgreSQL SQL dialect, PostScript, POVRay, PowerShell, Prolog, PromQL, Protocol Buffer, Puppet, Python 2, Python
Q | QBasic
R | R, Racket, Ragel, react, reg, reStructuredText, Rexx, Ruby, Rust
S | Sass, Scala, Scheme, Scilab, SCSS, Smalltalk, Smarty, Snobol, Solidity, SPARQL, SQL, SquidConf, Swift, SYSTEMD, systemverilog
T | TASM, Tcl, Tcsh, Termcap, Terminfo, Terraform, TeX, Thrift, TOML, TradingView, Transact-SQL, Turing, Turtle, Twig, TypeScript, TypoScript, TypoScriptCssData, TypoScriptHtmlData
R | R, Racket, Ragel, Raku, react, ReasonML, reg, reStructuredText, Rexx, Ruby, Rust
S | SAS, Sass, Scala, Scheme, Scilab, SCSS, Smalltalk, Smarty, Snobol, Solidity, SPARQL, SQL, SquidConf, Standard ML, Stylus, Svelte, Swift, SYSTEMD, systemverilog
T | TableGen, TASM, Tcl, Tcsh, Termcap, Terminfo, Terraform, TeX, Thrift, TOML, TradingView, Transact-SQL, Turing, Turtle, Twig, TypeScript, TypoScript, TypoScriptCssData, TypoScriptHtmlData
V | VB.net, verilog, VHDL, VimL, vue
W | WDTE
X | XML, Xorg
Y | YAML
Y | YAML, YANG
Z | Zig
_I will attempt to keep this section up to date, but an authoritative list can be
@ -183,6 +185,7 @@ following constructor options:
- `ClassPrefix(prefix)` - prefix each generated CSS class.
- `TabWidth(width)` - Set the rendered tab width, in characters.
- `WithLineNumbers()` - Render line numbers (style with `LineNumbers`).
- `LinkableLineNumbers()` - 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.
@ -208,13 +211,13 @@ using the included Python 3 script `pygments2chroma.py`. I use something like
the following:
```sh
python3 ~/Projects/chroma/_tools/pygments2chroma.py \
python3 _tools/pygments2chroma.py \
pygments.lexers.jvm.KotlinLexer \
> ~/Projects/chroma/lexers/kotlin.go \
&& gofmt -s -w ~/Projects/chroma/lexers/*.go
> lexers/k/kotlin.go \
&& gofmt -s -w lexers/k/kotlin.go
```
See notes in [pygments-lexers.go](https://github.com/alecthomas/chroma/blob/master/pygments-lexers.txt)
See notes in [pygments-lexers.txt](https://github.com/alecthomas/chroma/blob/master/pygments-lexers.txt)
for a list of lexers, and notes on some of the issues importing them.
<a id="markdown-formatters" name="formatters"></a>
@ -247,18 +250,34 @@ For a quick overview of the available styles and how they look, check out the [C
<a id="markdown-command-line-interface" name="command-line-interface"></a>
## Command-line interface
A command-line interface to Chroma is included. It can be installed with:
A command-line interface to Chroma is included.
```sh
go get -u github.com/alecthomas/chroma/cmd/chroma
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`.
<a id="markdown-whats-missing-compared-to-pygments" name="whats-missing-compared-to-pygments"></a>
## 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 Perl6's ability to nest code inside
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.

View File

@ -20,7 +20,7 @@ var (
return nil
}))
// Default HTML formatter outputs self-contained HTML.
htmlFull = Register("html", html.New(html.Standalone(), html.WithClasses())) // nolint
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)))
)

View File

@ -14,32 +14,66 @@ import (
type Option func(f *Formatter)
// Standalone configures the HTML formatter for generating a standalone HTML document.
func Standalone() Option { return func(f *Formatter) { f.standalone = true } }
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() Option { return func(f *Formatter) { f.Classes = true } }
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 } }
// 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() Option { return func(f *Formatter) { f.preventSurroundingPre = true } }
// PreventSurroundingPre prevents the surrounding pre tags around the generated code.
func PreventSurroundingPre(b bool) Option {
return func(f *Formatter) {
if b {
f.preWrapper = nopPreWrapper
} else {
f.preWrapper = defaultPreWrapper
}
}
}
// 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() Option {
func WithLineNumbers(b bool) Option {
return func(f *Formatter) {
f.lineNumbers = true
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() Option {
func LineNumbersInTable(b bool) Option {
return func(f *Formatter) {
f.lineNumbersInTable = true
f.lineNumbersInTable = b
}
}
// LinkableLineNumbers decorates the line numbers HTML elements with an "id"
// attribute so they can be linked.
func LinkableLineNumbers(b bool, prefix string) Option {
return func(f *Formatter) {
f.linkableLineNumbers = b
f.lineNumbersIDPrefix = prefix
}
}
@ -64,6 +98,7 @@ func BaseLineNumber(n int) Option {
func New(options ...Option) *Formatter {
f := &Formatter{
baseLineNumber: 1,
preWrapper: defaultPreWrapper,
}
for _, option := range options {
option(f)
@ -71,17 +106,69 @@ func New(options ...Option) *Formatter {
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 tabindex="0"%s><code>`, styleAttr)
}
return fmt.Sprintf(`<pre tabindex="0"%s>`, styleAttr)
},
end: func(code bool) string {
if code {
return `</code></pre>`
}
return `</pre>`
},
}
)
// Formatter that generates HTML.
type Formatter struct {
standalone bool
prefix string
Classes bool // Exported field to detect when classes are being used
preventSurroundingPre bool
tabWidth int
lineNumbers bool
lineNumbersInTable bool
highlightRanges highlightRanges
baseLineNumber int
standalone bool
prefix string
Classes bool // Exported field to detect when classes are being used
allClasses bool
preWrapper PreWrapper
tabWidth int
wrapLongLines bool
lineNumbers bool
lineNumbersInTable bool
linkableLineNumbers bool
lineNumbersIDPrefix string
highlightRanges highlightRanges
baseLineNumber int
}
type highlightRanges [][2]int
@ -121,17 +208,15 @@ func (f *Formatter) writeHTML(w io.Writer, style *chroma.Style, tokens []chroma.
wrapInTable := f.lineNumbers && f.lineNumbersInTable
lines := chroma.SplitTokensIntoLines(tokens)
lineDigits := len(fmt.Sprintf("%d", len(lines)))
lineDigits := len(fmt.Sprintf("%d", 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.Background))
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))
if !f.preventSurroundingPre {
fmt.Fprintf(w, "<pre%s>", f.styleAttr(css, chroma.Background))
}
fmt.Fprintf(w, f.preWrapper.Start(false, f.styleAttr(css, chroma.PreWrapper)))
for index := range lines {
line := f.baseLineNumber + index
highlight, next := f.shouldHighlight(highlightIndex, line)
@ -142,22 +227,19 @@ func (f *Formatter) writeHTML(w io.Writer, style *chroma.Style, tokens []chroma.
fmt.Fprintf(w, "<span%s>", f.styleAttr(css, chroma.LineHighlight))
}
fmt.Fprintf(w, "<span%s>%*d\n</span>", f.styleAttr(css, chroma.LineNumbersTable), lineDigits, line)
fmt.Fprintf(w, "<span%s%s>%s\n</span>", f.styleAttr(css, chroma.LineNumbersTable), f.lineIDAttribute(line), f.lineTitleWithLinkIfNeeded(lineDigits, line))
if highlight {
fmt.Fprintf(w, "</span>")
}
}
if !f.preventSurroundingPre {
fmt.Fprint(w, "</pre>")
}
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%"))
}
if !f.preventSurroundingPre {
fmt.Fprintf(w, "<pre%s>", f.styleAttr(css, chroma.Background))
}
fmt.Fprintf(w, f.preWrapper.Start(true, f.styleAttr(css, chroma.PreWrapper)))
highlightIndex = 0
for index, tokens := range lines {
// 1-based line number.
@ -166,14 +248,28 @@ func (f *Formatter) writeHTML(w io.Writer, style *chroma.Style, tokens []chroma.
if next {
highlightIndex++
}
// Start of Line
fmt.Fprint(w, `<span`)
if highlight {
fmt.Fprintf(w, "<span%s>", f.styleAttr(css, chroma.LineHighlight))
// 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>%*d</span>", f.styleAttr(css, chroma.LineNumbers), lineDigits, line)
fmt.Fprintf(w, "<span%s%s>%s</span>", f.styleAttr(css, chroma.LineNumbers), f.lineIDAttribute(line), f.lineTitleWithLinkIfNeeded(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)
@ -182,14 +278,13 @@ func (f *Formatter) writeHTML(w io.Writer, style *chroma.Style, tokens []chroma.
}
fmt.Fprint(w, html)
}
if highlight {
fmt.Fprintf(w, "</span>")
}
fmt.Fprint(w, `</span>`) // End of CodeLine
fmt.Fprint(w, `</span>`) // End of Line
}
if !f.preventSurroundingPre {
fmt.Fprint(w, "</pre>")
}
fmt.Fprintf(w, f.preWrapper.End(true))
if wrapInTable {
fmt.Fprint(w, "</td></tr></table>\n")
@ -204,6 +299,25 @@ func (f *Formatter) writeHTML(w io.Writer, style *chroma.Style, tokens []chroma.
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(lineDigits, line int) string {
title := fmt.Sprintf("%*d", lineDigits, line)
if !f.linkableLineNumbers {
return title
}
return fmt.Sprintf("<a style=\"outline: none; text-decoration:none; color:inherit\" href=\"#%s\">%s</a>", 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] {
@ -268,7 +382,11 @@ func (f *Formatter) tabWidthStyle() string {
func (f *Formatter) WriteCSS(w io.Writer, style *chroma.Style) error {
css := f.styleToCSS(style)
// Special-case background as it is mapped to the outer ".chroma" class.
if _, err := fmt.Fprintf(w, "/* %s */ .%schroma { %s }\n", chroma.Background, f.prefix, css[chroma.Background]); err != nil {
if _, err := fmt.Fprintf(w, "/* %s */ .%sbg { %s }\n", chroma.Background, f.prefix, css[chroma.Background]); err != nil {
return err
}
// Special-case PreWrapper as it is the ".chroma" class.
if _, err := fmt.Fprintf(w, "/* %s */ .%schroma { %s }\n", chroma.PreWrapper, f.prefix, css[chroma.PreWrapper]); err != nil {
return err
}
// Special-case code column of table to expand width.
@ -278,6 +396,13 @@ func (f *Formatter) WriteCSS(w io.Writer, style *chroma.Style) error {
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} {
fmt.Fprintf(w, "/* %s targeted by URL anchor */ .%schroma .%s:target { %s }\n", tt, f.prefix, f.class(tt), targetedLineCSS)
}
}
tts := []int{}
for tt := range css {
tts = append(tts, int(tt))
@ -285,11 +410,16 @@ func (f *Formatter) WriteCSS(w io.Writer, style *chroma.Style) error {
sort.Ints(tts)
for _, ti := range tts {
tt := chroma.TokenType(ti)
if tt == chroma.Background {
switch tt {
case chroma.Background, chroma.PreWrapper:
continue
}
class := f.class(tt)
if class == "" {
continue
}
styles := css[tt]
if _, err := fmt.Fprintf(w, "/* %s */ .%schroma .%s { %s }\n", tt, f.prefix, f.class(tt), styles); err != nil {
if _, err := fmt.Fprintf(w, "/* %s */ .%schroma .%s { %s }\n", tt, f.prefix, class, styles); err != nil {
return err
}
}
@ -305,18 +435,27 @@ func (f *Formatter) styleToCSS(style *chroma.Style) map[chroma.TokenType]string
if t != chroma.Background {
entry = entry.Sub(bg)
}
if entry.IsZero() {
if !f.allClasses && entry.IsZero() {
continue
}
classes[t] = StyleEntryToCSS(entry)
}
classes[chroma.Background] += f.tabWidthStyle()
lineNumbersStyle := "margin-right: 0.4em; padding: 0 0.4em 0 0.4em;"
classes[chroma.PreWrapper] += classes[chroma.Background] + `;`
// Make PreWrapper a grid to show highlight style with full width.
if len(f.highlightRanges) > 0 {
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; 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.LineHighlight] = "display: block; width: 100%;" + classes[chroma.LineHighlight]
classes[chroma.LineTable] = "border-spacing: 0; padding: 0; margin: 0; border: 0; width: auto; overflow: auto; display: block;" + classes[chroma.LineTable]
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]
return classes
}

View File

@ -120,7 +120,7 @@ func maxLineWidth(lines [][]chroma.Token) int {
for _, tokens := range lines {
length := 0
for _, token := range tokens {
length += len(strings.Replace(token.String(), ` `, " ", -1))
length += len(strings.ReplaceAll(token.String(), ` `, " "))
}
if length > maxWidth {
maxWidth = length
@ -136,7 +136,7 @@ func (f *Formatter) writeTokenBackgrounds(w io.Writer, lines [][]chroma.Token, s
for index, tokens := range lines {
lineLength := 0
for _, token := range tokens {
length := len(strings.Replace(token.String(), ` `, " ", -1))
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())

View File

@ -17,6 +17,20 @@ 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",
@ -227,15 +241,11 @@ type indexedTTYFormatter struct {
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() {
// TODO: Cache token lookups?
clr, ok := theme[token.Type]
if !ok {
clr, ok = theme[token.Type.SubCategory()]
if !ok {
clr = theme[token.Type.Category()]
// if !ok {
// clr = theme[chroma.InheritStyle]
// }
}
}
if clr != "" {
@ -249,10 +259,22 @@ func (c *indexedTTYFormatter) Format(w io.Writer, style *chroma.Style, it chroma
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("terminal", &indexedTTYFormatter{ttyTables[8]})
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.
//

View File

@ -1,24 +1,9 @@
module github.com/alecthomas/chroma
require (
github.com/GeertJohan/go.rice v1.0.0
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 // indirect
github.com/alecthomas/kong v0.2.1-0.20190708041108-0548c6b1afae
github.com/alecthomas/kong-hcl v0.1.8-0.20190615233001-b21fea9723c8
github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897 // indirect
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964
github.com/dlclark/regexp2 v1.1.6
github.com/gorilla/csrf v1.6.0
github.com/gorilla/handlers v1.4.1
github.com/gorilla/mux v1.7.3
github.com/mattn/go-colorable v0.0.9
github.com/mattn/go-isatty v0.0.4
github.com/sergi/go-diff v1.0.0 // indirect
github.com/stretchr/testify v1.3.0 // indirect
golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35 // indirect
)
replace github.com/GeertJohan/go.rice => github.com/alecthomas/go.rice v1.0.1-0.20190719113735-961b99d742e7
go 1.13
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dlclark/regexp2 v1.4.0
github.com/stretchr/testify v1.7.0
)

View File

@ -1,58 +1,14 @@
github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0=
github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U=
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI=
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 h1:JHZL0hZKJ1VENNfmXvHbgYlbUOvpzYzvy2aZU5gXVeo=
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0=
github.com/alecthomas/go.rice v1.0.1-0.20190719113735-961b99d742e7 h1:0cMlP9evwgTzs5JUe2C/3rLttYjmRm0kbz9fdGBIV1E=
github.com/alecthomas/go.rice v1.0.1-0.20190719113735-961b99d742e7/go.mod h1:af5vUNlDNkCjOZeSGFgIJxDje9qdjsO6hshx0gTmZt4=
github.com/alecthomas/kong v0.1.17-0.20190424132513-439c674f7ae0/go.mod h1:+inYUSluD+p4L8KdviBSgzcqEjUQOfC5fQDRFuc36lI=
github.com/alecthomas/kong v0.2.1-0.20190708041108-0548c6b1afae h1:C4Q9m+oXOxcSWwYk9XzzafY2xAVAaeubZbUHJkw3PlY=
github.com/alecthomas/kong v0.2.1-0.20190708041108-0548c6b1afae/go.mod h1:+inYUSluD+p4L8KdviBSgzcqEjUQOfC5fQDRFuc36lI=
github.com/alecthomas/kong-hcl v0.1.8-0.20190615233001-b21fea9723c8 h1:atLL+K8Hg0e8863K2X+k7qu+xz3M2a/mWFIACAPf55M=
github.com/alecthomas/kong-hcl v0.1.8-0.20190615233001-b21fea9723c8/go.mod h1:MRgZdU3vrFd05IQ89AxUZ0aYdF39BYoNFa324SodPCA=
github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897 h1:p9Sln00KOTlrYkxI1zYWl1QLnEqAqEARBEYa8FQnQcY=
github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ=
github.com/daaku/go.zipexe v1.0.0 h1:VSOgZtH418pH9L16hC/JrgSNJbbAL26pj7lmD1+CGdY=
github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E=
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ=
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dlclark/regexp2 v1.1.6 h1:CqB4MjHw0MFCDj+PHHjiESmHX+N7t0tJzKvC6M97BRg=
github.com/dlclark/regexp2 v1.1.6/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/gorilla/csrf v1.6.0 h1:60oN1cFdncCE8tjwQ3QEkFND5k37lQPcRjnlvm7CIJ0=
github.com/gorilla/csrf v1.6.0/go.mod h1:7tSf8kmjNYr7IWDCYhd3U8Ck34iQ/Yw5CJu7bAkHEGI=
github.com/gorilla/handlers v1.4.1 h1:BHvcRGJe/TrL+OqFxoKQGddTgeibiOjaBssV5a/N9sw=
github.com/gorilla/handlers v1.4.1/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/nkovacs/streamquote v1.0.0/go.mod h1:BN+NaZ2CmdKqUuTUXUEm9j95B2TRbpOWpxbJYzzgUsc=
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35 h1:YAFjXN64LMvktoUZH9zgY4lGc/msGN7HQfoSuKCgaDU=
golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -4,7 +4,7 @@ import "strings"
// An Iterator across tokens.
//
// nil will be returned at the end of the Token stream.
// 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

View File

@ -2,11 +2,13 @@ package chroma
import (
"fmt"
"strings"
)
var (
defaultOptions = &TokeniseOptions{
State: "root",
State: "root",
EnsureLF: true,
}
)
@ -80,6 +82,10 @@ type TokeniseOptions struct {
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.
@ -93,9 +99,11 @@ type Lexer interface {
// 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 l[i].Config().Name < l[j].Config().Name }
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

View File

@ -3,6 +3,9 @@
The tests in this directory feed a known input `testdata/<name>.actual` into the parser for `<name>` and check
that its output matches `<name>.exported`.
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:

View File

@ -6,7 +6,7 @@ import (
)
// ABAP lexer.
var Abap = internal.Register(MustNewLexer(
var Abap = internal.Register(MustNewLazyLexer(
&Config{
Name: "ABAP",
Aliases: []string{"abap"},
@ -14,7 +14,11 @@ var Abap = internal.Register(MustNewLexer(
MimeTypes: []string{"text/x-abap"},
CaseInsensitive: true,
},
Rules{
abapRules,
))
func abapRules() Rules {
return Rules{
"common": {
{`\s+`, Text, nil},
{`^\*.*$`, CommentSingle, nil},
@ -52,5 +56,5 @@ var Abap = internal.Register(MustNewLexer(
{`[/;:()\[\],.]`, Punctuation, nil},
{`(!)(\w+)`, ByGroups(Operator, Name), nil},
},
},
))
}
}

View File

@ -6,14 +6,18 @@ import (
)
// Abnf lexer.
var Abnf = internal.Register(MustNewLexer(
var Abnf = internal.Register(MustNewLazyLexer(
&Config{
Name: "ABNF",
Aliases: []string{"abnf"},
Filenames: []string{"*.abnf"},
MimeTypes: []string{"text/x-abnf"},
},
Rules{
abnfRules,
))
func abnfRules() Rules {
return Rules{
"root": {
{`;.*$`, CommentSingle, nil},
{`(%[si])?"[^"]*"`, Literal, nil},
@ -34,5 +38,5 @@ var Abnf = internal.Register(MustNewLexer(
{`\s+`, Text, nil},
{`.`, Text, nil},
},
},
))
}
}

View File

@ -6,7 +6,7 @@ import (
)
// Actionscript lexer.
var Actionscript = internal.Register(MustNewLexer(
var Actionscript = internal.Register(MustNewLazyLexer(
&Config{
Name: "ActionScript",
Aliases: []string{"as", "actionscript"},
@ -15,7 +15,11 @@ var Actionscript = internal.Register(MustNewLexer(
NotMultiline: true,
DotAll: true,
},
Rules{
actionscriptRules,
))
func actionscriptRules() Rules {
return Rules{
"root": {
{`\s+`, Text, nil},
{`//.*?\n`, CommentSingle, nil},
@ -35,5 +39,5 @@ var Actionscript = internal.Register(MustNewLexer(
{`"(\\\\|\\"|[^"])*"`, LiteralStringDouble, nil},
{`'(\\\\|\\'|[^'])*'`, LiteralStringSingle, nil},
},
},
))
}
}

View File

@ -6,7 +6,7 @@ import (
)
// Actionscript 3 lexer.
var Actionscript3 = internal.Register(MustNewLexer(
var Actionscript3 = internal.Register(MustNewLazyLexer(
&Config{
Name: "ActionScript 3",
Aliases: []string{"as3", "actionscript3"},
@ -14,7 +14,11 @@ var Actionscript3 = internal.Register(MustNewLexer(
MimeTypes: []string{"application/x-actionscript3", "text/x-actionscript3", "text/actionscript3"},
DotAll: true,
},
Rules{
actionscript3Rules,
))
func actionscript3Rules() Rules {
return Rules{
"root": {
{`\s+`, Text, nil},
{`(function\s+)([$a-zA-Z_]\w*)(\s*)(\()`, ByGroups(KeywordDeclaration, NameFunction, Text, Operator), Push("funcparams")},
@ -52,5 +56,5 @@ var Actionscript3 = internal.Register(MustNewLexer(
{`,`, Operator, Pop(1)},
Default(Pop(1)),
},
},
))
}
}

View File

@ -6,7 +6,7 @@ import (
)
// Ada lexer.
var Ada = internal.Register(MustNewLexer(
var Ada = internal.Register(MustNewLazyLexer(
&Config{
Name: "Ada",
Aliases: []string{"ada", "ada95", "ada2005"},
@ -14,7 +14,11 @@ var Ada = internal.Register(MustNewLexer(
MimeTypes: []string{"text/x-ada"},
CaseInsensitive: true,
},
Rules{
adaRules,
))
func adaRules() Rules {
return Rules{
"root": {
{`[^\S\n]+`, Text, nil},
{`--.*?\n`, CommentSingle, nil},
@ -110,5 +114,5 @@ var Ada = internal.Register(MustNewLexer(
{`\)`, Punctuation, Pop(1)},
Include("root"),
},
},
))
}
}

47
vendor/github.com/alecthomas/chroma/lexers/a/al.go generated vendored Normal file
View File

@ -0,0 +1,47 @@
package a
import (
. "github.com/alecthomas/chroma" // nolint
"github.com/alecthomas/chroma/lexers/internal"
)
// Al lexer.
var Al = internal.Register(MustNewLazyLexer(
&Config{
Name: "AL",
Aliases: []string{"al"},
Filenames: []string{"*.al", "*.dal"},
MimeTypes: []string{"text/x-al"},
DotAll: true,
CaseInsensitive: true,
},
alRules,
))
// https://github.com/microsoft/AL/blob/master/grammar/alsyntax.tmlanguage
func alRules() Rules {
return Rules{
"root": {
{`\s+`, TextWhitespace, nil},
{`(?s)\/\*.*?\\*\*\/`, CommentMultiline, nil},
{`(?s)//.*?\n`, CommentSingle, nil},
{`\"([^\"])*\"`, Text, nil},
{`'([^'])*'`, LiteralString, nil},
{`\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`, Keyword, nil},
{`\b(?i:(AND|DIV|MOD|NOT|OR|XOR))\b`, OperatorWord, nil},
{`\b(?i:(AVERAGE|CONST|COUNT|EXIST|FIELD|FILTER|LOOKUP|MAX|MIN|ORDER|SORTING|SUM|TABLEDATA|UPPERLIMIT|WHERE|ASCENDING|DESCENDING))\b`, Keyword, nil},
{`\b(?i:(CODEUNIT|PAGE|PAGEEXTENSION|PAGECUSTOMIZATION|DOTNET|ENUM|ENUMEXTENSION|VALUE|QUERY|REPORT|TABLE|TABLEEXTENSION|XMLPORT|PROFILE|CONTROLADDIN|REPORTEXTENSION|INTERFACE|PERMISSIONSET|PERMISSIONSETEXTENSION|ENTITLEMENT))\b`, Keyword, nil},
{`\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`, Keyword, nil},
{`\b([<>]=|<>|<|>)\b?`, Operator, nil},
{`\b(\-|\+|\/|\*)\b`, Operator, nil},
{`\s*(\:=|\+=|-=|\/=|\*=)\s*?`, Operator, nil},
{`\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`, Keyword, nil},
{`\s*[(\.\.)&\|]\s*`, Operator, nil},
{`\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`, LiteralNumber, nil},
{`[;:,]`, Punctuation, nil},
{`#[ \t]*(if|else|elif|endif|define|undef|region|endregion|pragma)\b.*?\n`, CommentPreproc, nil},
{`\w+`, Text, nil},
{`.`, Text, nil},
},
}
}

View File

@ -6,14 +6,18 @@ import (
)
// Angular2 lexer.
var Angular2 = internal.Register(MustNewLexer(
var Angular2 = internal.Register(MustNewLazyLexer(
&Config{
Name: "Angular2",
Aliases: []string{"ng2"},
Filenames: []string{},
MimeTypes: []string{},
},
Rules{
angular2Rules,
))
func angular2Rules() Rules {
return Rules{
"root": {
{`[^{([*#]+`, Other, nil},
{`(\{\{)(\s*)`, ByGroups(CommentPreproc, Text), Push("ngExpression")},
@ -38,5 +42,5 @@ var Angular2 = internal.Register(MustNewLexer(
{`'.*?'`, LiteralString, Pop(1)},
{`[^\s>]+`, LiteralString, Pop(1)},
},
},
))
}
}

View File

@ -6,14 +6,18 @@ import (
)
// ANTLR lexer.
var ANTLR = internal.Register(MustNewLexer(
var ANTLR = internal.Register(MustNewLazyLexer(
&Config{
Name: "ANTLR",
Aliases: []string{"antlr"},
Filenames: []string{},
MimeTypes: []string{},
},
Rules{
antlrRules,
))
func antlrRules() Rules {
return Rules{
"whitespace": {
{`\s+`, TextWhitespace, nil},
},
@ -97,5 +101,5 @@ var ANTLR = internal.Register(MustNewLexer(
{`(\$[a-zA-Z]+)(\.?)(text|value)?`, ByGroups(NameVariable, Punctuation, NameProperty), nil},
{`(\\\\|\\\]|\\\[|[^\[\]])+`, Other, nil},
},
},
))
}
}

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