Repository: chubin/cheat.sh Branch: master Commit: 031a5d3887f0 Files: 112 Total size: 747.8 KB Directory structure: gitextract_e4plvajh/ ├── .dockerignore ├── .github/ │ └── workflows/ │ ├── tests-macos.yml │ └── tests-ubuntu.yml ├── .gitignore ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── README.md ├── bin/ │ ├── app.py │ ├── clean_cache.py │ ├── release.py │ └── srv.py ├── doc/ │ ├── README-ja.md │ └── standalone.md ├── docker-compose.debug.yml ├── docker-compose.yml ├── etc/ │ └── config.yaml ├── lib/ │ ├── adapter/ │ │ ├── __init__.py │ │ ├── adapter.py │ │ ├── cheat_cheat.py │ │ ├── cheat_sheets.py │ │ ├── cmd.py │ │ ├── common.py │ │ ├── git_adapter.py │ │ ├── internal.py │ │ ├── latenz.py │ │ ├── learnxiny.py │ │ ├── question.py │ │ ├── rosetta.py │ │ ├── tldr.py │ │ └── upstream.py │ ├── buttons.py │ ├── cache.py │ ├── cheat_wrapper.py │ ├── cheat_wrapper_test.py │ ├── config.py │ ├── fetch.py │ ├── fmt/ │ │ ├── __init__.py │ │ ├── comments.py │ │ ├── internal.py │ │ └── markdown.py │ ├── frontend/ │ │ ├── __init__.py │ │ ├── ansi.py │ │ └── html.py │ ├── globals.py │ ├── languages_data.py │ ├── limits.py │ ├── options.py │ ├── panela/ │ │ ├── colors.json │ │ ├── colors.py │ │ └── panela_colors.py │ ├── post.py │ ├── postprocessing.py │ ├── routing.py │ ├── search.py │ ├── standalone.py │ └── stateful_queries.py ├── requirements.txt ├── share/ │ ├── adapters/ │ │ ├── chmod.grc │ │ ├── chmod.sh │ │ ├── oeis.sh │ │ └── rfc.sh │ ├── ansi2html.sh │ ├── bash_completion.txt │ ├── cht.sh.txt │ ├── emacs-ivy.txt │ ├── emacs.txt │ ├── firstpage-v1.txt │ ├── firstpage-v2.pnl │ ├── firstpage-v2.txt │ ├── fish.txt │ ├── help.txt │ ├── intro.txt │ ├── post.txt │ ├── scripts/ │ │ ├── cacheCleanup.go │ │ └── remove-from-cache.sh │ ├── static/ │ │ ├── 1.html │ │ ├── malformed-response.html │ │ ├── opensearch.xml │ │ └── style.css │ ├── styles-demo.txt │ ├── vim/ │ │ └── .vimrc │ ├── vim.txt │ └── zsh.txt └── tests/ ├── README.md ├── results/ │ ├── 1 │ ├── 10 │ ├── 11 │ ├── 12 │ ├── 13 │ ├── 14 │ ├── 15 │ ├── 16 │ ├── 17 │ ├── 18 │ ├── 19 │ ├── 2 │ ├── 20 │ ├── 21 │ ├── 22 │ ├── 23 │ ├── 24 │ ├── 25 │ ├── 3 │ ├── 4 │ ├── 5 │ ├── 6 │ ├── 7 │ ├── 8 │ └── 9 ├── run-tests.sh └── tests.txt ================================================ FILE CONTENTS ================================================ ================================================ FILE: .dockerignore ================================================ .git .gitignore docker-compose.yml Dockerfile ================================================ FILE: .github/workflows/tests-macos.yml ================================================ name: MacOS Tests on: push: branches: [ master ] pull_request: branches: [ master ] jobs: build: runs-on: macos-latest steps: - uses: actions/checkout@v4 - run: ./share/adapters/rfc.sh ================================================ FILE: .github/workflows/tests-ubuntu.yml ================================================ name: Ubuntu Tests on: push: branches: [ master ] pull_request: branches: [ master ] schedule: - cron: '0 9 * * 4' jobs: build: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - run: ./share/adapters/rfc.sh - name: install dependencies run: pip install --upgrade -r requirements.txt - name: fetch upstream cheat sheets run: python lib/fetch.py fetch-all - name: run bash tests run: bash tests/run-tests.sh - name: run pytest run: pytest lib/ docker: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 - run: docker-compose build - run: docker images - run: | docker-compose -f docker-compose.yml up -d # docker-compose -f docker-compose.yml -f docker-compose.debug.yml up -d docker-compose ps # wait until the web server is up wget --timeout 3 --tries=5 --spider localhost:8002 2>&1 | grep -i http docker-compose logs --no-color - run: CHEATSH_TEST_STANDALONE=NO bash tests/run-tests.sh ================================================ FILE: .gitignore ================================================ *.pyc *.swp log/ ve/ share/vim/.vim/ share/vim/.viminfo typescript venv/ bin/upstream upstream/ ================================================ FILE: CONTRIBUTING.md ================================================ There are several ways how you can contribute to cheat.sh and make it better and more useful: 1. Suggest a GitHub repository (or other information source) to be attached to cheat.sh. Just create an issue for it, where the name and the URL of the repository is specified. Please keep in mind, that the repository's license has to permit its usage by cheat.sh; 2. Create an adapter for some repository and add it to cheat.sh; 3. Create a Editor plugin for cheat.sh; 4. Create a new cheat sheet in one of its upstream repositories; 5. Go through the list of open issues, and try to fix some of them, or at least understand and share your opinion about them, if you have it. ================================================ FILE: Dockerfile ================================================ FROM alpine:3.14 # fetching cheat sheets ## installing dependencies RUN apk add --update --no-cache git py3-six py3-pygments py3-yaml py3-gevent \ libstdc++ py3-colorama py3-requests py3-icu py3-redis sed ## copying WORKDIR /app COPY . /app ## building missing python packages RUN apk add --no-cache --virtual build-deps py3-pip g++ python3-dev libffi-dev \ && pip3 install --no-cache-dir --upgrade pygments \ && pip3 install --no-cache-dir -r requirements.txt \ && apk del build-deps ## fetching cheat sheets RUN mkdir -p /root/.cheat.sh/log/ \ && python3 lib/fetch.py fetch-all # installing server dependencies RUN apk add --update --no-cache py3-jinja2 py3-flask bash gawk ENTRYPOINT ["python3", "-u", "bin/srv.py"] CMD [""] ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2025 Igor Chubin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ ![cheat.sh logo](http://cheat.sh/files/big-logo-v2-fixed.png) Unified access to the best community driven cheat sheets repositories of the world. Let's imagine for a moment that there is such a thing as an ideal cheat sheet. What should it look like? What features should it have? * **Concise** — It should only contain the things you need, and nothing else. * **Fast** — It should be possible to use it instantly. * **Comprehensive** — It should contain answers for every possible question. * **Universal** — It should be available everywhere, anytime, without any preparations. * **Unobtrusive** — It should not distract you from your main task. * **Tutoring** — It should help you to learn the subject. * **Inconspicuous** — It should be possible to use it completely unnoticed. Such a thing exists! It's easy to [install](#installation) and there's even [auto-complete](#tab-completion). ## Features **cheat.sh** * Has a simple curl/browser/editor interface. * Covers 56 programming languages, several DBMSes, and more than 1000 most important UNIX/Linux commands. * Provides access to the best community driven cheat sheets repositories in the world, on par with StackOverflow. * Available everywhere, no installation needed, but can be installed for offline usage. * Ultrafast, returns answers within 100 ms, as a rule. * Has a convenient command line client, `cht.sh`, that is very advantageous and helpful, though not mandatory. * Can be used directly from code editors, without opening a browser and not switching your mental context. * Supports a special stealth mode where it can be used fully invisibly without ever touching a key and making sounds.

## Contents * [Features](#features) * [Usage](#usage) * [Command line client, cht.sh](#command-line-client-chtsh) * [Installation](#installation) * [Client usage](#client-usage) * [Tab-completion](#tab-completion) - [Bash Tab completion](#bash-tab-completion) - [ZSH Tab completion](#zsh-tab-completion) * [Stealth mode](#stealth-mode) * [Windows command line client](#windows-command-line-client) * [Self-Hosting](#self-hosting) * [Docker](#docker) * [Editors integration](#editors-integration) * [Vim](#vim) * [Emacs](#emacs) * [Visual Studio Code](#visual-studio-code) * [Sublime](#sublime) * [IntelliJ IDEA](#intellij-idea) * [QT Creator](#qtcreator) * [Special pages](#special-pages) * [Search](#search) * [Programming languages cheat sheets](#programming-languages-cheat-sheets) * [Cheat sheets sources](#cheat-sheets-sources) * [How to contribute](#how-to-contribute) * [How to edit a cheat sheet](#how-to-edit-a-cheat-sheet) * [How to add a cheat sheet](#how-to-add-a-cheat-sheet) * [How to add a cheat sheet repository](#how-to-add-a-cheat-sheet-repository) ## Usage To get a cheat sheet for a UNIX/Linux command from a command line, query the service using `curl` or any other HTTP/HTTPS client specifying the name of the command in the query: ``` curl cheat.sh/tar curl cht.sh/curl curl https://cheat.sh/rsync curl https://cht.sh/tr ``` As you can see, you can use both HTTPS and HTTP to access the service, and both the long (cheat.sh) and the short (cht.sh) service names. Here `tar`, `curl`, `rsync`, and `tr` are names of the UNIX/Linux commands you want to get cheat sheets for. If you don't know the name of the command you need, you can search for it using the `~KEYWORD` notation. For example, to see how you can make `snapshots` of a filesystem/volume/something else: ``` curl cht.sh/~snapshot ```

The programming language cheat sheets are located in special namespaces dedicated to them. ``` curl cht.sh/go/Pointers curl cht.sh/scala/Functions curl cht.sh/python/lambda ``` To get the list of available programming language cheat sheets, use the special query `:list`: ``` curl cht.sh/go/:list ``` Almost each programming language has a special page named `:learn` that describes the language basics (that's a direct mapping from the *"Learn X in Y"* project). It could be a good starting point if you've just started learning a language. If there is no cheat sheet for a programming language query (and it is almost always the case), it is generated on the fly, based on available cheat sheets and answers on StackOverflow. Of course, there is no guarantee that the returned cheat sheet will be a 100% hit, but it is almost always exactly what you are looking for. Try these (and your own) queries to get the impression of that, what the answers look like: ``` curl cht.sh/go/reverse+a+list curl cht.sh/python/random+list+elements curl cht.sh/js/parse+json curl cht.sh/lua/merge+tables curl cht.sh/clojure/variadic+function ``` If you don't like an answer for your queries, you can pick another one. For that, repeat the query with an additional parameter `/1`, `/2` etc. appended: ``` curl cht.sh/python/random+string curl cht.sh/python/random+string/1 curl cht.sh/python/random+string/2 ``` Cheat sheets are formatted as code of the queried programming language (at least we are trying our best to do so) so they can be pasted into a program in this language directly. Text comments, if there are any, are formatted according to the language syntax. ```lua $ curl cht.sh/lua/table+keys -- lua: retrieve list of keys in a table local keyset={} local n=0 for k,v in pairs(tab) do n=n+1 keyset[n]=k end --[[ [ Note that you cannot guarantee any order in keyset. If you want the [ keys in sorted order, then sort keyset with table.sort(keyset). [ [ [lhf] [so/q/12674345] [cc by-sa 3.0] ]] ``` If you don't need text comments in the answer, you can eliminate them using a special option `\?Q`: ```lua $ curl cht.sh/lua/table+keys\?Q local keyset={} local n=0 for k,v in pairs(tab) do n=n+1 keyset[n]=k end ``` And if you don't need syntax highlighting, switch it off using `\?T`. You can combine the options together: ``` curl cht.sh/go/reverse+a+list\?Q curl cht.sh/python/random+list+elements\?Q curl cht.sh/js/parse+json\?Q curl cht.sh/lua/merge+tables\?QT curl cht.sh/clojure/variadic+function\?QT ``` Full list of all options described below and in `/:help`. Try your own queries. Follow these rules: 1. Try to be more specific (`/python/append+file` is better than `/python/file` and `/python/append`). 2. Ask practical question if possible (yet theoretical question are possible too). 3. Ask programming language questions only; specify the name of the programming language as the section name. 4. Separate words with `+` instead of spaces. 5. Do not use special characters, they are ignored anyway. 6. If you want to eliminate cheat sheets containing some word, add it to the query with `+-`: `python/multiply+matrices+-numpy` Read more about the programming languages queries below. ---- ## Command line client, cht.sh The cheat.sh service has its own command line client (`cht.sh`) that has several useful features compared to querying the service directly with `curl`: * Special shell mode with a persistent queries context and readline support. * Queries history. * Clipboard integration. * Tab completion support for shells (bash, fish, zsh). * Stealth mode. ### Installation To install the client: ```bash PATH_DIR="$HOME/bin" # or another directory on your $PATH mkdir -p "$PATH_DIR" curl https://cht.sh/:cht.sh > "$PATH_DIR/cht.sh" chmod +x "$PATH_DIR/cht.sh" ``` or to install it globally (for all users): ```bash curl -s https://cht.sh/:cht.sh | sudo tee /usr/local/bin/cht.sh && sudo chmod +x /usr/local/bin/cht.sh ``` Note: The package "rlwrap" is a required dependency to run in shell mode. Install this using `sudo apt install rlwrap` ### Client usage Now, you can use `cht.sh` instead of `curl`, and write your queries in more natural way, with spaces instead of `+`: ``` $ cht.sh go reverse a list $ cht.sh python random list elements $ cht.sh js parse json ``` It is even more convenient to start the client in a special shell mode: ``` $ cht.sh --shell cht.sh> go reverse a list ``` If all your queries are about the same language, you can change the context and spare repeating the programming language name: ``` $ cht.sh --shell cht.sh> cd go cht.sh/go> reverse a list ``` or even start the client in this context: ``` $ cht.sh --shell go cht.sh/go> reverse a list ... cht.sh/go> join a list ... ``` If you want to change the context, you can do it with the `cd` command, or if you want do a single query for some other language, just prepend it with `/`: ``` $ cht.sh --shell go ... cht.sh/go> /python dictionary comprehension ... ``` If you want to copy the last answer into the clipboard, you can use the `c` (`copy`) command, or `C` (`ccopy`, without comments). ``` cht.sh/python> append file # python - How do you append to a file? with open("test.txt", "a") as myfile: myfile.write("appended text") cht.sh/python> C copy: 2 lines copied to the selection ``` Type `help` for other internal `cht.sh` commands. ``` cht.sh> help help - show this help hush - do not show the 'help' string at start anymore cd LANG - change the language context copy - copy the last answer in the clipboard (aliases: yank, y, c) ccopy - copy the last answer w/o comments (cut comments; aliases: cc, Y, C) exit - exit the cheat shell (aliases: quit, ^D) id [ID] - set/show an unique session id ("reset" to reset, "remove" to remove) stealth - stealth mode (automatic queries for selected text) update - self update (only if the scriptfile is writeable) version - show current cht.sh version /:help - service help QUERY - space separated query staring (examples are below) cht.sh> python zip list cht.sh/python> zip list cht.sh/go> /python zip list ``` The `cht.sh` client has its configuration file which is located at `~/.cht.sh/cht.sh.conf` (location of the file can be overridden by the environment variable `CHTSH_CONF`). Use it to specify query options that you would use with each query. For example, to switch syntax highlighting off create the file with the following content: ```bash CHTSH_QUERY_OPTIONS="T" ``` Or if you want to use a special syntax highlighting theme: ```bash CHTSH_QUERY_OPTIONS="style=native" ``` (`curl cht.sh/:styles-demo` to see all supported styles). Other cht.sh configuration parameters: ```bash CHTSH_CURL_OPTIONS="-A curl" # curl options used for cht.sh queries CHTSH_URL=https://cht.sh # URL of the cheat.sh server ``` ### Tab completion #### Bash Tab completion To activate tab completion support for `cht.sh`, add the `:bash_completion` script to your `~/.bashrc`: ```bash curl https://cheat.sh/:bash_completion > ~/.bash.d/cht.sh . ~/.bash.d/cht.sh # and add . ~/.bash.d/cht.sh to ~/.bashrc ``` #### ZSH Tab completion To activate tab completion support for `cht.sh`, add the `:zsh` script to the *fpath* in your `~/.zshrc`: ```zsh curl https://cheat.sh/:zsh > ~/.zsh.d/_cht echo 'fpath=(~/.zsh.d/ $fpath)' >> ~/.zshrc # Open a new shell to load the plugin ``` ---- ### Stealth mode Being used fully unnoticed is one of the most important property of any cheat sheet. cheat.sh can be used completely unnoticed too. The cheat.sh client, `cht.sh`, has a special mode, called **stealth mode**. Using that, you don't even need to touch your keyboard to open a cheat sheet. In this mode, as soon as you select some text with the mouse (and thus adding it into the selection buffer of X Window System or into the clipboard) it's used as a query string for cheat.sh, and the correspondent cheat sheet is automatically shown. Let's imagine, that you are having an online interview, where your interviewer asks you some questions using a shared document (say Google Docs) and you are supposed to write your coding answers there (it's possible too that you'll type in the questions on your own, just to show to the interviewer that you've heard it right). When using the stealth mode of `cht.sh`, the only thing you need to do in order to see a cheat sheet for some question, is to select the question using the mouse. If you don't want any text in the answers and the only thing you need is code, use the `Q` option when starting the stealth mode.

``` You: Hi! | $ cht.sh --shell python She: Hi! | cht.sh/python> stealth Q She: Are you ready for a small interview? | stealth: you are in the stealth mode; select any text She: Just a couple of questions | stealth: selections longer than 5 words are ignored She: We will talk about python | stealth: query arguments: ?Q She: Let's start from something simple. | stealth: use ^C to leave this mode She: Do you know how to reverse a list in python? | You: Sure | You: (selecting "reverse a list") | stealth: reverse a list | reverse_lst = lst[::-1] You: lst[::-1]? | She: Good. | She: Do you know how to chain a list of lists? | You: (selecting "chain a list of lists") | stealth: chain a list of lists | import itertools | a = [["a","b"], ["c"]] | print list(itertools.chain.from_iterable(a)) You: May I use external modules? | She: What module do you want to use? | You: itertools | She: Yes, you may use it | You: Ok, then: | You: itertools.chain.from_iterable(a) | She: Good. Let's try something harder. | She: What about quicksort implementation? | You: (selecting "quicksort implementation") | stealth: quicksort implementation You: Let me think about it. | (some big and clumsy lowlevel implementation shown) You: Well...(starting typing it in) | def sort(array=[12,4,5,6,7,3,1,15]): | less = [] She: (seeing your ugly pascal style) | equal = [] She: Could you write it more concise? | greater = [] | if len(array) > 1: You: What do you mean? | pivot = array[0] | for x in array: She: I mean, | if x < pivot: less.append(x) She: do you really need all these ifs and fors? | if x == pivot: equal.append(x) She: Could you maybe just use filter instead? | if x > pivot: greater.append(x) | return sort(less)+equal+sort(greater) You: quicksort with filter? | else: | return array She: Yes | You: (selecting "quicksort with filter") | stealth: quicksort with filter You: Ok, I will try. | return qsort(filter(lt, L[1:]))+[pivot] \ You: Something like this? | +qsort(filter(ge, L[1:])) You: qsort(filter(lt, L[1:]))+[pivot] \ | + qsort(filter(ge, L[1:])) | | She: Yes! Perfect! Exactly what I wanted to see! | | ``` Of course, this is just for fun, and you should never cheat in your coding interviews, because you know what happens when you do. ![when you lie in your interview](http://cheat.sh/files/when-you-lie-katze.png) ### Windows command line client You can access cheat.sh from Windows command line too. Use cheat.sh command line client for that: [`cht.exe`](https://github.com/tpanj/cht.exe). It supports: * output colorization; * command line options; * its own configuration file. You can also use [`scoop`](https://github.com/lukesampson/scoop) command-line installer for Windows to get it: ```batch scoop install cht ``` ---- ## Self-Hosting ### Docker Currently, the easiest way to get a self-hosted instance running is by using the `docker-compose.yml` file. docker-compose up This builds and runs the image with baked in cheatsheets and starts the app and a Redis instance to back it, making the service available at http://localhost:8002 This is currently an early implementation and should probably not be used for anything outside of internal/dev/personal use right now. ## Editors integration You can use *cheat.sh* directly from the editor (*Emacs*, *Sublime*, *Vim*, and *Visual Studio Code* are currently supported; not all features are supported by all plugins though; see below). Instead of opening your browser, googling, browsing Stack Overflow and eventually copying the code snippets you need into the clipboard and later pasting them into the editor, you can achieve the same instantly and without leaving the editor at all! Here is what it looks like in Vim: 1. If you have a question while editing a program, you can just type your question directly in the buffer and press `KK`. You will get the answer to your question in pager. (with `KB` you'll get the answer in a separate buffer). 2. If you like the answer, you can manually paste it from the buffer or the pager, or if you are lazy you can use `KP` to paste it below/under your question (or replace you question using `KR`). If you want the answer without the comments, `KC` replays the last query toggling them. If you use some static analysis plugin such as *syntastic* (for Vim), you can use its warning and error messages as cheat.sh queries: place the cursor on the problem line and press `KE`: explanation for the warning will be opened in a new buffer. Features supported by cheat.sh plugins for different editors: |Feature |Emacs|Sublime|Vim|VSCode|IDEA|QtCreator| |-------------------|-----|-------|---|------|----|---------| |Command queries |✓ |✓ |✓ |✓ |✓ |✓ | |Queries from buffer| | |✓ |✓ | |✓ | |Toggle comments | | |✓ |✓ |✓ |✓ | |Prev/next answer | | |✓ |✓ |✓ |✓ | |Multiple answers | |✓ | | |✓ | | |Warnings as queries| | |✓ | | | | |Queries history | | |✓ |✓ | | | |Session id | | |✓ | | | | |Configurable server|✓ | |✓ |✓ | |✓ | ### Vim * [cheat.sh-vim](https://github.com/dbeniamine/cheat.sh-vim) — Vim support Here is Vim configuration example: ```vim " some configuration above ... let mapleader=" " call vundle#begin() Bundle 'gmarik/vundle' Bundle 'scrooloose/syntastic' Bundle 'dbeniamine/cheat.sh-vim' call vundle#end() let g:syntastic_javascript_checkers = [ 'jshint' ] let g:syntastic_ocaml_checkers = ['merlin'] let g:syntastic_python_checkers = ['pylint'] let g:syntastic_shell_checkers = ['shellcheck'] " some configuration below ... ``` In this example, several Vim plugins are used: * [gmarik/vundle](https://github.com/VundleVim/Vundle.vim) — Vim plugin manager * [scrooloose/syntastic](https://github.com/vim-syntastic/syntastic) — Syntax checking plugin * [cheat.sh-vim](https://github.com/dbeniamine/cheat.sh-vim) — Vim support Syntastic shows warnings and errors (found by code analysis tools: `jshint`, `merlin`, `pylint`, `shellcheck` etc.), and `cheat.sh-vim` shows you explanations for the errors and warnings and answers on programming languages queries written in the editor. Watch a demo, where the most important features of the cheat.sh Vim plugin are shown (5 Min):

Or, if you want to scroll and/or pause, the same on YouTube:

cheat.sh-vim: Using cheat.sh from vim

### Emacs * [cheat-sh.el](https://github.com/davep/cheat-sh.el) — Emacs support (available also at cheat.sh/:emacs) * cheat.sh/:emacs-ivy — Emacs support for ivy users [![asciicast](https://asciinema.org/a/3xvqwrsu9g4taj5w526sb2t35.png)](https://asciinema.org/a/3xvqwrsu9g4taj5w526sb2t35) ### Visual Studio Code * [vscode-snippet](https://github.com/mre/vscode-snippet) * Install it from [VSCode Marketplace](https://marketplace.visualstudio.com/items?itemName=vscode-snippet.Snippet) Usage: 1. Hit ⌘ Command + ⇧ Shift + p 2. Run `Snippet: Find`. 3. Type your query and hit enter. [![vscode-snippet](https://cheat.sh/files/vscode-snippet-demo.gif)](https://github.com/mre/vscode-snippet) *(GIF courtesy: Matthias Endler, @mre)* ### Sublime * [cheat.sh-sublime-plugin](https://github.com/gauravk-in/cheat.sh-sublime-plugin/) Usage: 1. Write your query string. 2. Select the query string. 3. Press Cmd + ⇧ Shift + B to replace the selected query string by the answer generated from `cht.sh`. [![cheat.sh-sublime-plugin-demo](https://cheat.sh/files/demo-sublime.gif)](https://github.com/gauravk-in/cheat.sh-sublime-plugin) *(GIF courtesy: Gaurav Kukreja, @gauravk-in)* ### IntelliJ IDEA * [idea-cheatsh-plugin](https://github.com/szymonprz/idea-cheatsh-plugin) * Install from [idea plugins marketplace](https://plugins.jetbrains.com/plugin/11942-cheat-sh-code-snippets) Usage: 1. Write query string 2. Select the query string 3. Press keyboard shortcut Alt + C , S to replace the selected query string by the answer [![idea-cheatsh-plugin](https://cheat.sh/files/idea-demo.gif)](https://github.com/szymonprz/idea-cheatsh-plugin) *(GIF courtesy: Szymon Przebierowski, @szymonprz)* ### QtCreator * [cheatsh-qtcreator](https://github.com/pozemka/cheatsh-qtcreator) Current features: * search word under cursor * search selected * query search * disable comments * paste answer (?TQ version) * custom server URL * custom search context (default is cpp) * hotkeys and menu [![cheatsh-qtcreator](https://user-images.githubusercontent.com/1259724/73876361-ecce5d00-4867-11ea-9f75-c5b127a9739c.gif)](https://github.com/pozemka/cheatsh-qtcreator) *(GIF courtesy: Pozemka, @pozemka)* ## Special pages There are several special pages that are not cheat sheets. Their names start with colon and have special meaning. Getting started: ``` :help description of all special pages and options :intro cheat.sh introduction, covering the most important usage questions :list list all cheat sheets (can be used in a subsection too: /go/:list) ``` Command line client `cht.sh` and shells support: ``` :cht.sh code of the cht.sh client :bash_completion bash function for tab completion :bash bash function and tab completion setup :fish fish function and tab completion setup :zsh zsh function and tab completion setup ``` Editors support: ``` :vim cheat.sh support for Vim :emacs cheat.sh function for Emacs :emacs-ivy cheat.sh function for Emacs (uses ivy) ``` Other pages: ``` :post how to post new cheat sheet :styles list of color styles :styles-demo show color styles usage examples :random fetches a random page (can be used in a subsection too: /go/:random) ``` ## Search To search for a keyword, use the query: ``` /~keyword ``` In this case search is not recursive — it is conducted only in a page of the specified level. For example: ``` /~snapshot look for snapshot in the first level cheat sheets /scala/~currying look for currying in scala cheat sheets ``` For a recursive search in all cheat sheets, use double slash: ``` /~snapshot/r look for snapshot in all cheat sheets ``` You can use special search options after the closing slash: ``` /~shot/bi case insensitive (i), word boundaries (b) ``` List of search options: ``` i case insensitive search b word boundaries r recursive search ``` ## Programming languages cheat sheets Cheat sheets related to programming languages are organized in namespaces (subdirectories), that are named according to the programming language. For each supported programming language there are several special cheat sheets: its own sheet, `hello`, `:list` and `:learn`. Say for lua it will look like: ``` lua lua/hello lua/:list lua/:learn ``` Some languages has the one-liners-cheat sheet, `1line`: ``` perl/1line ``` * `hello` describes how you can start with the language — install it if needed, build and run its programs, and it shows the "Hello world" program written in the language; * `:list` shows all topics related to the language * `:learn` shows a learn-x-in-minutes language cheat sheet perfect for getting started with the language. * `1line` is a collection of one-liners in this language * `weirdness` is a collection of examples of weird things in this language ![cheat.sh usage](http://cheat.sh/files/supported-languages-c++.png) At the moment, cheat.sh covers the 58 following programming languages (alphabetically sorted): |Prefix |Language |Basics|One-liners|Weirdness|StackOverflow| |-----------|----------|------|----------|---------|-------------| |`arduino/` |Arduino | | | |✓ | |`assembly/`|Assembly | | | |✓ | |`awk/` |AWK |✓ | | |✓ | |`bash/` |Bash |✓ | | |✓ | |`basic/` |BASIC | | | |✓ | |`bf/` |Brainfuck |✓ | | |✓ | |`c/` |C |✓ | | |✓ | |`chapel/` |Chapel |✓ | | |✓ | |`clean/` |Clean | | | |✓ | |`clojure/` |Clojure |✓ | | |✓ | |`coffee/` |CoffeeScript|✓ | | |✓ | |`cpp/` |C++ |✓ | | |✓ | |`csharp/` |C# |✓ | | |✓ | |`d/` |D |✓ | | |✓ | |`dart/` |Dart |✓ | | |✓ | |`delphi/` |Dephi | | | |✓ | |`dylan/` |Dylan |✓ | | |✓ | |`eiffel/` |Eiffel | | | |✓ | |`elixir/` |Elixir |✓ | | |✓ | |`elisp/` |ELisp |✓ | | |✓ | |`elm/` |Elm |✓ | | |✓ | |`erlang/` |Erlang |✓ | | |✓ | |`factor/` |Factor |✓ | | |✓ | |`fortran/` |Fortran |✓ | | |✓ | |`forth/` |Forth |✓ | | |✓ | |`fsharp/` |F# |✓ | | |✓ | |`go/` |Go |✓ | | |✓ | |`groovy/` |Groovy |✓ | | |✓ | |`haskell/` |Haskell |✓ | | |✓ | |`java/` |Java |✓ | | |✓ | |`js/` |JavaScript|✓ |✓ |✓ |✓ | |`julia/` |Julia |✓ | | |✓ | |`kotlin/` |Kotlin |✓ | | |✓ | |`latex/` |LaTeX |✓ | | |✓ | |`lisp/` |Lisp |✓ | | |✓ | |`lua/` |Lua |✓ | | |✓ | |`matlab/` |MATLAB |✓ | | |✓ | |`nim/` |Nim |✓ | | |✓ | |`ocaml/` |OCaml |✓ | | |✓ | |`octave/` |Octave |✓ | | |✓ | |`perl/` |Perl |✓ |✓ | |✓ | |`perl6/` |Perl 6 |✓ |✓ | |✓ | |`php/` |PHP |✓ | | |✓ | |`pike/` |Pike | | | |✓ | |`python/` |Python |✓ |✓ | |✓ | |`python3/` |Python 3 |✓ | | |✓ | |`r/` |R |✓ | | |✓ | |`racket/` |Racket |✓ | | |✓ | |`ruby/` |Ruby |✓ | | |✓ | |`rust/` |Rust |✓ | | |✓ | |`scala/` |Scala |✓ | | |✓ | |`scheme/` |Scheme |✓ | | |✓ | |`solidity/`|Solidity |✓ | | |✓ | |`swift/` |Swift |✓ | | |✓ | |`tcsh/` |Tcsh |✓ | | |✓ | |`tcl/` |Tcl |✓ | | |✓ | |`objective-c/`|Objective-C|✓ | | |✓ | |`vb/` |VisualBasic|✓ | | |✓ | |`vbnet/` |VB.Net |✓ | | |✓ | And several other topics, that are though related to programming, are not programming languages: |Prefix |Topic |Basics|StackOverflow| |-----------|----------|------|-------------| |`cmake/` |CMake |✓ |✓ | |`django/` |Django | |✓ | |`flask/` |Flask | |✓ | |`git/` |Git |✓ |✓ | ## Cheat sheets sources Instead of creating yet another mediocre cheat sheet repository, we are concentrating our efforts on creation of a unified mechanism to access selected existing well developed and good maintained cheat sheet repositories covering topics of our interest: programming and operating systems usage. *cheat.sh* uses selected community driven cheat sheet repositories and information sources, maintained by thousands of users, developers and authors all over the world (in the *Users* column number of contributors/number of stars is shown): | Cheat sheets | Repository | C/U* | Stars | Creation Date | |-------------------------|-------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------|---------------| | UNIX/Linux, programming | [cheat.sheets](https://github.com/chubin/cheat.sheets) | ![](https://img.shields.io/github/contributors-anon/chubin/cheat.sheets?label=%F0%9F%91%A5&labelColor=white) | ![](https://img.shields.io/github/stars/chubin/cheat.sheets?label=%E2%AD%90&labelColor=white) | May 1, 2017 | | UNIX/Linux commands | [tldr-pages/tldr](https://github.com/tldr-pages/tldr) | ![](https://img.shields.io/github/contributors-anon/tldr-pages/tldr?label=%F0%9F%91%A5&labelColor=white) | ![](https://img.shields.io/github/stars/tldr-pages/tldr?label=%E2%AD%90&labelColor=white) | Dec 8, 2013 | | UNIX/Linux commands | [cheat/cheat](https://github.com/cheat/cheat) | ![](https://img.shields.io/github/contributors-anon/cheat/cheat?label=%F0%9F%91%A5&labelColor=white) | ![](https://img.shields.io/github/stars/cheat/cheat?label=%E2%AD%90&labelColor=white) | Jul 28, 2013 | | Programming languages | [adambard/learnxinyminutes-docs](https://github.com/adambard/learnxinyminutes-docs) | ![](https://img.shields.io/github/contributors-anon/adambard/learnxinyminutes-docs?label=%F0%9F%91%A5&labelColor=white) | ![](https://img.shields.io/github/stars/adambard/learnxinyminutes-docs?label=%E2%AD%90&labelColor=white) | Jun 23, 2013 | | Go | [a8m/go-lang-cheat-sheet](https://github.com/a8m/go-lang-cheat-sheet) | ![](https://img.shields.io/github/contributors-anon/a8m/go-lang-cheat-sheet?label=%F0%9F%91%A5&labelColor=white) | ![](https://img.shields.io/github/stars/a8m/go-lang-cheat-sheet?label=%E2%AD%90&labelColor=white) | Feb 9, 2014 | | Perl | [pkrumnis/perl1line.txt](https://github.com/pkrumins/perl1line.txt) | ![](https://img.shields.io/github/contributors-anon/pkrumins/perl1line.txt?label=%F0%9F%91%A5&labelColor=white) | ![](https://img.shields.io/github/stars/pkrumins/perl1line.txt?label=%E2%AD%90&labelColor=white) | Nov 4, 2011 | | Programming languages | [StackOverflow](https://stackoverflow.com) | [14M](https://stackexchange.com/leagues/1/alltime/stackoverflow) | N/A | Sep 15, 2008 | (*) C/U — contributors for GitHub repositories, Users for Stackoverflow Pie diagram reflecting cheat sheets sources distribution (by number of cheat sheets on cheat.sh originating from a repository): ![cheat.sh cheat sheets repositories](http://cheat.sh/files/stat-2017-06-05.png) ## How to contribute ### How to edit a cheat sheet If you want to edit a cheat.sh cheat sheet, you should edit it in the upstream repository. You will find the name of the source repository in a browser when you open a cheat sheet. There are two github buttons at the bottom of the page: the second one is the button of the repository, which belongs the current cheat sheet. You can edit the cheat sheet directly in your browser (you need a github account for it). There is an edit button in the top right corner. If you click on it, an editor will be open. There you will change the cheat sheet (under the hood: the upstream repository is forked, your changes are committed in the forked repository, a pull request to the upstream repository owner is sent). ![cheat.sh cheat sheets repositories](http://cheat.sh/files/edit-cheat-sheet.png) ### How to add a cheat sheet If you want to add a cheat sheet, you have one of the following ways: * Add it to one of the external cheat sheets repositories; you should decide on your own what is the best repository for your cheat sheet; * Add it to the local cheat.sh repository ([cheat.sheets](https://github.com/chubin/cheat.sheets)) on github (fork, commit, pull request); * Post it on cheat.sh using curl or a web browser ([cheat.sh/:post](http://cheat.sh/:post)). If you want to change an existing cheat sheet, you have to find the original repository (when you open a cheat sheet in a browser, you see the repository's github button in the bottom of the cheat sheet), the cheat sheet is coming from, and change it there. After some time the changes will be synchronized on cheat.sh. ### How to add a cheat sheet repository If you want to add a cheat sheet repository to cheat.sh, please open an issue: * [Add a new repository](https://github.com/chubin/cheat.sh/issues/new) Please specify the name of the repository, and give its short description. ## Installation and standalone usage You don't need to install anything, to start using *cheat.sh*. There are two cases, when you want to install *cheat.sh* locally: 1. You plan to use it off-line, without Internet access; 2. You want to use your own cheat sheets (additionally, or as a replacement). Installation process in described in details here: [cheat.sh standalone installation](doc/standalone.md) ================================================ FILE: bin/app.py ================================================ #!/usr/bin/env python # vim: set encoding=utf-8 # pylint: disable=wrong-import-position,wrong-import-order """ Main server program. Configuration parameters: path.internal.malformed path.internal.static path.internal.templates path.log.main path.log.queries """ from __future__ import print_function import sys if sys.version_info[0] < 3: reload(sys) sys.setdefaultencoding("utf8") import sys import logging import os import requests import jinja2 from flask import Flask, request, send_from_directory, redirect, Response sys.path.append(os.path.abspath(os.path.join(__file__, "..", "..", "lib"))) from config import CONFIG from limits import Limits from cheat_wrapper import cheat_wrapper from post import process_post_request from options import parse_args from stateful_queries import save_query, last_query if not os.path.exists(os.path.dirname(CONFIG["path.log.main"])): os.makedirs(os.path.dirname(CONFIG["path.log.main"])) logging.basicConfig( filename=CONFIG["path.log.main"], level=logging.DEBUG, format="%(asctime)s %(message)s", ) # Fix Flask "exception and request logging" to `stderr`. # # When Flask's werkzeug detects that logging is already set, it # doesn't add its own logger that prints exceptions. stderr_handler = logging.StreamHandler() logging.getLogger().addHandler(stderr_handler) # # Alter log format to disting log lines from everything else stderr_handler.setFormatter(logging.Formatter("%(filename)s:%(lineno)s: %(message)s")) # # Sometimes werkzeug starts logging before an app is imported # (https://github.com/pallets/werkzeug/issues/1969) # resulting in duplicating lines. In that case we need root # stderr handler to skip lines from werkzeug. class SkipFlaskLogger(object): def filter(self, record): if record.name != "werkzeug": return True if logging.getLogger("werkzeug").handlers: stderr_handler.addFilter(SkipFlaskLogger()) app = Flask(__name__) # pylint: disable=invalid-name app.jinja_loader = jinja2.ChoiceLoader( [app.jinja_loader, jinja2.FileSystemLoader(CONFIG["path.internal.templates"])] ) LIMITS = Limits() PLAIN_TEXT_AGENTS = [ "curl", "httpie", "lwp-request", "wget", "python-requests", "openbsd ftp", "powershell", "fetch", "aiohttp", "xh", ] def _is_html_needed(user_agent): """ Basing on `user_agent`, return whether it needs HTML or ANSI """ return all([x not in user_agent for x in PLAIN_TEXT_AGENTS]) def is_result_a_script(query): return query in [":cht.sh"] @app.route("/files/") def send_static(path): """ Return static file `path`. Can be served by the HTTP frontend. """ return send_from_directory(CONFIG["path.internal.static"], path) @app.route("/favicon.ico") def send_favicon(): """ Return static file `favicon.ico`. Can be served by the HTTP frontend. """ return send_from_directory(CONFIG["path.internal.static"], "favicon.ico") @app.route("/malformed-response.html") def send_malformed(): """ Return static file `malformed-response.html`. Can be served by the HTTP frontend. """ dirname, filename = os.path.split(CONFIG["path.internal.malformed"]) return send_from_directory(dirname, filename) def log_query(ip_addr, found, topic, user_agent): """ Log processed query and some internal data """ log_entry = "%s %s %s %s\n" % (ip_addr, found, topic, user_agent) with open(CONFIG["path.log.queries"], "ab") as my_file: my_file.write(log_entry.encode("utf-8")) def get_request_ip(req): """ Extract IP address from `request` """ if req.headers.getlist("X-Forwarded-For"): ip_addr = req.headers.getlist("X-Forwarded-For")[0] if ip_addr.startswith("::ffff:"): ip_addr = ip_addr[7:] else: ip_addr = req.remote_addr if req.headers.getlist("X-Forwarded-For"): ip_addr = req.headers.getlist("X-Forwarded-For")[0] if ip_addr.startswith("::ffff:"): ip_addr = ip_addr[7:] else: ip_addr = req.remote_addr return ip_addr def get_answer_language(request): """ Return preferred answer language based on domain name, query arguments and headers """ def _parse_accept_language(accept_language): languages = accept_language.split(",") locale_q_pairs = [] for language in languages: try: if language.split(";")[0] == language: # no q => q = 1 locale_q_pairs.append((language.strip(), "1")) else: locale = language.split(";")[0].strip() weight = language.split(";")[1].split("=")[1] locale_q_pairs.append((locale, weight)) except IndexError: pass return locale_q_pairs def _find_supported_language(accepted_languages): for lang_tuple in accepted_languages: lang = lang_tuple[0] if "-" in lang: lang = lang.split("-", 1)[0] return lang return None lang = None hostname = request.headers["Host"] if hostname.endswith(".cheat.sh"): lang = hostname[:-9] if "lang" in request.args: lang = request.args.get("lang") header_accept_language = request.headers.get("Accept-Language", "") if lang is None and header_accept_language: lang = _find_supported_language(_parse_accept_language(header_accept_language)) return lang def _proxy(*args, **kwargs): # print "method=", request.method, # print "url=", request.url.replace('/:shell-x/', ':3000/') # print "headers=", {key: value for (key, value) in request.headers if key != 'Host'} # print "data=", request.get_data() # print "cookies=", request.cookies # print "allow_redirects=", False url_before, url_after = request.url.split("/:shell-x/", 1) url = url_before + ":3000/" if "q" in request.args: url_after = "?" + "&".join("arg=%s" % x for x in request.args["q"].split()) url += url_after print(url) print(request.get_data()) resp = requests.request( method=request.method, url=url, headers={key: value for (key, value) in request.headers if key != "Host"}, data=request.get_data(), cookies=request.cookies, allow_redirects=False, ) excluded_headers = [ "content-encoding", "content-length", "transfer-encoding", "connection", ] headers = [ (name, value) for (name, value) in resp.raw.headers.items() if name.lower() not in excluded_headers ] response = Response(resp.content, resp.status_code, headers) return response @app.route("/", methods=["GET", "POST"]) @app.route("/", methods=["GET", "POST"]) def answer(topic=None): """ Main rendering function, it processes incoming weather queries. Depending on user agent it returns output in HTML or ANSI format. Incoming data: request.args request.headers request.remote_addr request.referrer request.query_string """ user_agent = request.headers.get("User-Agent", "").lower() html_needed = _is_html_needed(user_agent) options = parse_args(request.args) if topic in [ "apple-touch-icon-precomposed.png", "apple-touch-icon.png", "apple-touch-icon-120x120-precomposed.png", ] or (topic is not None and any(topic.endswith("/" + x) for x in ["favicon.ico"])): return "" request_id = request.cookies.get("id") if topic is not None and topic.lstrip("/") == ":last": if request_id: topic = last_query(request_id) else: return "ERROR: you have to set id for your requests to use /:last\n" else: if request_id: save_query(request_id, topic) if request.method == "POST": process_post_request(request, html_needed) if html_needed: return redirect("/") return "OK\n" if "topic" in request.args: return redirect("/%s" % request.args.get("topic")) if topic is None: topic = ":firstpage" if topic.startswith(":shell-x/"): return _proxy() # return requests.get('http://127.0.0.1:3000'+topic[8:]).text lang = get_answer_language(request) if lang: options["lang"] = lang ip_address = get_request_ip(request) if "+" in topic: not_allowed = LIMITS.check_ip(ip_address) if not_allowed: return "429 %s\n" % not_allowed, 429 html_is_needed = _is_html_needed(user_agent) and not is_result_a_script(topic) if html_is_needed: output_format = "html" else: output_format = "ansi" result, found = cheat_wrapper( topic, request_options=options, output_format=output_format ) if "Please come back in several hours" in result and html_is_needed: malformed_response = open( os.path.join(CONFIG["path.internal.malformed"]) ).read() return malformed_response log_query(ip_address, found, topic, user_agent) if html_is_needed: return result return Response(result, mimetype="text/plain") ================================================ FILE: bin/clean_cache.py ================================================ import sys import redis REDIS = redis.Redis(host="localhost", port=6379, db=0) for key in sys.argv[1:]: REDIS.delete(key) ================================================ FILE: bin/release.py ================================================ #!/usr/bin/env python from __future__ import print_function from datetime import datetime import os from os import path import re import shutil import subprocess from subprocess import Popen import sys SHARE_DIR = path.join(path.dirname(__file__), "../share/") def run(args): return Popen(args, stdout=sys.stdout, stderr=sys.stderr).wait() status = subprocess.check_output(["git", "status", "--porcelain"]) if len(status) > 0: print("Unclean working tree. Commit or stash changes first.", file=sys.stderr) sys.exit(1) timestamp = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S +0000") cht_curr = path.join(SHARE_DIR, "cht.sh.txt") cht_new = path.join(SHARE_DIR, "cht.sh.txt.new") re_version = re.compile(r"^__CHTSH_VERSION=(.*)$") re_timestamp = re.compile(r"^__CHTSH_DATETIME=.*$") with open(cht_curr, "rt") as fin: with open(cht_new, "wt") as fout: for line in fin: match = re_version.match(line) if match: version = int(match.group(1)) + 1 fout.write("__CHTSH_VERSION=%s\n" % version) continue match = re_timestamp.match(line) if match: fout.write('__CHTSH_DATETIME="%s"\n' % timestamp) continue fout.write(line) shutil.copymode(cht_curr, cht_new) os.remove(cht_curr) os.rename(cht_new, cht_curr) message = "cht: v%s" % version run(["git", "add", cht_curr]) run(["git", "commit", "-m", message]) run(["git", "tag", "cht@%s" % version, "-m", message]) ================================================ FILE: bin/srv.py ================================================ #!/usr/bin/env python # # Serving cheat.sh with `gevent` # from gevent.monkey import patch_all from gevent.pywsgi import WSGIServer patch_all() import os import sys from app import app, CONFIG if "--debug" in sys.argv: # Not all debug mode features are available under `gevent` # https://github.com/pallets/flask/issues/3825 app.debug = True if "CHEATSH_PORT" in os.environ: port = int(os.environ.get("CHEATSH_PORT")) else: port = CONFIG["server.port"] srv = WSGIServer((CONFIG["server.bind"], port), app) print("Starting gevent server on {}:{}".format(srv.address[0], srv.address[1])) srv.serve_forever() ================================================ FILE: doc/README-ja.md ================================================ 唯一のチートシート https://cheat.sh/ が必要です 世界の最高のコミュニティ駆動チートシートリポジトリへの統一されたアクセス。 理想的なチートシートのようなものがあるとすぐに想像してみましょう。 どのように見える? どのような機能が必要ですか? 簡潔に – 簡潔にする必要があります。 それはあなたが必要とするものだけを含んでいなければなりません。 速く – それを即座に使用することが可能でなければなりません。 包括的 – あなたが持つ可能性があるすべての質問に対する回答を含める必要があります。 普遍的な – 準備ができていなくても、必要に応じてどこでもすぐに利用できるはずです。 邪魔になりません – あなたの主な仕事からあなたをそらすことはありません。 先入観 – それはあなたがその科目を学ぶのに役立ちます。 目立たない – 完全に気付かれないで使用することが可能でなければならない。 そんなことは存在しない。 特徴 チート.sh 単純なカール/ブラウザインターフェイスを備えています。 55のプログラミング言語、いくつかのDBMS、および1000以上の最も重要なUNIX / Linuxコマンドをカバーしています。 世界で最も優れたコミュニティ主導のチートシートリポジトリへのアクセスと、StackOverflowへのアクセスを提供します。 あらゆる場所で利用でき、インストールは必要ありません。 ultrafastは、原則として100ミリ秒以内に回答を返します。 便利で便利なコマンドラインクライアントcht.shがありますが、これは必須ではありません。 ブラウザを開いて精神的なコンテキストを切り替えることなく、コードエディタから直接使用することができます。 特殊モード(ステルスモード)をサポートしています。このモードでは、鍵に触れたり、音を出させたりすることなく、完全に目に見えない状態で使用できます。 使用法 コマンドラインからUNIX / Linuxコマンドのチートシートを取得するには、 curlまたは他のHTTP / HTTPSクライアントを使用して、クエリのコマンド名を指定してサービスをクエリします。 ``` curl cheat.sh/tar curl cht.sh/curl curl https://cheat.sh/rsync curl https://cht.sh/tr ``` ご覧のとおり、HTTPSとHTTPの両方を使用してサービスにアクセスし、長い(cheat.sh)サービス名と短い(cht.sh)サービス名の両方にアクセスできます。 ここで、 tar 、 curl 、 rsync 、 trはUNIX / Linuxコマンドの名前です。あなたはチートシートを入手したいと思っています。 必要なコマンドの名前がわからない場合は、 ~KEYWORD記法を使用して検索することができます。 たとえば、ファイルシステム/ボリューム/その他のsnapshotsを作成する方法を知るには: ``` curl cht.sh/~snapshot ``` プログラミング言語チートシートは、ルート名前空間には直接配置されず、専用の特別な名前空間に配置されます。 ``` curl cht.sh/go/Pointers curl cht.sh/scala/Functions curl cht.sh/python/lambda ``` 利用可能なプログラミング言語チートシートのリストを取得するには、特別なクエリを実行します。list: ``` curl cht.sh/go/:list ``` (ほぼ)それぞれのプログラミング言語には、 :learnという名前の特別なページがあります。これは、言語の基礎を説明しています( 「Learn X in Y」プロジェクトからの直接マッピングです)。 あなたが言語を学び始めたばかりの方は、良い出発点になるかもしれません。 いくつかのプログラミング言語のクエリ用のチートシートがない場合(ほとんどの場合そうです)、利用可能なチートシートとStackOverflowでの回答に基づいてオンザフライで生成されます。 もちろん、返されたチートシートが100%ヒットしたという保証はありませんが、ほとんど常にあなたが探しているものです。 これらの(そしてあなた自身の)クエリを試して、その印象をどのように見えるかを見てみましょう: ``` curl cht.sh/go/reverse+a+list curl cht.sh/python/random+list+elements curl cht.sh/js/parse+json curl cht.sh/lua/merge+tables curl cht.sh/clojure/variadic+function ``` いくつかのクエリの答えが気に入らない場合は、別のパラメータを選択することができます:追加のパラメータ/1 、 /2などをつけてクエリを繰り返します: ``` curl cht.sh/python/random+string curl cht.sh/python/random+string/1 curl cht.sh/python/random+string/2 ``` チートシートは照会されたプログラミング言語のコードとしてフォーマットされています(少なくともこれを行うために最善を尽くしています)。この言語のプログラムに直接貼り付けることができます。 テキストコメントがある場合は、言語構文に従って書式設定されます。 ``` $ curl cht.sh/lua/table+keys -- lua: retrieve list of keys in a table local keyset={} local n=0 for k,v in pairs(tab) do n=n+1 keyset[n]=k end --[[ [ Note that you cannot guarantee any order in keyset. If you want the [ keys in sorted order, then sort keyset with table.sort(keyset). [ [ [lhf] [so/q/12674345] [cc by-sa 3.0] ]] ``` 答えにテキストコメントが必要ない場合は、特別なオプションを使用してコメントを削除できます?Q : ``` $ curl cht.sh/lua/table+keys?Q local keyset={} local n=0 for k,v in pairs(tab) do n=n+1 keyset[n]=k end ``` 構文強調表示が必要ない場合は、 ?Tを使ってスイッチをオフにし?T 。 オプションを一緒に組み合わせることができます: ``` curl cht.sh/go/reverse+a+list?Q curl cht.sh/python/random+list+elements?Q curl cht.sh/js/parse+json?Q curl cht.sh/lua/merge+tables?QT curl cht.sh/clojure/variadic+function?QT ``` 下記および/:help記載されているすべてのオプションの完全なリスト あなた自身の質問をお試しください。 次のルールに従ってください。 より具体的になるようにしてください( /python/append+fileは/python/fileや/python/appendよりも優れてい/python/append )。 可能であれば実践的な質問をする(しかし理論的な質問も可能である)。 プログラミング言語に関する質問のみを行います。 セクション名としてプログラミング言語の名前を指定します。 空白ではなく+区切ります。 とにかく無視される特殊文字は使用しないでください。 以下のプログラミング言語のクエリについての詳細を読む。 コマンドラインクライアント、cht.sh cheat.shサービスには独自のコマンドラインクライアント( cht.sh )があり、 curlを使ってサービスを直接照会するのに比べ、いくつかの便利な機能があります。 永続的なクエリコンテキストとreadlineサポートを備えた特別なシェルモード。 クエリの履歴。 クリップボードの統合。 シェルのタブ補完のサポート(bash、fish、zsh); ステルスモード。 クライアントをインストールするには: ``` curl https://cht.sh/:cht.sh > ~/bin/cht.sh chmod +x ~/bin/cht.sh ``` さて、あなたはcurl代わりにcht.shを使い、より自然な方法であなたのクエリを+代わりにスペースで書くことができます: ``` $ cht.sh go reverse a list $ cht.sh python random list elements $ cht.sh js parse json ``` 特別なシェルモードでクライアントを起動する方がさらに便利です: ``` $ cht.sh --shell cht.sh> go reverse a list ``` すべてのクエリがほぼ同じ言語であると想定されている場合は、クエリのコンテキストを変更して、プログラミング言語の名前を繰り返すことができます。 ``` $ cht.sh --shell cht.sh> cd go cht.sh/go> reverse a list ``` このコンテキストでクライアントを起動することさえできます: ``` $ cht.sh --shell go cht.sh/go> reverse a list ... cht.sh/go> join a list ... ``` コンテキストを変更したい場合は、 cdコマンドで行うことができます。あるいは、他の言語のクエリを1つだけ実行する場合は、 /ください: ``` $ cht.sh --shell go ... cht.sh/go> /python dictionary comprehension ... ``` 最後の回答をクリップボードにcopyする場合は、 c ( copy )コマンドまたはC (コメントなしのccopy )コマンドを使用できます。 ``` cht.sh/python> append file # python - How do you append to a file? with open("test.txt", "a") as myfile: myfile.write("appended text") cht.sh/python> C copy: 2 lines copied to the selection ``` 他の内部cht.shコマンドのhelpを入力してください。 ``` cht.sh> help help - show this help hush - do not show the 'help' string at start anymore cd LANG - change the language context copy - copy the last answer in the clipboard (aliases: yank, y, c) ccopy - copy the last answer w/o comments (cut comments; aliases: cc, Y, C) exit - exit the cheat shell (aliases: quit, ^D) id [ID] - set/show an unique session id ("reset" to reset, "remove" to remove) stealth - stealth mode (automatic queries for selected text) update - self update (only if the scriptfile is writeable) version - show current cht.sh version /:help - service help QUERY - space separated query staring (examples are below) cht.sh> python zip list cht.sh/python> zip list cht.sh/go> /python zip list ``` cht.shクライアントの設定ファイルは~/.cht.sh/cht.sh.confます。 これを使用して、各クエリで使用するクエリオプションを指定します。 たとえば、構文の強調表示をオフに切り替えるには、次の内容のファイルを作成します。 ``` QUERY_OPTIONS="T" ``` または、特殊な構文強調表示テーマを使用する場合は、次のようにします。 ``` QUERY_OPTIONS="style=native" ``` ( curl cht.sh/:styles-demoサポートされているすべてのスタイルが表示されます)。 タブ補完 cht.shタブ補完のサポートをcht.shにするには、 ~/.bashrc : :bash_completionスクリプトを追加してください: $ curl https://cheat.sh/:bash_completion > ~/.bash.d/cht.sh $ . ~/.bash.d/cht.sh $ # and add . ~/.bash.d/cht.sh to ~/.bashrc ステルスモード 実際のチートシートの重要な特性の1つは、それが完全に気付かれずに使用できるということです。 cheat.shはまったく気付かれずに使えます。 cheat.shクライアント、 cht.shには、 ステルスモードと呼ばれる特別なモードがあります。これは、キーボードに触れてチートシートを開く必要がないことを利用しています。 このモードでは、マウスでテキストを選択すると(そしてX Window Systemの選択バッファやクリップボードに追加されるとすぐに)、それはcheat.shのクエリ文字列として使用され、特派員のチートシートは自動的に表示されます。 インタビュー担当者が共有文書(Google Docsなど)を使っていくつかの質問をし、そこにコーディング回答を書くことになっているオンラインインタビューをしていることを想像してみましょう。面接官にあなたがそれを正しく聞いたことを示すために) ステルスモードのcht.shを使用しているときは、何らかの質問のためにチートシートを見るために必要なのは、マウスを使って質問を選択することだけです。 答えにテキストを入れたくない場合は、コードだけが必要です。ステルスモードを開始するときには、 Qオプションを使用してください。 ``` You: Hi! | $ cht.sh --shell python She: Hi! | cht.sh/python> stealth Q She: Are you ready for a small interview? | stealth: you are in the stealth mode; select any text She: Just a couple of questions | stealth: selections longer than 5 words are ignored She: We will talk about python | stealth: query arguments: ?Q She: Let's start from something simple. | stealth: use ^C to leave this mode She: Do you know how to reverse a list in python? | You: Sure | You: (selecting "reverse a list") | stealth: reverse a list | reverse_lst = lst[::-1] You: lst[::-1]? | She: Good. | She: Do you know how to chain a list of lists? | You: (selecting "chain a list of lists") | stealth: chain a list of lists | import itertools | a = [["a","b"], ["c"]] | print list(itertools.chain.from_iterable(a)) You: May I use external modules? | She: What module do you want to use? | You: itertools | She: Yes, you may use it | You: Ok, then: | You: itertools.chain.from_iterable(a) | She: Good. Let's try something harder. | She: What about quicksort implementation? | You: (selecting "quicksort implementation") | stealth: quicksort implementation You: Let me think about it. | (some big and clumsy lowlevel implementation shown) You: Well...(starting typing it in) | def sort(array=[12,4,5,6,7,3,1,15]): | less = [] She: (seeing your ugly pascal style) | equal = [] She: Could you write it more concise? | greater = [] | if len(array) > 1: You: What do you mean? | pivot = array[0] | for x in array: She: I mean, | if x < pivot: less.append(x) She: do you really need all these ifs and fors? | if x == pivot: equal.append(x) She: Could you may be just use filter instead? | if x > pivot: greater.append(x) | return sort(less)+equal+sort(greater) You: quicksort with filter? | else: | return array She: Yes | You: (selecting "quicksort with filter") | stealth: quicksort with filter You: Ok, I will try. | return qsort(filter(lt, L[1:]))+[pivot] \ You: Something like this? | +qsort(filter(ge, L[1:])) You: qsort(filter(lt, L[1:]))+[pivot] \ | + qsort(filter(ge, L[1:])) | | She: Yes! Perfect! Exactly what I wanted to see! | | ``` もちろん、それはちょうど楽しいことです。あなたが行ったときに何が起こるかを知っているので、コーディングのインタビューで決して欺くべきではありません。 エディターの統合 エディタから直接cheat.shを使うことができます( VimとEmacsは現在サポートされています)。 あなたのブラウザを開き、グーグルで、スタックオーバーフローをブラウズし、最終的に必要なコードスニペットをクリップボードにコピーして後でエディタに貼り付けるのではなく、エディタを離れずに同じことを達成することができます。 これはVimのように見えます: プログラムの編集中に質問がある場合は、バッファに直接質問を入力してKKを押してください。 ポケットベルであなたの質問に対する答えが得られます。 ( KBすると、別のバッファで回答が得られます)。 答えが気に入ったらバッファやページャから手作業で貼り付けてください。怠け者の場合はKPを使って質問の下/下に貼り付けることができます。 コメントなしで回答が必要な場合は、 KCが最後のクエリを再生してそれらを切り替えます。 シンセシス (Vim用)などの静的解析プラグインを使用している場合は、警告とエラーメッセージをcheat.shクエリとして使用できます:カーソルを問題の行に置き、 KE :警告の説明を開きます新しいバッファに入れます。 ヴィム cheat.sh-vim – Vimのサポート ここにVimの設定例を示します: ``` " some configuration above ... let mapleader=" " call vundle#begin() Bundle 'gmarik/vundle' Bundle 'scrooloose/syntastic' Bundle 'dbeniamine/cheat.sh-vim' call vundle#end() let g:syntastic_javascript_checkers = [ 'jshint' ] let g:syntastic_ocaml_checkers = ['merlin'] let g:syntastic_python_checkers = ['pylint'] let g:syntastic_shell_checkers = ['shellcheck'] " some configuration below ... ``` この例では、いくつかのVimプラグインが使用されています: gmarik / vundle – Vimプラグインマネージャ scrooloose / syntastic – 構文チェックプラグイン cheat.sh-vim – Vimのサポート Syntasticは警告とエラー(code analysisツールで見つかった: jshint 、 jshint 、 pylint 、 shellcheckt etc.), and cheat.sh-vim`を表示すると、エディタに書き込まれたプログラミング言語のクエリに関するエラーと警告と回答の説明が表示されます。 cheat.sh Vimプラグインの最も重要な機能が表示されているデモをご覧ください(5分): または、スクロールしたり一時停止したりする場合は、YouTubeでも同じです: Emacs cheat-sh.el – Emacsのサポート(cheat.sh/:emacsでも利用可能) cheat.sh/:emacs-ivy – ivyユーザーのEmacsサポート 特別ページ いくつかの特別なページがあり(その名前は常にコロンで始まります)、チートシートではなく特別な意味を持っています。 入門: ``` :help description of all special pages and options :intro cheat.sh introduction, covering the most important usage questions :list list all cheat sheets (can be used in a subsection too: /go/:list) ``` コマンドライン・クライアントcht.shとシェルは次のものをサポートしています。 ``` :cht.sh code of the cht.sh client :bash_completion bash function for tab completion :bash bash function and tab completion setup :fish fish function and tab completion setup :zsh zsh function and tab completion setup ``` エディターのサポート: :vim cheat.sh support for Vim :emacs cheat.sh function for Emacs :emacs-ivy cheat.sh function for Emacs (uses ivy) その他のページ: ``` :post how to post new cheat sheet :styles list of color styles :styles-demo show color styles usage examples ``` サーチ キーワードを検索するには、次のクエリを使用します。 ``` /~keyword ``` この場合、検索は再帰的ではなく、指定されたレベルのページでのみ実行されます。 例えば: ``` /~snapshot look for snapshot in the first level cheat sheets /scala/~currying look for currying in scala cheat sheets ``` すべてのチートシートで再帰的検索を行うには、二重スラッシュを使用します。 /~snapshot/r look for snapshot in all cheat sheets スラッシュの後に特殊な検索オプションを使用することができます。 /~shot/bi case insensitive (i), word boundaries (b) 検索オプションのリスト: ``` i case insensitive search b word boundaries r recursive search ``` プログラミング言語チートシート プログラミング言語に関連するチートシートは、プログラミング言語に従って名前が付けられた名前空間(サブディレクトリ)に編成されています。 サポートされているプログラミング言語ごとに、独自のsheet、 hello 、 :list 、 :learnいくつかの特別なチートシートがあり:learn 。 それはルアのように見えます: ``` lua lua/hello lua/:list lua/:learn ``` いくつかの言語には、1行のチートシート、1 1line : perl/1line helloは、あなたがどのように言語を使い始めることができるかを記述します – 必要に応じてインストールし、プログラムをビルドして実行し、言語で書かれた “Hello world”プログラムを表示します。 :listには言語に関連するすべてのトピックが表示されます :learnは、言語を使い始めるのに最適なx-in-minutes言語のチートシートを表示します。 1lineはこの言語の1ライナーの集合です weirdnessなことはこの言語の奇妙なものの例の集まりです 現時点では、cheat.shは以下の55のプログラミング言語(アルファベット順にソートされています)をカバーしています: 接頭辞 言語 基本 ワンライナー 奇妙さ スタックオーバーフロー arduino/ Arduino ✓ assembly/ アセンブリ ✓ awk/ AWK ✓ ✓ bash/ バッシュ ✓ ✓ basic/ ベーシック ✓ bf/ 頭痛 ✓ ✓ c/ C ✓ ✓ chapel/ チャペル ✓ ✓ clean/ クリーン ✓ clojure/ Clojure ✓ ✓ coffee/ CoffeeScript ✓ ✓ cpp/ C ++ ✓ ✓ csharp/ C# ✓ ✓ d/ D ✓ ✓ dart/ ダーツ ✓ ✓ delphi/ デフ ✓ dylan/ ディラン ✓ ✓ eiffel/ エッフェル ✓ elixir/ エリクシール ✓ ✓ elisp/ ELisp ✓ ✓ elm/ エルム ✓ ✓ erlang/ アーラン ✓ ✓ factor/ 因子 ✓ ✓ fortran/ Fortran ✓ ✓ forth/ 四方 ✓ ✓ fsharp/ F# ✓ ✓ go/ 行こう ✓ ✓ groovy/ Groovy ✓ ✓ haskell/ ハスケル ✓ ✓ java/ Java ✓ ✓ js/ JavaScript ✓ ✓ ✓ ✓ julia/ ジュリア ✓ ✓ kotlin/ コトリン ✓ ✓ lisp/ Lisp ✓ ✓ lua/ ルア ✓ ✓ matlab/ MATLAB ✓ ✓ ocaml/ OCaml ✓ ✓ perl/ Perl ✓ ✓ ✓ perl6/ Perl 6 ✓ ✓ ✓ php/ PHP ✓ ✓ pike/ パイク ✓ python/ Python ✓ ✓ python3/ Python 3 ✓ ✓ r/ R ✓ ✓ racket/ ラケット ✓ ✓ ruby/ ルビー ✓ ✓ rust/ 錆 ✓ ✓ scala/ スカラ ✓ ✓ scheme/ スキーム ✓ ✓ swift/ 迅速 ✓ ✓ tcsh/ Tcsh ✓ ✓ tcl/ Tcl ✓ ✓ objective-c/ 目標-C ✓ ✓ vb/ VisualBasic ✓ ✓ vbnet/ VB.Net ✓ ✓ チートシートのソース さらに別の平凡なチートシートリポジトリを作成するのではなく、既存のよく開発されたよく管理されたチートシートリポジトリにアクセスする統一メカニズムの作成に力を注いでいます。 cheat.shは世界中の何千ものユーザー、開発者、作者によって管理されている選択されたコミュニティ駆動のチートシートリポジトリと情報ソースを使用します(貢献者のユーザー列数/星数が表示されます)。 カンニングペーパー リポジトリ ユーザー 作成日 UNIX / Linux、プログラミング カンニングペーパー 6/54 2017年5月1日 UNIX / Linuxコマンド tldr-pages / tldr 541/17360 2013年12月8日 UNIX / Linuxコマンド クリサンレン/チート 105/4193 2013年7月28日 プログラミング言語 adambard / learnxinyminutes-docs 1096/5285 2013年6月23日 行こう a8m / go-lang-cheat-sheet 29/3034 2014年2月9日 Perl pkrumnis / perl1line.txt 4/165 2011年11月4日 プログラミング言語 スタックオーバーフロー 9M 2008年9月15日 チートシートソースの分布を反映するパイダイアグラム(リポジトリから生成されたチート.shのチートシートの数による): 貢献する方法 チートシートの編集方法 cheat.shチートシートを編集する場合は、アップストリームレポジトリで編集する必要があります。 チートシートを開くと、ブラウザにソースリポジトリの名前が表示されます。 ページ下部に2つのgithubボタンがあります:2番目のボタンは、現在のチートシートに属するリポジトリのボタンです。 チートシートをブラウザで直接編集できます(githubアカウントが必要です)。 右上に編集ボタンがあります。 それをクリックすると、エディタが開きます。 そこではチートシートを変更します(ボンネットの下:アップストレームリポジトリはフォークされ、変更はフォークされたリポジトリでコミットされ、上流のリポジトリ所有者へのプルリクエストが送信されます)。 チートシートを追加する方法 チートシートを追加する場合は、次のいずれかの方法があります。 それを外部のチートシートレポジトリの1つに追加します。 自分のチートシートに最適なリポジトリが何であるかを決める必要があります。 github(fork、commit、pull request)のローカルcheat.shリポジトリ( cheat.sheets )に追加します。 curlやウェブブラウザ( cheat.sh/:post )を使ってcheat.shに投稿してください。 既存のチートシートを変更したい場合は、元のリポジトリを見つける必要があります(ブラウザでチートシートを開くと、チートシートの下部にリポジトリのgithubボタンが表示されます)、チートシートは、それを変更してください。 しばらくすると、変更はcheat.shで同期されます。 チートシートリポジトリを追加する方法 cheat.shにチートシートリポジトリを追加する場合は、問題を開いてください: 新しいリポジトリを追加する リポジトリの名前を指定し、簡単な説明をしてください。 ================================================ FILE: doc/standalone.md ================================================ You don't need to install anything, to start using *cheat.sh*. The only tool that you need is *curl*, which is typically installed in every system. In the rare cases when *curl* is not installed, there should be one of its alternatives in the system: *wget*, *wget2*, *httpie*, *ftp* (with HTTP support), *fetch*, etc. There are two cases, when you want to install *cheat.sh* locally: 1. You plan to use it off-line, without Internet access; 2. You want to use your own cheat sheets (additionally, or as a replacement). In this case you need to install cheat.sh locally. ## How to install cheat.sh locally To use cheat.sh offline, you need to: 1. Install it, 2. Fetch its data sources. If you already have the cht.sh cli client locally, you can use it for the standalone installation. Otherwise it must be installed first. ``` curl https://cht.sh/:cht.sh > ~/bin/cht.sh chmod +x ~/bin/cht.sh ``` Now you can install cheat.sh locally: ``` cht.sh --standalone-install ``` During the installation process, cheat.sh and its data sources will be installed locally. By default `~/.cheat.sh` is used as the installation directory. ![cheat.sh standalone installation](https://user-images.githubusercontent.com/3875145/57986904-ef3f1b80-7a7a-11e9-9531-ef37ec74b03a.png) If you don't plan to use Redis for caching, switch the caching off in the config file: ``` $ vim ~/.cheat.sh/etc/config.yaml cache: type: none ``` or with the environment variable `CHEATSH_CACHE_TYPE=none`. ## Update cheat sheets Cheat sheets are fetched and installed to `~/.cheat.sh/upstream`. To keep the cheat sheets up to date, run the `cheat.sh` `update-all` command on regular basis. Ideally, add it to *cron*: ``` 0 5 0 0 0 $HOME/.cheat.sh/ve/bin/python $HOME/.cheat.sh/lib/fetch.py update-all ``` In this example, all information sources will be updated each day at 5:00 local time, on regular basis. ## cheat.sh server mode Your local cheat.sh installation is full-fledged, and it can handle incoming HTTP/HTTPS queries. To start cheat.sh in the server mode, run: ``` $HOME/.cheat.sh/ve/bin/python $HOME/.cheat.sh/bin/srv.py ``` You can also use `gunicorn` to start the cheat.sh server. ## Docker You can deploy cheat.sh as a docker container. Use `Dockerfile` in the source root directory, to build the Docker image: ``` docker build . ``` ## Limitations Some cheat sheets not available in the offline mode for the moment. The reason for that is that to process some queries, cheat.sh needs to access the Internet itself, because it does not have the necessary data locally. We are working on that how to overcome this limitation, but for the moment it still exists. ## Mac OS X Notes ### Installing Redis To install Redis on Mac OS X (using `brew`): ``` $ brew install redis $ ln -sfv /usr/local/opt/redis/*.plist ~/Library/LaunchAgents $ launchctl load ~/Library/LaunchAgents/homebrew.mxcl.redis.plist $ redis-cli ping PONG ``` ================================================ FILE: docker-compose.debug.yml ================================================ # Compose override, see https://docs.docker.com/compose/extends/ # # - Run `flask` standalone server with more debug aids instead of `gevent` # - Turn on Flask debug mode to print tracebacks and autoreload code # - Mounts fresh sources from current dir as volume # # Usage: # docker-compose -f docker-compose.yml -f docker-compose.debug.yml up # version: '2' services: app: environment: FLASK_ENV: development #FLASK_RUN_RELOAD: False FLASK_APP: "bin/app.py" FLASK_RUN_HOST: 0.0.0.0 FLASK_RUN_PORT: 8002 entrypoint: ["/usr/bin/flask", "run"] ================================================ FILE: docker-compose.yml ================================================ version: '2' services: app: build: . image: cheat.sh container_name: chtsh depends_on: - redis environment: - CHEATSH_CACHE_REDIS_HOST=redis ports: - "8002:8002" volumes: - .:/app:Z redis: image: redis:4-alpine volumes: - redis_data:/data volumes: redis_data: ================================================ FILE: etc/config.yaml ================================================ server: address: "0.0.0.0" cache: type: redis ================================================ FILE: lib/adapter/__init__.py ================================================ """ Import all adapters from the current directory and make them available for import as adapter_module.AdapterName """ # pylint: disable=wildcard-import,relative-import from os.path import dirname, basename, isfile, join import glob __all__ = [ basename(f)[:-3] for f in glob.glob(join(dirname(__file__), "*.py")) if isfile(f) and not f.endswith("__init__.py") ] from .adapter import all_adapters from . import * ================================================ FILE: lib/adapter/adapter.py ================================================ """ `Adapter`, base class of the adapters. Configuration parameters: path.repositories """ import abc import os from six import with_metaclass from config import CONFIG class AdapterMC(type): """ Adapter Metaclass. Defines string representation of adapters """ def __repr__(cls): if hasattr(cls, "_class_repr"): return getattr(cls, "_class_repr")() return super(AdapterMC, cls).__repr__() class Adapter(with_metaclass(AdapterMC, object)): """ An abstract class, defines methods: (cheat sheets retrieval) * get_list * is_found * is_cache_needed (repositories management) " fetch * update and several properties that have to be set in each adapter subclass. """ _adapter_name = None _output_format = "code" _cache_needed = False _repository_url = None _local_repository_location = None _cheatsheet_files_prefix = "" _cheatsheet_files_extension = "" _pages_list = [] @classmethod def _class_repr(cls): return "[Adapter: %s (%s)]" % (cls._adapter_name, cls.__name__) def __init__(self): self._list = {None: self._get_list()} @classmethod def name(cls): """ Return name of the adapter """ return cls._adapter_name @abc.abstractmethod def _get_list(self, prefix=None): return self._pages_list def get_list(self, prefix=None): """ Return available pages for `prefix` """ if prefix in self._list: return self._list[prefix] self._list[prefix] = set(self._get_list(prefix=prefix)) return self._list[prefix] def is_found(self, topic): """ check if `topic` is available CAUTION: only root is checked """ return topic in self._list[None] def is_cache_needed(self): """ Return True if answers should be cached. Return False if answers should not be cached. """ return self._cache_needed @staticmethod def _format_page(text): """ Preformatting page hook. Converts `text` (as in the initial repository) to text (as to be displayed). """ return text @abc.abstractmethod def _get_page(self, topic, request_options=None): """ Return page for `topic` """ pass def _get_output_format(self, topic): if "/" in topic: subquery = topic.split("/")[-1] else: subquery = topic if subquery in [":list"]: return "text" return self._output_format # pylint: disable=unused-argument @staticmethod def _get_filetype(topic): """ Return language name (filetype) for `topic` """ return None def get_page_dict(self, topic, request_options=None): """ Return page dict for `topic` """ # # if _get_page() returns a dict, use the dictionary # for the answer. It is possible to specify some # useful properties as the part of the answer # (e.g. "cache") # answer by _get_page() always overrides all default properties # answer = self._get_page(topic, request_options=request_options) if not isinstance(answer, dict): answer = {"answer": answer} answer_dict = { "topic": topic, "topic_type": self._adapter_name, "format": self._get_output_format(topic), "cache": self._cache_needed, } answer_dict.update(answer) # pylint: disable=assignment-from-none filetype = self._get_filetype(topic) if filetype: answer_dict["filetype"] = filetype return answer_dict @classmethod def local_repository_location(cls, cheat_sheets_location=False): """ Return local repository location. If name `self._repository_url` for the class is not specified, return None It is possible that several adapters has the same repository_url, in this case they should use the same local directory. If for some reason the local repository location should be overridden (e.g. if several different branches of the same repository are used) if should set in `self._local_repository_location` of the adapter. If `cheat_sheets_location` is specified, return path of the cheat sheets directory instead of the repository directory. """ dirname = None if cls._local_repository_location: dirname = cls._local_repository_location if not dirname and cls._repository_url: dirname = cls._repository_url if dirname.startswith("https://"): dirname = dirname[8:] elif dirname.startswith("http://"): dirname = dirname[7:] # if we did not manage to find out dirname up to this point, # that means that neither repository url, not repository location # is specified for the adapter, so it should be skipped if not dirname: return None if dirname.startswith("/"): return dirname # it is possible that several repositories will # be mapped to the same location name # (because only the last part of the path is used) # in this case provide the name in _local_repository_location # (detected by fetch.py) if "/" in dirname: dirname = dirname.split("/")[-1] path = os.path.join(CONFIG["path.repositories"], dirname) if cheat_sheets_location: path = os.path.join(path, cls._cheatsheet_files_prefix) return path @classmethod def repository_url(cls): """ Return URL of the upstream repository """ return cls._repository_url @classmethod def fetch_command(cls): """ Initial fetch of the repository. Return cmdline that has to be executed to fetch the repository. Skipping if `self._repository_url` is not specified """ if not cls._repository_url: return None # in this case `fetch` has to be implemented # in the distinct adapter subclass raise RuntimeError( "Do not known how to handle this repository: %s" % cls._repository_url ) @classmethod def update_command(cls): """ Update of the repository. Return cmdline that has to be executed to update the repository inside `local_repository_location()`. """ if not cls._repository_url: return None local_repository_dir = cls.local_repository_location() if not local_repository_dir: return None # in this case `update` has to be implemented # in the distinct adapter subclass raise RuntimeError( "Do not known how to handle this repository: %s" % cls._repository_url ) @classmethod def current_state_command(cls): """ Get current state of repository (current revision). This is used to find what cache entries should be invalidated. """ if not cls._repository_url: return None local_repository_dir = cls.local_repository_location() if not local_repository_dir: return None # in this case `update` has to be implemented # in the distinct adapter subclass raise RuntimeError( "Do not known how to handle this repository: %s" % cls._repository_url ) @classmethod def save_state(cls, state): """ Save state `state` of the repository. Must be called after the cache clean up. """ local_repository_dir = cls.local_repository_location() state_filename = os.path.join(local_repository_dir, ".cached_revision") open(state_filename, "w").write(state) @classmethod def get_state(cls): """ Return the saved `state` of the repository. If state cannot be read, return None """ local_repository_dir = cls.local_repository_location() state_filename = os.path.join(local_repository_dir, ".cached_revision") state = None if os.path.exists(state_filename): state = open(state_filename, "r").read() return state @classmethod def get_updates_list_command(cls): """ Return the command to get the list of updates since the last update whose id is saved as the repository state (`cached_state`). The list is used to invalidate the cache. """ return None @classmethod def get_updates_list(cls, updated_files_list): """ Return the pages that have to be invalidated if the files `updates_files_list` were updated in the repository. """ if not cls._cheatsheet_files_prefix: return updated_files_list answer = [] cut_len = len(cls._cheatsheet_files_prefix) for entry in updated_files_list: if entry.startswith(cls._cheatsheet_files_prefix): answer.append(entry[cut_len:]) else: answer.append(entry) return answer def all_adapters(as_dict=False): """ Return list of all known adapters If `as_dict` is True, return dict {'name': adapter} instead of a list. """ def _all_subclasses(cls): return set(cls.__subclasses__()).union( set([s for c in cls.__subclasses__() for s in _all_subclasses(c)]) ) if as_dict: return {x.name(): x for x in _all_subclasses(Adapter)} return list(_all_subclasses(Adapter)) def adapter_by_name(name): """ Return adapter having this name, or None if nothing found """ return all_adapters(as_dict=True).get(name) ================================================ FILE: lib/adapter/cheat_cheat.py ================================================ """ Adapter for https://github.com/cheat/cheat Cheatsheets are located in `cheat/cheatsheets/` Each cheat sheet is a separate file without extension """ # pylint: disable=relative-import,abstract-method from .git_adapter import GitRepositoryAdapter class Cheat(GitRepositoryAdapter): """ cheat/cheat adapter """ _adapter_name = "cheat" _output_format = "code" _cache_needed = True _repository_url = "https://github.com/cheat/cheatsheets" _cheatsheet_files_prefix = "" _cheatsheet_file_mask = "*" ================================================ FILE: lib/adapter/cheat_sheets.py ================================================ """ Implementation of the adapter for the native cheat.sh cheat sheets repository, cheat.sheets. The cheat sheets repository is hierarchically structured: cheat sheets covering programming languages are are located in subdirectories. """ # pylint: disable=relative-import import os import glob from .git_adapter import GitRepositoryAdapter def _remove_initial_underscore(filename): if filename.startswith("_"): filename = filename[1:] return filename def _sanitize_dirnames(filename, restore=False): """ Remove (or add) leading _ in the directories names in `filename` The `restore` param means that the path name should be restored from the queryname, i.e. conversion should be done in the opposite direction """ parts = filename.split("/") newparts = [] for part in parts[:-1]: if restore: newparts.append("_" + part) continue if part.startswith("_"): newparts.append(part[1:]) else: newparts.append(part) newparts.append(parts[-1]) return "/".join(newparts) class CheatSheets(GitRepositoryAdapter): """ Adapter for the cheat.sheets cheat sheets. """ _adapter_name = "cheat.sheets" _output_format = "code" _repository_url = "https://github.com/chubin/cheat.sheets" _cheatsheet_files_prefix = "sheets/" def _get_list(self, prefix=None): """ Return all files on the first and the second level, excluding directories and hidden files """ hidden_files = ["_info.yaml"] answer = [] prefix = os.path.join( self.local_repository_location(), self._cheatsheet_files_prefix ) for mask in ["*", "*/*"]: template = os.path.join(prefix, mask) answer += [ _sanitize_dirnames(f_name[len(prefix) :]) for f_name in glob.glob(template) if not os.path.isdir(f_name) and os.path.basename(f_name) not in hidden_files ] return sorted(answer) def _get_page(self, topic, request_options=None): filename = os.path.join( self.local_repository_location(), self._cheatsheet_files_prefix, _sanitize_dirnames(topic, restore=True), ) if os.path.exists(filename): answer = self._format_page(open(filename, "r").read()) else: # though it should not happen answer = "%s:%s not found" % (str(self.__class__), topic) return answer class CheatSheetsDir(CheatSheets): """ Adapter for the cheat sheets directories. Provides pages named according to subdirectories: _dir => dir/ (currently only _get_list() is used; _get_page is shadowed by the CheatSheets adapter) """ _adapter_name = "cheat.sheets dir" _output_format = "text" def _get_list(self, prefix=None): template = os.path.join( self.local_repository_location(), self._cheatsheet_files_prefix, "*" ) answer = sorted( [ _remove_initial_underscore(os.path.basename(f_name)) + "/" for f_name in glob.glob(template) if os.path.isdir(f_name) ] ) return answer def _get_page(self, topic, request_options=None): """ Content of the `topic` dir is the list of the pages in the dir """ template = os.path.join( self.local_repository_location(), self._cheatsheet_files_prefix, topic.rstrip("/"), "*", ) answer = sorted([os.path.basename(f_name) for f_name in glob.glob(template)]) return "\n".join(answer) + "\n" def is_found(self, topic): return CheatSheets.is_found(self, topic.rstrip("/")) ================================================ FILE: lib/adapter/cmd.py ================================================ """ """ # pylint: disable=unused-argument,abstract-method import os.path import re from subprocess import Popen, PIPE from .adapter import Adapter def _get_abspath(path): """Find absolute path of the specified `path` according to its """ if path.startswith("/"): return path import __main__ return os.path.join(os.path.dirname(os.path.dirname(__main__.__file__)), path) class CommandAdapter(Adapter): """ """ _command = [] def _get_command(self, topic, request_options=None): return self._command def _get_page(self, topic, request_options=None): cmd = self._get_command(topic, request_options=request_options) if cmd: try: proc = Popen(cmd, stdout=PIPE, stderr=PIPE) answer = proc.communicate()[0].decode("utf-8", "ignore") except OSError: return ( 'ERROR of the "%s" adapter: please create an issue' % self._adapter_name ) return answer return "" class Fosdem(CommandAdapter): """ Show the output of the `current-fosdem-slide` command, which shows the current slide open in some terminal. This was used during the talk at FOSDEM 2019. https://www.youtube.com/watch?v=PmiK0JCdh5A `sudo` is used here because the session was running under a different user; to be able to use the command via sudo, the following `/etc/suders` entry was added: srv ALL=(ALL:ALL) NOPASSWD: /usr/local/bin/current-fosdem-slide Here `srv` is the user under which the cheat.sh server was running """ _adapter_name = "fosdem" _output_format = "ansi" _pages_list = [":fosdem"] _command = ["sudo", "/usr/local/bin/current-fosdem-slide"] class Translation(CommandAdapter): """ """ _adapter_name = "translation" _output_format = "text" _cache_needed = True def _get_page(self, topic, request_options=None): from_, topic = topic.split("/", 1) to_ = request_options.get("lang", "en") if "-" in from_: from_, to_ = from_.split("-", 1) return [ "/home/igor/cheat.sh/bin/get_translation", from_, to_, topic.replace("+", " "), ] class AdapterRfc(CommandAdapter): """ Show RFC by its number. Exported as: "/rfc/NUMBER" """ _adapter_name = "rfc" _output_format = "text" _cache_needed = True _command = ["share/adapters/rfc.sh"] def _get_command(self, topic, request_options=None): cmd = self._command[:] if not cmd[0].startswith("/"): cmd[0] = _get_abspath(cmd[0]) # cut rfc/ off if topic.startswith("rfc/"): topic = topic[4:] return cmd + [topic] def _get_list(self, prefix=None): return list("rfc/%s" % x for x in range(1, 8649)) def is_found(self, topic): return True class AdapterOeis(CommandAdapter): """ Show OEIS by its number. Exported as: "/oeis/NUMBER" """ _adapter_name = "oeis" _output_format = "text+code" _cache_needed = True _command = ["share/adapters/oeis.sh"] @staticmethod def _get_filetype(topic): if "/" in topic: language = topic.split("/")[-1].lower() return language return "bash" def _get_command(self, topic, request_options=None): cmd = self._command[:] if not cmd[0].startswith("/"): cmd[0] = _get_abspath(cmd[0]) # cut oeis/ off # Replace all non (alphanumeric, '-', ':') chars with Spaces to delimit args to oeis.sh if topic.startswith("oeis/"): topic = topic[5:] suffix = "" if topic.endswith("/:list"): suffix = " :list" topic = topic[:-6] topic = re.sub("[^a-zA-Z0-9-:]+", " ", topic) + suffix return cmd + [topic] def is_found(self, topic): return True class AdapterChmod(CommandAdapter): """ Show chmod numeric values and strings Exported as: "/chmod/NUMBER" """ _adapter_name = "chmod" _output_format = "text" _cache_needed = True _command = ["share/adapters/chmod.sh"] def _get_command(self, topic, request_options=None): cmd = self._command[:] # cut chmod/ off # remove all non (alphanumeric, '-') chars if topic.startswith("chmod/"): topic = topic[6:] topic = re.sub("[^a-zA-Z0-9-]", "", topic) return cmd + [topic] def is_found(self, topic): return True ================================================ FILE: lib/adapter/common.py ================================================ class Adapter(object): pass class cheatAdapter(Adapter): pass ================================================ FILE: lib/adapter/git_adapter.py ================================================ """ Implementation of `GitRepositoryAdapter`, adapter that is used to handle git repositories """ import glob import os from .adapter import Adapter # pylint: disable=relative-import def _get_filenames(path): return [os.path.split(topic)[1] for topic in glob.glob(path)] class RepositoryAdapter(Adapter): """ Implements methods needed to handle standard repository based adapters. """ def _get_list(self, prefix=None): """ List of files in the cheat sheets directory with the extension removed """ answer = _get_filenames( os.path.join( self.local_repository_location(), self._cheatsheet_files_prefix, "*" + self._cheatsheet_files_extension, ) ) ext = self._cheatsheet_files_extension if ext: answer = [ filename[: -len(ext)] for filename in answer if filename.endswith(ext) ] return answer def _get_page(self, topic, request_options=None): filename = os.path.join( self.local_repository_location(), self._cheatsheet_files_prefix, topic ) if os.path.exists(filename) and not os.path.isdir(filename): answer = self._format_page(open(filename, "r").read()) else: # though it should not happen answer = "%s:%s not found" % (str(self.__class__), topic) return answer class GitRepositoryAdapter(RepositoryAdapter): # pylint: disable=abstract-method """ Implements all methods needed to handle cache handling for git-repository-based adapters """ @classmethod def fetch_command(cls): """ Initial fetch of the repository. Return cmdline that has to be executed to fetch the repository. Skipping if `self._repository_url` is not specified """ if not cls._repository_url: return None if not cls._repository_url.startswith("https://github.com/"): # in this case `fetch` has to be implemented # in the distinct adapter subclass raise RuntimeError( "Do not known how to handle this repository: %s" % cls._repository_url ) local_repository_dir = cls.local_repository_location() if not local_repository_dir: return None return ["git", "clone", "--depth=1", cls._repository_url, local_repository_dir] @classmethod def update_command(cls): """ Update of the repository. Return cmdline that has to be executed to update the repository inside `local_repository_location()`. """ if not cls._repository_url: return None local_repository_dir = cls.local_repository_location() if not local_repository_dir: return None if not cls._repository_url.startswith("https://github.com/"): # in this case `update` has to be implemented # in the distinct adapter subclass raise RuntimeError( "Do not known how to handle this repository: %s" % cls._repository_url ) return ["git", "pull"] @classmethod def current_state_command(cls): """ Get current state of repository (current revision). This is used to find what cache entries should be invalidated. """ if not cls._repository_url: return None local_repository_dir = cls.local_repository_location() if not local_repository_dir: return None if not cls._repository_url.startswith("https://github.com/"): # in this case `update` has to be implemented # in the distinct adapter subclass raise RuntimeError( "Do not known how to handle this repository: %s" % cls._repository_url ) return ["git", "rev-parse", "--short", "HEAD", "--"] @classmethod def save_state(cls, state): """ Save state `state` of the repository. Must be called after the cache clean up. """ local_repository_dir = cls.local_repository_location() state_filename = os.path.join(local_repository_dir, ".cached_revision") open(state_filename, "wb").write(state) @classmethod def get_state(cls): """ Return the saved `state` of the repository. If state cannot be read, return None """ local_repository_dir = cls.local_repository_location() state_filename = os.path.join(local_repository_dir, ".cached_revision") state = None if os.path.exists(state_filename): state = open(state_filename, "r").read() return state @classmethod def get_updates_list_command(cls): """ Return list of updates since the last update whose id is saved as the repository state. The list is used to invalidate the cache. """ current_state = cls.get_state() if not current_state: return ["git", "ls-tree", "--full-tree", "-r", "--name-only", "HEAD", "--"] return ["git", "diff", "--name-only", current_state, "HEAD", "--"] ================================================ FILE: lib/adapter/internal.py ================================================ """ Configuration parameters: frontend.styles path.internal.pages """ import sys import os import collections try: from rapidfuzz import process, fuzz _USING_FUZZYWUZZY = False except ImportError: from fuzzywuzzy import process, fuzz _USING_FUZZYWUZZY = True from config import CONFIG from .adapter import Adapter from fmt.internal import colorize_internal _INTERNAL_TOPICS = [ ":cht.sh", ":bash_completion", ":emacs", ":emacs-ivy", ":firstpage", ":firstpage-v1", ":firstpage-v2", ":fish", ":help", ":intro", ":list", ":post", ":styles", ":styles-demo", ":vim", ":zsh", ] _COLORIZED_INTERNAL_TOPICS = [ ":intro", ] class InternalPages(Adapter): _adapter_name = "internal" _output_format = "ansi" def __init__(self, get_topic_type=None, get_topics_list=None): Adapter.__init__(self) self.get_topic_type = get_topic_type self.get_topics_list = get_topics_list def _get_stat(self): stat = collections.Counter( [self.get_topic_type(topic) for topic in self.get_topics_list()] ) answer = "" for key, val in stat.items(): answer += "%s %s\n" % (key, val) return answer @staticmethod def get_list(prefix=None): return _INTERNAL_TOPICS def _get_list_answer(self, topic, request_options=None): if "/" in topic: topic_type, topic_name = topic.split("/", 1) if topic_name == ":list": topic_list = [ x[len(topic_type) + 1 :] for x in self.get_topics_list() if x.startswith(topic_type + "/") ] return "\n".join(topic_list) + "\n" answer = "" if topic == ":list": answer = "\n".join(x for x in self.get_topics_list()) + "\n" return answer def _get_page(self, topic, request_options=None): if topic.endswith("/:list") or topic.lstrip("/") == ":list": return self._get_list_answer(topic) answer = "" if topic == ":styles": answer = "\n".join(CONFIG["frontend.styles"]) + "\n" elif topic == ":stat": answer = self._get_stat() + "\n" elif topic in _INTERNAL_TOPICS: answer = open( os.path.join(CONFIG["path.internal.pages"], topic[1:] + ".txt"), "r" ).read() if topic in _COLORIZED_INTERNAL_TOPICS: answer = colorize_internal(answer) return answer def is_found(self, topic): return topic in self.get_list() or topic.endswith("/:list") class UnknownPages(InternalPages): _adapter_name = "unknown" _output_format = "text" @staticmethod def get_list(prefix=None): return [] @staticmethod def is_found(topic): return True def _get_page(self, topic, request_options=None): topics_list = self.get_topics_list() if topic.startswith(":"): topics_list = [x for x in topics_list if x.startswith(":")] else: topics_list = [x for x in topics_list if not x.startswith(":")] if _USING_FUZZYWUZZY: possible_topics = process.extract(topic, topics_list, scorer=fuzz.ratio)[:3] else: possible_topics = process.extract( topic, topics_list, limit=3, scorer=fuzz.ratio ) possible_topics_text = "\n".join( [(" * %s %s" % (x[0], int(x[1]))) for x in possible_topics] ) return ( """ Unknown topic. Do you mean one of these topics maybe? %s """ % possible_topics_text ) class Search(Adapter): _adapter_name = "search" _output_format = "text" _cache_needed = False @staticmethod def get_list(prefix=None): return [] def is_found(self, topic): return False ================================================ FILE: lib/adapter/latenz.py ================================================ """ Adapter for the curlable latencies numbers (chubin/late.nz) This module can be an example of a adapter for a python project. The adapter exposes one page ("latencies") and several its aliases ("latencies", "late.nz", "latency") """ # pylint: disable=relative-import import sys import os from .git_adapter import GitRepositoryAdapter class Latenz(GitRepositoryAdapter): """ chubin/late.nz Adapter """ _adapter_name = "late.nz" _output_format = "ansi" _repository_url = "https://github.com/chubin/late.nz" def _get_page(self, topic, request_options=None): sys.path.append(os.path.join(self.local_repository_location(), "bin")) import latencies return latencies.render() def _get_list(self, prefix=None): return ["latencies"] def is_found(self, topic): return topic.lower() in ["latencies", "late.nz", "latency"] ================================================ FILE: lib/adapter/learnxiny.py ================================================ """ Adapters for the cheat sheets from the Learn X in Y project Configuration parameters: log.level """ # pylint: disable=relative-import from __future__ import print_function import os import re from config import CONFIG from .git_adapter import GitRepositoryAdapter class LearnXinY(GitRepositoryAdapter): """ Adapter for the LearnXinY project """ _adapter_name = "learnxiny" _output_format = "code" _cache_needed = True _repository_url = "https://github.com/adambard/learnxinyminutes-docs" def __init__(self): self.adapters = _ADAPTERS GitRepositoryAdapter.__init__(self) def _get_page(self, topic, request_options=None): """ Return cheat sheet for `topic` or empty string if nothing found """ lang, topic = topic.split("/", 1) if lang not in self.adapters: return "" return self.adapters[lang].get_page(topic) def _get_list(self, prefix=None): """ Return list of all learnxiny topics """ answer = [] for language_adapter in self.adapters.values(): answer += language_adapter.get_list(prefix=True) return answer def is_found(self, topic): """ Return whether `topic` is a valid learnxiny topic """ if "/" not in topic: return False lang, topic = topic.split("/", 1) if lang not in self.adapters: return False return self.adapters[lang].is_valid(topic) class LearnXYAdapter(object): """ Parent class of all languages adapters """ _learn_xy_path = LearnXinY.local_repository_location() _replace_with = {} _filename = "" prefix = "" _replace_with = {} _splitted = True _block_cut_start = 2 _block_cut_end = 0 def __init__(self): self._whole_cheatsheet = self._read_cheatsheet() self._blocks = self._extract_blocks() self._topics_list = [x for x, _ in self._blocks] if "Comments" in self._topics_list: self._topics_list = [x for x in self._topics_list if x != "Comments"] + [ "Comments" ] self._topics_list += [":learn", ":list"] if self._whole_cheatsheet and CONFIG.get("log.level") >= 5: print(self.prefix, self._topics_list) def _is_block_separator(self, before, now, after): if ( re.match(r"////////*", before) and re.match(r"// ", now) and re.match(r"////////*", after) ): block_name = re.sub(r"//\s*", "", now).replace("(", "").replace(")", "") block_name = "_".join(block_name.strip(", ").split()) for character in "/,": block_name = block_name.replace(character, "") for k in self._replace_with: if k in block_name: block_name = self._replace_with[k] return block_name return None def _cut_block(self, block, start_block=False): if not start_block: answer = block[self._block_cut_start : -self._block_cut_end] if answer == []: return answer if answer[0].strip() == "": answer = answer[1:] if answer[-1].strip() == "": answer = answer[:1] return answer def _read_cheatsheet(self): filename = os.path.join(self._learn_xy_path, self._filename) # if cheat sheets are not there (e.g. were not yet fetched), # just skip it if not os.path.exists(filename): return None with open(filename) as f_cheat_sheet: code_mode = False answer = [] for line in f_cheat_sheet.readlines(): if line.startswith("```"): if not code_mode: code_mode = True continue else: code_mode = False if code_mode: answer.append(line.rstrip("\n")) return answer def _extract_blocks(self): if not self._splitted: return [] lines = self._whole_cheatsheet if lines is None: return [] answer = [] block = [] block_name = "Comments" for before, now, after in zip([""] + lines, lines, lines[1:]): new_block_name = self._is_block_separator(before, now, after) if new_block_name: if block_name: block_text = self._cut_block(block) if block_text != []: answer.append((block_name, block_text)) block_name = new_block_name block = [] continue else: block.append(before) answer.append((block_name, self._cut_block(block))) return answer def is_valid(self, name): """ Check whether topic `name` is valid. """ for topic_list in self._topics_list: if topic_list == name: return True return False def get_list(self, prefix=None): """ Get list of topics for `prefix` """ if prefix: return ["%s/%s" % (self.prefix, x) for x in self._topics_list] return self._topics_list def get_page(self, name, partial=False): """ Return specified cheat sheet `name` for the language. If `partial`, cheat sheet name may be shortened """ if name == ":list": return "\n".join(self.get_list()) + "\n" if name == ":learn": if self._whole_cheatsheet: return "\n".join(self._whole_cheatsheet) + "\n" else: return "" if partial: possible_names = [] for block_name, _ in self._blocks: if block_name.startswith(name): possible_names.append(block_name) if possible_names == [] or len(possible_names) > 1: return None name = possible_names[0] for block_name, block_contents in self._blocks: if block_name == name: return "\n".join(block_contents) return None # # Specific programming languages LearnXY cheat sheets configurations # Contains much code for the moment; should contain data only # ideally should be replaced with YAML # class LearnAwkAdapter(LearnXYAdapter): "Learn AWK in Y Minutes" prefix = "awk" _filename = "awk.html.markdown" _splitted = False class LearnBashAdapter(LearnXYAdapter): "Learn Bash in Y Minutes" prefix = "bash" _filename = "bash.html.markdown" _splitted = False class LearnBfAdapter(LearnXYAdapter): "Learn Brainfuck in Y Minutes" prefix = "bf" _filename = "bf.html.markdown" _splitted = False class LearnCAdapter(LearnXYAdapter): "Learn C in Y Minutes" prefix = "c" _filename = "c.html.markdown" _splitted = False class LearnChapelAdapter(LearnXYAdapter): "Learn Chapel in Y Minutes" prefix = "chapel" _filename = "chapel.html.markdown" _splitted = False class LearnClojureAdapter(LearnXYAdapter): """ Learn Clojure in Y Minutes """ prefix = "clojure" _filename = "clojure.html.markdown" def _is_block_separator(self, before, now, after): if ( re.match(r"\s*$", before) and re.match(r";\s*", now) and re.match(r";;;;;;+", after) ): block_name = re.sub(r";\s*", "", now) block_name = "_".join( [x.strip(",&:") for x in block_name.strip(", ").split()] ) return block_name return None @staticmethod def _cut_block(block, start_block=False): if not start_block: answer = block[2:] if answer[0].split() == "": answer = answer[1:] if answer[-1].split() == "": answer = answer[:1] return answer class LearnCoffeeScriptAdapter(LearnXYAdapter): "Learn coffeescript in Y Minutes" prefix = "coffee" _filename = "coffeescript.html.markdown" _splitted = False class LearnCppAdapter(LearnXYAdapter): """ Learn C++ in Y Minutes """ prefix = "cpp" _filename = "c++.html.markdown" _replace_with = { "More_about_Objects": "Prototypes", } def _is_block_separator(self, before, now, after): if ( re.match(r"////////*", before) and re.match(r"// ", now) and re.match(r"////////*", after) ): block_name = re.sub(r"//\s*", "", now).replace("(", "").replace(")", "") block_name = "_".join(block_name.strip(", ").split()) for character in "/,": block_name = block_name.replace(character, "") for k in self._replace_with: if k in block_name: block_name = self._replace_with[k] return block_name return None @staticmethod def _cut_block(block, start_block=False): answer = block[2:-1] if answer == []: return answer if answer[0].split() == "": answer = answer[1:] if answer[-1].split() == "": answer = answer[:1] return answer class LearnCsharpAdapter(LearnXYAdapter): "Learn C# in Y Minutes" prefix = "csharp" _filename = "csharp.html.markdown" _splitted = False class LearnDAdapter(LearnXYAdapter): "Learn D in Y Minutes" prefix = "d" _filename = "d.html.markdown" _splitted = False class LearnDartAdapter(LearnXYAdapter): "Learn Dart in Y Minutes" prefix = "dart" _filename = "dart.html.markdown" _splitted = False class LearnFactorAdapter(LearnXYAdapter): "Learn Factor in Y Minutes" prefix = "factor" _filename = "factor.html.markdown" _splitted = False class LearnForthAdapter(LearnXYAdapter): "Learn Forth in Y Minutes" prefix = "forth" _filename = "forth.html.markdown" _splitted = False class LearnFsharpAdapter(LearnXYAdapter): "Learn F# in Y Minutes" prefix = "fsharp" _filename = "fsharp.html.markdown" _splitted = False class LearnElispAdapter(LearnXYAdapter): "Learn Elisp in Y Minutes" prefix = "elisp" _filename = "elisp.html.markdown" _splitted = False class LearnElixirAdapter(LearnXYAdapter): """ Learn Elixir in Y Minutes """ prefix = "elixir" _filename = "elixir.html.markdown" _replace_with = { "More_about_Objects": "Prototypes", } def _is_block_separator(self, before, now, after): if ( re.match(r"## ---*", before) and re.match(r"## --", now) and re.match(r"## ---*", after) ): block_name = re.sub(r"## --\s*", "", now) block_name = "_".join(block_name.strip(", ").split()) for character in "/,": block_name = block_name.replace(character, "") for k in self._replace_with: if k in block_name: block_name = self._replace_with[k] return block_name return None @staticmethod def _cut_block(block, start_block=False): answer = block[2:-1] if answer[0].split() == "": answer = answer[1:] if answer[-1].split() == "": answer = answer[:1] return answer class LearnElmAdapter(LearnXYAdapter): """ Learn Elm in Y Minutes """ prefix = "elm" _filename = "elm.html.markdown" _replace_with = { "More_about_Objects": "Prototypes", } def _is_block_separator(self, before, now, after): if ( re.match(r"\s*", before) and re.match(r"\{--.*--\}", now) and re.match(r"\s*", after) ): block_name = re.sub(r"\{--+\s*", "", now) block_name = re.sub(r"--\}", "", block_name) block_name = "_".join(block_name.strip(", ").split()) for character in "/,": block_name = block_name.replace(character, "") for k in self._replace_with: if k in block_name: block_name = self._replace_with[k] return block_name return None @staticmethod def _cut_block(block, start_block=False): answer = block[2:-1] if answer[0].split() == "": answer = answer[1:] if answer[-1].split() == "": answer = answer[:1] return answer class LearnErlangAdapter(LearnXYAdapter): """ Learn Erlang in Y Minutes """ prefix = "erlang" _filename = "erlang.html.markdown" def _is_block_separator(self, before, now, after): if ( re.match("%%%%%%+", before) and re.match(r"%%\s+[0-9]+\.", now) and re.match("%%%%%%+", after) ): block_name = re.sub(r"%%+\s+[0-9]+\.\s*", "", now) block_name = "_".join(block_name.strip(".").strip().split()) return block_name return None @staticmethod def _cut_block(block, start_block=False): answer = block[2:-1] if answer[0].split() == "": answer = answer[1:] if answer[-1].split() == "": answer = answer[:1] return answer class LearnFortranAdapter(LearnXYAdapter): "Learn Fortran in Y Minutes" prefix = "fortran" _filename = "fortran95.html.markdown" _splitted = False class LearnGoAdapter(LearnXYAdapter): "Learn Go in Y Minutes" prefix = "go" _filename = "go.html.markdown" _splitted = False class LearnGroovyAdapter(LearnXYAdapter): "Learn Groovy in Y Minutes" prefix = "groovy" _filename = "groovy.html.markdown" _splitted = False class LearnJavaAdapter(LearnXYAdapter): "Learn Java in Y Minutes" prefix = "java" _filename = "java.html.markdown" _splitted = False class LearnJavaScriptAdapter(LearnXYAdapter): """ Learn JavaScript in Y Minutes """ prefix = "js" _filename = "javascript.html.markdown" _replace_with = { "More_about_Objects": "Prototypes", } def _is_block_separator(self, before, now, after): if ( re.match("//////+", before) and re.match(r"//+\s+[0-9]+\.", now) and re.match(r"\s*", after) ): block_name = re.sub(r"//+\s+[0-9]+\.\s*", "", now) block_name = "_".join(block_name.strip(", ").split()) for k in self._replace_with: if k in block_name: block_name = self._replace_with[k] return block_name return None @staticmethod def _cut_block(block, start_block=False): answer = block[2:-1] if answer[0].split() == "": answer = answer[1:] if answer[-1].split() == "": answer = answer[:1] return answer class LearnJuliaAdapter(LearnXYAdapter): """ Learn Julia in Y Minutes """ prefix = "julia" _filename = "julia.html.markdown" def _is_block_separator(self, before, now, after): if ( re.match("####+", before) and re.match(r"##\s*", now) and re.match("####+", after) ): block_name = re.sub(r"##\s+[0-9]+\.\s*", "", now) block_name = "_".join(block_name.strip(", ").split()) return block_name return None @staticmethod def _cut_block(block, start_block=False): answer = block[2:-1] if answer[0].split() == "": answer = answer[1:] if answer[-1].split() == "": answer = answer[:1] return answer class LearnHaskellAdapter(LearnXYAdapter): """ Learn Haskell in Y Minutes """ prefix = "haskell" _filename = "haskell.html.markdown" _replace_with = { "More_about_Objects": "Prototypes", } def _is_block_separator(self, before, now, after): if ( re.match("------+", before) and re.match(r"--+\s+[0-9]+\.", now) and re.match("------+", after) ): block_name = re.sub(r"--+\s+[0-9]+\.\s*", "", now) block_name = "_".join(block_name.strip(", ").split()) for k in self._replace_with: if k in block_name: block_name = self._replace_with[k] return block_name return None @staticmethod def _cut_block(block, start_block=False): answer = block[2:-1] if answer[0].split() == "": answer = answer[1:] if answer[-1].split() == "": answer = answer[:1] return answer class LearnLispAdapter(LearnXYAdapter): "Learn Lisp in Y Minutes" prefix = "lisp" _filename = "common-lisp.html.markdown" _splitted = False class LearnLuaAdapter(LearnXYAdapter): """ Learn Lua in Y Minutes """ prefix = "lua" _filename = "lua.html.markdown" _replace_with = { "1_Metatables_and_metamethods": "Metatables", "2_Class-like_tables_and_inheritance": "Class-like_tables", "Variables_and_flow_control": "Flow_control", } def _is_block_separator(self, before, now, after): if ( re.match("-----+", before) and re.match("-------+", after) and re.match(r"--\s+[0-9]+\.", now) ): block_name = re.sub(r"--+\s+[0-9]+\.\s*", "", now) block_name = "_".join(block_name.strip(".").strip().split()) if block_name in self._replace_with: block_name = self._replace_with[block_name] return block_name return None @staticmethod def _cut_block(block, start_block=False): answer = block[2:-1] if answer[0].split() == "": answer = answer[1:] if answer[-1].split() == "": answer = answer[:1] return answer class LearnMathematicaAdapter(LearnXYAdapter): "Learn Mathematica in Y Minutes" prefix = "mathematica" _filename = "wolfram.html.markdown" _splitted = False class LearnMatlabAdapter(LearnXYAdapter): "Learn Matlab in Y Minutes" prefix = "matlab" _filename = "matlab.html.markdown" _splitted = False class LearnOctaveAdapter(LearnXYAdapter): "Learn Octave in Y Minutes" prefix = "octave" _filename = "matlab.html.markdown" _splitted = False class LearnKotlinAdapter(LearnXYAdapter): """ Learn Kotlin in Y Minutes """ prefix = "kotlin" _filename = "kotlin.html.markdown" def _is_block_separator(self, before, now, after): if ( re.match("#######+", before) and re.match("#######+", after) and re.match(r"#+\s+[0-9]+\.", now) ): block_name = re.sub(r"#+\s+[0-9]+\.\s*", "", now) block_name = "_".join(block_name.strip().split()) return block_name return None @staticmethod def _cut_block(block, start_block=False): answer = block[2:-1] if answer[0].split() == "": answer = answer[1:] if answer[-1].split() == "": answer = answer[:1] return answer class LearnObjectiveCAdapter(LearnXYAdapter): "Learn Objective C in Y Minutes" prefix = "objective-c" _filename = "objective-c.html.markdown" _splitted = False class LearnOCamlAdapter(LearnXYAdapter): """ Learn OCaml in Y Minutes """ prefix = "ocaml" _filename = "ocaml.html.markdown" _replace_with = { "More_about_Objects": "Prototypes", } def _is_block_separator(self, before, now, after): if ( re.match(r"\s*", before) and re.match(r"\(\*\*\*+", now) and re.match(r"\s*", after) ): block_name = re.sub(r"\(\*\*\*+\s*", "", now) block_name = re.sub(r"\s*\*\*\*\)", "", block_name) block_name = "_".join(block_name.strip(", ").split()) for k in self._replace_with: if k in block_name: block_name = self._replace_with[k] return block_name return None @staticmethod def _cut_block(block, start_block=False): answer = block[2:-1] if answer[0].split() == "": answer = answer[1:] if answer[-1].split() == "": answer = answer[:1] return answer class LearnPerlAdapter(LearnXYAdapter): """ Learn Perl in Y Minutes """ prefix = "perl" _filename = "perl.html.markdown" _replace_with = { "Conditional_and_looping_constructs": "Control_Flow", "Perl_variable_types": "Types", "Files_and_I/O": "Files", "Writing_subroutines": "Subroutines", } def _is_block_separator(self, before, now, after): if re.match(r"####+\s+", now): block_name = re.sub(r"#+\s", "", now) block_name = "_".join(block_name.strip().split()) if block_name in self._replace_with: block_name = self._replace_with[block_name] return block_name else: return None @staticmethod def _cut_block(block, start_block=False): if not start_block: answer = block[2:] if answer == []: return answer if answer[0].split() == "": answer = answer[1:] if answer[-1].split() == "": answer = answer[:1] return answer class LearnPerl6Adapter(LearnXYAdapter): "Learn Perl 6 in Y Minutes" prefix = "perl6" _filename = "perl6.html.markdown" _splitted = False class LearnPHPAdapter(LearnXYAdapter): """ Learn PHP in Y Minutes """ prefix = "php" _filename = "php.html.markdown" def _is_block_separator(self, before, now, after): if ( re.match(r"/\*\*\*\*\*+", before) and re.match(r"\s*\*/", after) and re.match(r"\s*\*\s*", now) ): block_name = re.sub(r"\s*\*\s*", "", now) block_name = re.sub(r"&", "", block_name) block_name = "_".join(block_name.strip().split()) return block_name return None @staticmethod def _cut_block(block, start_block=False): return block[2:] class LearnPythonAdapter(LearnXYAdapter): """ Learn Python in Y Minutes """ prefix = "python" _filename = "python.html.markdown" def _is_block_separator(self, before, now, after): if ( re.match("#######+", before) and re.match("#######+", after) and re.match(r"#+\s+[0-9]+\.", now) ): block_name = re.sub(r"#+\s+[0-9]+\.\s*", "", now) block_name = "_".join(block_name.strip().split()) return block_name return None @staticmethod def _cut_block(block, start_block=False): answer = block[2:-1] if answer[0].split() == "": answer = answer[1:] if answer[-1].split() == "": answer = answer[:1] return answer class LearnPython3Adapter(LearnXYAdapter): "Learn Python 3 in Y Minutes" prefix = "python3" _filename = "python3.html.markdown" _splitted = False class LearnRAdapter(LearnXYAdapter): "Learn R in Y Minutes" prefix = "r" _filename = "r.html.markdown" _splitted = False class LearnRacketAdapter(LearnXYAdapter): "Learn Racket in Y Minutes" prefix = "racket" _filename = "racket.html.markdown" _splitted = False class LearnRubyAdapter(LearnXYAdapter): """ Learn Ruby in Y Minutes Format of the file was changed, so we have to fix the function too. This case is a good case for health check: if number of extracted cheat sheets is suddenly became 1, one should check the markup """ prefix = "ruby" _filename = "ruby.html.markdown" def _is_block_separator(self, before, now, after): if ( re.match("#######+", before) and re.match("#######+", after) and re.match(r"#+\s+[0-9]+\.", now) ): block_name = re.sub(r"#+\s+[0-9]+\.\s*", "", now) block_name = "_".join(block_name.strip().split()) return block_name return None @staticmethod def _cut_block(block, start_block=False): answer = block[2:-1] if answer[0].split() == "": answer = answer[1:] if answer[-1].split() == "": answer = answer[:1] return answer class LearnRustAdapter(LearnXYAdapter): "Learn Rust in Y Minutes" prefix = "rust" _filename = "rust.html.markdown" _splitted = False class LearnSolidityAdapter(LearnXYAdapter): "Learn Solidity in Y Minutes" prefix = "solidity" _filename = "solidity.html.markdown" _splitted = False class LearnSwiftAdapter(LearnXYAdapter): "Learn Swift in Y Minutes" prefix = "swift" _filename = "swift.html.markdown" _splitted = False class LearnTclAdapter(LearnXYAdapter): "Learn Tcl in Y Minutes" prefix = "tcl" _filename = "tcl.html.markdown" _splitted = False class LearnTcshAdapter(LearnXYAdapter): "Learn Tcsh in Y Minutes" prefix = "tcsh" _filename = "tcsh.html.markdown" _splitted = False class LearnVisualBasicAdapter(LearnXYAdapter): "Learn Visual Basic in Y Minutes" prefix = "vb" _filename = "visualbasic.html.markdown" _splitted = False class LearnCMakeAdapter(LearnXYAdapter): "Learn CMake in Y Minutes" prefix = "cmake" _filename = "cmake.html.markdown" _splitted = False class LearnNimAdapter(LearnXYAdapter): "Learn Nim in Y Minutes" prefix = "nim" _filename = "nim.html.markdown" _splitted = False class LearnGitAdapter(LearnXYAdapter): "Learn Git in Y Minutes" prefix = "git" _filename = "git.html.markdown" _splitted = False class LearnLatexAdapter(LearnXYAdapter): "Learn Nim in Y Minutes" prefix = "latex" _filename = "latex.html.markdown" _splitted = False _ADAPTERS = {cls.prefix: cls() for cls in vars()["LearnXYAdapter"].__subclasses__()} ================================================ FILE: lib/adapter/question.py ================================================ """ Configuration parameters: path.internal.bin.upstream """ # pylint: disable=relative-import from __future__ import print_function import os import re from subprocess import Popen, PIPE from polyglot.detect import Detector from polyglot.detect.base import UnknownLanguage from config import CONFIG from languages_data import SO_NAME from .upstream import UpstreamAdapter NOT_FOUND_MESSAGE = """404 NOT FOUND Unknown cheat sheet. Please try to reformulate your query. Query format: /LANG/QUESTION Examples: /python/read+json /golang/run+external+program /js/regex+search See /:help for more info. If the problem persists, file a GitHub issue at github.com/chubin/cheat.sh or ping @igor_chubin """ class Question(UpstreamAdapter): """ Answer to a programming language question, using Stackoverflow as the main data source. Heavy lifting is done by an external program `CONFIG["path.internal.bin.upstream"]`. If the program is not found, fallback to the superclass `UpstreamAdapter`, which queries the upstream server (by default https://cheat.sh/) for the answer """ _adapter_name = "question" _output_format = "text+code" _cache_needed = True def _get_page(self, topic, request_options=None): """ Find answer for the `topic` question. """ if not os.path.exists(CONFIG["path.internal.bin.upstream"]): # if the upstream program is not found, use normal upstream adapter self._output_format = "ansi" return UpstreamAdapter._get_page( self, topic, request_options=request_options ) topic = topic.replace("+", " ") # if there is a language name in the section name, # cut it off (de:python => python) if "/" in topic: section_name, topic = topic.split("/", 1) if ":" in section_name: _, section_name = section_name.split(":", 1) section_name = SO_NAME.get(section_name, section_name) topic = "%s/%s" % (section_name, topic) # some clients send queries with - instead of + so we have to rewrite them to topic = re.sub(r"(? 2 or supposed_lang in [ "az", "ru", "uk", "de", "fr", "es", "it", "nl", ]: lang = supposed_lang if supposed_lang.startswith("zh_") or supposed_lang == "zh": lang = "zh" elif supposed_lang.startswith("pt_"): lang = "pt" if supposed_lang in ["ja", "ko"]: lang = supposed_lang except UnknownLanguage: print("Unknown language (%s)" % query_text) if lang != "en": topic = ["--human-language", lang, topic] else: topic = [topic] cmd = [CONFIG["path.internal.bin.upstream"]] + topic proc = Popen(cmd, stdin=open(os.devnull, "r"), stdout=PIPE, stderr=PIPE) answer = proc.communicate()[0].decode("utf-8") if not answer: return NOT_FOUND_MESSAGE return answer def get_list(self, prefix=None): return [] def is_found(self, topic): return True ================================================ FILE: lib/adapter/rosetta.py ================================================ """ Implementation of RosettaCode Adapter. Exports: Rosetta(GitRepositoryAdapter) """ # pylint: disable=relative-import import os import glob import yaml from .git_adapter import GitRepositoryAdapter from .cheat_sheets import CheatSheets class Rosetta(GitRepositoryAdapter): """ Adapter for RosettaCode """ _adapter_name = "rosetta" _output_format = "code" _local_repository_location = "RosettaCodeData" _repository_url = "https://github.com/acmeism/RosettaCodeData" __section_name = "rosetta" def __init__(self): GitRepositoryAdapter.__init__(self) self._rosetta_code_name = self._load_rosetta_code_names() @staticmethod def _load_rosetta_code_names(): answer = {} lang_files_location = CheatSheets.local_repository_location( cheat_sheets_location=True ) for filename in glob.glob(os.path.join(lang_files_location, "*/_info.yaml")): text = open(filename, "r").read() data = yaml.load(text, Loader=yaml.SafeLoader) if data is None: continue lang = os.path.basename(os.path.dirname(filename)) if lang.startswith("_"): lang = lang[1:] if "rosetta" in data: answer[lang] = data["rosetta"] return answer def _rosetta_get_list(self, query, task=None): if query not in self._rosetta_code_name: return [] lang = self._rosetta_code_name[query] answer = [] if task: glob_path = os.path.join( self.local_repository_location(), "Lang", lang, task, "*" ) else: glob_path = os.path.join( self.local_repository_location(), "Lang", lang, "*" ) for filename in glob.glob(glob_path): taskname = os.path.basename(filename) answer.append(taskname) answer = "".join("%s\n" % x for x in sorted(answer)) return answer @staticmethod def _parse_query(query): if "/" in query: task, subquery = query.split("/", 1) else: task, subquery = query, None return task, subquery def _get_task(self, lang, query): if lang not in self._rosetta_code_name: return "" task, subquery = self._parse_query(query) if task == ":list": return self._rosetta_get_list(lang) if subquery == ":list": return self._rosetta_get_list(lang, task=task) # if it is not a number or the number is too big, just ignore it index = 1 if subquery: try: index = int(subquery) except ValueError: pass lang_name = self._rosetta_code_name[lang] tasks = sorted( glob.glob( os.path.join( self.local_repository_location(), "Lang", lang_name, task, "*" ) ) ) if not tasks: return "" if len(tasks) < index or index < 1: index = 1 answer_filename = tasks[index - 1] answer = open(answer_filename, "r").read() return answer def _starting_page(self, query): number_of_pages = self._rosetta_get_list(query) answer = ("# %s pages available\n" "# use /:list to list") % number_of_pages return answer def _get_page(self, topic, request_options=None): if "/" not in topic: return self._rosetta_get_list(topic) lang, topic = topic.split("/", 1) # this part should be generalized # currently we just remove the name of the adapter from the path if topic == self.__section_name: return self._starting_page(topic) if topic.startswith(self.__section_name + "/"): topic = topic[len(self.__section_name + "/") :] return self._get_task(lang, topic) def _get_list(self, prefix=None): return [] def get_list(self, prefix=None): answer = [self.__section_name] for i in self._rosetta_code_name: answer.append("%s/%s/" % (i, self.__section_name)) return answer def is_found(self, _): return True ================================================ FILE: lib/adapter/tldr.py ================================================ """ Adapter for https://github.com/cheat/cheat Cheatsheets are located in `pages/*/` Each cheat sheet is a separate file with extension .md The pages are formatted with a markdown dialect """ # pylint: disable=relative-import,abstract-method import re import os from .git_adapter import GitRepositoryAdapter class Tldr(GitRepositoryAdapter): """ tldr-pages/tldr adapter """ _adapter_name = "tldr" _output_format = "code" _cache_needed = True _repository_url = "https://github.com/tldr-pages/tldr" _cheatsheet_files_prefix = "pages/*/" _cheatsheet_files_extension = ".md" @staticmethod def _format_page(text): """ Trivial tldr Markdown implementation. * Header goes until the first empty line after > prefixed lines. * code surrounded with `` => code * {{var}} => var """ answer = [] skip_empty = False header = 2 for line in text.splitlines(): if line.strip() == "": if skip_empty and not header: continue if header == 1: header = 0 if header: continue else: skip_empty = False if line.startswith("-"): line = "# " + line[2:] skip_empty = True elif line.startswith("> "): if header == 2: header = 1 line = "# " + line[2:] skip_empty = True elif line.startswith("`") and line.endswith("`"): line = line[1:-1] line = re.sub(r"{{(.*?)}}", r"\1", line) answer.append(line) return "\n".join(answer) def _get_page(self, topic, request_options=None): """ Go through pages/{common,linux,osx,sunos,windows}/ and as soon as anything is found, format and return it. """ search_order = ["common", "linux", "osx", "sunos", "windows", "android"] local_rep = self.local_repository_location() ext = self._cheatsheet_files_extension filename = None for subdir in search_order: _filename = os.path.join(local_rep, "pages", subdir, "%s%s" % (topic, ext)) if os.path.exists(_filename): filename = _filename break if filename: answer = self._format_page(open(filename, "r").read()) else: # though it should not happen answer = "" return answer @classmethod def get_updates_list(cls, updated_files_list): """ If a .md file was updated, invalidate cache entry with the name of this file """ answer = [] ext = cls._cheatsheet_files_extension for entry in updated_files_list: if entry.endswith(ext): answer.append(entry.split("/")[-1][: -len(ext)]) return answer ================================================ FILE: lib/adapter/upstream.py ================================================ """ Adapter for an external cheat sheets service (i.e. for cheat.sh) Configuration parameters: upstream.url upstream.timeout """ # pylint: disable=relative-import import textwrap import requests from config import CONFIG from .adapter import Adapter def _are_you_offline(): return textwrap.dedent( """ . Are you offline? _________________ | | ___________ |o| Though it could be theoretically possible | | ___________ | | to use cheat.sh fully offline, | | ___________ | | and for *the programming languages questions* too, | | ___________ | | this very feature is not yet implemented. | |_____________| | | _______ | If you find it useful, please visit | | | || https://github.com/chubin/issues/140 | DD | | V| and drop a couple of lines to encourage |____|_______|____| the authors to develop it as soon as possible . """ ) class UpstreamAdapter(Adapter): """ Connect to the upstream server `CONFIG["upstream.url"]` and fetch response from it. The response is supposed to have the "ansi" format. If the server does not respond within `CONFIG["upstream.timeout"]` seconds, or if a connection error occurs, the "are you offline" banner is displayed. Answers are by default cached; the failure answer is marked with the no-cache property ("cache": False). """ _adapter_name = "upstream" _output_format = "ansi" _cache_needed = False def _get_page(self, topic, request_options=None): options_string = "&".join( ["%s=%s" % (x, y) for (x, y) in request_options.items()] ) url = ( CONFIG["upstream.url"].rstrip("/") + "/" + topic.lstrip("/") + "?" + options_string ) try: response = requests.get(url, timeout=CONFIG["upstream.timeout"]) answer = {"cache": False, "answer": response.text} except requests.exceptions.ConnectionError: answer = {"cache": False, "answer": _are_you_offline()} return answer def _get_list(self, prefix=None): return [] ================================================ FILE: lib/buttons.py ================================================ TWITTER_BUTTON = """ """ GITHUB_BUTTON = """ cheat.sh """ GITHUB_BUTTON_2 = """ cheat.sheets """ GITHUB_BUTTON_FOOTER = """ """ ================================================ FILE: lib/cache.py ================================================ """ Cache implementation. Currently only two types of cache are allowed: * "none" cache switched off * "redis" use redis for cache Configuration parameters: cache.type = redis | none cache.redis.db cache.redis.host cache.redis.port """ import os import json from config import CONFIG _REDIS = None if CONFIG["cache.type"] == "redis": import redis _REDIS = redis.Redis( host=CONFIG["cache.redis.host"], port=CONFIG["cache.redis.port"], db=CONFIG["cache.redis.db"], ) _REDIS_PREFIX = "" if CONFIG.get("cache.redis.prefix", ""): _REDIS_PREFIX = CONFIG["cache.redis.prefix"] + ":" def put(key, value): """ Save `value` with `key`, and serialize it if needed """ if _REDIS_PREFIX: key = _REDIS_PREFIX + key if CONFIG["cache.type"] == "redis" and _REDIS: if isinstance(value, (dict, list)): value = json.dumps(value) _REDIS.set(key, value) def get(key): """ Read `value` by `key`, and deserialize it if needed """ if _REDIS_PREFIX: key = _REDIS_PREFIX + key if CONFIG["cache.type"] == "redis" and _REDIS: value = _REDIS.get(key) try: value = json.loads(value) except (ValueError, TypeError): pass return value return None def delete(key): """ Remove `key` from the database """ if _REDIS: if _REDIS_PREFIX: key = _REDIS_PREFIX + key _REDIS.delete(key) return None ================================================ FILE: lib/cheat_wrapper.py ================================================ """ Main cheat.sh wrapper. Parse the query, get answers from getters (using get_answer), visualize it using frontends and return the result. Exports: cheat_wrapper() """ import re import json from routing import get_answers, get_topics_list from search import find_answers_by_keyword from languages_data import LANGUAGE_ALIAS, rewrite_editor_section_name import postprocessing import frontend.html import frontend.ansi def _add_section_name(query): # temporary solution before we don't find a fixed one if " " not in query and "+" not in query: return query if "/" in query: return query if " " in query: return re.sub(r" +", "/", query, count=1) if "+" in query: # replace only single + to avoid catching g++ and friends return re.sub(r"([^\+])\+([^\+])", r"\1/\2", query, count=1) def cheat_wrapper(query, request_options=None, output_format="ansi"): """ Function that delivers cheat sheet for `query`. If `html` is True, the answer is formatted as HTML. Additional request options specified in `request_options`. """ def _rewrite_aliases(word): if word == ":bash.completion": return ":bash_completion" return word def _rewrite_section_name(query): """ Rewriting special section names: * EDITOR:NAME => emacs:go-mode """ if "/" not in query: return query section_name, rest = query.split("/", 1) if ":" in section_name: section_name = rewrite_editor_section_name(section_name) section_name = LANGUAGE_ALIAS.get(section_name, section_name) return "%s/%s" % (section_name, rest) def _sanitize_query(query): return re.sub('[<>"]', "", query) def _strip_hyperlink(query): return re.sub("(,[0-9]+)+$", "", query) def _parse_query(query): topic = query keyword = None search_options = "" keyword = None if "~" in query: topic = query pos = topic.index("~") keyword = topic[pos + 1 :] topic = topic[:pos] if "/" in keyword: search_options = keyword[::-1] search_options = search_options[: search_options.index("/")] keyword = keyword[: -len(search_options) - 1] return topic, keyword, search_options query = _sanitize_query(query) query = _add_section_name(query) query = _rewrite_aliases(query) query = _rewrite_section_name(query) # at the moment, we just remove trailing slashes # so queries python/ and python are equal # query = _strip_hyperlink(query.rstrip('/')) topic, keyword, search_options = _parse_query(query) if keyword: answers = find_answers_by_keyword( topic, keyword, options=search_options, request_options=request_options ) else: answers = get_answers(topic, request_options=request_options) answers = [ postprocessing.postprocess( answer, keyword, search_options, request_options=request_options ) for answer in answers ] answer_data = { "query": query, "keyword": keyword, "answers": answers, } if output_format == "html": answer_data["topics_list"] = get_topics_list() return frontend.html.visualize(answer_data, request_options) elif output_format == "json": return json.dumps(answer_data, indent=4) return frontend.ansi.visualize(answer_data, request_options) ================================================ FILE: lib/cheat_wrapper_test.py ================================================ from cheat_wrapper import _add_section_name unchanged = """ python/:list ls + g++ g/+ clang++ btrfs~volume :intro :cht.sh python/copy+file python/rosetta/:list emacs:go-mode/:list g++g++ """ split = """ python copy file python/copy file python file python/file python+file python/file g++ -O1 g++/-O1 """ def test_header_split(): for inp in unchanged.strip().splitlines(): assert inp == _add_section_name(inp) for test in split.strip().split("\n\n"): inp, outp = test.split("\n") assert outp == _add_section_name(inp) ================================================ FILE: lib/config.py ================================================ """ Global configuration of the project. All configurable parameters are stored in the global variable CONFIG, the only variable which is exported from the module. Default values of all configuration parameters are specified in the `_CONFIG` dictionary. Those parameters can be overridden by three means: * config file `etc/config.yaml` located in the work dir * config file `etc/config.yaml` located in the project dir (if the work dir and the project dir are not the same) * environment variables prefixed with `CHEATSH_` Configuration placement priorities, from high to low: * environment variables; * configuration file in the workdir * configuration file in the project dir * default values specified in the `_CONFIG` dictionary If the work dir and the project dir are not the same, we do not recommend that you use the config file located in the project dir, except the cases when you use your own cheat.sh fork, and thus configuration is a part of the project repository. In all other cases `WORKDIR/etc/config.yaml` should be preferred. Location of this config file can be overridden by the `CHEATSH_PATH_CONFIG` environment variable. Configuration parameters set by environment variables are mapped in this way: * CHEATSH_ prefix is trimmed * _ replaced with . * the string is lowercased For instance, an environment variable named `CHEATSH_SERVER_PORT` specifies the value for the `server.port` configuration parameter. Only parameters that imply scalar values (integer or string) can be set using environment variables, for the rest config files should be used. If a parameter implies an integer, and the value specified by an environment variable is not an integer, it is ignored. """ from __future__ import print_function import os from pygments.styles import get_all_styles # def get_all_styles(): # return [] _ENV_VAR_PREFIX = "CHEATSH" _MYDIR = os.path.abspath(os.path.join(__file__, "..", "..")) def _config_locations(): """ Return three possible config locations where configuration can be found: * `_WORKDIR`, `_CONF_FILE_WORKDIR`, `_CONF_FILE_MYDIR` """ var = _ENV_VAR_PREFIX + "_PATH_WORKDIR" workdir = ( os.environ[var] if var in os.environ else os.path.join(os.environ["HOME"], ".cheat.sh") ) var = _ENV_VAR_PREFIX + "_CONFIG" conf_file_workdir = ( os.environ[var] if var in os.environ else os.path.join(workdir, "etc/config.yaml") ) conf_file_mydir = os.path.join(_MYDIR, "etc/config.yaml") return workdir, conf_file_workdir, conf_file_mydir _WORKDIR, _CONF_FILE_WORKDIR, _CONF_FILE_MYDIR = _config_locations() _CONFIG = { "adapters.active": [ "tldr", "cheat", "fosdem", "translation", "rosetta", "late.nz", "question", "cheat.sheets", "cheat.sheets dir", "learnxiny", "rfc", "oeis", "chmod", ], "adapters.mandatory": [ "search", ], "cache.redis.db": 0, "cache.redis.host": "localhost", "cache.redis.port": 6379, "cache.redis.prefix": "", "cache.type": "redis", "frontend.styles": sorted(list(get_all_styles())), "log.level": 4, "path.internal.ansi2html": os.path.join(_MYDIR, "share/ansi2html.sh"), "path.internal.bin": os.path.join(_MYDIR, "bin"), "path.internal.bin.upstream": os.path.join(_MYDIR, "bin", "upstream"), "path.internal.malformed": os.path.join( _MYDIR, "share/static/malformed-response.html" ), "path.internal.pages": os.path.join(_MYDIR, "share"), "path.internal.static": os.path.join(_MYDIR, "share/static"), "path.internal.templates": os.path.join(_MYDIR, "share/templates"), "path.internal.vim": os.path.join(_MYDIR, "share/vim"), "path.log.main": "log/main.log", "path.log.queries": "log/queries.log", "path.log.fetch": "log/fetch.log", "path.repositories": "upstream", "path.spool": "spool", "path.workdir": _WORKDIR, "routing.pre": [ ("^$", "search"), ("^[^/]*/rosetta(/|$)", "rosetta"), ("^rfc/", "rfc"), ("^oeis/", "oeis"), ("^chmod/", "chmod"), ("^:", "internal"), ("/:list$", "internal"), ("/$", "cheat.sheets dir"), ], "routing.main": [ ("", "cheat.sheets"), ("", "cheat"), ("", "tldr"), ("", "late.nz"), ("", "fosdem"), ("", "learnxiny"), ], "routing.post": [ ("^[^/ +]*$", "unknown"), ("^[a-z][a-z]-[a-z][a-z]$", "translation"), ], "routing.default": "question", "upstream.url": "https://cheat.sh", "upstream.timeout": 5, "search.limit": 20, "server.bind": "0.0.0.0", "server.port": 8002, } class Config(dict): """ configuration dictionary that handles relative paths properly (making them relative to path.workdir) """ def _absolute_path(self, val): if val.startswith("/"): return val return os.path.join(self["path.workdir"], val) def __init__(self, *args, **kwargs): dict.__init__(self) self.update(*args, **kwargs) def __setitem__(self, key, val): if key.startswith("path.") and not val.startswith("/"): val = self._absolute_path(val) dict.__setitem__(self, key, val) def update(self, *args, **kwargs): """ the built-in __init__ doesn't call update, and the built-in update doesn't call __setitem__, so `update` should be overridden """ newdict = dict(*args, **kwargs) if "path.workdir" in newdict: self["path.workdir"] = newdict["path.workdir"] for key, val in newdict.items(): self[key] = val def _load_config_from_environ(config): update = {} for key, val in config.items(): if not isinstance(val, str) or isinstance(val, int): continue env_var = _ENV_VAR_PREFIX + "_" + key.replace(".", "_").upper() if not env_var in os.environ: continue env_val = os.environ[env_var] if isinstance(val, int): try: env_val = int(env_val) except (ValueError, TypeError): continue update[key] = env_val return update def _get_nested(data, key): """ Return value for a hierrachical key (like a.b.c). Return None if nothing found. If there is a key with . in the name, and a subdictionary, the former is preferred: >>> print(_get_nested({'a.b': 10, 'a':{'b': 20}}, 'a.b')) 10 >>> print(_get_nested({'a': {'b': 20}}, 'a.b')) 20 >>> print(_get_nested({'a': {'b': {'c': 30}}}, 'a.b.c')) 30 """ if not data or not isinstance(data, dict): return None if "." not in key: return data.get(key) if key in data: return data[key] parts = key.split(".") for i in range(len(parts))[::-1]: prefix = ".".join(parts[:i]) if prefix in data: return _get_nested(data[prefix], ".".join(parts[i:])) return None def _load_config_from_file(default_config, filename): import yaml update = {} if not os.path.exists(filename): return update with open(filename) as f: newconfig = yaml.load(f.read(), Loader=yaml.SafeLoader) for key, val in default_config.items(): newval = _get_nested(newconfig, key) if newval is None: continue if isinstance(val, int): try: newval = int(newval) except (ValueError, TypeError): continue update[key] = newval return update CONFIG = Config() CONFIG.update(_CONFIG) CONFIG.update(_load_config_from_file(_CONFIG, _CONF_FILE_MYDIR)) if _CONF_FILE_WORKDIR != _CONF_FILE_MYDIR: CONFIG.update(_load_config_from_file(_CONFIG, _CONF_FILE_WORKDIR)) CONFIG.update(_load_config_from_environ(_CONFIG)) if __name__ == "__main__": import doctest doctest.testmod() ================================================ FILE: lib/fetch.py ================================================ """ Repositories fetch and update This module makes real network and OS interaction, and the adapters only say how exctly this interaction should be done. Configuration parameters: * path.log.fetch """ from __future__ import print_function import sys import logging import os import subprocess import textwrap from globals import fatal import adapter import cache from config import CONFIG def _log(*message): logging.info(*message) if len(message) > 1: message = message[0].rstrip("\n") % tuple(message[1:]) else: message = message[0].rstrip("\n") sys.stdout.write(message + "\n") def _run_cmd(cmd): shell = isinstance(cmd, str) process = subprocess.Popen( cmd, shell=shell, stdout=subprocess.PIPE, stderr=subprocess.STDOUT ) output = process.communicate()[0] return process.returncode, output def fetch_all(skip_existing=True): """ Fetch all known repositories mentioned in the adapters """ def _fetch_locations(known_location): for location, adptr in known_location.items(): if location in existing_locations: continue cmd = adptr.fetch_command() if not cmd: continue sys.stdout.write("Fetching %s..." % (adptr)) sys.stdout.flush() try: process = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, ) except OSError: print("\nERROR: %s" % cmd) raise output = process.communicate()[0] if process.returncode != 0: sys.stdout.write("\nERROR:\n---\n" + output) fatal("---\nCould not fetch %s" % adptr) else: print("Done") # Searching for location duplicates for different repositories known_location = {} for adptr in adapter.adapter.all_adapters(): location = adptr.local_repository_location() if not location: continue if ( location in known_location and adptr.repository_url() != known_location[location].repository_url() ): fatal( "Duplicate location: %s for %s and %s" % (location, adptr, known_location[location]) ) known_location[location] = adptr # Parent directories creation # target subdirectories will be create during the checkout process, # but the parent directories should be created explicitly. # Also we should make sure, that the target directory does not exist existing_locations = [] for location in known_location: if os.path.exists(location): if skip_existing: existing_locations.append(location) print("Already exists %s" % (location)) else: fatal("%s already exists" % location) parent = os.path.dirname(location) if os.path.exists(parent): continue os.makedirs(parent) known_location = { k: v for k, v in known_location.items() if k not in existing_locations } _fetch_locations(known_location) def _update_adapter(adptr): """ Update implementation. If `adptr` returns no update_command(), it is being ignored. """ os.chdir(adptr.local_repository_location()) cmd = adptr.update_command() if not cmd: return True errorcode, output = _run_cmd(cmd) if errorcode: _log( "\nERROR:\n---%s\n" % output.decode("utf-8") + "\n---\nCould not update %s" % adptr ) return False # Getting current repository state # This state will be saved after the update procedure is finished # (all cache entries invalidated) cmd = adptr.current_state_command() state = None if cmd: errorcode, state = _run_cmd(cmd) if errorcode: _log( "\nERROR:\n---\n" + state + "\n---\nCould not get repository state: %s" % adptr ) return False state = state.strip() # Getting list of files that were changed # that will be later converted to the list of the pages to be invalidated cmd = adptr.get_updates_list_command() updates = [] if cmd: errorcode, output = _run_cmd(cmd) output = output.decode("utf-8") if errorcode: _log( "\nERROR:\n---\n" + output + "\n---\nCould not get list of pages to be updated: %s" % adptr ) return False updates = output.splitlines() entries = adptr.get_updates_list(updates) if entries: _log("%s Entries to be updated: %s", adptr, len(entries)) name = adptr.name() for entry in entries: cache_name = name + ":" + entry _log("+ invalidating %s", cache_name) cache.delete(cache_name) if entries: _log("Done") adptr.save_state(state) return True def update_all(): """ Update all known repositories, mentioned in the adapters and fetched locally. If repository is not fetched, it is skipped. """ for adptr in adapter.adapter.all_adapters(): location = adptr.local_repository_location() if not location: continue if not os.path.exists(location): continue _update_adapter(adptr) def update_by_name(name): """ Find adapter by its `name` and update only it. """ pass def _show_usage(): sys.stdout.write( textwrap.dedent( """ Usage: python lib/fetch.py [command] Commands: update-all -- update all configured repositories update [name] -- update repository of the adapter `name` fetch-all -- fetch all configured repositories """ ) ) def main(args): """ function for the initial repositories fetch and manual repositories updates """ if not args: _show_usage() sys.exit(0) logdir = os.path.dirname(CONFIG["path.log.fetch"]) if not os.path.exists(logdir): os.makedirs(logdir) logging.basicConfig( filename=CONFIG["path.log.fetch"], level=logging.DEBUG, format="%(asctime)s %(message)s", ) if args[0] == "fetch-all": fetch_all() elif args[0] == "update": update_by_name(sys.argv[1]) elif args[0] == "update-all": update_all() else: _show_usage() sys.exit(0) if __name__ == "__main__": main(sys.argv[1:]) ================================================ FILE: lib/fmt/__init__.py ================================================ ================================================ FILE: lib/fmt/comments.py ================================================ """ Extract text from the text-code stream and comment it. Supports three modes of normalization and commenting: 1. Don't add any comments 2. Add comments 3. Remove text, leave code only Since several operations are quite expensive, it actively uses caching. Exported functions: beautify(text, lang, options) code_blocks(text) Configuration parameters: """ from __future__ import print_function import sys import os import textwrap import hashlib import re from itertools import groupby, chain from subprocess import Popen from tempfile import NamedTemporaryFile from config import CONFIG from languages_data import VIM_NAME import cache FNULL = open(os.devnull, "w") TEXT = 0 CODE = 1 UNDEFINED = -1 CODE_WHITESPACE = -2 def _language_name(name): return VIM_NAME.get(name, name) def _remove_empty_lines_from_beginning(lines): start = 0 while start < len(lines) and lines[start].strip() == "": start += 1 lines = lines[start:] return lines def _remove_empty_lines_from_end(lines): end = len(lines) - 1 while end >= 0 and lines[end].strip() == "": end -= 1 lines = lines[: end + 1] return lines def _cleanup_lines(lines): """ Cleanup `lines` a little bit: remove empty lines at the beginning and at the end; remove too many empty lines in between. """ lines = _remove_empty_lines_from_beginning(lines) lines = _remove_empty_lines_from_end(lines) if lines == []: return lines # remove repeating empty lines lines = list( chain.from_iterable( [ (list(x[1]) if x[0] else [""]) for x in groupby(lines, key=lambda x: x.strip() != "") ] ) ) return lines def _line_type(line): """ Classify each line and say which of them are text (0) and which of them are code (1). A line is considered to be code, if it starts with four spaces. A line is considerer to be text if it is not empty and is not code. If line is empty, it is considered to be code if it surrounded but two other code lines, or if it is the first/last line and it has code on the other side. """ if line.strip() == "": return UNDEFINED # some line may start with spaces but still be not code. # we need some heuristics here, but for the moment just # whitelist such cases: if line.strip().startswith("* ") or re.match(r"[0-9]+\.", line.strip()): return TEXT if line.startswith(" "): return CODE return TEXT def _classify_lines(lines): line_types = [_line_type(line) for line in lines] # pass 2: # adding empty code lines to the code for i in range(len(line_types) - 1): if line_types[i] == CODE and line_types[i + 1] == UNDEFINED: line_types[i + 1] = CODE_WHITESPACE changed = True for i in range(len(line_types) - 1)[::-1]: if line_types[i] == UNDEFINED and line_types[i + 1] == CODE: line_types[i] = CODE_WHITESPACE changed = True line_types = [CODE if x == CODE_WHITESPACE else x for x in line_types] # pass 3: # fixing undefined line types (-1) changed = True while changed: changed = False # changing all lines types that are near the text for i in range(len(line_types) - 1): if line_types[i] == TEXT and line_types[i + 1] == UNDEFINED: line_types[i + 1] = TEXT changed = True for i in range(len(line_types) - 1)[::-1]: if line_types[i] == UNDEFINED and line_types[i + 1] == TEXT: line_types[i] = TEXT changed = True # everything what is still undefined, change to code type line_types = [CODE if x == UNDEFINED else x for x in line_types] return line_types def _unindent_code(line, shift=0): if shift == -1 and line != "": return " " + line if shift > 0 and line.startswith(" " * shift): return line[shift:] return line def _wrap_lines(lines_classes, unindent_code=False): """ Wrap classified lines. Add the split lines to the stream. If `unindent_code` is True, remove leading four spaces. """ result = [] for line_type, line_content in lines_classes: if line_type == CODE: shift = 3 if unindent_code else -1 result.append((line_type, _unindent_code(line_content, shift=shift))) else: if line_content.strip() == "": result.append((line_type, "")) for line in textwrap.fill(line_content).splitlines(): result.append((line_type, line)) return result def _run_vim_script(script_lines, text_lines): """ Apply `script_lines` to `lines_classes` and returns the result """ script_vim = NamedTemporaryFile(delete=True) textfile = NamedTemporaryFile(delete=True) open(script_vim.name, "w").write("\n".join(script_lines)) open(textfile.name, "w").write("\n".join(text_lines)) script_vim.file.close() textfile.file.close() my_env = os.environ.copy() my_env["HOME"] = CONFIG["path.internal.vim"] cmd = ["script", "-q", "-c", "vim -S %s %s" % (script_vim.name, textfile.name)] Popen( cmd, shell=False, stdin=open(os.devnull, "r"), stdout=FNULL, stderr=FNULL, env=my_env, ).communicate() return open(textfile.name, "r").read() def _commenting_script(lines_blocks, filetype): script_lines = [] block_start = 1 for block in lines_blocks: lines = list(block[1]) block_end = block_start + len(lines) - 1 if block[0] == 0: comment_type = "sexy" if block_end - block_start < 1 or filetype == "ruby": comment_type = "comment" script_lines.insert( 0, "%s,%s call NERDComment(1, '%s')" % (block_start, block_end, comment_type), ) script_lines.insert( 0, "%s,%s call NERDComment(1, 'uncomment')" % (block_start, block_end) ) block_start = block_end + 1 script_lines.insert(0, "set ft=%s" % _language_name(filetype)) script_lines.append("wq") return script_lines def _beautify(text, filetype, add_comments=False, remove_text=False): """ Main function that actually does the whole beautification job. """ # We shift the code if and only if we either convert the text into comments # or remove the text completely. Otherwise the code has to remain aligned unindent_code = add_comments or remove_text lines = [x.decode("utf-8").rstrip("\n") for x in text.splitlines()] lines = _cleanup_lines(lines) lines_classes = zip(_classify_lines(lines), lines) lines_classes = _wrap_lines(lines_classes, unindent_code=unindent_code) if remove_text: lines = [line[1] for line in lines_classes if line[0] == 1] lines = _cleanup_lines(lines) output = "\n".join(lines) if not output.endswith("\n"): output += "\n" elif not add_comments: output = "\n".join(line[1] for line in lines_classes) else: lines_blocks = groupby(lines_classes, key=lambda x: x[0]) script_lines = _commenting_script(lines_blocks, filetype) output = _run_vim_script(script_lines, [line for (_, line) in lines_classes]) return output def code_blocks(text, wrap_lines=False, unindent_code=False): """ Split `text` into blocks of text and code. Return list of tuples TYPE, TEXT """ text = text.encode("utf-8") lines = [x.rstrip("\n") for x in text.splitlines()] lines_classes = zip(_classify_lines(lines), lines) if wrap_lines: lines_classes = _wrap_lines(lines_classes, unindent_code=unindent_code) lines_blocks = groupby(lines_classes, key=lambda x: x[0]) answer = [(x[0], "\n".join([y[1] for y in x[1]]) + "\n") for x in lines_blocks] return answer def beautify(text, lang, options): """ Process input `text` according to the specified `mode`. Adds comments if needed, according to the `lang` rules. Caches the results. The whole work (except caching) is done by _beautify(). """ options = options or {} beauty_options = dict( (k, v) for k, v in options.items() if k in ["add_comments", "remove_text"] ) mode = "" if beauty_options.get("add_comments"): mode += "c" if beauty_options.get("remove_text"): mode += "q" if beauty_options == {}: # if mode is unknown, just don't transform the text at all return text if isinstance(text, str): text = text.encode("utf-8") digest = "t:%s:%s:%s" % (hashlib.md5(text).hexdigest(), lang, mode) # temporary added line that removes invalid cache entries # that used wrong commenting methods if lang in ["git", "django", "flask", "cmake"]: cache.delete(digest) answer = cache.get(digest) if answer: return answer answer = _beautify(text, lang, **beauty_options) cache.put(digest, answer) return answer def __main__(): text = sys.stdin.read() filetype = sys.argv[1] options = { "": {}, "c": dict(add_comments=True), "C": dict(add_comments=False), "q": dict(remove_text=True), }[sys.argv[2]] result = beautify(text, filetype, options) sys.stdout.write(result) if __name__ == "__main__": __main__() ================================================ FILE: lib/fmt/internal.py ================================================ """ Colorize internal cheat sheets. Will be merged with panela later. """ import re from colorama import Fore, Back, Style import colored PALETTES = { 0: { 1: Fore.WHITE, 2: Style.DIM, }, 1: { 1: Fore.CYAN, 2: Fore.GREEN, 3: colored.fg("orange_3"), 4: Style.DIM, 5: Style.DIM, }, 2: { 1: Fore.RED, 2: Style.DIM, }, } def _reverse_palette(code): return {1: Fore.BLACK + _back_color(code), 2: Style.DIM} def _back_color(code): if code == 0 or (isinstance(code, str) and code.lower() == "white"): return Back.WHITE if code == 1 or (isinstance(code, str) and code.lower() == "cyan"): return Back.CYAN if code == 2 or (isinstance(code, str) and code.lower() == "red"): return Back.RED return Back.WHITE def colorize_internal(text, palette_number=1): """ Colorize `text`, use `palette` """ palette = PALETTES[palette_number] palette_reverse = _reverse_palette(palette_number) def _process_text(text): text = text.group()[1:-1] factor = 1 if text.startswith("-"): text = text[1:] factor = -1 stripped = text.lstrip("0123456789") return (text, stripped, factor) def _extract_color_number(text, stripped, factor=1): return int(text[: len(text) - len(stripped)]) * factor def _colorize_curlies_block(text): text, stripped, factor = _process_text(text) color_number = _extract_color_number(text, stripped, factor) if stripped.startswith("="): stripped = stripped[1:] reverse = color_number < 0 if reverse: color_number = -color_number if reverse: stripped = palette_reverse[color_number] + stripped + Style.RESET_ALL else: stripped = palette[color_number] + stripped + Style.RESET_ALL return stripped def _colorize_headers(text): if text.group(0).endswith("\n"): newline = "\n" else: newline = "" color_number = 3 return palette[color_number] + text.group(0).strip() + Style.RESET_ALL + newline text = re.sub("{.*?}", _colorize_curlies_block, text) text = re.sub("#(.*?)\n", _colorize_headers, text) return text def colorize_internal_firstpage_v1(answer): """ Colorize "/:firstpage-v1". Legacy. """ def _colorize_line(line): if line.startswith("T"): line = colored.fg("grey_62") + line + colored.attr("reset") line = re.sub( r"\{(.*?)\}", colored.fg("orange_3") + r"\1" + colored.fg("grey_35"), line, ) return line line = re.sub( r"\[(F.*?)\]", colored.bg("black") + colored.fg("cyan") + r"[\1]" + colored.attr("reset"), line, ) line = re.sub( r"\[(g.*?)\]", colored.bg("dark_gray") + colored.fg("grey_0") + r"[\1]" + colored.attr("reset"), line, ) line = re.sub( r"\{(.*?)\}", colored.fg("orange_3") + r"\1" + colored.attr("reset"), line ) line = re.sub( r"<(.*?)>", colored.fg("cyan") + r"\1" + colored.attr("reset"), line ) return line lines = answer.splitlines() answer_lines = lines[:9] answer_lines.append(colored.fg("grey_35") + lines[9] + colored.attr("reset")) for line in lines[10:]: answer_lines.append(_colorize_line(line)) answer = "\n".join(answer_lines) + "\n" return answer ================================================ FILE: lib/fmt/markdown.py ================================================ """ Markdown support. Exports: format_text(text, config=None, highlighter=None): Uses external pygments formatters for highlighting (passed as an argument). """ import re import ansiwrap import colored def format_text(text, config=None, highlighter=None): """ Renders `text` according to markdown rules. Uses `highlighter` for syntax highlighting. Returns a dictionary with "output" and "links". """ return _format_section(text, config=config, highlighter=highlighter) def _split_into_paragraphs(text): return re.split("\n\n+", text) def _colorize(text): return re.sub( r"`(.*?)`", colored.bg("dark_gray") + colored.fg("white") + " " + r"\1" + " " + colored.attr("reset"), re.sub( r"\*\*(.*?)\*\*", colored.attr("bold") + colored.fg("white") + r"\1" + colored.attr("reset"), text, ), ) def _format_section(section_text, config=None, highlighter=None): answer = "" # cut code blocks block_number = 0 while True: section_text, replacements = re.subn( "^```.*?^```", "MULTILINE_BLOCK_%s" % block_number, section_text, 1, flags=re.S | re.MULTILINE, ) block_number += 1 if not replacements: break # cut links links = [] while True: regexp = re.compile(r"\[(.*?)\]\((.*?)\)") match = regexp.search(section_text) if match: links.append(match.group(0)) text = match.group(1) # links are not yet supported # text = "\x1b]8;;%s\x1b\\\\%s\x1b]8;;\x1b\\\\" % ( match.group(2), match.group(1), ) else: break section_text, replacements = regexp.subn( text, section_text, 1 # 'LINK_%s' % len(links), ) block_number += 1 if not replacements: break for paragraph in _split_into_paragraphs(section_text): answer += ( "\n".join( ansiwrap.fill(_colorize(line)) + "\n" for line in paragraph.splitlines() ) + "\n" ) return {"ansi": answer, "links": links} ================================================ FILE: lib/frontend/__init__.py ================================================ ================================================ FILE: lib/frontend/ansi.py ================================================ """ ANSI frontend. Exports: visualize(answer_data, request_options) Format: answer_data = { 'answers': '...',} answers = [answer,...] answer = { 'topic': '...', 'topic_type': '...', 'answer': '...', 'format': 'ansi|code|markdown|text...', } Configuration parameters: frontend.styles """ import os import sys import re import colored from pygments import highlight as pygments_highlight from pygments.formatters import ( Terminal256Formatter, ) # pylint: disable=no-name-in-module # pylint: disable=wrong-import-position sys.path.append(os.path.abspath(os.path.join(__file__, ".."))) from config import CONFIG import languages_data # pylint: enable=wrong-import-position import fmt.internal import fmt.comments def visualize(answer_data, request_options): """ Renders `answer_data` as ANSI output. """ answers = answer_data["answers"] return _visualize( answers, request_options, search_mode=bool(answer_data["keyword"]) ) ANSI_ESCAPE = re.compile(r"(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]") def remove_ansi(sometext): """ Remove ANSI sequences from `sometext` and convert it into plaintext. """ return ANSI_ESCAPE.sub("", sometext) def _limited_answer(answer): return ( colored.bg("dark_goldenrod") + colored.fg("yellow_1") + " " + answer + " " + colored.attr("reset") + "\n" ) def _colorize_ansi_answer( topic, answer, color_style, # pylint: disable=too-many-arguments highlight_all=True, highlight_code=False, unindent_code=False, language=None, ): color_style = color_style or "native" lexer_class = languages_data.LEXER["bash"] if "/" in topic: if language is None: section_name = topic.split("/", 1)[0].lower() else: section_name = language section_name = languages_data.get_lexer_name(section_name) lexer_class = languages_data.LEXER.get(section_name, lexer_class) if section_name == "php": answer = "\n" % answer if highlight_all: highlight = ( lambda answer: pygments_highlight( answer, lexer_class(), Terminal256Formatter(style=color_style) ).strip("\n") + "\n" ) else: highlight = lambda x: x if highlight_code: blocks = fmt.comments.code_blocks( answer, wrap_lines=True, unindent_code=(4 if unindent_code else False) ) highlighted_blocks = [] for block in blocks: if block[0] == 1: this_block = highlight(block[1]) else: this_block = block[1].strip("\n") + "\n" highlighted_blocks.append(this_block) result = "\n".join(highlighted_blocks) else: result = highlight(answer).lstrip("\n") return result def _visualize(answers, request_options, search_mode=False): highlight = not bool(request_options and request_options.get("no-terminal")) color_style = (request_options or {}).get("style", "") if color_style not in CONFIG["frontend.styles"]: color_style = "" # if there is more than one answer, # show the source of the answer multiple_answers = len(answers) > 1 found = True result = "" for answer_dict in answers: topic = answer_dict["topic"] topic_type = answer_dict["topic_type"] answer = answer_dict["answer"] found = found and not topic_type == "unknown" if multiple_answers and topic != "LIMITED": section_name = f"{topic_type}:{topic}" if not highlight: result += f"#[{section_name}]\n" else: result += "".join( [ "\n", colored.bg("dark_gray"), colored.attr("res_underlined"), f" {section_name} ", colored.attr("res_underlined"), colored.attr("reset"), "\n", ] ) if answer_dict["format"] in ["ansi", "text"]: result += answer elif topic == ":firstpage-v1": result += fmt.internal.colorize_internal_firstpage_v1(answer) elif topic == "LIMITED": result += _limited_answer(topic) else: result += _colorize_ansi_answer( topic, answer, color_style, highlight_all=highlight, highlight_code=( topic_type == "question" and not request_options.get("add_comments") and not request_options.get("remove_text") ), language=answer_dict.get("filetype"), ) if request_options.get("no-terminal"): result = remove_ansi(result) result = result.strip("\n") + "\n" return result, found ================================================ FILE: lib/frontend/html.py ================================================ """ Configuration parameters: path.internal.ansi2html """ import sys import os import re from subprocess import Popen, PIPE MYDIR = os.path.abspath(os.path.join(__file__, "..", "..")) sys.path.append("%s/lib/" % MYDIR) # pylint: disable=wrong-import-position from config import CONFIG from globals import error from buttons import TWITTER_BUTTON, GITHUB_BUTTON, GITHUB_BUTTON_FOOTER import frontend.ansi # temporary having it here, but actually we have the same data # in the adapter module GITHUB_REPOSITORY = { "late.nz": "chubin/late.nz", "cheat.sheets": "chubin/cheat.sheets", "cheat.sheets dir": "chubin/cheat.sheets", "tldr": "tldr-pages/tldr", "cheat": "chrisallenlane/cheat", "learnxiny": "adambard/learnxinyminutes-docs", "internal": "", "search": "", "unknown": "", } def visualize(answer_data, request_options): query = answer_data["query"] answers = answer_data["answers"] topics_list = answer_data["topics_list"] editable = len(answers) == 1 and answers[0]["topic_type"] == "cheat.sheets" repository_button = "" if len(answers) == 1: repository_button = _github_button(answers[0]["topic_type"]) result, found = frontend.ansi.visualize(answer_data, request_options) return ( _render_html( query, result, editable, repository_button, topics_list, request_options ), found, ) def _github_button(topic_type): full_name = GITHUB_REPOSITORY.get(topic_type, "") if not full_name: return "" short_name = full_name.split("/", 1)[1] # pylint: disable=unused-variable button = ( "" '%(short_name)s' ) % locals() return button def _render_html( query, result, editable, repository_button, topics_list, request_options ): def _html_wrapper(data): """ Convert ANSI text `data` to HTML """ cmd = [ "bash", CONFIG["path.internal.ansi2html"], "--palette=solarized", "--bg=dark", ] try: proc = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) except FileNotFoundError: print("ERROR: %s" % cmd) raise data = data.encode("utf-8") stdout, stderr = proc.communicate(data) if proc.returncode != 0: error((stdout + stderr).decode("utf-8")) return stdout.decode("utf-8") result = result + "\n$" result = _html_wrapper(result) title = "cheat.sh/%s" % query submit_button = ( '' ) topic_list = '%s' % ( "\n".join("" % x for x in topics_list) ) curl_line = "$ curl cheat.sh/" if query == ":firstpage": query = "" form_html = ( '
' "%s%s" "' "%s" "" ) % (submit_button, curl_line, query, topic_list) edit_button = "" if editable: # It's possible that topic directory starts with omitted underscore if "/" in query: query = "_" + query edit_page_link = ( "https://github.com/chubin/cheat.sheets/edit/master/sheets/" + query ) edit_button = ( '
'
            '[edit]'
            "
" ) % edit_page_link result = re.sub("
", edit_button + form_html + "
", result)
    result = re.sub("", "" + title, result)
    if not request_options.get("quiet"):
        result = result.replace(
            "",
            TWITTER_BUTTON
            + GITHUB_BUTTON
            + repository_button
            + GITHUB_BUTTON_FOOTER
            + "",
        )
    return result


================================================
FILE: lib/globals.py
================================================
"""
Global functions that our used everywhere in the project.
Please, no global variables here.
For the configuration related things see `config.py`
"""

from __future__ import print_function

import sys
import logging


def fatal(text):
    """
    Fatal error function.

    The function is being used in the standalone mode only
    """
    sys.stderr.write("ERROR: %s\n" % text)
    sys.exit(1)


def error(text):
    """
    Log error `text` and produce a RuntimeError exception
    """
    if not text.startswith("Too many queries"):
        print(text)
    logging.error("ERROR %s", text)
    raise RuntimeError(text)


def log(text):
    """
    Log error `text` (if it does not start with 'Too many queries')
    """
    if not text.startswith("Too many queries"):
        print(text)
        logging.info(text)


================================================
FILE: lib/languages_data.py
================================================
"""

Programming languages information.
Will be (probably) moved to a separate file/directory
from the project tree.

"""

import pygments.lexers

LEXER = {
    "assembly": pygments.lexers.NasmLexer,
    "awk": pygments.lexers.AwkLexer,
    "bash": pygments.lexers.BashLexer,
    "basic": pygments.lexers.QBasicLexer,
    "bf": pygments.lexers.BrainfuckLexer,
    "chapel": pygments.lexers.ChapelLexer,
    "clojure": pygments.lexers.ClojureLexer,
    "coffee": pygments.lexers.CoffeeScriptLexer,
    "cpp": pygments.lexers.CppLexer,
    "c": pygments.lexers.CLexer,
    "csharp": pygments.lexers.CSharpLexer,
    "d": pygments.lexers.DLexer,
    "dart": pygments.lexers.DartLexer,
    "delphi": pygments.lexers.DelphiLexer,
    "elisp": pygments.lexers.EmacsLispLexer,
    "elixir": pygments.lexers.ElixirLexer,
    "elm": pygments.lexers.ElmLexer,
    "erlang": pygments.lexers.ErlangLexer,
    "factor": pygments.lexers.FactorLexer,
    "forth": pygments.lexers.ForthLexer,
    "fortran": pygments.lexers.FortranLexer,
    "fsharp": pygments.lexers.FSharpLexer,
    "git": pygments.lexers.BashLexer,
    "go": pygments.lexers.GoLexer,
    "groovy": pygments.lexers.GroovyLexer,
    "haskell": pygments.lexers.HaskellLexer,
    "java": pygments.lexers.JavaLexer,
    "js": pygments.lexers.JavascriptLexer,
    "julia": pygments.lexers.JuliaLexer,
    "kotlin": pygments.lexers.KotlinLexer,
    "latex": pygments.lexers.TexLexer,
    "lisp": pygments.lexers.CommonLispLexer,
    "lua": pygments.lexers.LuaLexer,
    "mathematica": pygments.lexers.MathematicaLexer,
    "matlab": pygments.lexers.MatlabLexer,
    "mongo": pygments.lexers.JavascriptLexer,
    "nim": pygments.lexers.NimrodLexer,
    "objective-c": pygments.lexers.ObjectiveCppLexer,
    "ocaml": pygments.lexers.OcamlLexer,
    "octave": pygments.lexers.OctaveLexer,
    "perl": pygments.lexers.PerlLexer,
    "perl6": pygments.lexers.Perl6Lexer,
    "php": pygments.lexers.PhpLexer,
    "psql": pygments.lexers.PostgresLexer,
    "python": pygments.lexers.PythonLexer,
    "python3": pygments.lexers.Python3Lexer,
    "r": pygments.lexers.SLexer,
    "racket": pygments.lexers.RacketLexer,
    "ruby": pygments.lexers.RubyLexer,
    "rust": pygments.lexers.RustLexer,
    "solidity": pygments.lexers.JavascriptLexer,
    "scala": pygments.lexers.ScalaLexer,
    "scheme": pygments.lexers.SchemeLexer,
    "psql": pygments.lexers.SqlLexer,
    "sql": pygments.lexers.SqlLexer,
    "swift": pygments.lexers.SwiftLexer,
    "tcl": pygments.lexers.TclLexer,
    "tcsh": pygments.lexers.TcshLexer,
    "vb": pygments.lexers.VbNetLexer,
    "vbnet": pygments.lexers.VbNetLexer,
    "vim": pygments.lexers.VimLexer,
    # experimental
    "arduino": pygments.lexers.ArduinoLexer,
    "pike": pygments.lexers.PikeLexer,
    "eiffel": pygments.lexers.EiffelLexer,
    "clean": pygments.lexers.CleanLexer,
    "dylan": pygments.lexers.DylanLexer,
    # not languages
    "cmake": pygments.lexers.CMakeLexer,
    "django": pygments.lexers.PythonLexer,
    "flask": pygments.lexers.PythonLexer,
}

# canonical names are on the right side
LANGUAGE_ALIAS = {
    "asm": "assembly",
    "assembler": "assembly",
    "c++": "cpp",
    "c#": "csharp",
    "clisp": "lisp",
    "coffeescript": "coffee",
    "cplusplus": "cpp",
    "dlang": "d",
    "f#": "fsharp",
    "golang": "go",
    "javascript": "js",
    "objc": "objective-c",
    "p6": "perl6",
    "sh": "bash",
    "visualbasic": "vb",
    "vba": "vb",
    "wolfram": "mathematica",
    "mma": "mathematica",
    "wolfram-mathematica": "mathematica",
    "m": "octave",
}

VIM_NAME = {
    "assembly": "asm",
    "bash": "sh",
    "coffeescript": "coffee",
    "csharp": "cs",
    "delphi": "pascal",
    "dlang": "d",
    "elisp": "newlisp",
    "latex": "tex",
    "forth": "fs",
    "nim": "nimrod",
    "perl6": "perl",
    "python3": "python",
    "python-3.x": "python",
    "tcsh": "sh",
    "solidity": "js",
    "mathematica": "mma",
    "wolfram-mathematica": "mma",
    "psql": "sql",
    # not languages
    "cmake": "sh",
    "git": "sh",
    "django": "python",
    "flask": "python",
}

SO_NAME = {
    "coffee": "coffeescript",
    "js": "javascript",
    "python3": "python-3.x",
    "vb": "vba",
    "mathematica": "wolfram-mathematica",
}


#
# conversion of internal programmin language names
# into canonical cheat.sh names
#

ATOM_FT_NAME = {}

EMACS_FT_NAME = {
    "asm-mode": "asm",
    "awk-mode": "awk",
    "sh-mode": "bash",
    # basic
    "brainfuck-mode": "bf",
    # chapel
    "clojure-mode": "clojure",
    "coffee-mode": "coffee",
    "c++-mode": "cpp",
    "c-mode": "c",
    "csharp-mode": "csharp",
    "d-mode": "d",
    "dart-mode": "dart",
    "dylan-mode": "dylan",
    "delphi-mode": "delphi",
    "emacs-lisp-mode": "elisp",
    # elixir
    "elm-mode": "elm",
    "erlang-mode": "erlang",
    # factor
    "forth-mode": "forth",
    "fortran-mode": "fortran",
    "fsharp-mode": "fsharp",
    "go-mode": "go",
    "groovy-mode": "groovy",
    "haskell-mode": "haskell",
    # "hy-mode"
    "java-mode": "java",
    "js-jsx-mode": "js",
    "js-mode": "js",
    "js2-jsx-mode": "js",
    "js2-mode": "js",
    "julia-mode": "julia",
    "kotlin-mode": "kotlin",
    "lisp-interaction-mode": "lisp",
    "lisp-mode": "lisp",
    "lua-mode": "lua",
    # mathematica
    "matlab-mode": "matlab",
    # mongo
    "objc-mode": "objective-c",
    # ocaml
    "perl-mode": "perl",
    "perl6-mode": "perl6",
    "php-mode": "php",
    # psql
    "python-mode": "python",
    # python3
    # r -- ess looks it, but I don't know the mode name off hand
    "racket-mode": "racket",
    "ruby-mode": "ruby",
    "rust-mode": "rust",
    "solidity-mode": "solidity",
    "scala-mode": "scala",
    "scheme-mode": "scheme",
    "sql-mode": "sql",
    "swift-mode": "swift",
    "tcl-mode": "tcl",
    # tcsh
    "visual-basic-mode": "vb",
    # vbnet
    # vim
}

SUBLIME_FT_NAME = {}

VIM_FT_NAME = {
    "asm": "assembler",
    "javascript": "js",
    "octave": "matlab",
}

VSCODE_FT_NAME = {}


def rewrite_editor_section_name(section_name):
    """
    section name cen be specified in form "editor:editor-filetype"
    and it will be rewritten into form "filetype"
    basing on the editor filetypes names data.
    If editor name is unknown, it is just cut off:  notepad:js => js

    Known editors:
        * atom
        * vim
        * emacs
        * sublime
        * vscode

    >>> rewrite_editor_section_name('js')
    'js'
    >>> rewrite_editor_section_name('vscode:js')
    'js'
    """
    if ":" not in section_name:
        return section_name

    editor_name, section_name = section_name.split(":", 1)
    editor_name_mapping = {
        "atom": ATOM_FT_NAME,
        "emacs": EMACS_FT_NAME,
        "sublime": SUBLIME_FT_NAME,
        "vim": VIM_FT_NAME,
        "vscode": VSCODE_FT_NAME,
    }
    if editor_name not in editor_name_mapping:
        return section_name
    return editor_name_mapping[editor_name].get(section_name, section_name)


def get_lexer_name(section_name):
    """
    Rewrite `section_name` for the further lexer search (for syntax highlighting)
    """
    if ":" in section_name:
        section_name = rewrite_editor_section_name(section_name)
    return LANGUAGE_ALIAS.get(section_name, section_name)


if __name__ == "__main__":
    import doctest

    doctest.testmod()


================================================
FILE: lib/limits.py
================================================
"""
Connection limitation.

Number of connections from one IP is limited.
We have nothing against scripting and automated queries.
Even the opposite, we encourage them. But there are some
connection limits that even we can't handle.
Currently the limits are quite restrictive, but they will be relaxed
in the future.

Usage:

        limits = Limits()
        not_allowed = limits.check_ip(ip_address)
        if not_allowed:
            return "ERROR: %s" % not_allowed
"""

import time
from globals import log

_WHITELIST = ["5.9.243.177"]


def _time_caps(minutes, hours, days):
    return {
        "min": minutes,
        "hour": hours,
        "day": days,
    }


class Limits(object):
    """
    Queries limitation (by IP).

    Exports:

        check_ip(ip_address)
    """

    def __init__(self):
        self.intervals = ["min", "hour", "day"]

        self.divisor = _time_caps(60, 3600, 86400)
        self.limit = _time_caps(30, 600, 1000)
        self.last_update = _time_caps(0, 0, 0)

        self.counter = {
            "min": {},
            "hour": {},
            "day": {},
        }

        self._clear_counters_if_needed()

    def _log_visit(self, interval, ip_address):
        if ip_address not in self.counter[interval]:
            self.counter[interval][ip_address] = 0
        self.counter[interval][ip_address] += 1

    def _limit_exceeded(self, interval, ip_address):
        visits = self.counter[interval][ip_address]
        limit = self._get_limit(interval)
        return visits > limit

    def _get_limit(self, interval):
        return self.limit[interval]

    def _report_excessive_visits(self, interval, ip_address):
        log(
            "%s LIMITED [%s for %s]" % (ip_address, self._get_limit(interval), interval)
        )

    def check_ip(self, ip_address):
        """
        Check if `ip_address` is allowed, and if not raise an RuntimeError exception.
        Return True otherwise
        """
        if ip_address in _WHITELIST:
            return None
        self._clear_counters_if_needed()
        for interval in self.intervals:
            self._log_visit(interval, ip_address)
            if self._limit_exceeded(interval, ip_address):
                self._report_excessive_visits(interval, ip_address)
                return "Not so fast! Number of queries per %s is limited to %s" % (
                    interval,
                    self._get_limit(interval),
                )
        return None

    def reset(self):
        """
        Reset all counters for all IPs
        """
        for interval in self.intervals:
            self.counter[interval] = {}

    def _clear_counters_if_needed(self):
        current_time = int(time.time())
        for interval in self.intervals:
            if current_time // self.divisor[interval] != self.last_update[interval]:
                self.counter[interval] = {}
                self.last_update[interval] = current_time / self.divisor[interval]


================================================
FILE: lib/options.py
================================================
"""
Parse query arguments.
"""


def parse_args(args):
    """
    Parse arguments and options.
    Replace short options with their long counterparts.
    """
    result = {
        "add_comments": True,
    }

    query = ""
    newargs = {}
    for key, val in args.items():
        if val == "" or val == [] or val == [""]:
            query += key
            continue
        if val == "True":
            val = True
        if val == "False":
            val = False
        newargs[key] = val

    options_meaning = {
        "c": dict(add_comments=False, unindent_code=False),
        "C": dict(add_comments=False, unindent_code=True),
        "Q": dict(remove_text=True),
        "q": dict(quiet=True),
        "T": {"no-terminal": True},
    }
    for option, meaning in options_meaning.items():
        if option in query:
            result.update(meaning)

    result.update(newargs)

    return result


================================================
FILE: lib/panela/colors.json
================================================
[{"colorId":0,"hexString":"#000000","rgb":{"r":0,"g":0,"b":0},"hsl":{"h":0,"s":0,"l":0},"name":"Black"},{"colorId":1,"hexString":"#800000","rgb":{"r":128,"g":0,"b":0},"hsl":{"h":0,"s":100,"l":25},"name":"Maroon"},{"colorId":2,"hexString":"#008000","rgb":{"r":0,"g":128,"b":0},"hsl":{"h":120,"s":100,"l":25},"name":"Green"},{"colorId":3,"hexString":"#808000","rgb":{"r":128,"g":128,"b":0},"hsl":{"h":60,"s":100,"l":25},"name":"Olive"},{"colorId":4,"hexString":"#000080","rgb":{"r":0,"g":0,"b":128},"hsl":{"h":240,"s":100,"l":25},"name":"Navy"},{"colorId":5,"hexString":"#800080","rgb":{"r":128,"g":0,"b":128},"hsl":{"h":300,"s":100,"l":25},"name":"Purple"},{"colorId":6,"hexString":"#008080","rgb":{"r":0,"g":128,"b":128},"hsl":{"h":180,"s":100,"l":25},"name":"Teal"},{"colorId":7,"hexString":"#c0c0c0","rgb":{"r":192,"g":192,"b":192},"hsl":{"h":0,"s":0,"l":75},"name":"Silver"},{"colorId":8,"hexString":"#808080","rgb":{"r":128,"g":128,"b":128},"hsl":{"h":0,"s":0,"l":50},"name":"Grey"},{"colorId":9,"hexString":"#ff0000","rgb":{"r":255,"g":0,"b":0},"hsl":{"h":0,"s":100,"l":50},"name":"Red"},{"colorId":10,"hexString":"#00ff00","rgb":{"r":0,"g":255,"b":0},"hsl":{"h":120,"s":100,"l":50},"name":"Lime"},{"colorId":11,"hexString":"#ffff00","rgb":{"r":255,"g":255,"b":0},"hsl":{"h":60,"s":100,"l":50},"name":"Yellow"},{"colorId":12,"hexString":"#0000ff","rgb":{"r":0,"g":0,"b":255},"hsl":{"h":240,"s":100,"l":50},"name":"Blue"},{"colorId":13,"hexString":"#ff00ff","rgb":{"r":255,"g":0,"b":255},"hsl":{"h":300,"s":100,"l":50},"name":"Fuchsia"},{"colorId":14,"hexString":"#00ffff","rgb":{"r":0,"g":255,"b":255},"hsl":{"h":180,"s":100,"l":50},"name":"Aqua"},{"colorId":15,"hexString":"#ffffff","rgb":{"r":255,"g":255,"b":255},"hsl":{"h":0,"s":0,"l":100},"name":"White"},{"colorId":16,"hexString":"#000000","rgb":{"r":0,"g":0,"b":0},"hsl":{"h":0,"s":0,"l":0},"name":"Grey0"},{"colorId":17,"hexString":"#00005f","rgb":{"r":0,"g":0,"b":95},"hsl":{"h":240,"s":100,"l":18},"name":"NavyBlue"},{"colorId":18,"hexString":"#000087","rgb":{"r":0,"g":0,"b":135},"hsl":{"h":240,"s":100,"l":26},"name":"DarkBlue"},{"colorId":19,"hexString":"#0000af","rgb":{"r":0,"g":0,"b":175},"hsl":{"h":240,"s":100,"l":34},"name":"Blue3"},{"colorId":20,"hexString":"#0000d7","rgb":{"r":0,"g":0,"b":215},"hsl":{"h":240,"s":100,"l":42},"name":"Blue3"},{"colorId":21,"hexString":"#0000ff","rgb":{"r":0,"g":0,"b":255},"hsl":{"h":240,"s":100,"l":50},"name":"Blue1"},{"colorId":22,"hexString":"#005f00","rgb":{"r":0,"g":95,"b":0},"hsl":{"h":120,"s":100,"l":18},"name":"DarkGreen"},{"colorId":23,"hexString":"#005f5f","rgb":{"r":0,"g":95,"b":95},"hsl":{"h":180,"s":100,"l":18},"name":"DeepSkyBlue4"},{"colorId":24,"hexString":"#005f87","rgb":{"r":0,"g":95,"b":135},"hsl":{"h":197.777777777778,"s":100,"l":26},"name":"DeepSkyBlue4"},{"colorId":25,"hexString":"#005faf","rgb":{"r":0,"g":95,"b":175},"hsl":{"h":207.428571428571,"s":100,"l":34},"name":"DeepSkyBlue4"},{"colorId":26,"hexString":"#005fd7","rgb":{"r":0,"g":95,"b":215},"hsl":{"h":213.488372093023,"s":100,"l":42},"name":"DodgerBlue3"},{"colorId":27,"hexString":"#005fff","rgb":{"r":0,"g":95,"b":255},"hsl":{"h":217.647058823529,"s":100,"l":50},"name":"DodgerBlue2"},{"colorId":28,"hexString":"#008700","rgb":{"r":0,"g":135,"b":0},"hsl":{"h":120,"s":100,"l":26},"name":"Green4"},{"colorId":29,"hexString":"#00875f","rgb":{"r":0,"g":135,"b":95},"hsl":{"h":162.222222222222,"s":100,"l":26},"name":"SpringGreen4"},{"colorId":30,"hexString":"#008787","rgb":{"r":0,"g":135,"b":135},"hsl":{"h":180,"s":100,"l":26},"name":"Turquoise4"},{"colorId":31,"hexString":"#0087af","rgb":{"r":0,"g":135,"b":175},"hsl":{"h":193.714285714286,"s":100,"l":34},"name":"DeepSkyBlue3"},{"colorId":32,"hexString":"#0087d7","rgb":{"r":0,"g":135,"b":215},"hsl":{"h":202.325581395349,"s":100,"l":42},"name":"DeepSkyBlue3"},{"colorId":33,"hexString":"#0087ff","rgb":{"r":0,"g":135,"b":255},"hsl":{"h":208.235294117647,"s":100,"l":50},"name":"DodgerBlue1"},{"colorId":34,"hexString":"#00af00","rgb":{"r":0,"g":175,"b":0},"hsl":{"h":120,"s":100,"l":34},"name":"Green3"},{"colorId":35,"hexString":"#00af5f","rgb":{"r":0,"g":175,"b":95},"hsl":{"h":152.571428571429,"s":100,"l":34},"name":"SpringGreen3"},{"colorId":36,"hexString":"#00af87","rgb":{"r":0,"g":175,"b":135},"hsl":{"h":166.285714285714,"s":100,"l":34},"name":"DarkCyan"},{"colorId":37,"hexString":"#00afaf","rgb":{"r":0,"g":175,"b":175},"hsl":{"h":180,"s":100,"l":34},"name":"LightSeaGreen"},{"colorId":38,"hexString":"#00afd7","rgb":{"r":0,"g":175,"b":215},"hsl":{"h":191.162790697674,"s":100,"l":42},"name":"DeepSkyBlue2"},{"colorId":39,"hexString":"#00afff","rgb":{"r":0,"g":175,"b":255},"hsl":{"h":198.823529411765,"s":100,"l":50},"name":"DeepSkyBlue1"},{"colorId":40,"hexString":"#00d700","rgb":{"r":0,"g":215,"b":0},"hsl":{"h":120,"s":100,"l":42},"name":"Green3"},{"colorId":41,"hexString":"#00d75f","rgb":{"r":0,"g":215,"b":95},"hsl":{"h":146.511627906977,"s":100,"l":42},"name":"SpringGreen3"},{"colorId":42,"hexString":"#00d787","rgb":{"r":0,"g":215,"b":135},"hsl":{"h":157.674418604651,"s":100,"l":42},"name":"SpringGreen2"},{"colorId":43,"hexString":"#00d7af","rgb":{"r":0,"g":215,"b":175},"hsl":{"h":168.837209302326,"s":100,"l":42},"name":"Cyan3"},{"colorId":44,"hexString":"#00d7d7","rgb":{"r":0,"g":215,"b":215},"hsl":{"h":180,"s":100,"l":42},"name":"DarkTurquoise"},{"colorId":45,"hexString":"#00d7ff","rgb":{"r":0,"g":215,"b":255},"hsl":{"h":189.411764705882,"s":100,"l":50},"name":"Turquoise2"},{"colorId":46,"hexString":"#00ff00","rgb":{"r":0,"g":255,"b":0},"hsl":{"h":120,"s":100,"l":50},"name":"Green1"},{"colorId":47,"hexString":"#00ff5f","rgb":{"r":0,"g":255,"b":95},"hsl":{"h":142.352941176471,"s":100,"l":50},"name":"SpringGreen2"},{"colorId":48,"hexString":"#00ff87","rgb":{"r":0,"g":255,"b":135},"hsl":{"h":151.764705882353,"s":100,"l":50},"name":"SpringGreen1"},{"colorId":49,"hexString":"#00ffaf","rgb":{"r":0,"g":255,"b":175},"hsl":{"h":161.176470588235,"s":100,"l":50},"name":"MediumSpringGreen"},{"colorId":50,"hexString":"#00ffd7","rgb":{"r":0,"g":255,"b":215},"hsl":{"h":170.588235294118,"s":100,"l":50},"name":"Cyan2"},{"colorId":51,"hexString":"#00ffff","rgb":{"r":0,"g":255,"b":255},"hsl":{"h":180,"s":100,"l":50},"name":"Cyan1"},{"colorId":52,"hexString":"#5f0000","rgb":{"r":95,"g":0,"b":0},"hsl":{"h":0,"s":100,"l":18},"name":"DarkRed"},{"colorId":53,"hexString":"#5f005f","rgb":{"r":95,"g":0,"b":95},"hsl":{"h":300,"s":100,"l":18},"name":"DeepPink4"},{"colorId":54,"hexString":"#5f0087","rgb":{"r":95,"g":0,"b":135},"hsl":{"h":282.222222222222,"s":100,"l":26},"name":"Purple4"},{"colorId":55,"hexString":"#5f00af","rgb":{"r":95,"g":0,"b":175},"hsl":{"h":272.571428571429,"s":100,"l":34},"name":"Purple4"},{"colorId":56,"hexString":"#5f00d7","rgb":{"r":95,"g":0,"b":215},"hsl":{"h":266.511627906977,"s":100,"l":42},"name":"Purple3"},{"colorId":57,"hexString":"#5f00ff","rgb":{"r":95,"g":0,"b":255},"hsl":{"h":262.352941176471,"s":100,"l":50},"name":"BlueViolet"},{"colorId":58,"hexString":"#5f5f00","rgb":{"r":95,"g":95,"b":0},"hsl":{"h":60,"s":100,"l":18},"name":"Orange4"},{"colorId":59,"hexString":"#5f5f5f","rgb":{"r":95,"g":95,"b":95},"hsl":{"h":0,"s":0,"l":37},"name":"Grey37"},{"colorId":60,"hexString":"#5f5f87","rgb":{"r":95,"g":95,"b":135},"hsl":{"h":240,"s":17,"l":45},"name":"MediumPurple4"},{"colorId":61,"hexString":"#5f5faf","rgb":{"r":95,"g":95,"b":175},"hsl":{"h":240,"s":33,"l":52},"name":"SlateBlue3"},{"colorId":62,"hexString":"#5f5fd7","rgb":{"r":95,"g":95,"b":215},"hsl":{"h":240,"s":60,"l":60},"name":"SlateBlue3"},{"colorId":63,"hexString":"#5f5fff","rgb":{"r":95,"g":95,"b":255},"hsl":{"h":240,"s":100,"l":68},"name":"RoyalBlue1"},{"colorId":64,"hexString":"#5f8700","rgb":{"r":95,"g":135,"b":0},"hsl":{"h":77.7777777777778,"s":100,"l":26},"name":"Chartreuse4"},{"colorId":65,"hexString":"#5f875f","rgb":{"r":95,"g":135,"b":95},"hsl":{"h":120,"s":17,"l":45},"name":"DarkSeaGreen4"},{"colorId":66,"hexString":"#5f8787","rgb":{"r":95,"g":135,"b":135},"hsl":{"h":180,"s":17,"l":45},"name":"PaleTurquoise4"},{"colorId":67,"hexString":"#5f87af","rgb":{"r":95,"g":135,"b":175},"hsl":{"h":210,"s":33,"l":52},"name":"SteelBlue"},{"colorId":68,"hexString":"#5f87d7","rgb":{"r":95,"g":135,"b":215},"hsl":{"h":220,"s":60,"l":60},"name":"SteelBlue3"},{"colorId":69,"hexString":"#5f87ff","rgb":{"r":95,"g":135,"b":255},"hsl":{"h":225,"s":100,"l":68},"name":"CornflowerBlue"},{"colorId":70,"hexString":"#5faf00","rgb":{"r":95,"g":175,"b":0},"hsl":{"h":87.4285714285714,"s":100,"l":34},"name":"Chartreuse3"},{"colorId":71,"hexString":"#5faf5f","rgb":{"r":95,"g":175,"b":95},"hsl":{"h":120,"s":33,"l":52},"name":"DarkSeaGreen4"},{"colorId":72,"hexString":"#5faf87","rgb":{"r":95,"g":175,"b":135},"hsl":{"h":150,"s":33,"l":52},"name":"CadetBlue"},{"colorId":73,"hexString":"#5fafaf","rgb":{"r":95,"g":175,"b":175},"hsl":{"h":180,"s":33,"l":52},"name":"CadetBlue"},{"colorId":74,"hexString":"#5fafd7","rgb":{"r":95,"g":175,"b":215},"hsl":{"h":200,"s":60,"l":60},"name":"SkyBlue3"},{"colorId":75,"hexString":"#5fafff","rgb":{"r":95,"g":175,"b":255},"hsl":{"h":210,"s":100,"l":68},"name":"SteelBlue1"},{"colorId":76,"hexString":"#5fd700","rgb":{"r":95,"g":215,"b":0},"hsl":{"h":93.4883720930233,"s":100,"l":42},"name":"Chartreuse3"},{"colorId":77,"hexString":"#5fd75f","rgb":{"r":95,"g":215,"b":95},"hsl":{"h":120,"s":60,"l":60},"name":"PaleGreen3"},{"colorId":78,"hexString":"#5fd787","rgb":{"r":95,"g":215,"b":135},"hsl":{"h":140,"s":60,"l":60},"name":"SeaGreen3"},{"colorId":79,"hexString":"#5fd7af","rgb":{"r":95,"g":215,"b":175},"hsl":{"h":160,"s":60,"l":60},"name":"Aquamarine3"},{"colorId":80,"hexString":"#5fd7d7","rgb":{"r":95,"g":215,"b":215},"hsl":{"h":180,"s":60,"l":60},"name":"MediumTurquoise"},{"colorId":81,"hexString":"#5fd7ff","rgb":{"r":95,"g":215,"b":255},"hsl":{"h":195,"s":100,"l":68},"name":"SteelBlue1"},{"colorId":82,"hexString":"#5fff00","rgb":{"r":95,"g":255,"b":0},"hsl":{"h":97.6470588235294,"s":100,"l":50},"name":"Chartreuse2"},{"colorId":83,"hexString":"#5fff5f","rgb":{"r":95,"g":255,"b":95},"hsl":{"h":120,"s":100,"l":68},"name":"SeaGreen2"},{"colorId":84,"hexString":"#5fff87","rgb":{"r":95,"g":255,"b":135},"hsl":{"h":135,"s":100,"l":68},"name":"SeaGreen1"},{"colorId":85,"hexString":"#5fffaf","rgb":{"r":95,"g":255,"b":175},"hsl":{"h":150,"s":100,"l":68},"name":"SeaGreen1"},{"colorId":86,"hexString":"#5fffd7","rgb":{"r":95,"g":255,"b":215},"hsl":{"h":165,"s":100,"l":68},"name":"Aquamarine1"},{"colorId":87,"hexString":"#5fffff","rgb":{"r":95,"g":255,"b":255},"hsl":{"h":180,"s":100,"l":68},"name":"DarkSlateGray2"},{"colorId":88,"hexString":"#870000","rgb":{"r":135,"g":0,"b":0},"hsl":{"h":0,"s":100,"l":26},"name":"DarkRed"},{"colorId":89,"hexString":"#87005f","rgb":{"r":135,"g":0,"b":95},"hsl":{"h":317.777777777778,"s":100,"l":26},"name":"DeepPink4"},{"colorId":90,"hexString":"#870087","rgb":{"r":135,"g":0,"b":135},"hsl":{"h":300,"s":100,"l":26},"name":"DarkMagenta"},{"colorId":91,"hexString":"#8700af","rgb":{"r":135,"g":0,"b":175},"hsl":{"h":286.285714285714,"s":100,"l":34},"name":"DarkMagenta"},{"colorId":92,"hexString":"#8700d7","rgb":{"r":135,"g":0,"b":215},"hsl":{"h":277.674418604651,"s":100,"l":42},"name":"DarkViolet"},{"colorId":93,"hexString":"#8700ff","rgb":{"r":135,"g":0,"b":255},"hsl":{"h":271.764705882353,"s":100,"l":50},"name":"Purple"},{"colorId":94,"hexString":"#875f00","rgb":{"r":135,"g":95,"b":0},"hsl":{"h":42.2222222222222,"s":100,"l":26},"name":"Orange4"},{"colorId":95,"hexString":"#875f5f","rgb":{"r":135,"g":95,"b":95},"hsl":{"h":0,"s":17,"l":45},"name":"LightPink4"},{"colorId":96,"hexString":"#875f87","rgb":{"r":135,"g":95,"b":135},"hsl":{"h":300,"s":17,"l":45},"name":"Plum4"},{"colorId":97,"hexString":"#875faf","rgb":{"r":135,"g":95,"b":175},"hsl":{"h":270,"s":33,"l":52},"name":"MediumPurple3"},{"colorId":98,"hexString":"#875fd7","rgb":{"r":135,"g":95,"b":215},"hsl":{"h":260,"s":60,"l":60},"name":"MediumPurple3"},{"colorId":99,"hexString":"#875fff","rgb":{"r":135,"g":95,"b":255},"hsl":{"h":255,"s":100,"l":68},"name":"SlateBlue1"},{"colorId":100,"hexString":"#878700","rgb":{"r":135,"g":135,"b":0},"hsl":{"h":60,"s":100,"l":26},"name":"Yellow4"},{"colorId":101,"hexString":"#87875f","rgb":{"r":135,"g":135,"b":95},"hsl":{"h":60,"s":17,"l":45},"name":"Wheat4"},{"colorId":102,"hexString":"#878787","rgb":{"r":135,"g":135,"b":135},"hsl":{"h":0,"s":0,"l":52},"name":"Grey53"},{"colorId":103,"hexString":"#8787af","rgb":{"r":135,"g":135,"b":175},"hsl":{"h":240,"s":20,"l":60},"name":"LightSlateGrey"},{"colorId":104,"hexString":"#8787d7","rgb":{"r":135,"g":135,"b":215},"hsl":{"h":240,"s":50,"l":68},"name":"MediumPurple"},{"colorId":105,"hexString":"#8787ff","rgb":{"r":135,"g":135,"b":255},"hsl":{"h":240,"s":100,"l":76},"name":"LightSlateBlue"},{"colorId":106,"hexString":"#87af00","rgb":{"r":135,"g":175,"b":0},"hsl":{"h":73.7142857142857,"s":100,"l":34},"name":"Yellow4"},{"colorId":107,"hexString":"#87af5f","rgb":{"r":135,"g":175,"b":95},"hsl":{"h":90,"s":33,"l":52},"name":"DarkOliveGreen3"},{"colorId":108,"hexString":"#87af87","rgb":{"r":135,"g":175,"b":135},"hsl":{"h":120,"s":20,"l":60},"name":"DarkSeaGreen"},{"colorId":109,"hexString":"#87afaf","rgb":{"r":135,"g":175,"b":175},"hsl":{"h":180,"s":20,"l":60},"name":"LightSkyBlue3"},{"colorId":110,"hexString":"#87afd7","rgb":{"r":135,"g":175,"b":215},"hsl":{"h":210,"s":50,"l":68},"name":"LightSkyBlue3"},{"colorId":111,"hexString":"#87afff","rgb":{"r":135,"g":175,"b":255},"hsl":{"h":220,"s":100,"l":76},"name":"SkyBlue2"},{"colorId":112,"hexString":"#87d700","rgb":{"r":135,"g":215,"b":0},"hsl":{"h":82.3255813953488,"s":100,"l":42},"name":"Chartreuse2"},{"colorId":113,"hexString":"#87d75f","rgb":{"r":135,"g":215,"b":95},"hsl":{"h":100,"s":60,"l":60},"name":"DarkOliveGreen3"},{"colorId":114,"hexString":"#87d787","rgb":{"r":135,"g":215,"b":135},"hsl":{"h":120,"s":50,"l":68},"name":"PaleGreen3"},{"colorId":115,"hexString":"#87d7af","rgb":{"r":135,"g":215,"b":175},"hsl":{"h":150,"s":50,"l":68},"name":"DarkSeaGreen3"},{"colorId":116,"hexString":"#87d7d7","rgb":{"r":135,"g":215,"b":215},"hsl":{"h":180,"s":50,"l":68},"name":"DarkSlateGray3"},{"colorId":117,"hexString":"#87d7ff","rgb":{"r":135,"g":215,"b":255},"hsl":{"h":200,"s":100,"l":76},"name":"SkyBlue1"},{"colorId":118,"hexString":"#87ff00","rgb":{"r":135,"g":255,"b":0},"hsl":{"h":88.2352941176471,"s":100,"l":50},"name":"Chartreuse1"},{"colorId":119,"hexString":"#87ff5f","rgb":{"r":135,"g":255,"b":95},"hsl":{"h":105,"s":100,"l":68},"name":"LightGreen"},{"colorId":120,"hexString":"#87ff87","rgb":{"r":135,"g":255,"b":135},"hsl":{"h":120,"s":100,"l":76},"name":"LightGreen"},{"colorId":121,"hexString":"#87ffaf","rgb":{"r":135,"g":255,"b":175},"hsl":{"h":140,"s":100,"l":76},"name":"PaleGreen1"},{"colorId":122,"hexString":"#87ffd7","rgb":{"r":135,"g":255,"b":215},"hsl":{"h":160,"s":100,"l":76},"name":"Aquamarine1"},{"colorId":123,"hexString":"#87ffff","rgb":{"r":135,"g":255,"b":255},"hsl":{"h":180,"s":100,"l":76},"name":"DarkSlateGray1"},{"colorId":124,"hexString":"#af0000","rgb":{"r":175,"g":0,"b":0},"hsl":{"h":0,"s":100,"l":34},"name":"Red3"},{"colorId":125,"hexString":"#af005f","rgb":{"r":175,"g":0,"b":95},"hsl":{"h":327.428571428571,"s":100,"l":34},"name":"DeepPink4"},{"colorId":126,"hexString":"#af0087","rgb":{"r":175,"g":0,"b":135},"hsl":{"h":313.714285714286,"s":100,"l":34},"name":"MediumVioletRed"},{"colorId":127,"hexString":"#af00af","rgb":{"r":175,"g":0,"b":175},"hsl":{"h":300,"s":100,"l":34},"name":"Magenta3"},{"colorId":128,"hexString":"#af00d7","rgb":{"r":175,"g":0,"b":215},"hsl":{"h":288.837209302326,"s":100,"l":42},"name":"DarkViolet"},{"colorId":129,"hexString":"#af00ff","rgb":{"r":175,"g":0,"b":255},"hsl":{"h":281.176470588235,"s":100,"l":50},"name":"Purple"},{"colorId":130,"hexString":"#af5f00","rgb":{"r":175,"g":95,"b":0},"hsl":{"h":32.5714285714286,"s":100,"l":34},"name":"DarkOrange3"},{"colorId":131,"hexString":"#af5f5f","rgb":{"r":175,"g":95,"b":95},"hsl":{"h":0,"s":33,"l":52},"name":"IndianRed"},{"colorId":132,"hexString":"#af5f87","rgb":{"r":175,"g":95,"b":135},"hsl":{"h":330,"s":33,"l":52},"name":"HotPink3"},{"colorId":133,"hexString":"#af5faf","rgb":{"r":175,"g":95,"b":175},"hsl":{"h":300,"s":33,"l":52},"name":"MediumOrchid3"},{"colorId":134,"hexString":"#af5fd7","rgb":{"r":175,"g":95,"b":215},"hsl":{"h":280,"s":60,"l":60},"name":"MediumOrchid"},{"colorId":135,"hexString":"#af5fff","rgb":{"r":175,"g":95,"b":255},"hsl":{"h":270,"s":100,"l":68},"name":"MediumPurple2"},{"colorId":136,"hexString":"#af8700","rgb":{"r":175,"g":135,"b":0},"hsl":{"h":46.2857142857143,"s":100,"l":34},"name":"DarkGoldenrod"},{"colorId":137,"hexString":"#af875f","rgb":{"r":175,"g":135,"b":95},"hsl":{"h":30,"s":33,"l":52},"name":"LightSalmon3"},{"colorId":138,"hexString":"#af8787","rgb":{"r":175,"g":135,"b":135},"hsl":{"h":0,"s":20,"l":60},"name":"RosyBrown"},{"colorId":139,"hexString":"#af87af","rgb":{"r":175,"g":135,"b":175},"hsl":{"h":300,"s":20,"l":60},"name":"Grey63"},{"colorId":140,"hexString":"#af87d7","rgb":{"r":175,"g":135,"b":215},"hsl":{"h":270,"s":50,"l":68},"name":"MediumPurple2"},{"colorId":141,"hexString":"#af87ff","rgb":{"r":175,"g":135,"b":255},"hsl":{"h":260,"s":100,"l":76},"name":"MediumPurple1"},{"colorId":142,"hexString":"#afaf00","rgb":{"r":175,"g":175,"b":0},"hsl":{"h":60,"s":100,"l":34},"name":"Gold3"},{"colorId":143,"hexString":"#afaf5f","rgb":{"r":175,"g":175,"b":95},"hsl":{"h":60,"s":33,"l":52},"name":"DarkKhaki"},{"colorId":144,"hexString":"#afaf87","rgb":{"r":175,"g":175,"b":135},"hsl":{"h":60,"s":20,"l":60},"name":"NavajoWhite3"},{"colorId":145,"hexString":"#afafaf","rgb":{"r":175,"g":175,"b":175},"hsl":{"h":0,"s":0,"l":68},"name":"Grey69"},{"colorId":146,"hexString":"#afafd7","rgb":{"r":175,"g":175,"b":215},"hsl":{"h":240,"s":33,"l":76},"name":"LightSteelBlue3"},{"colorId":147,"hexString":"#afafff","rgb":{"r":175,"g":175,"b":255},"hsl":{"h":240,"s":100,"l":84},"name":"LightSteelBlue"},{"colorId":148,"hexString":"#afd700","rgb":{"r":175,"g":215,"b":0},"hsl":{"h":71.1627906976744,"s":100,"l":42},"name":"Yellow3"},{"colorId":149,"hexString":"#afd75f","rgb":{"r":175,"g":215,"b":95},"hsl":{"h":80,"s":60,"l":60},"name":"DarkOliveGreen3"},{"colorId":150,"hexString":"#afd787","rgb":{"r":175,"g":215,"b":135},"hsl":{"h":90,"s":50,"l":68},"name":"DarkSeaGreen3"},{"colorId":151,"hexString":"#afd7af","rgb":{"r":175,"g":215,"b":175},"hsl":{"h":120,"s":33,"l":76},"name":"DarkSeaGreen2"},{"colorId":152,"hexString":"#afd7d7","rgb":{"r":175,"g":215,"b":215},"hsl":{"h":180,"s":33,"l":76},"name":"LightCyan3"},{"colorId":153,"hexString":"#afd7ff","rgb":{"r":175,"g":215,"b":255},"hsl":{"h":210,"s":100,"l":84},"name":"LightSkyBlue1"},{"colorId":154,"hexString":"#afff00","rgb":{"r":175,"g":255,"b":0},"hsl":{"h":78.8235294117647,"s":100,"l":50},"name":"GreenYellow"},{"colorId":155,"hexString":"#afff5f","rgb":{"r":175,"g":255,"b":95},"hsl":{"h":90,"s":100,"l":68},"name":"DarkOliveGreen2"},{"colorId":156,"hexString":"#afff87","rgb":{"r":175,"g":255,"b":135},"hsl":{"h":100,"s":100,"l":76},"name":"PaleGreen1"},{"colorId":157,"hexString":"#afffaf","rgb":{"r":175,"g":255,"b":175},"hsl":{"h":120,"s":100,"l":84},"name":"DarkSeaGreen2"},{"colorId":158,"hexString":"#afffd7","rgb":{"r":175,"g":255,"b":215},"hsl":{"h":150,"s":100,"l":84},"name":"DarkSeaGreen1"},{"colorId":159,"hexString":"#afffff","rgb":{"r":175,"g":255,"b":255},"hsl":{"h":180,"s":100,"l":84},"name":"PaleTurquoise1"},{"colorId":160,"hexString":"#d70000","rgb":{"r":215,"g":0,"b":0},"hsl":{"h":0,"s":100,"l":42},"name":"Red3"},{"colorId":161,"hexString":"#d7005f","rgb":{"r":215,"g":0,"b":95},"hsl":{"h":333.488372093023,"s":100,"l":42},"name":"DeepPink3"},{"colorId":162,"hexString":"#d70087","rgb":{"r":215,"g":0,"b":135},"hsl":{"h":322.325581395349,"s":100,"l":42},"name":"DeepPink3"},{"colorId":163,"hexString":"#d700af","rgb":{"r":215,"g":0,"b":175},"hsl":{"h":311.162790697674,"s":100,"l":42},"name":"Magenta3"},{"colorId":164,"hexString":"#d700d7","rgb":{"r":215,"g":0,"b":215},"hsl":{"h":300,"s":100,"l":42},"name":"Magenta3"},{"colorId":165,"hexString":"#d700ff","rgb":{"r":215,"g":0,"b":255},"hsl":{"h":290.588235294118,"s":100,"l":50},"name":"Magenta2"},{"colorId":166,"hexString":"#d75f00","rgb":{"r":215,"g":95,"b":0},"hsl":{"h":26.5116279069767,"s":100,"l":42},"name":"DarkOrange3"},{"colorId":167,"hexString":"#d75f5f","rgb":{"r":215,"g":95,"b":95},"hsl":{"h":0,"s":60,"l":60},"name":"IndianRed"},{"colorId":168,"hexString":"#d75f87","rgb":{"r":215,"g":95,"b":135},"hsl":{"h":340,"s":60,"l":60},"name":"HotPink3"},{"colorId":169,"hexString":"#d75faf","rgb":{"r":215,"g":95,"b":175},"hsl":{"h":320,"s":60,"l":60},"name":"HotPink2"},{"colorId":170,"hexString":"#d75fd7","rgb":{"r":215,"g":95,"b":215},"hsl":{"h":300,"s":60,"l":60},"name":"Orchid"},{"colorId":171,"hexString":"#d75fff","rgb":{"r":215,"g":95,"b":255},"hsl":{"h":285,"s":100,"l":68},"name":"MediumOrchid1"},{"colorId":172,"hexString":"#d78700","rgb":{"r":215,"g":135,"b":0},"hsl":{"h":37.6744186046512,"s":100,"l":42},"name":"Orange3"},{"colorId":173,"hexString":"#d7875f","rgb":{"r":215,"g":135,"b":95},"hsl":{"h":20,"s":60,"l":60},"name":"LightSalmon3"},{"colorId":174,"hexString":"#d78787","rgb":{"r":215,"g":135,"b":135},"hsl":{"h":0,"s":50,"l":68},"name":"LightPink3"},{"colorId":175,"hexString":"#d787af","rgb":{"r":215,"g":135,"b":175},"hsl":{"h":330,"s":50,"l":68},"name":"Pink3"},{"colorId":176,"hexString":"#d787d7","rgb":{"r":215,"g":135,"b":215},"hsl":{"h":300,"s":50,"l":68},"name":"Plum3"},{"colorId":177,"hexString":"#d787ff","rgb":{"r":215,"g":135,"b":255},"hsl":{"h":280,"s":100,"l":76},"name":"Violet"},{"colorId":178,"hexString":"#d7af00","rgb":{"r":215,"g":175,"b":0},"hsl":{"h":48.8372093023256,"s":100,"l":42},"name":"Gold3"},{"colorId":179,"hexString":"#d7af5f","rgb":{"r":215,"g":175,"b":95},"hsl":{"h":40,"s":60,"l":60},"name":"LightGoldenrod3"},{"colorId":180,"hexString":"#d7af87","rgb":{"r":215,"g":175,"b":135},"hsl":{"h":30,"s":50,"l":68},"name":"Tan"},{"colorId":181,"hexString":"#d7afaf","rgb":{"r":215,"g":175,"b":175},"hsl":{"h":0,"s":33,"l":76},"name":"MistyRose3"},{"colorId":182,"hexString":"#d7afd7","rgb":{"r":215,"g":175,"b":215},"hsl":{"h":300,"s":33,"l":76},"name":"Thistle3"},{"colorId":183,"hexString":"#d7afff","rgb":{"r":215,"g":175,"b":255},"hsl":{"h":270,"s":100,"l":84},"name":"Plum2"},{"colorId":184,"hexString":"#d7d700","rgb":{"r":215,"g":215,"b":0},"hsl":{"h":60,"s":100,"l":42},"name":"Yellow3"},{"colorId":185,"hexString":"#d7d75f","rgb":{"r":215,"g":215,"b":95},"hsl":{"h":60,"s":60,"l":60},"name":"Khaki3"},{"colorId":186,"hexString":"#d7d787","rgb":{"r":215,"g":215,"b":135},"hsl":{"h":60,"s":50,"l":68},"name":"LightGoldenrod2"},{"colorId":187,"hexString":"#d7d7af","rgb":{"r":215,"g":215,"b":175},"hsl":{"h":60,"s":33,"l":76},"name":"LightYellow3"},{"colorId":188,"hexString":"#d7d7d7","rgb":{"r":215,"g":215,"b":215},"hsl":{"h":0,"s":0,"l":84},"name":"Grey84"},{"colorId":189,"hexString":"#d7d7ff","rgb":{"r":215,"g":215,"b":255},"hsl":{"h":240,"s":100,"l":92},"name":"LightSteelBlue1"},{"colorId":190,"hexString":"#d7ff00","rgb":{"r":215,"g":255,"b":0},"hsl":{"h":69.4117647058823,"s":100,"l":50},"name":"Yellow2"},{"colorId":191,"hexString":"#d7ff5f","rgb":{"r":215,"g":255,"b":95},"hsl":{"h":75,"s":100,"l":68},"name":"DarkOliveGreen1"},{"colorId":192,"hexString":"#d7ff87","rgb":{"r":215,"g":255,"b":135},"hsl":{"h":80,"s":100,"l":76},"name":"DarkOliveGreen1"},{"colorId":193,"hexString":"#d7ffaf","rgb":{"r":215,"g":255,"b":175},"hsl":{"h":90,"s":100,"l":84},"name":"DarkSeaGreen1"},{"colorId":194,"hexString":"#d7ffd7","rgb":{"r":215,"g":255,"b":215},"hsl":{"h":120,"s":100,"l":92},"name":"Honeydew2"},{"colorId":195,"hexString":"#d7ffff","rgb":{"r":215,"g":255,"b":255},"hsl":{"h":180,"s":100,"l":92},"name":"LightCyan1"},{"colorId":196,"hexString":"#ff0000","rgb":{"r":255,"g":0,"b":0},"hsl":{"h":0,"s":100,"l":50},"name":"Red1"},{"colorId":197,"hexString":"#ff005f","rgb":{"r":255,"g":0,"b":95},"hsl":{"h":337.647058823529,"s":100,"l":50},"name":"DeepPink2"},{"colorId":198,"hexString":"#ff0087","rgb":{"r":255,"g":0,"b":135},"hsl":{"h":328.235294117647,"s":100,"l":50},"name":"DeepPink1"},{"colorId":199,"hexString":"#ff00af","rgb":{"r":255,"g":0,"b":175},"hsl":{"h":318.823529411765,"s":100,"l":50},"name":"DeepPink1"},{"colorId":200,"hexString":"#ff00d7","rgb":{"r":255,"g":0,"b":215},"hsl":{"h":309.411764705882,"s":100,"l":50},"name":"Magenta2"},{"colorId":201,"hexString":"#ff00ff","rgb":{"r":255,"g":0,"b":255},"hsl":{"h":300,"s":100,"l":50},"name":"Magenta1"},{"colorId":202,"hexString":"#ff5f00","rgb":{"r":255,"g":95,"b":0},"hsl":{"h":22.3529411764706,"s":100,"l":50},"name":"OrangeRed1"},{"colorId":203,"hexString":"#ff5f5f","rgb":{"r":255,"g":95,"b":95},"hsl":{"h":0,"s":100,"l":68},"name":"IndianRed1"},{"colorId":204,"hexString":"#ff5f87","rgb":{"r":255,"g":95,"b":135},"hsl":{"h":345,"s":100,"l":68},"name":"IndianRed1"},{"colorId":205,"hexString":"#ff5faf","rgb":{"r":255,"g":95,"b":175},"hsl":{"h":330,"s":100,"l":68},"name":"HotPink"},{"colorId":206,"hexString":"#ff5fd7","rgb":{"r":255,"g":95,"b":215},"hsl":{"h":315,"s":100,"l":68},"name":"HotPink"},{"colorId":207,"hexString":"#ff5fff","rgb":{"r":255,"g":95,"b":255},"hsl":{"h":300,"s":100,"l":68},"name":"MediumOrchid1"},{"colorId":208,"hexString":"#ff8700","rgb":{"r":255,"g":135,"b":0},"hsl":{"h":31.7647058823529,"s":100,"l":50},"name":"DarkOrange"},{"colorId":209,"hexString":"#ff875f","rgb":{"r":255,"g":135,"b":95},"hsl":{"h":15,"s":100,"l":68},"name":"Salmon1"},{"colorId":210,"hexString":"#ff8787","rgb":{"r":255,"g":135,"b":135},"hsl":{"h":0,"s":100,"l":76},"name":"LightCoral"},{"colorId":211,"hexString":"#ff87af","rgb":{"r":255,"g":135,"b":175},"hsl":{"h":340,"s":100,"l":76},"name":"PaleVioletRed1"},{"colorId":212,"hexString":"#ff87d7","rgb":{"r":255,"g":135,"b":215},"hsl":{"h":320,"s":100,"l":76},"name":"Orchid2"},{"colorId":213,"hexString":"#ff87ff","rgb":{"r":255,"g":135,"b":255},"hsl":{"h":300,"s":100,"l":76},"name":"Orchid1"},{"colorId":214,"hexString":"#ffaf00","rgb":{"r":255,"g":175,"b":0},"hsl":{"h":41.1764705882353,"s":100,"l":50},"name":"Orange1"},{"colorId":215,"hexString":"#ffaf5f","rgb":{"r":255,"g":175,"b":95},"hsl":{"h":30,"s":100,"l":68},"name":"SandyBrown"},{"colorId":216,"hexString":"#ffaf87","rgb":{"r":255,"g":175,"b":135},"hsl":{"h":20,"s":100,"l":76},"name":"LightSalmon1"},{"colorId":217,"hexString":"#ffafaf","rgb":{"r":255,"g":175,"b":175},"hsl":{"h":0,"s":100,"l":84},"name":"LightPink1"},{"colorId":218,"hexString":"#ffafd7","rgb":{"r":255,"g":175,"b":215},"hsl":{"h":330,"s":100,"l":84},"name":"Pink1"},{"colorId":219,"hexString":"#ffafff","rgb":{"r":255,"g":175,"b":255},"hsl":{"h":300,"s":100,"l":84},"name":"Plum1"},{"colorId":220,"hexString":"#ffd700","rgb":{"r":255,"g":215,"b":0},"hsl":{"h":50.5882352941176,"s":100,"l":50},"name":"Gold1"},{"colorId":221,"hexString":"#ffd75f","rgb":{"r":255,"g":215,"b":95},"hsl":{"h":45,"s":100,"l":68},"name":"LightGoldenrod2"},{"colorId":222,"hexString":"#ffd787","rgb":{"r":255,"g":215,"b":135},"hsl":{"h":40,"s":100,"l":76},"name":"LightGoldenrod2"},{"colorId":223,"hexString":"#ffd7af","rgb":{"r":255,"g":215,"b":175},"hsl":{"h":30,"s":100,"l":84},"name":"NavajoWhite1"},{"colorId":224,"hexString":"#ffd7d7","rgb":{"r":255,"g":215,"b":215},"hsl":{"h":0,"s":100,"l":92},"name":"MistyRose1"},{"colorId":225,"hexString":"#ffd7ff","rgb":{"r":255,"g":215,"b":255},"hsl":{"h":300,"s":100,"l":92},"name":"Thistle1"},{"colorId":226,"hexString":"#ffff00","rgb":{"r":255,"g":255,"b":0},"hsl":{"h":60,"s":100,"l":50},"name":"Yellow1"},{"colorId":227,"hexString":"#ffff5f","rgb":{"r":255,"g":255,"b":95},"hsl":{"h":60,"s":100,"l":68},"name":"LightGoldenrod1"},{"colorId":228,"hexString":"#ffff87","rgb":{"r":255,"g":255,"b":135},"hsl":{"h":60,"s":100,"l":76},"name":"Khaki1"},{"colorId":229,"hexString":"#ffffaf","rgb":{"r":255,"g":255,"b":175},"hsl":{"h":60,"s":100,"l":84},"name":"Wheat1"},{"colorId":230,"hexString":"#ffffd7","rgb":{"r":255,"g":255,"b":215},"hsl":{"h":60,"s":100,"l":92},"name":"Cornsilk1"},{"colorId":231,"hexString":"#ffffff","rgb":{"r":255,"g":255,"b":255},"hsl":{"h":0,"s":0,"l":100},"name":"Grey100"},{"colorId":232,"hexString":"#080808","rgb":{"r":8,"g":8,"b":8},"hsl":{"h":0,"s":0,"l":3},"name":"Grey3"},{"colorId":233,"hexString":"#121212","rgb":{"r":18,"g":18,"b":18},"hsl":{"h":0,"s":0,"l":7},"name":"Grey7"},{"colorId":234,"hexString":"#1c1c1c","rgb":{"r":28,"g":28,"b":28},"hsl":{"h":0,"s":0,"l":10},"name":"Grey11"},{"colorId":235,"hexString":"#262626","rgb":{"r":38,"g":38,"b":38},"hsl":{"h":0,"s":0,"l":14},"name":"Grey15"},{"colorId":236,"hexString":"#303030","rgb":{"r":48,"g":48,"b":48},"hsl":{"h":0,"s":0,"l":18},"name":"Grey19"},{"colorId":237,"hexString":"#3a3a3a","rgb":{"r":58,"g":58,"b":58},"hsl":{"h":0,"s":0,"l":22},"name":"Grey23"},{"colorId":238,"hexString":"#444444","rgb":{"r":68,"g":68,"b":68},"hsl":{"h":0,"s":0,"l":26},"name":"Grey27"},{"colorId":239,"hexString":"#4e4e4e","rgb":{"r":78,"g":78,"b":78},"hsl":{"h":0,"s":0,"l":30},"name":"Grey30"},{"colorId":240,"hexString":"#585858","rgb":{"r":88,"g":88,"b":88},"hsl":{"h":0,"s":0,"l":34},"name":"Grey35"},{"colorId":241,"hexString":"#626262","rgb":{"r":98,"g":98,"b":98},"hsl":{"h":0,"s":0,"l":37},"name":"Grey39"},{"colorId":242,"hexString":"#6c6c6c","rgb":{"r":108,"g":108,"b":108},"hsl":{"h":0,"s":0,"l":40},"name":"Grey42"},{"colorId":243,"hexString":"#767676","rgb":{"r":118,"g":118,"b":118},"hsl":{"h":0,"s":0,"l":46},"name":"Grey46"},{"colorId":244,"hexString":"#808080","rgb":{"r":128,"g":128,"b":128},"hsl":{"h":0,"s":0,"l":50},"name":"Grey50"},{"colorId":245,"hexString":"#8a8a8a","rgb":{"r":138,"g":138,"b":138},"hsl":{"h":0,"s":0,"l":54},"name":"Grey54"},{"colorId":246,"hexString":"#949494","rgb":{"r":148,"g":148,"b":148},"hsl":{"h":0,"s":0,"l":58},"name":"Grey58"},{"colorId":247,"hexString":"#9e9e9e","rgb":{"r":158,"g":158,"b":158},"hsl":{"h":0,"s":0,"l":61},"name":"Grey62"},{"colorId":248,"hexString":"#a8a8a8","rgb":{"r":168,"g":168,"b":168},"hsl":{"h":0,"s":0,"l":65},"name":"Grey66"},{"colorId":249,"hexString":"#b2b2b2","rgb":{"r":178,"g":178,"b":178},"hsl":{"h":0,"s":0,"l":69},"name":"Grey70"},{"colorId":250,"hexString":"#bcbcbc","rgb":{"r":188,"g":188,"b":188},"hsl":{"h":0,"s":0,"l":73},"name":"Grey74"},{"colorId":251,"hexString":"#c6c6c6","rgb":{"r":198,"g":198,"b":198},"hsl":{"h":0,"s":0,"l":77},"name":"Grey78"},{"colorId":252,"hexString":"#d0d0d0","rgb":{"r":208,"g":208,"b":208},"hsl":{"h":0,"s":0,"l":81},"name":"Grey82"},{"colorId":253,"hexString":"#dadada","rgb":{"r":218,"g":218,"b":218},"hsl":{"h":0,"s":0,"l":85},"name":"Grey85"},{"colorId":254,"hexString":"#e4e4e4","rgb":{"r":228,"g":228,"b":228},"hsl":{"h":0,"s":0,"l":89},"name":"Grey89"},{"colorId":255,"hexString":"#eeeeee","rgb":{"r":238,"g":238,"b":238},"hsl":{"h":0,"s":0,"l":93},"name":"Grey93"}]

================================================
FILE: lib/panela/colors.py
================================================
import os
import json

COLORS_JSON = os.path.join(os.path.dirname(os.path.abspath(__file__)), "colors.json")
COLOR_TABLE = json.loads(open(COLORS_JSON, "r").read())
VALID_COLORS = [x["hexString"] for x in COLOR_TABLE]
HEX_TO_ANSI = {x["hexString"]: x["colorId"] for x in COLOR_TABLE}


def rgb_from_str(s):
    # s starts with a #.
    r, g, b = int(s[1:3], 16), int(s[3:5], 16), int(s[5:7], 16)
    return r, g, b


def find_nearest_color(hex_color):
    R, G, B = rgb_from_str(hex_color)
    mindiff = None
    for d in VALID_COLORS:
        r, g, b = rgb_from_str(d)
        diff = abs(R - r) * 256 + abs(G - g) * 256 + abs(B - b) * 256
        if mindiff is None or diff < mindiff:
            mindiff = diff
            mincolorname = d
    return mincolorname


================================================
FILE: lib/panela/panela_colors.py
================================================
# vim: encoding=utf-8

import os
import sys
import colored
import itertools
from globals import MYDIR

"""

After panela will be ready for it, it will be split out in a separate project,
that will be used for all chubin's console services.
There are several features that not yet implemented (see ___doc___ in Panela)

TODO:
    * html output
    * png output

"""

from wcwidth import wcswidth
from colors import find_nearest_color, HEX_TO_ANSI, rgb_from_str
import pyte

# http://stackoverflow.com/questions/19782975/convert-rgb-color-to-the-nearest-color-in-palette-web-safe-color

try:
    basestring  # Python 2
except NameError:
    basestring = str  # Python 3


def color_mapping(clr):
    if clr == "default":
        return None
    return clr


class Point(object):
    """
    One point (character) on a terminal
    """

    def __init__(self, char=None, foreground=None, background=None):
        self.foreground = foreground
        self.background = background
        self.char = char


class Panela:
    """
    To implement:

    Blocks manipulation:

        [*] copy
        [*] crop
        [*] cut
        [*] extend
        [ ] join
        [ ] move
        [*] paste
        [*] strip

    Colors manipulation:

        [*] paint           foreground/background
        [*] paint_line
        [ ] paint_svg
        [ ] fill            background
        [ ] fill_line
        [ ] fill_svg
        [ ] trans

    Drawing:

        [*] put_point
        [*] put_line
        [*] put_circle
        [*] put_rectangle

    Printing and reading:
        ansi            reads vt100 sequence
    """

    def __init__(self, x=80, y=25, panela=None, field=None):

        if panela:
            self.field = [x for x in panela.field]
            self.size_x = panela.size_x
            self.size_y = panela.size_y
            return

        if field:
            self.field = field
            self.size_x = len(field[0])
            self.size_y = len(field)
            return

        self.field = [[Point() for _ in range(x)] for _ in range(y)]
        self.size_x = x
        self.size_y = y

    def in_field(self, col, row):
        if col < 0:
            return False
        if row < 0:
            return False
        if col >= self.size_x:
            return False
        if row >= self.size_y:
            return False
        return True

    #
    # Blocks manipulation
    #

    def copy(self, x1, y1, x2, y2):

        if x1 < 0:
            x1 += self.size_x
        if x2 < 0:
            x2 += self.size_x
        if x1 > x2:
            x1, x2 = x2, x1

        if y1 < 0:
            y1 += self.size_y
        if y2 < 0:
            y2 += self.size_y
        if y1 > y2:
            y1, y2 = y2, y1

        field = [self.field[i] for i in range(y1, y2 + 1)]
        field = [line[x1 : x2 + 1] for line in field]

        return Panela(field=field)

    def cut(self, x1, y1, x2, y2):
        """ """
        if x1 < 0:
            x1 += self.size_x
        if x2 < 0:
            x2 += self.size_x
        if x1 > x2:
            x1, x2 = x2, x1

        if y1 < 0:
            y1 += self.size_y
        if y2 < 0:
            y2 += self.size_y
        if y1 > y2:
            y1, y2 = y2, y1

        copied = self.copy(x1, y1, x2, y2)

        for y in range(y1, y2 + 1):
            for x in range(x1, x2 + 1):
                self.field[y][x] = Point()

        return copied

    def extend(self, cols=None, rows=None):
        """
        Adds [cols] columns from the right
        and [rows] rows from the bottom
        """
        if cols and cols > 0:
            self.field = [x + [Point() for _ in range(cols)] for x in self.field]
            self.size_x += cols

        if rows and rows > 0:
            self.field = self.field + [
                [Point() for _ in range(self.size_x)] for _ in range(rows)
            ]
            self.size_y += rows

    def crop(self, left=None, right=None, top=None, bottom=None):
        """
        Crop panela.
        Remove ,  columns from left or right,
        and  and  rows from top and bottom.
        """

        if left:
            if left >= self.size_x:
                left = self.size_x
            self.field = [x[left:] for x in self.field]
            self.size_x -= left

        if right:
            if right >= self.size_x:
                right = self.size_x
            self.field = [x[:-right] for x in self.field]
            self.size_x -= right

        if top:
            if top >= self.size_y:
                top = self.size_y
            self.field = self.field[top:]
            self.size_y -= top

        if bottom:
            if bottom >= self.size_y:
                bottom = self.size_y
            self.field = self.field[:-bottom]
            self.size_y -= bottom

    def paste(self, panela, x1, y1, extend=False, transparence=False):
        """
        Paste  starting at , .
        If  is True current panela space will be automatically extended
        If  is True, then  is overlaid and characters behind them are seen
        """

        # FIXME:
        #  negative x1, y1
        #  x1,y1 > size_x, size_y

        if extend:
            x_extend = 0
            y_extend = 0
            if x1 + panela.size_x > self.size_x:
                x_extend = x1 + panela.size_x - self.size_x
            if y1 + panela.size_y > self.size_y:
                y_extend = y1 + panela.size_y - self.size_y
            self.extend(cols=x_extend, rows=y_extend)

        for i in range(y1, min(self.size_y, y1 + panela.size_y)):
            for j in range(x1, min(self.size_x, x1 + panela.size_x)):
                if transparence:
                    if (
                        panela.field[i - y1][j - x1].char
                        and panela.field[i - y1][j - x1].char != " "
                    ):
                        if panela.field[i - y1][j - x1].foreground:
                            self.field[i][j].foreground = panela.field[i - y1][
                                j - x1
                            ].foreground
                        if panela.field[i - y1][j - x1].background:
                            self.field[i][j].background = panela.field[i - y1][
                                j - x1
                            ].background
                        self.field[i][j].char = panela.field[i - y1][j - x1].char
                else:
                    self.field[i][j] = panela.field[i - y1][j - x1]

    def strip(self):
        """
        Strip panela: remove empty spaces around panels rectangle
        """

        def left_spaces(line):
            answer = 0
            for elem in line:
                if not elem.char:
                    answer += 1
                else:
                    break
            return answer

        def right_spaces(line):
            return left_spaces(line[::-1])

        def empty_line(line):
            return left_spaces(line) == len(line)

        left_space = []
        right_space = []
        for line in self.field:
            left_space.append(left_spaces(line))
            right_space.append(right_spaces(line))
        left = min(left_space)
        right = min(right_space)

        top = 0
        while top < self.size_y and empty_line(self.field[top]):
            top += 1

        bottom = 0
        while bottom < self.size_y and empty_line(self.field[-(bottom + 1)]):
            bottom += 1

        self.crop(left=left, right=right, top=top, bottom=bottom)

    #
    # Drawing and painting
    #

    def put_point(self, col, row, char=None, color=None, background=None):
        """
        Puts character with color and background color on the field.
        Char can be a Point or a character.
        """

        if not self.in_field(col, row):
            return
        if isinstance(char, Point):
            self.field[row][col] = char
        elif char is None:
            if background:
                self.field[row][col].background = background
            if color:
                self.field[row][col].foreground = color
        else:
            self.field[row][col] = Point(
                char=char, foreground=color, background=background
            )

    def put_string(self, col, row, s=None, color=None, background=None):
        """
        Put string  with foreground color  and background color 
        ad , 
        """
        for i, c in enumerate(s):
            self.put_point(col + i, row, c, color=color, background=background)

    def put_line(self, x1, y1, x2, y2, char=None, color=None, background=None):
        """
        Draw line (x1, y1) - (x2, y2) fill foreground color , background color 
        and character , if specified.
        """

        def get_line(start, end):
            """Bresenham's Line Algorithm
            Produces a list of tuples from start and end

            Source: http://www.roguebasin.com/index.php?title=Bresenham%27s_Line_Algorithm#Python

            >>> points1 = get_line((0, 0), (3, 4))
            >>> points2 = get_line((3, 4), (0, 0))
            >>> assert(set(points1) == set(points2))
            >>> print points1
            [(0, 0), (1, 1), (1, 2), (2, 3), (3, 4)]
            >>> print points2
            [(3, 4), (2, 3), (1, 2), (1, 1), (0, 0)]
            """
            # Setup initial conditions
            x1, y1 = start
            x2, y2 = end
            dx = x2 - x1
            dy = y2 - y1

            # Determine how steep the line is
            is_steep = abs(dy) > abs(dx)

            # Rotate line
            if is_steep:
                x1, y1 = y1, x1
                x2, y2 = y2, x2

            # Swap start and end points if necessary and store swap state
            swapped = False
            if x1 > x2:
                x1, x2 = x2, x1
                y1, y2 = y2, y1
                swapped = True

            # Recalculate differentials
            dx = x2 - x1
            dy = y2 - y1

            # Calculate error
            error = int(dx / 2.0)
            ystep = 1 if y1 < y2 else -1

            # Iterate over bounding box generating points between start and end
            y = y1
            points = []
            for x in range(x1, x2 + 1):
                coord = (y, x) if is_steep else (x, y)
                points.append(coord)
                error -= abs(dy)
                if error < 0:
                    y += ystep
                    error += dx

            # Reverse the list if the coordinates were swapped
            if swapped:
                points.reverse()
            return points

        if color and not isinstance(color, basestring):
            color_iter = itertools.cycle(color)
        else:
            color_iter = itertools.repeat(color)

        if background and not isinstance(background, basestring):
            background_iter = itertools.cycle(background)
        else:
            background_iter = itertools.repeat(background)

        if char:
            char_iter = itertools.cycle(char)
        else:
            char_iter = itertools.repeat(char)

        for x, y in get_line((x1, y1), (x2, y2)):
            char = next(char_iter)
            color = next(color_iter)
            background = next(background_iter)

            self.put_point(x, y, char=char, color=color, background=background)

    def paint(
        self, x1, y1, x2, y2, c1, c2=None, bg1=None, bg2=None, angle=None, angle_bg=None
    ):
        """
        Paint rectangle (x1,y1) (x2,y2) with foreground color c1 and background bg1 if specified.
        If specified colors c2/bg2, rectangle is painted with linear gradient (inclined under angle).
        """

        def calculate_color(i, j):
            if angle == None:
                a = 0
            else:
                a = angle

            r1, g1, b1 = rgb_from_str(c1)
            r2, g2, b2 = rgb_from_str(c2)
            k = 1.0 * (j - x1) / (x2 - x1) * (1 - a)
            l = 1.0 * (i - y1) / (y2 - y1) * a
            r3, g3, b3 = (
                int(r1 + 1.0 * (r2 - r1) * (k + l)),
                int(g1 + 1.0 * (g2 - g1) * (k + l)),
                int(b1 + 1.0 * (b2 - b1) * (k + l)),
            )

            return "#%02x%02x%02x" % (r3, g3, b3)

        def calculate_bg(i, j):
            if angle_bg == None:
                a = 0
            else:
                a = angle

            r1, g1, b1 = rgb_from_str(bg1)
            r2, g2, b2 = rgb_from_str(bg2)
            k = 1.0 * (j - x1) / (x2 - x1) * (1 - a)
            l = 1.0 * (i - y1) / (y2 - y1) * a
            r3, g3, b3 = (
                int(r1 + 1.0 * (r2 - r1) * (k + l)),
                int(g1 + 1.0 * (g2 - g1) * (k + l)),
                int(b1 + 1.0 * (b2 - b1) * (k + l)),
            )

            return "#%02x%02x%02x" % (r3, g3, b3)

        if c2 == None:
            for i in range(y1, y2):
                for j in range(x1, x2):
                    self.field[i][j].foreground = c1
                    if bg1:
                        if bg2:
                            self.field[i][j].background = calculate_bg(i, j)
                        else:
                            self.field[i][j].background = bg1
        else:
            for i in range(y1, y2):
                for j in range(x1, x2):
                    self.field[i][j].foreground = calculate_color(i, j)
                    if bg1:
                        if bg2:
                            self.field[i][j].background = calculate_bg(i, j)
                        else:
                            self.field[i][j].background = bg1

        return self

    def put_rectangle(
        self, x1, y1, x2, y2, char=None, frame=None, color=None, background=None
    ):
        """
        Draw rectangle (x1,y1), (x2,y2) using  character,  and  color
        """

        frame_chars = {
            "ascii": "++++-|",
            "single": "┌┐└┘─│",
            "double": "┌┐└┘─│",
        }
        if frame in frame_chars:
            chars = frame_chars[frame]
        else:
            chars = char * 6

        for x in range(x1, x2):
            self.put_point(x, y1, char=chars[4], color=color, background=background)
            self.put_point(x, y2, char=chars[4], color=color, background=background)

        for y in range(y1, y2):
            self.put_point(x1, y, char=chars[5], color=color, background=background)
            self.put_point(x2, y, char=chars[5], color=color, background=background)

        self.put_point(x1, y1, char=chars[0], color=color, background=background)
        self.put_point(x2, y1, char=chars[1], color=color, background=background)
        self.put_point(x1, y2, char=chars[2], color=color, background=background)
        self.put_point(x2, y2, char=chars[3], color=color, background=background)

    def put_circle(self, x0, y0, radius, char=None, color=None, background=None):
        """
        Draw cricle with center in (x, y) and radius r (x1,y1), (x2,y2)
        using  character,  and  color
        """

        def k(x):
            return int(x * 1.9)

        f = 1 - radius
        ddf_x = 1
        ddf_y = -2 * radius
        x = 0
        y = radius
        self.put_point(x0, y0 + radius, char=char, color=color, background=background)
        self.put_point(x0, y0 - radius, char=char, color=color, background=background)
        self.put_point(
            x0 + k(radius), y0, char=char, color=color, background=background
        )
        self.put_point(
            x0 - k(radius), y0, char=char, color=color, background=background
        )

        char = "x"
        while x < y:
            if f >= 0:
                y -= 1
                ddf_y += 2
                f += ddf_y
            x += 1
            ddf_x += 2
            f += ddf_x
            self.put_point(
                x0 + k(x), y0 + y, char=char, color=color, background=background
            )
            self.put_point(
                x0 - k(x), y0 + y, char=char, color=color, background=background
            )
            self.put_point(
                x0 + k(x), y0 - y, char=char, color=color, background=background
            )
            self.put_point(
                x0 - k(x), y0 - y, char=char, color=color, background=background
            )
            self.put_point(
                x0 + k(y), y0 + x, char=char, color=color, background=background
            )
            self.put_point(
                x0 - k(y), y0 + x, char=char, color=color, background=background
            )
            self.put_point(
                x0 + k(y), y0 - x, char=char, color=color, background=background
            )
            self.put_point(
                x0 - k(y), y0 - x, char=char, color=color, background=background
            )

    def read_ansi(self, seq, x=0, y=0, transparence=True):
        """
        Read ANSI sequence and render it to the panela starting from x and y.
        If transparence is True, replace spaces with ""
        """
        screen = pyte.screens.Screen(self.size_x, self.size_y + 1)

        stream = pyte.streams.ByteStream()
        stream.attach(screen)

        stream.feed(seq.replace("\n", "\r\n"))

        for i, line in sorted(screen.buffer.items(), key=lambda x: x[0]):
            for j, char in sorted(line.items(), key=lambda x: x[0]):
                if j >= self.size_x:
                    break
                self.field[i][j] = Point(
                    char.data, color_mapping(char.fg), color_mapping(char.bg)
                )

    def __str__(self):
        answer = ""
        skip_next = False
        for i, line in enumerate(self.field):
            for j, c in enumerate(line):
                fg_ansi = ""
                bg_ansi = ""
                stop = ""

                if self.field[i][j].foreground:
                    fg_ansi = "\033[38;2;%s;%s;%sm" % rgb_from_str(
                        self.field[i][j].foreground
                    )
                    stop = colored.attr("reset")

                if self.field[i][j].background:
                    bg_ansi = "\033[48;2;%s;%s;%sm" % rgb_from_str(
                        self.field[i][j].background
                    )
                    stop = colored.attr("reset")

                char = c.char or " "
                if not skip_next:
                    answer += fg_ansi + bg_ansi + char.encode("utf-8") + stop
                skip_next = wcswidth(char) == 2

            # answer += "...\n"
            answer += "\n"
        return answer


########################################################################################################


class Template(object):
    def __init__(self):
        self._mode = "page"
        self.page = []
        self.mask = []
        self.code = []
        self.panela = None

        self._colors = {
            "A": "#00cc00",
            "B": "#00cc00",
            "C": "#00aacc",
            "D": "#888888",
            "E": "#cccc00",
            "F": "#ff0000",
            "H": "#22aa22",
            "I": "#cc0000",
            "J": "#000000",
        }

        self._bg_colors = {
            "G": "#555555",
            "J": "#555555",
        }

    def _process_line(self, line):
        if line == "mask":
            self._mode = "mask"
        if line == "":
            self._mode = "code"

    def read(self, filename):
        """
        Read template from `filename`
        """
        with open(filename) as f:
            self._mode = "page"
            for line in f.readlines():
                line = line.rstrip("\n")
                if line.startswith("==[") and line.endswith("]=="):
                    self._process_line(line[3:-3].strip())
                    continue

                if self._mode == "page":
                    self.page.append(line)
                elif self._mode == "mask":
                    self.mask.append(line)
                elif self._mode == "code":
                    self.mask.append(line)

    def apply_mask(self):

        lines = self.page
        x_size = max([len(x) for x in lines])
        y_size = len(lines)

        self.panela = Panela(x=x_size, y=y_size)
        self.panela.read_ansi("".join("%s\n" % x for x in self.page))

        for i, line in enumerate(self.mask):
            for j, char in enumerate(line):
                if char in self._colors or char in self._bg_colors:
                    color = self._colors.get(char)
                    bg_color = self._bg_colors.get(char)
                    self.panela.put_point(j, i, color=color, background=bg_color)

    def show(self):

        if self.panela:
            return str(self.panela)

        return self.page


def main():
    "Only for experiments"

    pagepath = os.path.join(MYDIR, "share/firstpage-v2.pnl")
    template = Template()
    template.read(pagepath)
    template.apply_mask()
    sys.stdout.write(template.show())


if __name__ == "__main__":
    main()


================================================
FILE: lib/post.py
================================================
"""
POST requests processing.
Currently used only for new cheat sheets submission.

Configuration parameters:

    path.spool
"""

import string
import os
import random
from config import CONFIG


def _save_cheatsheet(topic_name, cheatsheet):
    """
    Save posted cheat sheet `cheatsheet` with `topic_name`
    in the spool directory
    """

    nonce = "".join(
        random.choice(string.ascii_uppercase + string.digits) for _ in range(9)
    )
    filename = topic_name.replace("/", ".") + "." + nonce
    filename = os.path.join(CONFIG["path.spool"], filename)

    open(filename, "w").write(cheatsheet)


def process_post_request(req, topic):
    """
    Process POST request `req`.
    """
    for key, val in req.form.items():
        if key == "":
            if topic is None:
                topic_name = "UNNAMED"
            else:
                topic_name = topic
            cheatsheet = val
        else:
            if val == "":
                if topic is None:
                    topic_name = "UNNAMED"
                else:
                    topic_name = topic
                cheatsheet = key
            else:
                topic_name = key
                cheatsheet = val

        _save_cheatsheet(topic_name, cheatsheet)


================================================
FILE: lib/postprocessing.py
================================================
import search
import fmt.comments


def postprocess(answer, keyword, options, request_options=None):
    answer = _answer_add_comments(answer, request_options=request_options)
    answer = _answer_filter_by_keyword(
        answer, keyword, options, request_options=request_options
    )
    return answer


def _answer_add_comments(answer, request_options=None):

    if answer["format"] != "text+code":
        return answer

    topic = answer["topic"]
    if "filetype" in answer:
        filetype = answer["filetype"]
    else:
        filetype = "bash"
        if "/" in topic:
            filetype = topic.split("/", 1)[0]
            if filetype.startswith("q:"):
                filetype = filetype[2:]

    answer["answer"] = fmt.comments.beautify(
        answer["answer"], filetype, request_options
    )
    answer["format"] = "code"
    answer["filetype"] = filetype
    return answer


def _answer_filter_by_keyword(answer, keyword, options, request_options=None):
    answer["answer"] = _filter_by_keyword(answer["answer"], keyword, options)
    return answer


def _filter_by_keyword(answer, keyword, options):

    def _join_paragraphs(paragraphs):
        answer = "\n".join(paragraphs)
        return answer

    def _split_paragraphs(text):
        answer = []
        paragraph = ""
        if isinstance(text, bytes):
            text = text.decode("utf-8")
        for line in text.splitlines():
            if line == "":
                answer.append(paragraph)
                paragraph = ""
            else:
                paragraph += line + "\n"
        answer.append(paragraph)
        return answer

    paragraphs = [
        p
        for p in _split_paragraphs(answer)
        if search.match(p, keyword, options=options)
    ]
    if not paragraphs:
        return ""

    return _join_paragraphs(paragraphs)


================================================
FILE: lib/routing.py
================================================
"""
Queries routing and caching.

Exports:

    get_topics_list()
    get_answers()
"""

import random
import re
from typing import Any, Dict, List

import cache
import adapter.cheat_sheets
import adapter.cmd
import adapter.internal
import adapter.latenz
import adapter.learnxiny
import adapter.question
import adapter.rosetta
from config import CONFIG


class Router(object):
    """
    Implementation of query routing. Routing is based on `routing_table`
    and the data exported by the adapters (functions `get_list()` and `is_found()`).

    `get_topics_list()` returns available topics (accessible at /:list).
    `get_answer_dict()` return answer for the query.
    """

    def __init__(self):

        self._cached_topics_list = []
        self._cached_topic_type = {}

        adapter_class = adapter.all_adapters(as_dict=True)

        active_adapters = set(CONFIG["adapters.active"] + CONFIG["adapters.mandatory"])

        self._adapter = {
            "internal": adapter.internal.InternalPages(
                get_topic_type=self.get_topic_type, get_topics_list=self.get_topics_list
            ),
            "unknown": adapter.internal.UnknownPages(
                get_topic_type=self.get_topic_type, get_topics_list=self.get_topics_list
            ),
        }

        for by_name in active_adapters:
            if by_name not in self._adapter:
                self._adapter[by_name] = adapter_class[by_name]()

        self._topic_list = {key: obj.get_list() for key, obj in self._adapter.items()}

        self.routing_table = CONFIG["routing.main"]
        self.routing_table = (
            CONFIG["routing.pre"] + self.routing_table + CONFIG["routing.post"]
        )

    def get_topics_list(self, skip_dirs=False, skip_internal=False):
        """
        List of topics returned on /:list
        """

        if self._cached_topics_list:
            return self._cached_topics_list

        skip = ["fosdem"]
        if skip_dirs:
            skip.append("cheat.sheets dir")
        if skip_internal:
            skip.append("internal")
        sources_to_merge = [x for x in self._adapter if x not in skip]

        answer = {}
        for key in sources_to_merge:
            answer.update({name: key for name in self._topic_list[key]})
        answer = sorted(set(answer.keys()))

        self._cached_topics_list = answer
        return answer

    def get_topic_type(self, topic: str) -> List[str]:
        """
        Return list of topic types for `topic`
        or ["unknown"] if topic can't be determined.
        """

        def __get_topic_type(topic: str) -> List[str]:
            result = []
            for regexp, route in self.routing_table:
                if re.search(regexp, topic):
                    if route in self._adapter:
                        if self._adapter[route].is_found(topic):
                            result.append(route)
                    else:
                        result.append(route)
            if not result:
                return [CONFIG["routing.default"]]

            # cut the default route off, if there are more than one route found
            if len(result) > 1:
                return result[:-1]
            return result

        if topic not in self._cached_topic_type:
            self._cached_topic_type[topic] = __get_topic_type(topic)
        return self._cached_topic_type[topic]

    def _get_page_dict(self, query, topic_type, request_options=None):
        """
        Return answer_dict for the `query`.
        """
        return self._adapter[topic_type].get_page_dict(
            query, request_options=request_options
        )

    def handle_if_random_request(self, topic):
        """
        Check if the `query` is a :random one,
        if yes we check its correctness and then randomly select a topic,
        based on the provided prefix.

        """

        def __select_random_topic(prefix, topic_list):
            # Here we remove the special cases
            cleaned_topic_list = [
                x for x in topic_list if "/" not in x and ":" not in x
            ]

            # Here we still check that cleaned_topic_list in not empty
            if not cleaned_topic_list:
                return prefix

            random_topic = random.choice(cleaned_topic_list)
            return prefix + random_topic

        if topic.endswith("/:random") or topic.lstrip("/") == ":random":
            # We strip the :random part and see if the query is valid by running a get_topics_list()
            if topic.lstrip("/") == ":random":
                topic = topic.lstrip("/")
            prefix = topic[:-7]

            topic_list = [
                x[len(prefix) :] for x in self.get_topics_list() if x.startswith(prefix)
            ]

            if "" in topic_list:
                topic_list.remove("")

            if topic_list:
                # This is a correct formatted random query like /cpp/:random as the topic_list is not empty.
                random_topic = __select_random_topic(prefix, topic_list)
                return random_topic
            else:
                # This is a wrongly formatted random query like /xyxyxy/:random as the topic_list is empty
                # we just strip the /:random and let the already implemented logic handle it.
                wrongly_formatted_random = topic[:-8]
                return wrongly_formatted_random

        # Here if not a random request, we just forward the topic
        return topic

    def get_answers(
        self, topic: str, request_options: Dict[str, str] = None
    ) -> List[Dict[str, Any]]:
        """
        Find cheat sheets for the topic.

        Args:
            `topic` (str):    the name of the topic of the cheat sheet

        Returns:
            [answer_dict]:    list of answers (dictionaries)
        """

        # if topic specified as :,
        # cut  off
        topic_type = ""
        if re.match("[^/]+:", topic):
            topic_type, topic = topic.split(":", 1)

        topic = self.handle_if_random_request(topic)
        topic_types = self.get_topic_type(topic)

        # if topic_type is specified explicitly,
        # show pages only of that type
        if topic_type and topic_type in topic_types:
            topic_types = [topic_type]

        # 'question' queries are pretty expensive, that's why they should be handled
        # in a special way:
        # we do not drop the old style cache entries and try to reuse them if possible
        if topic_types == ["question"]:
            answer = cache.get("q:" + topic)
            if answer:
                if isinstance(answer, dict):
                    return [answer]
                return [
                    {
                        "topic": topic,
                        "topic_type": "question",
                        "answer": answer,
                        "format": "text+code",
                    }
                ]

            answer = self._get_page_dict(
                topic, topic_types[0], request_options=request_options
            )
            if answer.get("cache", True):
                cache.put("q:" + topic, answer)
            return [answer]

        # Try to find cacheable queries in the cache.
        # If answer was not found in the cache, resolve it in a normal way and save in the cache
        answers = []
        for topic_type in topic_types:

            cache_entry_name = f"{topic_type}:{topic}"
            cache_needed = self._adapter[topic_type].is_cache_needed()

            if cache_needed:
                answer = cache.get(cache_entry_name)
                if not isinstance(answer, dict):
                    answer = None
                if answer:
                    answers.append(answer)
                    continue

            answer = self._get_page_dict(
                topic, topic_type, request_options=request_options
            )
            if isinstance(answer, dict):
                if "cache" in answer:
                    cache_needed = answer["cache"]

            if cache_needed and answer:
                cache.put(cache_entry_name, answer)

            answers.append(answer)

        return answers


# pylint: disable=invalid-name
_ROUTER = Router()
get_topics_list = _ROUTER.get_topics_list
get_answers = _ROUTER.get_answers


================================================
FILE: lib/search.py
================================================
"""
Very naive search implementation. Just a placeholder.

Exports:

    find_answer_by_keyword()

It should be implemented on the adapter basis:

    1. adapter.search(keyword) returns list of matching answers
        * maybe with some initial weight
    2. ranking is done
    3. sorted results are returned
    4. eage page are cut by keyword
    5. results are paginated

Configuration parameters:

    search.limit
"""

import re

from config import CONFIG
from routing import get_answers, get_topics_list


def _limited_entry():
    return {
        "topic_type": "LIMITED",
        "topic": "LIMITED",
        "answer": "LIMITED TO %s ANSWERS" % CONFIG["search.limit"],
        "format": "code",
    }


def _parse_options(options):
    """Parse search options string into optiond_dict"""

    if options is None:
        return {}

    search_options = {
        "insensitive": "i" in options,
        "word_boundaries": "b" in options,
        "recursive": "r" in options,
    }
    return search_options


def match(paragraph, keyword, options=None, options_dict=None):
    """Search for each keyword from `keywords` in `page`
    and if all of them are found, return `True`.
    Otherwise return `False`.

    Several keywords can be joined together using ~
    For example: ~ssh~passphrase
    """

    if keyword is None:
        return True

    if "~" in keyword:
        keywords = keyword.split("~")
    else:
        keywords = [keyword]

    if options_dict is None:
        options_dict = _parse_options(options)

    for kwrd in keywords:
        if not kwrd:
            continue

        regex = re.escape(kwrd)
        if options_dict["word_boundaries"]:
            regex = r"\b%s\b" % kwrd

        if options_dict["insensitive"]:
            if not re.search(regex, paragraph, re.IGNORECASE):
                return False
        else:
            if not re.search(regex, paragraph):
                return False
    return True


def find_answers_by_keyword(directory, keyword, options="", request_options=None):
    """
    Search in the whole tree of all cheatsheets or in its subtree `directory`
    by `keyword`
    """

    options_dict = _parse_options(options)

    answers_found = []
    for topic in get_topics_list(skip_internal=True, skip_dirs=True):

        if not topic.startswith(directory):
            continue

        subtopic = topic[len(directory) :]
        if not options_dict["recursive"] and "/" in subtopic:
            continue

        answer_dicts = get_answers(topic, request_options=request_options)
        for answer_dict in answer_dicts:
            answer_text = answer_dict.get("answer", "")
            # Temporary hotfix:
            # In some cases answer_text may be 'bytes' and not 'str'
            if type(b"") == type(answer_text):
                answer_text = answer_text.decode("utf-8")

            if match(answer_text, keyword, options_dict=options_dict):
                answers_found.append(answer_dict)

        if len(answers_found) > CONFIG["search.limit"]:
            answers_found.append(_limited_entry())
            break

    return answers_found


================================================
FILE: lib/standalone.py
================================================
"""
Standalone wrapper for the cheat.sh server.
"""

from __future__ import print_function

import sys
import textwrap

try:
    import urlparse
except ModuleNotFoundError:
    import urllib.parse as urlparse

import config

config.CONFIG["cache.type"] = "none"

import cheat_wrapper
import options


def show_usage():
    """
    Show how to use the program in the standalone mode
    """

    print(
        textwrap.dedent(
            """
        Usage:

            lib/standalone.py [OPTIONS] QUERY

        For OPTIONS see :help
    """
        )[1:-1]
    )


def parse_cmdline(args):
    """
    Parses command line arguments and returns
    query and request_options
    """

    if not args:
        show_usage()
        sys.exit(0)

    query_string = " ".join(args)
    parsed = urlparse.urlparse("https://srv:0/%s" % query_string)
    request_options = options.parse_args(
        urlparse.parse_qs(parsed.query, keep_blank_values=True)
    )

    query = parsed.path.lstrip("/")
    if not query:
        query = ":firstpage"

    return query, request_options


def main(args):
    """
    standalone wrapper for cheat_wrapper()
    """

    query, request_options = parse_cmdline(args)
    answer, _ = cheat_wrapper.cheat_wrapper(query, request_options=request_options)
    sys.stdout.write(answer)


if __name__ == "__main__":
    main(sys.argv[1:])


================================================
FILE: lib/stateful_queries.py
================================================
"""
Support for the stateful queries
"""

import cache


def save_query(client_id, query):
    """
    Save the last query `query` for the client `client_id`
    """
    cache.put("l:%s" % client_id, query)


def last_query(client_id):
    """
    Return the last query for the client `client_id`
    """
    return cache.get("l:%s" % client_id)


================================================
FILE: requirements.txt
================================================
wheel
gevent
flask
requests
pygments
dateutils
fuzzywuzzy
redis
colored<1.4.3
langdetect
cffi
polyglot
PyICU
pycld2
colorama
pyyaml
python-Levenshtein
pytest
black


================================================
FILE: share/adapters/chmod.grc
================================================
# with option -sI
# e.g. grc curl -sI mysite.com
# http
regexp=^(HTTP).*
colours=yellow
======
# server
regexp=^(Server).*
colours=cyan
======
# site
regexp=^(Link).*
colours=magenta
======
# colouring the output of script 'cheat chmod'
# e.g. grc curl -s cheat.sh/chmod/755
# linux string
regexp=^(Linux.*Permissions).*(String:)\s+(.*)$
colours=yellow,green,red
======
# linux number
regexp=^(Linux.*Permissions).*(Number:)\s+(.*)$
colours=yellow,green,red
======
# header
regexp=^(Special)\s+(Owner)\s+(Group)\s+(Public)$
colours=white
======
# setuid
regexp=^(Setuid)\s+\[.*\].*(Read).*
colours=green,bold cyan
======
# setgid
regexp=^(Setgid)\s+\[.*\].*(Write).*
colours=green,bold yellow
======
# sticky bit
regexp=^(Sticky bit)\s+\[.*\].*(Execute).*
colours=green,bold magenta
======


================================================
FILE: share/adapters/chmod.sh
================================================
#!/usr/bin/env bash

# Contributed by Erez Binyamin (github.com/ErezBinyamin)

GRC_STYLESHEET="${BASH_SOURCE[0]}"; GRC_STYLESHEET=${GRC_STYLESHEET%.sh}.grc

# Translate between chmod string and number
# Inspired by http://permissions-calculator.org/
# Contrib to chubin - cheat.sh
chmod_calc(){
  p_s=""
  p_n=""
  R=()
  W=()
  X=()
  setuid=' '
  setgid=' '
  sticky=' '
  # If permission number is given -> calc string
  if [[ $1 =~ ^-?[0-9]+$ && ${#1} -ge 1 && ${#1} -le 4 ]]
  then
    p_n=$(printf "%04s\n" "$1" | tr ' ' '0')
    echo $p_n | grep -q '[8-9]' && return 1
    for (( i=0; i<${#p_n}; i++ ))
    do
      num=$(echo "obase=2;${p_n:$i:1}" | bc | xargs printf '%03d')
      # If 4 digit input -> process specials
      if [ $i -eq 0 ]
      then
        [ ${num:0:1} -eq 1 ] && setuid='X' || setuid=' '
        [ ${num:1:1} -eq 1 ] && setgid='X' || setgid=' '
        [ ${num:2:1} -eq 1 ] && sticky='X' || sticky=' '
      else
        # Build p_s string
        [ ${num:0:1} -eq 1 ] && p_s+='r' || p_s+='-'
        [ ${num:1:1} -eq 1 ] && p_s+='w' || p_s+='-'
        # Use sS or tT instead of x- according to specials
        if [[ $i -eq 1 && $setuid == 'X' ]]
        then
          [ ${num:2:1} -eq 1 ] && p_s+='s' || p_s+='S'
        elif [[ $i -eq 2 && $setgid == 'X' ]]
        then
          [ ${num:2:1} -eq 1 ] && p_s+='s' || p_s+='S'
        elif [[ $i -eq 3 && $sticky == 'X' ]]
        then
          [ ${num:2:1} -eq 1 ] && p_s+='t' || p_s+='T'
        else
          [ ${num:2:1} -eq 1 ] && p_s+='x' || p_s+='-'
        fi
        # Populate arrays for the table
        [ ${num:0:1} -eq 1 ] && R+=('X') || R+=(' ')
        [ ${num:1:1} -eq 1 ] && W+=('X') || W+=(' ')
        [ ${num:2:1} -eq 1 ] && X+=('X') || X+=(' ')
      fi
    done
  # If permission string is given -> calc number
  elif [[ $1 =~ ^[r,s,S,t,T,w,x,-]+$ ]]
  then
    # FULL STRING
    if [[ ${#1} -eq 9 ]]
    then
      p_s=$1
      num=0
      # Process specials
      [[ 'sS' =~ ${p_s:2:1} ]] && setuid='X' && num=$((num+4))
      [[ 'sS' =~ ${p_s:5:1} ]] && setgid='X' && num=$((num+2))
      [[ 'tT' =~ ${p_s:8:1} ]] && sticky='X' && num=$((num+1))
      [ ${num} -gt 0 ] && p_n+="$num"
      # Calculate rest of p_n number while populating arrays for table
      for (( i=0; i<${#p_s}; i+=0 ))
      do
        num=0
        [[ "r-" =~ ${p_s:$i:1} ]] || return 1
        [[ ${p_s:$i:1} == 'r' ]] && R+=('X') || R+=(' ')
        [[ ${p_s:$((i++)):1} == 'r' ]] && let num++
        num=$(( num << 1 ))
        [[ "w-" =~ ${p_s:$i:1} ]] || return 1
        [[ ${p_s:$i:1} == 'w' ]] && W+=('X') || W+=(' ')
        [[ ${p_s:$((i++)):1} == 'w' ]] && let num++
        num=$(( num << 1 ))
        if [ $i -lt 6 ]
        then
          [[ "sSx-" =~ ${p_s:$i:1} ]] || return 1
          [[ "sx" =~ ${p_s:$i:1} ]] && X+=('X') || X+=(' ')
          [[ "sx" =~ ${p_s:$((i++)):1} ]] && let num++
        else
          [[ "tTx-" =~ ${p_s:$i:1} ]] || return 1
          [[ "tx" =~ ${p_s:$i:1} ]] && X+=('X') || X+=(' ')
          [[ "tx" =~ ${p_s:$((i++)):1} ]] && let num++
        fi
        p_n+="$num"
      done
    # PARTIAL STRING
    elif [[ $1 =~ ^[r,s,t,w,x]+$ ]]
    then
      p_s='---------'
      p_n0=0
      p_n1=0
      p_n2=0
      p_n3=0
      R=(' ' ' ' ' ')
      W=(' ' ' ' ' ')
      X=(' ' ' ' ' ')
      if [[ $1 =~ 'r' ]]
      then
        p_s=$(echo $p_s | sed 's/./r/1; s/./r/4; s/./r/7;')
        let p_n1+=4
        let p_n2+=4
        let p_n3+=4
        R=('X' 'X' 'X')
      fi
      if [[ $1 =~ 'w' ]]
      then
        p_s=$(echo $p_s | sed 's/./w/2')
        let p_n1+=2
        W=('X' ' ' ' ')
      fi
      if [[ $1 =~ 'x' ]]
      then
        p_s=$(echo $p_s | sed 's/./x/3; s/./x/6; s/./x/9;')
        let p_n1+=1
        let p_n2+=1
        let p_n3+=1
        X=('X' 'X' 'X')
      fi
      if [[ $1 =~ 's' ]]
      then
        [[ ${p_s:2:1} == 'x' ]] && p_s=$(echo $p_s | sed 's/./s/3') || p_s=$(echo $p_s | sed 's/./S/3')
        [[ ${p_s:5:1} == 'x' ]] && p_s=$(echo $p_s | sed 's/./s/6') || p_s=$(echo $p_s | sed 's/./S/6')
        let p_n0+=6
        setuid='X'
        setgid='X'
      fi
      if [[ $1 =~ 't' ]]
      then
        let p_n0+=1
        [[ ${p_s:8:1} == 'x' ]] && p_s=$(echo $p_s | sed 's/./t/9') || p_s=$(echo $p_s | sed 's/./T/9')
        sticky='X'
      fi
      p_n="${p_n0}${p_n1}${p_n2}${p_n3}"
    else
      return 1
    fi
  else
    return 1
  fi
  # Print Final results table
  printf "
Linux Permissions String:\t${p_s}
Linux Permissions Number:\t${p_n}

Special\t\tOwner\t\tGroup\t\tPublic

Setuid     [$setuid]\tRead    [${R[0]}]\tRead    [${R[1]}]\tRead    [${R[2]}]
Setgid     [$setgid]\tWrite   [${W[0]}]\tWrite   [${W[1]}]\tWrite   [${W[2]}]
Sticky bit [$sticky]\tExecute [${X[0]}]\tExecute [${X[1]}]\tExecute [${X[2]}]

" | grcat "$GRC_STYLESHEET"
}

chmod_calc $@
[ $? -ne 0 ] && printf "Invalid permissions string: $@\n"


================================================
FILE: share/adapters/oeis.sh
================================================
#!/usr/bin/env bash

# Written by Erez Binyamin (github.com/ErezBinyamin)

# Search for an integer sequence
# USAGE:
#	oeis  
#	oeis  
#	oeis 
oeis() (
  local URL='https://oeis.org/search?q='
  local TMP=$(mktemp -d oeis.XXXXXXX)
  local DOC=${TMP}/doc.html
  local MAX_TERMS_LONG=30
  local MAX_TERMS_SHORT=10
  mkdir -p $TMP
  rm -f ${TMP}/authors ${TMP}/bibliograpy ${TMP}/section $TMP/code_snippet
  # -- MAIN --
  # Search sequence by ID (optional language arg)
  # 	. oeis 
  # 	. oeis  
# . oeis
isNum='^[0-9]+$' # Search for specific sequence (and potentially language or :SECTION (list) if [ $# -ge 1 ] \ && [[ $(echo $1 | tr -d 'aA') =~ $isNum || $(echo $2 | tr -d 'aA') =~ $isNum ]] \ && [[ ! $(echo $1 | tr -d 'aA') =~ $isNum || ! $(echo $2 | tr -d 'aA') =~ $isNum ]] then # Arg-Parse ID, Generate URL if [[ $(echo $1 | tr -d 'aA') =~ $isNum ]] then ID=${1^^} SECTION=$2 else ID=${2^^} SECTION=$1 fi [[ ${ID:0:1} == 'A' ]] && ID=${ID:1} ID=$(bc <<< "$ID") ID="A$(printf '%06d' ${ID})" URL+="id:${ID}&fmt=text" curl $URL 2>/dev/null > $DOC # :list available language code_snippets if [[ ${SECTION^^} == ':LIST' || ${SECTION^^} == ':PROG' ]] then grep -q '%p' $DOC && echo 'maple' >> $TMP/section grep -q '%t' $DOC && echo 'mathematica' >> $TMP/section grep '%o' $DOC \ | grep "${ID} (" \ | sed "s/^.*${ID} (//; s/).*//" \ | awk 'NF == 1' \ >> $TMP/section [[ -f $TMP/section && $(wc -c < $TMP/section) -ne 0 ]] \ && cat ${TMP}/section | sort -u \ || printf 'No code snippets available.\n' rm -rf $TMP return 0 fi # Print ID printf "ID: ${ID}\n" # Print Description (%N) grep '%N' $DOC | sed "s/^.*${ID} //" printf '\n' # Print Sequence (Three sections %S %T and %U) grep '%S' $DOC | sed "s/^.*${ID} //" | tr -d '\n' > $TMP/seq grep '%T' $DOC | sed "s/^.*${ID} //" | tr -d '\n' >> $TMP/seq grep '%U' $DOC | sed "s/^.*${ID} //" | tr -d '\n' >> $TMP/seq cat $TMP/seq \ | cut -d ',' -f 1-${MAX_TERMS_LONG} \ | sed 's/,/, /g; s/$/ .../' # Generate code snippet (%p, %t, %o) (maple, mathematica, prog sections) if [ $# -gt 1 ] then printf "\n\n" # MAPLE section (%p) if [[ ${SECTION^^} == 'MAPLE' ]] && grep -q '%p' $DOC then grep '%p' $DOC | sed "s/^.*${ID} //" > $TMP/code_snippet # MATHEMATICA section (%t) elif [[ ${SECTION^^} == 'MATHEMATICA' ]] && grep -q '%t' $DOC then grep '%t' $DOC | sed "s/^.*${ID} //" > $TMP/code_snippet # PROG section (%o) elif grep -qi '%o' $DOC && grep -qi $SECTION $DOC then # Print out code sample for specified language grep '%o' $DOC \ | sed "s/%o ${ID} //" \ | awk -v tgt="${SECTION^^}" -F'[()]' '{act=$2} sub(/^\([^()]+\) */,""){f=(tgt==toupper(act))} f' \ > ${TMP}/code_snippet fi # Print code snippet with 4-space indent to enable colorization if [[ -f $TMP/code_snippet && $(wc -c < $TMP/code_snippet) -ne 0 ]] then # Get authors grep -o ' _[A-Z].* [A-Z].*_, [A-Z].*[0-9]' ${TMP}/code_snippet \ | sed 's/,.*//' \ | sort -u \ > ${TMP}/authors i=1 # Replace authors with numbers while read author do author=$(<<<"$author" sed 's/[]\\\*\(\.[]/\\&/g') sed -i "s|${author}.*[0-9]|[${i}]|" ${TMP}/code_snippet author=$(echo $author | tr -d '_\\') author_url='https://oeis.org/wiki/User:'"$(echo ${author} | tr ' ' '_')" echo "[${i}] [${author}] [${author_url}]" \ >> ${TMP}/bibliograpy let i++ done <${TMP}/authors # Print snippet sed 's/^/ /' ${TMP}/code_snippet else printf "${SECTION^^} unavailable. Use :list to view available languages.\n" fi fi # Search unknown sequence elif [ $# -gt 1 ] && ! echo $@ | grep -q -e [a-z] -e [A-Z] then # Build URL URL+="signed:$(echo $@ | tr -sc '[:digit:]-' ',')&fmt=short" curl $URL 2>/dev/null > $DOC # Sequence IDs grep -o '"/A[0-9][0-9][0-9][0-9][0-9][0-9]">A[0-9][0-9][0-9][0-9][0-9][0-9]' $DOC \ | sed 's/.*>//' \ > $TMP/id readarray -t ID < $TMP/id # Descriptions grep -A 1 '' $DOC \ | sed '/--/d; s/<[^>]*>//g; /^\s*$/d; s/^[ \t]*//' \ | sed 's/ / /g; s/\&/\&/g; s/>/>/g; s/</ $TMP/desc readarray -t DESC < $TMP/desc # Sequences grep 'style="color:black;font-size:120%' $DOC \ | sed 's/<[^>]*>//g; s/^[ \t]*//' \ | cut -d ',' -f 1-${MAX_TERMS_SHORT} \ | sed 's/,/, /g; s/$/ .../' \ > $TMP/seq readarray -t SEQ < $TMP/seq # Print all ID, DESC, SEQ for i in ${!ID[@]} do printf "${ID[$i]}: ${DESC[$i]}\n" printf "${SEQ[$i]}\n\n" done else printf " # oeis # # The On-Line Encyclopedia of Integer Sequences (OEIS), # also cited simply as Sloane's, is an online database of integer sequences. # Find all possible OEIS sequences for some sequence (1,1,1,1...) curl cheat.sh/oeis/1+1+1+1 # Describe an OEIS sequence (A2) curl cheat.sh/oeis/A2 # Implementation of the A2 OEIS sequence in Python curl cheat.sh/oeis/A2/python # List all available implementations of the A2 OEIS sequence curl cheat.sh/oeis/A2/:list " rm -rf $TMP return 1 fi # Error statements grep 'results, too many to show. Please refine your search.' $DOC | sed -e 's/<[^>]*>//g; s/^[ \t]*//' grep -o 'Sorry, but the terms do not match anything in the table.' $DOC # print bibliography printf "\n\n" [ -f ${TMP}/bibliograpy ] && cat ${TMP}/bibliograpy # Print URL for user printf "[${URL}]\n" \ | rev \ | sed 's/,//' \ | rev \ | sed 's/&.*/]/' rm -rf $TMP return 0 ) oeis $@ ================================================ FILE: share/adapters/rfc.sh ================================================ #!/usr/bin/env bash # Contributed by Erez Binyamin (github.com/ErezBinyamin) # Search for an RFC # Contrib to chubin - cheat.sh RFC_get() ( rfc_describe() { sed -ne '/0001/,$p' ${RFC_INDEX} \ | tr '\n' '#' \ | sed 's/##/\n/g' \ | sed 's/# //g' \ | grep -o '.*\. ' \ | sed -E 's/^(.*)(January|February|March|April|May|June|July|August|September|October|November|December) [[:digit:]]{4}(.*)$/\1/' } UNAME=$(uname -s) if [ "$UNAME" = "Darwin" ]; then SED_I="sed -i ''" else SED_I="sed -i" fi mkdir -p /tmp/RFC_get local WEB_RESP="/tmp/RFC_get/rfc_get_web_resp_${RANDOM}.html" local RFC_INDEX="/tmp/RFC_get/rfc_index.html" local isNum='^[0-9]+$' # Update RFC_INDEX if file does not exist [ -f ${RFC_INDEX} ] || curl 'https://www.ietf.org/download/rfc-index.txt' 2>/dev/null > ${RFC_INDEX} local MIN_RFC=1 local MAX_RFC=$(sed '/^ / d' ${RFC_INDEX} | tail -n 1 | sed 's/ .*//') local arg_lower=$(echo "$1" | tr '[:upper:]' '[:lower:]') # Syntax check Usage statement if [ $# -lt 1 ] || [ "$arg_lower" = "-h" ] || [ "$arg_lower" = "--help" ] || [ "$arg_lower" = ":help" ] || [ "$arg_lower" = ":usage" ] then printf " USAGE: rfc Search RFC by number rfc Search RFC by topic rfc :list List available RFC's rfc :usage Show this help message \n" return 0 fi # Get corresponding RFC by number if [[ ${1} =~ $isNum ]] then # Validate RFC range if [ "$1" -gt $MAX_RFC ] || [ "$1" -lt $MIN_RFC ] then echo "Valid RFC numbers: [ ${MIN_RFC} - ${MAX_RFC} ]" return 1 fi # If valid N: Retrieve RFC curl "https://www.ietf.org/rfc/rfc${1}.txt" --write-out %{http_code} --silent --output ${WEB_RESP} 2>/dev/null | grep -q '200' if [ $? -ne 0 ] then # Attempt to retrieve PDF link to RFC curl "https://www.rfc-editor.org/info/rfc${1}" --write-out %{http_code} --silent --output ${WEB_RESP} 2>/dev/null | grep -q '200' # Webpage error code (Not 200 OK) if [ $? -ne 0 ] then echo "Error retrieving https://www.rfc-editor.org/info/rfc${1}" echo "Please create github issue at https://github.com/chubin/cheat.sh/issues" return 2 # RFC never issued elif grep -q '

Not Issued

' ${WEB_RESP} then echo "RFC ${1} was never issued" return 0 # RFC does not exist elif grep -q 'does not exist' ${WEB_RESP} then echo "RFC ${1} does not exist" return 0 # RFC exists only as PDF elif grep -q 'https.*\.pdf' $WEB_RESP then grep -o 'https.*\.pdf' $WEB_RESP return 0 # Unknown error else echo "Error retrieving RFC $1" echo "Please create github issue at https://github.com/chubin/cheat.sh/issues" return 2 fi fi # Print list of available RFCs elif [ "$arg_lower" = ":list" ] then # Format RFC_INDEX to show short description of each RFC rfc_describe \ | grep -v 'Not Issued' \ | sed 's/ .*//; s/^0*//' return 0 # Print list of available RFCs elif [ "$arg_lower" = ":describe" ] then # Format RFC_INDEX to show short description of each RFC rfc_describe return 0 # Format list of RFCs related to keyword: RFC_N RFC_Title else ARG="$*" rfc_describe \ | grep -i "$ARG" \ > $WEB_RESP fi # Format nicely and print $SED_I -e '/Page [0-9]/,+2d; /page [0-9]/,+2d' ${WEB_RESP} if grep -q '' ${WEB_RESP} then echo "Error retrieving RFC $1" echo "Please create github issue at https://github.com/chubin/cheat.sh/issues" return 2 else cat -s ${WEB_RESP} return 0 fi ) RFC_get "$1" ================================================ FILE: share/ansi2html.sh ================================================ #!/bin/sh # Convert ANSI (terminal) colours and attributes to HTML # Licence: LGPLv2 # Author: # http://www.pixelbeat.org/docs/terminal_colours/ # Examples: # ls -l --color=always | ansi2html.sh > ls.html # git show --color | ansi2html.sh > last_change.html # Generally one can use the `script` util to capture full terminal output. # Changes: # V0.1, 24 Apr 2008, Initial release # V0.2, 01 Jan 2009, Phil Harnish # Support `git diff --color` output by # matching ANSI codes that specify only # bold or background colour. # P@draigBrady.com # Support `ls --color` output by stripping # redundant leading 0s from ANSI codes. # Support `grep --color=always` by stripping # unhandled ANSI codes (specifically ^[[K). # V0.3, 20 Mar 2009, http://eexpress.blog.ubuntu.org.cn/ # Remove cat -v usage which mangled non ascii input. # Cleanup regular expressions used. # Support other attributes like reverse, ... # P@draigBrady.com # Correctly nest tags (even across lines). # Add a command line option to use a dark background. # Strip more terminal control codes. # V0.4, 17 Sep 2009, P@draigBrady.com # Handle codes with combined attributes and color. # Handle isolated attributes with css. # Strip more terminal control codes. # V0.23, 22 Dec 2015 # http://github.com/pixelb/scripts/commits/master/scripts/ansi2html.sh gawk --version >/dev/null || exit 1 if [ "$1" = "--version" ]; then printf '0.22\n' && exit fi if [ "$1" = "--help" ]; then printf '%s\n' \ 'This utility converts ANSI codes in data passed to stdin It has 2 optional parameters: --bg=dark --palette=linux|solarized|tango|xterm E.g.: ls -l --color=always | ansi2html.sh --bg=dark > ls.html' >&2 exit fi [ "$1" = "--bg=dark" ] && { dark_bg=yes; shift; } if [ "$1" = "--palette=solarized" ]; then # See http://ethanschoonover.com/solarized P0=073642; P1=D30102; P2=859900; P3=B58900; P4=268BD2; P5=D33682; P6=2AA198; P7=EEE8D5; P8=002B36; P9=CB4B16; P10=586E75; P11=657B83; P12=839496; P13=6C71C4; P14=93A1A1; P15=FDF6E3; shift; elif [ "$1" = "--palette=solarized-xterm" ]; then # Above mapped onto the xterm 256 color palette P0=262626; P1=AF0000; P2=5F8700; P3=AF8700; P4=0087FF; P5=AF005F; P6=00AFAF; P7=E4E4E4; P8=1C1C1C; P9=D75F00; P10=585858; P11=626262; P12=808080; P13=5F5FAF; P14=8A8A8A; P15=FFFFD7; shift; elif [ "$1" = "--palette=tango" ]; then # Gnome default P0=000000; P1=CC0000; P2=4E9A06; P3=C4A000; P4=3465A4; P5=75507B; P6=06989A; P7=D3D7CF; P8=555753; P9=EF2929; P10=8AE234; P11=FCE94F; P12=729FCF; P13=AD7FA8; P14=34E2E2; P15=EEEEEC; shift; elif [ "$1" = "--palette=xterm" ]; then P0=000000; P1=CD0000; P2=00CD00; P3=CDCD00; P4=0000EE; P5=CD00CD; P6=00CDCD; P7=E5E5E5; P8=7F7F7F; P9=FF0000; P10=00FF00; P11=FFFF00; P12=5C5CFF; P13=FF00FF; P14=00FFFF; P15=FFFFFF; shift; else # linux console P0=000000; P1=AA0000; P2=00AA00; P3=AA5500; P4=0000AA; P5=AA00AA; P6=00AAAA; P7=AAAAAA; P8=555555; P9=FF5555; P10=55FF55; P11=FFFF55; P12=5555FF; P13=FF55FF; P14=55FFFF; P15=FFFFFF; [ "$1" = "--palette=linux" ] && shift fi [ "$1" = "--bg=dark" ] && { dark_bg=yes; shift; } # Mac OSX's GNU sed is installed as gsed # use e.g. homebrew 'gnu-sed' to get it if ! sed --version >/dev/null 2>&1; then if gsed --version >/dev/null 2>&1; then alias sed=gsed else echo "Error, can't find an acceptable GNU sed." >&2 exit 1 fi fi printf '%s' "
'

p='\x1b\['        #shortcut to match escape codes

# Handle various xterm control sequences.
# See /usr/share/doc/xterm-*/ctlseqs.txt
sed "
# escape ampersand and quote
s#&#\&#g; s#\"#\"#g;
s#\x1b[^\x1b]*\x1b\\\##g  # strip anything between \e and ST
s#\x1b][0-9]*;[^\a]*\a##g # strip any OSC (xterm title etc.)

s#\r\$## # strip trailing \r

# strip other non SGR escape sequences
s#[\x07]##g
s#\x1b[]>=\][0-9;]*##g
s#\x1bP+.\{5\}##g
# Mark cursor positioning codes \"Jr;c;
s#${p}\([0-9]\{1,2\}\)G#\"J;\1;#g
s#${p}\([0-9]\{1,2\}\);\([0-9]\{1,2\}\)H#\"J\1;\2;#g

# Mark clear as \"Cn where n=1 is screen and n=0 is to end-of-line
s#${p}H#\"C1;#g
s#${p}K#\"C0;#g
# Mark Cursor move columns as \"Mn where n is +ve for right, -ve for left
s#${p}C#\"M1;#g
s#${p}\([0-9]\{1,\}\)C#\"M\1;#g
s#${p}\([0-9]\{1,\}\)D#\"M-\1;#g
s#${p}\([0-9]\{1,\}\)P#\"X\1;#g

s#${p}[0-9;?]*[^0-9;?m]##g

" |

# Normalize the input before transformation
sed "
# escape HTML (ampersand and quote done above)
s#>#\>#g; s#<#\<#g;

# normalize SGR codes a little

# split 256 colors out and mark so that they're not
# recognised by the following 'split combined' line
:e
s#${p}\([0-9;]\{1,\}\);\([34]8;5;[0-9]\{1,3\}\)m#${p}\1m${p}¬\2m#g; t e
s#${p}\([34]8;5;[0-9]\{1,3\}\)m#${p}¬\1m#g;

:c
s#${p}\([0-9]\{1,\}\);\([0-9;]\{1,\}\)m#${p}\1m${p}\2m#g; t c   # split combined
s#${p}0\([0-7]\)#${p}\1#g                                 #strip leading 0
s#${p}1m\(\(${p}[4579]m\)*\)#\1${p}1m#g                   #bold last (with clr)
s#${p}m#${p}0m#g                                          #add leading 0 to norm

# undo any 256 color marking
s#${p}¬\([34]8;5;[0-9]\{1,3\}\)m#${p}\1m#g;

# map 16 color codes to color + bold
s#${p}9\([0-7]\)m#${p}3\1m${p}1m#g;
s#${p}10\([0-7]\)m#${p}4\1m${p}1m#g;

# change 'reset' code to \"R
s#${p}0m#\"R;#g
" |

# Convert SGR sequences to HTML
sed "
# common combinations to minimise html (optional)
:f
s#${p}3[0-7]m${p}3\([0-7]\)m#${p}3\1m#g; t f
:b
s#${p}4[0-7]m${p}4\([0-7]\)m#${p}4\1m#g; t b
s#${p}3\([0-7]\)m${p}4\([0-7]\)m##g
s#${p}4\([0-7]\)m${p}3\([0-7]\)m##g

s#${p}1m##g
s#${p}4m##g
s#${p}5m##g
s#${p}7m##g
s#${p}9m##g
s#${p}3\([0-9]\)m##g
s#${p}4\([0-9]\)m##g

s#${p}38;5;\([0-9]\{1,3\}\)m##g
s#${p}48;5;\([0-9]\{1,3\}\)m##g

s#${p}[0-9;]*m##g # strip unhandled codes
" |

# Convert alternative character set and handle cursor movement codes
# Note we convert here, as if we do at start we have to worry about avoiding
# conversion of SGR codes etc., whereas doing here we only have to
# avoid conversions of stuff between &...; or <...>
#
# Note we could use sed to do this based around:
#   sed 'y/abcdefghijklmnopqrstuvwxyz{}`~/▒␉␌␍␊°±␤␋┘┐┌└┼⎺⎻─⎼⎽├┤┴┬│≤≥π£◆·/'
# However that would be very awkward as we need to only conv some input.
# The basic scheme that we do in the awk script below is:
#  1. enable transliterate once "T1; is seen
#  2. disable once "T0; is seen (may be on diff line)
#  3. never transliterate between &; or <> chars
#  4. track x,y movements and active display mode at each position
#  5. buffer line/screen and dump when required
sed "
# change 'smacs' and 'rmacs' to \"T1 and \"T0 to simplify matching.
s#\x1b(0#\"T1;#g;
s#\x0E#\"T1;#g;

s#\x1b(B#\"T0;#g
s#\x0F#\"T0;#g
" |
(
gawk '
function dump_line(l,del,c,blanks,ret) {
  for(c=1;c")
  for(i=1;i<=spc;i++) {
    rm=rm?rm:(a[i]!=attr[i]">")
    if(rm) {
      ret=ret ""
      delete a[i];
    }
  }
  for(i=1;i"
    if(a[i]!=attr[i]) {
      a[i]=attr[i]
      ret = ret attr[i]
    }
  }
  return ret
}

function encode(string,start,end,i,ret,pos,sc,buf) {
   if(!end) end=length(string);
   if(!start) start=1;
   state=3
   for(i=1;i<=length(string);i++) {
     c=substr(string,i,1)
     if(state==2) {
       sc=sc c
       if(c==";") {
          c=sc
          state=last_mode
       } else continue
     } else {
       if(c=="\r") { x=1; continue }
       if(c=="<") {
         # Change attributes - store current active
         # attributes in span array
         split(substr(string,i),cord,">");
         i+=length(cord[1])
         span[++spc]=cord[1] ">"
         continue
       }
       else if(c=="&") {
         # All goes to single position till we see a semicolon
         sc=c
         state=2
         continue
       }
       else if(c=="\b") {
          # backspace move insertion point back 1
          if(spc) attr[x,y]=atos(span)
          x=x>1?x-1:1
          continue
       }
       else if(c=="\"") {
          split(substr(string,i+2),cord,";")
          cc=substr(string,i+1,1);
          if(cc=="T") {
              # Transliterate on/off
              if(cord[1]==1&&state==3) last_mode=state=4
              if(cord[1]==0&&state==4) last_mode=state=3
          }
          else if(cc=="C") {
              # Clear
              if(cord[1]+0) {
                # Screen - if Recording dump screen
                if(dumpStatus==dsActive) ret=ret dump_screen()
                dumpStatus=dsActive
                delete dump
                delete attr
                x=y=1
              } else {
                # To end of line
                for(pos=x;posmaxY) maxY=y
                # Change y - start recording
                dumpStatus=dumpStatus?dumpStatus:dsReset
              }
          }
          else if(cc=="M") {
              # Move left/right on current line
              x+=cord[1]
          }
          else if(cc=="X") {
              # delete on right
              for(pos=x;pos<=maxX;pos++) {
                nx=pos+cord[1]
                if(nx=start&&i<=end&&c in Trans) c=Trans[c]
     }
     if(dumpStatus==dsReset) {
       delete dump
       delete attr
       ret=ret"\n"
       dumpStatus=dsActive
     }
     if(dumpStatus==dsNew) {
       # After moving/clearing we are now ready to write
       # something to the screen so start recording now
       ret=ret"\n"
       dumpStatus=dsActive
     }
     if(dumpStatus==dsActive||dumpStatus==dsOff) {
       dump[x,y] = c
       if(!spc) delete attr[x,y]
       else attr[x,y] = atos(span)
       if(++x>maxX) maxX=x;
     }
    }
    # End of line if dumping increment y and set x back to first col
    x=1
    if(!dumpStatus) return ret dump_line(y,1);
    else if(++y>maxY) maxY=y;
    return ret
}
BEGIN{
  OFS=FS
  # dump screen status
  dsOff=0    # Not dumping screen contents just write output direct
  dsNew=1    # Just after move/clear waiting for activity to start recording
  dsReset=2  # Screen cleared build new empty buffer and record
  dsActive=3 # Currently recording
  F="abcdefghijklmnopqrstuvwxyz{}`~"
  T="▒␉␌␍␊°±␤␋┘┐┌└┼⎺⎻─⎼⎽├┤┴┬│≤≥π£◆·"
  maxX=80
  delete cur;
  x=y=1
  for(i=1;i<=length(F);i++)Trans[substr(F,i,1)]=substr(T,i,1);
}

{ $0=encode($0) }
1
END {
  if(dumpStatus) {
    print dump_screen();
  }
}'
)

printf '
\n' ================================================ FILE: share/bash_completion.txt ================================================ _cht_complete() { local cur prev opts _get_comp_words_by_ref -n : cur COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" opts="$(curl -s cheat.sh/:list)" if [ ${COMP_CWORD} = 1 ]; then COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) __ltrim_colon_completions "$cur" fi return 0 } complete -F _cht_complete cht.sh ================================================ FILE: share/cht.sh.txt ================================================ #!/bin/bash # shellcheck disable=SC1117,SC2001 # # [X] open section # [X] one shot mode # [X] usage info # [X] dependencies check # [X] help # [X] yank/y/copy/c # [X] Y/C # [X] eof problem # [X] more # [X] stealth mode # # here are several examples for the stealth mode: # # zip lists # list permutation # random list element # reverse a list # read json from file # append string to a file # run process in background # count words in text counter # group elements list __CHTSH_VERSION=0.0.4 __CHTSH_DATETIME="2021-04-25 12:30:30 +0200" # cht.sh configuration loading # # configuration is stored in ~/.cht.sh/ (can be overridden with CHTSH env var.) # CHTSH_HOME=${CHTSH:-"$HOME"/.cht.sh} [ -z "$CHTSH_CONF" ] && CHTSH_CONF=$CHTSH_HOME/cht.sh.conf # shellcheck disable=SC1090,SC2002 [ -e "$CHTSH_CONF" ] && source "$CHTSH_CONF" [ -z "$CHTSH_URL" ] && CHTSH_URL=https://cht.sh # currently we support only two modes: # * lite = access the server using curl # * auto = try standalone usage first CHTSH_MODE="$(cat "$CHTSH_HOME"/mode 2> /dev/null)" [ "$CHTSH_MODE" != lite ] && CHTSH_MODE=auto CHEATSH_INSTALLATION="$(cat "$CHTSH_HOME/standalone" 2> /dev/null)" export LESSSECURE=1 STEALTH_MAX_SELECTION_LENGTH=5 case "$(uname -s)" in Darwin) is_macos=yes ;; *) is_macos=no ;; esac # for KSH93 # shellcheck disable=SC2034,SC2039,SC2168 if echo "$KSH_VERSION" | grep -q ' 93' && ! local foo 2>/dev/null; then alias local=typeset fi fatal() { echo "ERROR: $*" >&2 exit 1 } _say_what_i_do() { [ -n "$LOG" ] && echo "$(date '+[%Y-%m-%d %H:%M%S]') $*" >> "$LOG" local this_prompt="\033[0;1;4;32m>>\033[0m" printf "\n${this_prompt}%s\033[0m\n" " $* " } cheatsh_standalone_install() { # the function installs cheat.sh with the upstream repositories # in the standalone mode local installdir; installdir="$1" local default_installdir="$HOME/.cheat.sh" [ -z "$installdir" ] && installdir=${default_installdir} if [ "$installdir" = help ]; then cat </dev/null || \ { echo "DEPENDENCY: \"$dep\" is needed to install cheat.sh in the standalone mode" >&2; _exit_code=1; } done [ "$_exit_code" -ne 0 ] && return "$_exit_code" while true; do local _installdir echo -n "Where should cheat.sh be installed [$installdir]? "; read -r _installdir [ -n "$_installdir" ] && installdir=$_installdir if [ "$installdir" = y ] \ || [ "$installdir" = Y ] \ || [ "$(echo "$installdir" | tr "[:upper:]" "[:lower:]")" = yes ] then echo Please enter the directory name echo If it was the directory name already, please prepend it with \"./\": "./$installdir" else break fi done if [ -e "$installdir" ]; then echo "ERROR: Installation directory [$installdir] exists already" echo "Please remove it first before continuing" return 1 fi if ! mkdir -p "$installdir"; then echo "ERROR: Could not create the installation directory \"$installdir\"" echo "ERROR: Please check the permissions and start the script again" return 1 fi local space_needed=700 local space_available; space_available=$(($(df -k "$installdir" | awk '{print $4}' | tail -1)/1024)) if [ "$space_available" -lt "$space_needed" ]; then echo "ERROR: Installation directory has no enough space (needed: ${space_needed}M, available: ${space_available}M" echo "ERROR: Please clean up and start the script again" rmdir "$installdir" return 1 fi _say_what_i_do Cloning cheat.sh locally local url=https://github.com/chubin/cheat.sh rmdir "$installdir" git clone "$url" "$installdir" || fatal Could not clone "$url" with git into "$installdir" cd "$installdir" || fatal "Cannot cd into $installdir" # after the repository cloned, we may have the log directory # and we can write our installation log into it mkdir -p "log/" LOG="$PWD/log/install.log" # we use tee everywhere so we should set -o pipefail set -o pipefail # currently the script uses python 2, # but cheat.sh supports python 3 too # if you want to switch it to python 3 # set PYTHON2 to NO: # PYTHON2=NO # PYTHON2=NO if [[ $PYTHON2 = YES ]]; then python="python2" pip="pip" virtualenv_python3_option=() else python="python3" pip="pip3" virtualenv_python3_option=(-p python3) fi _say_what_i_do Creating virtual environment virtualenv "${virtualenv_python3_option[@]}" ve \ || fatal "Could not create virtual environment with 'virtualenv ve'" export CHEATSH_PATH_WORKDIR=$PWD # rapidfuzz does not support Python 2, # so if we are using Python 2, install fuzzywuzzy instead if [[ $PYTHON2 = YES ]]; then sed -i s/rapidfuzz/fuzzywuzzy/ requirements.txt echo "python-Levenshtein" >> requirements.txt fi _say_what_i_do Installing python requirements into the virtual environment ve/bin/"$pip" install -r requirements.txt > "$LOG" \ || { echo "ERROR:" echo "---" tail -n 10 "$LOG" echo "---" echo "See $LOG for more" fatal Could not install python dependencies into the virtual environment } echo "$(ve/bin/"$pip" freeze | wc -l) dependencies were successfully installed" _say_what_i_do Fetching the upstream cheat sheets repositories ve/bin/python lib/fetch.py fetch-all | tee -a "$LOG" _say_what_i_do Running self-tests ( cd tests || exit if CHEATSH_TEST_STANDALONE=YES \ CHEATSH_TEST_SKIP_ONLINE=NO \ CHEATSH_TEST_SHOW_DETAILS=NO \ PYTHON=../ve/bin/python bash run-tests.sh | tee -a "$LOG" then printf "\033[0;32m%s\033[0m\n" "SUCCESS" else printf "\033[0;31m%s\033[0m\n" "FAILED" echo "Some tests were failed. Run the tests manually for further investigation:" echo " cd $PWD; bash run-tests.sh)" fi ) mkdir -p "$CHTSH_HOME" echo "$installdir" > "$CHTSH_HOME/standalone" echo auto > "$CHTSH_HOME/mode" _say_what_i_do Done local v1; v1=$(printf "\033[0;1;32m") local v2; v2=$(printf "\033[0m") cat < "$CHTSH_HOME/mode" echo "Configured mode: $mode" fi else echo "Unknown mode: $mode" echo Supported modes: echo " auto use the standalone installation first" echo " lite use the cheat sheets server directly" fi } get_query_options() { local query="$*" if [ -n "$CHTSH_QUERY_OPTIONS" ]; then case $query in *\?*) query="$query&${CHTSH_QUERY_OPTIONS}";; *) query="$query?${CHTSH_QUERY_OPTIONS}";; esac fi printf "%s" "$query" } do_query() { local query="$*" local b_opts= local uri="${CHTSH_URL}/\"\$(get_query_options $query)\"" if [ -e "$CHTSH_HOME/id" ]; then b_opts="-b \"\$CHTSH_HOME/id\"" fi eval curl "$b_opts" -s "$uri" > "$TMP1" if [ -z "$lines" ] || [ "$(wc -l "$TMP1" | awk '{print $1}')" -lt "$lines" ]; then cat "$TMP1" else ${PAGER:-$defpager} "$TMP1" fi } prepare_query() { local section="$1"; shift local input="$1"; shift local arguments="$1" local query if [ -z "$section" ] || [ x"${input}" != x"${input#/}" ]; then query=$(printf %s "$input" | sed 's@ @/@; s@ @+@g') else query=$(printf %s "$section/$input" | sed 's@ @+@g') fi [ -n "$arguments" ] && arguments="?$arguments" printf %s "$query$arguments" } get_list_of_sections() { curl -s "${CHTSH_URL}"/:list | grep -v '/.*/' | grep '/$' | xargs } gen_random_str() ( len=$1 if command -v openssl >/dev/null; then openssl rand -base64 $((len*3/4)) | awk -v ORS='' // else rdev=/dev/urandom for d in /dev/{srandom,random,arandom}; do test -r "$d" && rdev=$d done if command -v hexdump >/dev/null; then hexdump -vn $((len/2)) -e '1/1 "%02X" 1 ""' "$rdev" elif command -v xxd >/dev/null; then xxd -l $((len/2)) -ps "$rdev" | awk -v ORS='' // else cd /tmp || { echo Cannot cd into /tmp >&2; exit 1; } s= # shellcheck disable=SC2000 while [ "$(echo "$s" | wc -c)" -lt "$len" ]; do s="$s$(mktemp -u XXXXXXXXXX)" done printf "%.${len}s" "$s" fi fi ) if [ "$CHTSH_MODE" = auto ] && [ -d "$CHEATSH_INSTALLATION" ]; then curl() { # ignoring all options # currently the standalone.py does not support them anyway local opt while getopts "b:s" opt; do : done shift $((OPTIND - 1)) local url; url="$1"; shift PYTHONIOENCODING=UTF-8 "$CHEATSH_INSTALLATION/ve/bin/python" "$CHEATSH_INSTALLATION/lib/standalone.py" "${url#"$CHTSH_URL"}" "$@" } elif [ "$(uname -s)" = OpenBSD ] && [ -x /usr/bin/ftp ]; then # any better test not involving either OS matching or actual query? curl() { local opt args="-o -" while getopts "b:s" opt; do case $opt in b) args="$args -c $OPTARG";; s) args="$args -M -V";; *) echo "internal error: unsupported cURL option '$opt'" >&2; exit 1;; esac done shift $((OPTIND - 1)) /usr/bin/ftp "$args" "$@" } else command -v curl >/dev/null || { echo 'DEPENDENCY: install "curl" to use cht.sh' >&2; exit 1; } _CURL=$(command -v curl) if [ x"$CHTSH_CURL_OPTIONS" != x ]; then curl() { $_CURL "${CHTSH_CURL_OPTIONS}" "$@" } fi fi if [ "$1" = --read ]; then read -r a || a="exit" printf "%s\n" "$a" exit 0 elif [ x"$1" = x--help ] || [ -z "$1" ]; then n=${0##*/} s=$(echo "$n" | sed "s/./ /"g) cat </dev/null || echo 'DEPENDENCY: please install "wl-copy" for "copy"' >&2 else command -v xsel >/dev/null || echo 'DEPENDENCY: please install "xsel" for "copy"' >&2 fi fi command -v rlwrap >/dev/null || { echo 'DEPENDENCY: install "rlwrap" to use cht.sh in the shell mode' >&2; exit 1; } mkdir -p "$CHTSH_HOME/" lines=$(tput lines) if command -v less >/dev/null; then defpager="less -R" elif command -v more >/dev/null; then defpager="more" else defpager="cat" fi cmd_cd() { if [ $# -eq 0 ]; then section="" else new_section=$(echo "$input" | sed 's/cd *//; s@/*$@@; s@^/*@@') if [ -z "$new_section" ] || [ ".." = "$new_section" ]; then section="" else valid_sections=$(get_list_of_sections) valid=no; for q in $valid_sections; do [ "$q" = "$new_section/" ] && { valid=yes; break; }; done if [ "$valid" = no ]; then echo "Invalid section: $new_section" echo "Valid sections:" echo "$valid_sections" \ | xargs printf "%-10s\n" \ | tr ' ' . \ | xargs -n 10 \ | sed 's/\./ /g; s/^/ /' else section="$new_section" fi fi fi } cmd_copy() { if [ -z "$DISPLAY" ] && [ "$is_macos" != yes ]; then echo copy: supported only in the Desktop version elif [ -z "$input" ]; then echo copy: Make at least one query first. else curl -s "${CHTSH_URL}"/"$(get_query_options "$query"?T)" > "$TMP1" if [ "$is_macos" != yes ]; then if [ "$XDG_SESSION_TYPE" = wayland ]; then wl-copy < "$TMP1" else xsel -bi < "$TMP1" fi else pbcopy < "$TMP1" fi echo "copy: $(wc -l "$TMP1" | awk '{print $1}') lines copied to the selection" fi } cmd_ccopy() { if [ -z "$DISPLAY" ] && [ "$is_macos" != yes ]; then echo copy: supported only in the Desktop version elif [ -z "$input" ]; then echo copy: Make at least one query first. else curl -s "${CHTSH_URL}"/"$(get_query_options "$query"?TQ)" > "$TMP1" if [ "$is_macos" != yes ]; then if [ "$XDG_SESSION_TYPE" = wayland ]; then wl-copy < "$TMP1" else xsel -bi < "$TMP1" fi else pbcopy < "$TMP1" fi echo "copy: $(wc -l "$TMP1" | awk '{print $1}') lines copied to the selection" fi } cmd_exit() { exit 0 } cmd_help() { cat < python zip list cht.sh/python> zip list cht.sh/go> /python zip list EOF } cmd_hush() { mkdir -p "$CHTSH_HOME/" && touch "$CHTSH_HOME/.hushlogin" && echo "Initial 'use help' message was disabled" } cmd_id() { id_file="$CHTSH_HOME/id" if [ id = "$input" ]; then new_id="" else new_id=$(echo "$input" | sed 's/id *//; s/ *$//; s/ /+/g') fi if [ "$new_id" = remove ]; then if [ -e "$id_file" ]; then rm -f -- "$id_file" && echo "id is removed" else echo "id was not set, so you can't remove it" fi return fi if [ -n "$new_id" ] && [ reset != "$new_id" ] && [ "$(/bin/echo -n "$new_id" | wc -c)" -lt 16 ]; then echo "ERROR: $new_id: Too short id. Minimal id length is 16. Use 'id reset' for a random id" return fi if [ -z "$new_id" ]; then # if new_id is not specified check if we have some id already # if yes, just show it # if not, generate a new id if [ -e "$id_file" ]; then awk '$6 == "id" {print $NF}' <"$id_file" | tail -n 1 return else new_id=reset fi fi if [ "$new_id" = reset ]; then new_id=$(gen_random_str 12) else echo WARNING: if someone gueses your id, he can read your cht.sh search history fi if [ -e "$id_file" ] && grep -q '\tid\t[^\t][^\t]*$' "$id_file" 2> /dev/null; then sed -i 's/\tid\t[^\t][^\t]*$/ id '"$new_id"'/' "$id_file" else if ! [ -e "$id_file" ]; then printf '#\n\n' > "$id_file" fi printf ".cht.sh\tTRUE\t/\tTRUE\t0\tid\t$new_id\n" >> "$id_file" fi echo "$new_id" } cmd_query() { query=$(prepare_query "$section" "$input") do_query "$query" } cmd_stealth() { if [ "$input" != stealth ]; then arguments=$(echo "$input" | sed 's/stealth //; s/ /\&/') fi trap break INT if [ "$is_macos" = yes ]; then past=$(pbpaste) else if [ "$XDG_SESSION_TYPE" = wayland ]; then past=$(wl-paste -p) else past=$(xsel -o) fi fi printf "\033[0;31mstealth:\033[0m you are in the stealth mode; select any text in any window for a query\n" printf "\033[0;31mstealth:\033[0m selections longer than $STEALTH_MAX_SELECTION_LENGTH words are ignored\n" if [ -n "$arguments" ]; then printf "\033[0;31mstealth:\033[0m query arguments: ?$arguments\n" fi printf "\033[0;31mstealth:\033[0m use ^C to leave this mode\n" while true; do if [ "$is_macos" = yes ]; then current=$(pbpaste) else if [ "$XDG_SESSION_TYPE" = wayland ]; then current=$(wl-paste -p) else current=$(xsel -o) fi fi if [ "$past" != "$current" ]; then past=$current current_text="$(echo $current | tr -c '[a-zA-Z0-9]' ' ')" if [ "$(echo "$current_text" | wc -w)" -gt "$STEALTH_MAX_SELECTION_LENGTH" ]; then printf "\033[0;31mstealth:\033[0m selection length is longer than $STEALTH_MAX_SELECTION_LENGTH words; ignoring\n" continue else printf "\n\033[0;31mstealth: \033[7m $current_text\033[0m\n" query=$(prepare_query "$section" "$current_text" "$arguments") do_query "$query" fi fi sleep 1; done trap - INT } cmd_update() { [ -w "$0" ] || { echo "The script is readonly; please update manually: curl -s ${CHTSH_URL}/:cht.sh | sudo tee $0"; return; } TMP2=$(mktemp /tmp/cht.sh.XXXXXXXXXXXXX) curl -s "${CHTSH_URL}"/:cht.sh > "$TMP2" if ! cmp "$0" "$TMP2" > /dev/null 2>&1; then if grep -q ^__CHTSH_VERSION= "$TMP2"; then # section was validated by us already args=(--shell "$section") cp "$TMP2" "$0" && echo "Updated. Restarting..." && rm "$TMP2" && CHEATSH_RESTART=1 exec "$0" "${args[@]}" else echo "Something went wrong. Please update manually" fi else echo "cht.sh is up to date. No update needed" fi rm -f "$TMP2" > /dev/null 2>&1 } cmd_version() { insttime=$(ls -l -- "$0" | sed 's/ */ /g' | cut -d ' ' -f 6-8) echo "cht.sh version $__CHTSH_VERSION of $__CHTSH_DATETIME; installed at: $insttime" TMP2=$(mktemp /tmp/cht.sh.XXXXXXXXXXXXX) if curl -s "${CHTSH_URL}"/:cht.sh > "$TMP2"; then if ! cmp "$0" "$TMP2" > /dev/null 2>&1; then echo "Update needed (type 'update' for that)". else echo "Up to date. No update needed" fi fi rm -f "$TMP2" > /dev/null 2>&1 } TMP1=$(mktemp /tmp/cht.sh.XXXXXXXXXXXXX) trap 'rm -f $TMP1 $TMP2' EXIT trap 'true' INT if ! [ -e "$CHTSH_HOME/.hushlogin" ] && [ -z "$this_query" ]; then echo "type 'help' for the cht.sh shell help" fi while true; do if [ "$section" != "" ]; then full_prompt="$prompt/$section> " else full_prompt="$prompt> " fi input=$( rlwrap -H "$CHTSH_HOME/history" -pgreen -C cht.sh -S "$full_prompt" bash "$0" --read | sed 's/ *#.*//' ) cmd_name=${input%% *} cmd_args=${input#* } case $cmd_name in "") continue;; # skip empty input lines '?'|h|help) cmd_name=help;; hush) cmd_name=hush;; cd) cmd_name="cd";; exit|quit) cmd_name="exit";; copy|yank|c|y) cmd_name=copy;; ccopy|cc|C|Y) cmd_name=ccopy;; id) cmd_name=id;; stealth) cmd_name=stealth;; update) cmd_name=update;; version) cmd_name=version;; *) cmd_name="query"; cmd_args="$input";; esac "cmd_$cmd_name" $cmd_args done ================================================ FILE: share/emacs-ivy.txt ================================================ ;; ;; Written by RenJMR ;; https://www.reddit.com/r/emacs/comments/6ddr7p/snippet_search_cheatsh_using_ivy/ ;; ;;; ;;; Install the package `ivy' and use `(require 'ivy)' if you do not have ;;; access to the `ivy-read' function. (defun ejmr-search-cheat-sh () "Search `http://cheat.sh/' for help on commands and code." (interactive) (ivy-read "Command or Topic: " (process-lines "curl" "--silent" "http://cheat.sh/:list?T&q") :require-match t :sort t :history 'ejmr-search-cheat-sh :action (lambda (input) (browse-url (concat "http://cheat.sh/" input "?T&q"))) :caller 'ejmr-search-cheat-sh)) ================================================ FILE: share/emacs.txt ================================================ ;;; cheat-sh.el --- Interact with cheat.sh -*- lexical-binding: t -*- ;; Copyright 2017 by Dave Pearson ;; Author: Dave Pearson ;; Version: 1.7 ;; Keywords: docs, help ;; URL: https://github.com/davep/cheat-sh.el ;; Package-Requires: ((emacs "24")) ;; cheat-sh.el is free software distributed under the terms of the GNU ;; General Public Licence, version 2 or (at your option) any later version. ;; For details see the file COPYING. ;;; Commentary: ;; ;; cheat-sh.el provides a simple Emacs interface for looking things up on ;; cheat.sh. ;;; Code: (require 'url-vars) (defgroup cheat-sh nil "Interact with cheat.sh." :group 'docs) (defface cheat-sh-section '((t :inherit (bold font-lock-doc-face))) "Face used on sections in a cheat-sh output window." :group 'cheat-sh) (defface cheat-sh-caption '((t :inherit (bold font-lock-function-name-face))) "Face used on captions in the cheat-sh output window." :group 'cheat-sh) (defcustom cheat-sh-list-timeout (* 60 60 4) "Seconds to wait before deciding the cached sheet list is \"stale\"." :type 'integer :group 'cheat-sh) (defconst cheat-sh-url "http://cheat.sh/%s?T" "URL for cheat.sh.") (defconst cheat-sh-user-agent "cheat-sh.el (curl)" "User agent to send to cheat.sh. Note that \"curl\" should ideally be included in the user agent string because of the way cheat.sh works. cheat.sh looks for a specific set of clients in the user agent (see https://goo.gl/8gh95X for this) to decide if it should deliver plain text rather than HTML. cheat-sh.el requires plain text.") (defun cheat-sh-get (thing) "Get THING from cheat.sh." (let* ((url-request-extra-headers `(("User-Agent" . ,cheat-sh-user-agent))) (buffer (url-retrieve-synchronously (format cheat-sh-url (url-hexify-string thing)) t t))) (when buffer (with-current-buffer buffer (setf (point) (point-min)) (when (search-forward-regexp "^$" nil t) (buffer-substring (point) (point-max))))))) (defvar cheat-sh-sheet-list nil "List of all available sheets.") (defvar cheat-sh-sheet-list-acquired nil "The time when variable `cheat-sh-sheet-list' was populated.") (defun cheat-sh-sheet-list-cache () "Return the list of sheets. The list is cached in memory, and is considered \"stale\" and is refreshed after `cheat-sh-list-timeout' seconds." (when (and cheat-sh-sheet-list-acquired (> (- (time-to-seconds) cheat-sh-sheet-list-acquired) cheat-sh-list-timeout)) (setq cheat-sh-sheet-list nil)) (or cheat-sh-sheet-list (let ((list (cheat-sh-get ":list"))) (when list (setq cheat-sh-sheet-list-acquired (time-to-seconds)) (setq cheat-sh-sheet-list (split-string list "\n")))))) (defun cheat-sh-read (prompt) "Read input from the user, showing PROMPT to prompt them. This function is used by some `interactive' functions in cheat-sh.el to get the item to look up. It provides completion based of the sheets that are available on cheat.sh." (completing-read prompt (cheat-sh-sheet-list-cache))) (defun cheat-sh-decorate-all (buffer regexp face) "Decorate BUFFER, finding REGEXP and setting face to FACE." (with-current-buffer buffer (save-excursion (setf (point) (point-min)) (while (search-forward-regexp regexp nil t) (replace-match (propertize (match-string 1) 'font-lock-face face) nil t))))) (defun cheat-sh-decorate-results (buffer) "Decorate BUFFER with properties to highlight results." ;; "[Search section]" (cheat-sh-decorate-all buffer "^\\(\\[.*\\]\\)$" 'cheat-sh-section) ;; "# Result caption" (cheat-sh-decorate-all buffer "^\\(#.*\\)$" 'cheat-sh-caption) ;; "cheat-sh help caption:" (cheat-sh-decorate-all buffer "^\\([^[:space:]].*:\\)$" 'cheat-sh-caption)) ;;;###autoload (defun cheat-sh (thing) "Look up THING on cheat.sh and display the result." (interactive (list (cheat-sh-read "Lookup: "))) (let ((result (cheat-sh-get thing))) (if result (with-help-window "*cheat.sh*" (princ result) (cheat-sh-decorate-results standard-output)) (error "Can't find anything for %s on cheat.sh" thing)))) ;;;###autoload (defun cheat-sh-region (start end) "Look up the text between START and END on cheat.sh." (interactive "r") (deactivate-mark) (cheat-sh (buffer-substring-no-properties start end))) ;;;###autoload (defun cheat-sh-maybe-region () "If region is active lookup content of region, otherwise prompt." (interactive) (call-interactively (if mark-active #'cheat-sh-region #'cheat-sh))) ;;;###autoload (defun cheat-sh-help () "Get help on using cheat.sh." (interactive) (cheat-sh ":help")) ;;;###autoload (defun cheat-sh-list (thing) "Get a list of topics available on cheat.sh. Either gets a topic list for subject THING, or simply gets a list of all available topics on cheat.sh if THING is supplied as an empty string." (interactive (list (cheat-sh-read "List sheets for: "))) (cheat-sh (format "%s/:list" thing))) ;;;###autoload (defun cheat-sh-search (thing) "Search for THING on cheat.sh and display the result." (interactive "sSearch: ") (cheat-sh (concat "~" thing))) ;;;###autoload (defun cheat-sh-search-topic (topic thing) "Search TOPIC for THING on cheat.sh and display the result." (interactive (list (cheat-sh-read "Topic: ") (read-string "Search: "))) (cheat-sh (concat topic "/~" thing))) (provide 'cheat-sh) ;;; cheat-sh.el ends here ================================================ FILE: share/firstpage-v1.txt ================================================ oooo . oooo `888 .o8 `888 .ooooo. 888 .oo. .ooooo. .oooo. .o888oo .oooo.o 888 .oo. d88' `"Y8 888P"Y88b d88' `88b `P )88b 888 d88( "8 888P"Y88b 888 888 888 888ooo888 .oP"888 888 `"Y88b. 888 888 888 .o8 888 888 888 .o d8( 888 888 . .o. o. )88b 888 888 `Y8bod8P' o888o o888o `Y8bod8P' `Y888""8o "888" Y8P 8""888P' o888o o888o ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * the fastest way to find | you need * provides access to community driven cheat sheets repositories * delivers <906> cheat sheets in <16> areas and growing * covers UNIX/Linux commands and programming languages * programming languages cheat sheets are under: * database management systems: * supports bash completion (add {/:bash_completion} to your {~/.bashrc}) * editors integration (see {/:emacs} and {/:vim}) To show a cheat sheet: $ curl cheat.sh/{command} To find a cheat sheet (details in {/:help}): $ curl cheat.sh/{~keyword} To post a cheat sheet (details in {/:post}): $ curl -F 'newcmd=<-' cheat.sh [Follow @igor_chubin for updates] [github.com/chubin/cheat.sh] ================================================ FILE: share/firstpage-v2.pnl ================================================ _ _ _ __ ___| |__ ___ __ _| |_ ___| |__ \ \ The only cheat sheet you need / __| '_ \ / _ \/ _` | __| / __| '_ \ \ \ Unified access to the best | (__| | | | __/ (_| | |_ _\__ \ | | |/ / community driven documentation \___|_| |_|\___|\__,_|\__(_)___/_| |_/_/ repositories of the world +------------------------+ +------------------------+ +------------------------+ | $ curl cheat.sh/ls | | $ cht.sh btrfs | | $ cht.sh lua/:learn | | $ curl cht.sh/btrfs | | $ cht.sh tar~list | | Learn any* programming | | $ curl cht.sh/tar~list | | | | language not leaving | | $ curl https://cht.sh | | | | your shell | | | | | | *) any of 60 | | | | | | | +-- queries with curl ---+ +- own optional client --+ +- learn, learn, learn! -+ +------------------------+ +------------------------+ +------------------------+ | $ cht.sh go/f| | $ cht.sh --shell | | $ cht.sh go zip lists | | go/for go/func | | cht.sh> help | | Ask any question using | | $ cht.sh go/for | | ... | | cht.sh or curl cht.sh: | | ... | | $ curl cht.sh/:cht.sh | | /go/zip+lists | | | | #!/bin/sh | | (use /,+ when curling) | | | | ... | | | +---- TAB-completion ----+ +-- interactive shell ---+ +- programming questions-+ +------------------------+ +------------------------+ +------------------------+ | $ curl cht.sh/:help | | $ vim prg.py | | $ time curl cht.sh/ | | see /:help and /:intro | | ... | | ... | | for usage information | | zip lists _ | | real 0m0.075s | | and README.md on GitHub| | KK | | | | for the details | | *awesome* | | | | *start here*| | | | | +--- self-documented ----+ +- queries from editor! -+ +---- instant answers ---+ [Follow @igor_chubin for updates][github.com/chubin/cheat.sh] ==[mask]== KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKAAAAA KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKAAAAA GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKAAAA DDDDDDDDDDDDDDDDDDDDDDDDDDDDDD KKKKKKKKKKKKKKKKKKKKKKKKKKKLKKKKKKKKKKKAAAA DDDDDDDDDDDDDDDDDDDDDDDDDDDDDD KKKKKKKKKKKKKKKKKKKKKKKKKKLLLKKKKKKKKKAAAAA DDDDDDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDD D $ BBBB CCCCCCCCCCCCCCC D D $ BBBBBB CCCCCCCCCCCCC D D $ CCCCCCCCCCCBBBBBB D D $ BBBB CCCCCCCCCCCCCCC D D $ BBBBBB CCCCCCCCCCCCC D D Learn anyF programming D D $ BBBB CCCCCCCCCCCCCCC D D D D languages not leaving D D $ CCCC BBBBBBCCCCCCCCC D D D D your shell D D D D D D FF any of 60 D D D D D D D DDDEEEEEEEEEEEEEEEEEEEDDDD DD EEEEEEEEEEEEEEEEEEE DDD DD EEEEEEEEEEEEEEEEEEEE DD DDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDD D $ CCCCCCCCCCCBBBBBBBBBBD D $ CCCCCC BBBBBBB D D $ CCCCCC BBBBBBBBBBBB D D go/for go/func D D HHHHHHH help D D ask any question using D D $ cht.sh go/for D D ... D D cht.sh or curl cht.sh: D D ... D D $ BBBB CCCCCCCCCCCCCCC D D /goHzipHlists D D D D D D (use H,H when curling) D D D D D D D DDDDDEEEEEEEEEEEEEEEEDDDDD DDDEEEEEEEEEEEEEEEEEEEDDDD DDEEEEEEEEEEEEEEEEEEEEEEDD DDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDD D $ CCCCCCCCCCCCBBBBB D D $ BBB CCCCCC D D $ BBBB curl cht.sh/ D D see HHHHHH and HHHHHHH D D ... D D ... D D for usage information D D zip lists _ D D real HHHHHHHH D D and readme.md on CCCCCCD D HHHHHHHHHH D D D D for the details D D * awesome * D D D D FFFFFFFFFFFFD D D D D DDDD EEEEEEEEEEEEEEE DDDDD DD EEEEEEEEEEEEEEEEEEEE DD DDDDD EEEEEEEEEEEEEEE DDDD DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD ==[code]== set_color A green set_color B white set_color C gray set_color D cyan set_color E yellow set_color A #00cc00 set_color B #00cc00 set_color C #00aacc set_color D #888888 set_color E #cccc00 set_color F #ff0000 set_color H #22aa22 set_color I #cc0000 set_color J #000000 set_bg_color G #555555 set_bg_color J #555555 ================================================ FILE: share/firstpage-v2.txt ================================================ _ _ _ __    ___| |__ ___ __ _| |_ ___| |__ \ \      The only cheat sheet you need    / __| '_ \ / _ \/ _` | __| / __| '_ \ \ \  Unified access to the best     | (__| | | | __/ (_| | |_ _\__ \ | | |/ /  community driven documentation \___|_| |_|\___|\__,_|\__(_)___/_| |_/_/   repositories of the world      +------------------------+ +------------------------+ +------------------------+ | $ curl cheat.sh/ls     | | $ cht.sh btrfs         | | $ cht.sh lua/:learn | | $ curl cht.sh/btrfs    | | $ cht.sh tar~list      | | Learn any* programming | | $ curl cht.sh/tar~list | | | | language not leaving | | $ curl https://cht.sh  | | | | your shell | | | | | | *) any of 60 | | | | | | | +-- queries with curl ---+ +- own optional client --+ +- learn, learn, learn! -+ +------------------------+ +------------------------+ +------------------------+ | $ cht.sh go/f<tab><tab>| | $ cht.sh --shell | | $ cht.sh go zip lists | | go/for go/func | | cht.sh> help | | Ask any question using | | $ cht.sh go/for | | ... | | cht.sh or curl cht.sh: | | ... | | | | /go/zip+lists | | | | | | (use /,+ when curling) | | | | | | | +---- TAB-completion ----+ +-- interactive shell ---+ +- programming questions-+ +------------------------+ +------------------------+ +------------------------+ | $ curl cht.sh/:help | | $ vim prg.py | | $ time curl cht.sh/ | | see /:help and /:intro | | ... | | ... | | for usage information | | zip lists _ | | real 0m0.075s | | and README.md on GitHub| | <leader>KK | | | | for the details | | *awesome* | | | | *start here*| | | | | +--- self-documented ----+ +- queries from editor! -+ +---- instant answers ---+ [Follow @igor_chubin for updates][github.com/chubin/cheat.sh] ================================================ FILE: share/fish.txt ================================================ # add it to your ~/.config/fish/config.fish # retrieve command cheat sheets from cheat.sh # fish version by @tobiasreischmann function cheat.sh curl cheat.sh/$argv end # register completions (on-the-fly, non-cached, because the actual command won't be cached anyway complete -c cheat.sh -xa '(curl -s cheat.sh/:list)' ================================================ FILE: share/help.txt ================================================ Usage: $ curl cheat.sh/TOPIC show cheat sheet on the TOPIC $ curl cheat.sh/TOPIC/SUB show cheat sheet on the SUB topic in TOPIC $ curl cheat.sh/~KEYWORD search cheat sheets for KEYWORD Options: ?OPTIONS q quiet mode, don't show github/twitter buttons T text only, no ANSI sequences style=STYLE color style c do not comment text, do not shift code (QUERY+ only) C do not comment text, shift code (QUERY+ only) Q code only, don't show text (QUERY+ only) Options can be combined together in this way: curl 'cheat.sh/for?qT&style=bw' (when using & in shell, don't forget to specify the quotes or escape & with \) Special pages: :help this page :list list all cheat sheets :post how to post new cheat sheet :cht.sh shell client (cht.sh) :bash_completion bash function for tab completion :styles list of color styles :styles-demo show color styles usage examples :random fetches a random cheat sheet Shell client: $ curl https://cht.sh/:cht.sh > ~/bin/cht.sh $ chmod +x ~/bin/cht.sh $ cht.sh python :learn $ cht.sh --shell Tab completion: $ mkdir -p ~/.bash.d/ $ curl cheat.sh/:bash_completion > ~/.bash.d/cht.sh $ . ~/.bash.d/cht.sh $ echo '. ~/.bash.d/cht.sh' >> ~/.bashrc Editor integration: :emacs see the page for the Emacs configuration :vim see the page for the Vim configuration Search: /~snapshot look for "snapshot" in the first level cheat sheets /~ssh~passphrase several keywords can be combined together using ~ /scala/~closure look for "closure" in scala cheat sheets /~snapshot/r look for "snapshot" in all cheat sheets recursively You can use special search options after the closing slash: /~shot/bi case insensitive (i), word boundaries (b) List of search options: b word boundaries i case insensitive search r recursive Programming languages topics: Each programming language topic has the following subtopics: hello hello world + how to start the program :learn big cheat sheet for learning language from scratch :list list of topics :random fetches a random cheat sheet belonging to the topic ================================================ FILE: share/intro.txt ================================================ ## curl cht.sh To access a cheat sheet you can simply issue a plain HTTP or HTTPS request specifying the topic name in the query URL: {1curl cheat.sh}{2/tar} {1curl https://cheat.sh}{2/tar} You can use the full service name, {2cheat.sh}, or the shorter variant, {2cht.sh}. They are equivalent: {1curl https://}{2cht.sh}{1/tar} {1curl https://}{2cheat.sh}{1/tar} The preferred access protocol is HTTPS, and you should always use it when possible. Cheat sheets in the root namespaces cover UNIX/Linux commands. Cheat sheets covering programming languages are located in subsections: {1curl cht.sh/}{2go/func} All cheat sheets in a subsection can be listed using a special query {2:list} : {1curl cht.sh/go/}{2:list} There are several other special queries. All of them start with a {2colon}. See {2/:help} for the full list of the special queries. ## Search If a cheat sheet is too large, you can cut the needed part out using an additional search parameter. In this case, only the paragraph that contains the search term will be displayed: {1curl cht.sh/tar}{2~extract} If the name of the cheat sheet is omitted, and only the search query is specified, all cheat sheets in the namespace are scanned, and the found occurrences are displayed: {1curl cht.sh/}{2~extract} ## Options cheat.sh queries as well as search queries have many options. They can be specified as a part of the query string in the URL, after {2?}. Short single letter options can be joined together. Long options are separated with {2&}. For example, to switch syntax highlighting off the {2T} switch is used: {1curl cht.sh/tar}{2?T} A full list of all available cheat.sh options as well as description of all modes of operation can be found in {2/:help}. {1curl cht.sh}{2/:help} ## cht.sh client Though it's perfectly possible to access cheat.sh using {1curl} (or any other HTTP client) alone, there is a special client that has several advantages over plain curling: {2cht.sh}. To install the client in {2~/bin}: {1curl} {2https://cht.sh/:cht.sh} {1> ~/bin/cht.sh} {1chmod +x ~/bin/cht.sh} Queries look the same, but you can use {1spaces} to separate words in addition to {1+} used with curl. {1cht.sh} {2python zip lists} ## cht.sh shell If you're always issuing queries about the same programming language, it can be more convenient to run the client in the shell mode and specify the query's context: {1$} {2cht.sh --shell python} {1cht.sh/python> zip lists} Of course, you can start the shell without the context too: {1$} {2cht.sh --shell} {1cht.sh> python zip lists} {1cht.sh> go http query} {1cht.sh> js iterate list} If you use one language predominantly, but sometimes issue queries about others, you may prepend the query with {2/}: {1cht.sh/python>} {2zip lists} {1cht.sh/python>} {2/go http query} {1cht.sh/python>} {2/js iterate list} ## :learn If you are just starting to learn a new programming language and you have no distinct queries for the moment, cheat.sh can be a good starting point. As you know, it exports cheat sheets from the best cheat sheet repositories, like {1Learn X in Y}, a repository of concise documentation devoted (but not limited) to learning programming languages from scratch. If you want start learning a new programming language, do (use less -R because the output could be quite big): {1curl cht.sh/elixir/}{2:learn} {1| less -R} Or simply {2:learn} with cht.sh (you don't need {2less -R} here, because {1cht.sh} starts pager if needed automatically): {4cht.sh/elixir>} {2:learn} ## Programming languages questions One of the most important features of cheat.sh is that you can ask it any questions about programming languages and instantly get answers. You can use either direct HTTP queries or the cht.sh client: {1curl cht.sh/}{2python/reverse+list} {4cht.sh/python>} {2reverse list} In the latter case you don't need + to separate the words in the query, you can do it in a more natural way, with spaces. If context in the cht.sh shell is not specified, you have to write the programming language name as the first word in the query: {4cht.sh>} {2python reverse list} But if you are using only one programming language and all queries are about it, it's better to change the current context. ## Comments Text in the answers is syntactically formatted as a comment in the corresponding programming language When using cht.sh, you can copy the result of the last query into the selection buffer (you may also call it "clipboard") using {2C} (or {2c}, with text): {1cht.sh/python> reverse list} {4...} {1cht.sh/python>} {2C} {4=1 lines copied} ## bash TAB-completion for cht.sh One of the advantages of the {1cht.sh} client comparing to plain curl is that you can use TAB completion when writing its queries in {1bash} (other supported shells: {1zsh} and {1fish}). To install the TAB completion script, assuming you use bash, you have to do: {1mkdir -p ~/.bash.d/} {1curl} {2https://cht.sh/:bash_completion} {1> ~/.bash.d/cht.sh} {1echo 'source ~/.bash.d/cht.sh' >> ~/.bashrc} {1source ~/.bash.d/cht.sh} ## Editor You can access cheat.sh directly from editors: {1Vim} and {1Emacs}. It's a very important feature! You will absolutely like it. {1Imagine:} instead of switching to your browser, googling, browsing Stack Overflow and eventually copying the code snippets you need and later pasting them into the editor, you can achieve the same instantly and without leaving the editor at all! Here is how it works: 1. In Vim, if you have a question while editing a program, you can just type your question {1directly in the buffer} and press {2KK}. You will get the answer to your question in {1pager}. (with {2KB} you'll get the answer in a separate {1buffer}). 2. If you like the answer. You can manually paste it from the buffer or the pager, or if you are lazy you can use {2KP} to paste it under your question ({2KR} will replace your question). If you want the answer without the comments, {2KC} replays the last query toggling them. You have to install cheat.sh {1Vim/Emacs plugins} for the editor support. See {2/:vim} or {2/:emacs} for detailed installation instructions. ## Feature requests, feedback and contribution If you want to submit a new community driver repository for cheat.sh please open a ticket on the project page on GitHub. If you want to modify an existing cheat sheet, please check the source of the cheat sheet (it is always displayed in the cheat sheet bottom line). If you want to add a new cheat sheet, add it here: {1https://github.com/chubin/cheat.sheets} If you want to suggest a new feature for cheat.sh, or if you've found a bug, please open a new issue on github: {1https://github.com/chubin/cheat.sh} If you want to get the major project updates, follow @igor_chubin in Twitter or this RSS feed: {1https://twitrss.me/twitter_user_to_rss/?user=igor_chubin} ================================================ FILE: share/post.txt ================================================ You can add a new entry to a cheat sheet or create a new cheat sheet in one of the following ways (your cheat sheet will be eventually added to chubin/cheat.sheets): 1. With curl: cat ${cheatsheet} | curl -F 'newcmd=<-' cheat.sh curl --data-binary @${cheatsheet} cheat.sh/newcmd newcmd is the the name of the command you want to post (use your @twitter or email@ in the post). 2. With git: ~~~ # clone chubin/cheat.sheets git pull https://github.com/${you}/cheat.sheets && cd cheat.sheets cp ${cheatsheet} newcmd git add newcmd git commit -m 'added newcmd cheat sheet' newcmd git push # send pull request ~~~ 3. With a browser: Go to the end of the cheat sheet, click the dollar sign with the mouse and post your cheat sheet. It will be saved automatically and reviewed. When writing an entry for a cheat sheet, please keep in mind: 1. We don't try to repeat manuals and documentation sites 2. We don't try to document each and every special usage case of a tool 3. We try to find and gather the most interesting usage cases If you want contribute to the project, but you have no idea what cheat sheet you should post, check the list of the most wanted cheat sheets: cheat.sh/:wanted ================================================ FILE: share/scripts/cacheCleanup.go ================================================ package main // Remove invalid cache entries. // Cache entry is invalid, if it contains a special substring. import ( "context" "log" "strings" "github.com/go-redis/redis" ) var invalidEntrySubstr = "Unknown cheat sheet" func removeInvalidEntries() error { rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", Password: "", DB: 0, }) ctx := context.Background() allKeys, err := rdb.Keys(ctx, "*").Result() if err != nil { return err } var counter int for _, key := range allKeys { val, err := rdb.Get(ctx, key).Result() if err != nil { return err } if strings.Contains(val, invalidEntrySubstr) { err = rdb.Del(ctx, key).Err() if err != nil { return err } counter++ } } log.Println("invalid entries removed:", counter) return nil } func main() { err := removeInvalidEntries() if err != nil { log.Println(err) } } ================================================ FILE: share/scripts/remove-from-cache.sh ================================================ #!/usr/bin/env bash remove_by_name() { local query; query="$1" local q redis-cli KEYS "$query" | while read -r q; do redis-cli DEL "$q" done } if [ -z "$1" ]; then echo Usage: echo echo " $0 QUERY" exit 1 fi remove_by_name "$1" ================================================ FILE: share/static/1.html ================================================
================================================ FILE: share/static/malformed-response.html ================================================ cheat.sh
#  Sorry, we are experiencing extremely high load now.
#  We are working on the problem and hope to get it fixed soon.
#  Please come back in several hours or try some other queries:
#  
#  For example:

 curl cht.sh/:list               to list available cheat sheets
 curl cht.sh/LANGUAGE/:list      to list available cheat sheets for LANGUAGE
 curl cht.sh/LANGUAGE/:learn     to learn the LANGUAGE

 for the updates. 

If you do not use Twitter, drop a short email to Igor Chubin (igor@chub.in),
and you will be notified as soon as the service is fixed.
If you have any feature requests, wishes, ideas, criticism,
you can use them as the payload for this email.

If you like to code (and you surely do), you can check the cheat.sh repository
to see how the scalability problem is (not yet) solved.

================================================ FILE: share/static/opensearch.xml ================================================ cheat.sh The only cheat sheet you need. Unified access to the best community driven documentation repositories of the world. cheatsheet curl terminal command-line cli examples documentation help tldr en-US https://cheat.sh ================================================ FILE: share/static/style.css ================================================ body { background: black; color: #bbbbbb; } .pre, pre { /* font-family: source_code_proregular; */ /* font-family: Courier New,Courier,Lucida Sans Typewriter,Lucida Typewriter,monospace; font-size: 70%; */ /*font-family: Lucida Console,Lucida Sans Typewriter,monaco,Bitstream Vera Sans Mono,monospace; */ /*Droid Sans Mono*/ font-family: "DejaVu Sans Mono", Menlo, "Lucida Sans Typewriter", "Lucida Console", monaco, "Bitstream Vera Sans Mono", monospace; /*font-family: bitstream_vera_sans_monoroman;*/ font-size: 75%; } input[type="text"]{ border: none; background: transparent; color: #bbbbbb; } ================================================ FILE: share/styles-demo.txt ================================================ -------- abap -------- # basic loop for i in 1 2 3 4 5 6 7 8 9 10 do echo $i done -------- algol -------- # basic loop for i in 1 2 3 4 5 6 7 8 9 10 do echo $i done -------- algol_nu -------- # basic loop for i in 1 2 3 4 5 6 7 8 9 10 do echo $i done -------- arduino -------- # basic loop for i in 1 2 3 4 5 6 7 8 9 10 do echo $i done -------- autumn -------- # basic loop for i in 1 2 3 4 5 6 7 8 9 10 do echo $i done -------- borland -------- # basic loop for i in 1 2 3 4 5 6 7 8 9 10 do echo $i done -------- bw -------- # basic loop for i in 1 2 3 4 5 6 7 8 9 10 do echo $i done -------- colorful -------- # basic loop for i in 1 2 3 4 5 6 7 8 9 10 do echo $i done -------- default -------- # basic loop for i in 1 2 3 4 5 6 7 8 9 10 do echo $i done -------- emacs -------- # basic loop for i in 1 2 3 4 5 6 7 8 9 10 do echo $i done -------- friendly -------- # basic loop for i in 1 2 3 4 5 6 7 8 9 10 do echo $i done -------- fruity -------- # basic loop for i in 1 2 3 4 5 6 7 8 9 10 do  echo $i done -------- igor -------- # basic loop for i in 1 2 3 4 5 6 7 8 9 10 do echo $i done -------- lovelace -------- # basic loop for i in 1 2 3 4 5 6 7 8 9 10 do echo $i done -------- manni -------- # basic loop for i in 1 2 3 4 5 6 7 8 9 10 do echo $i done -------- monokai -------- # basic loop for i in 1 2 3 4 5 6 7 8 9 10 do  echo $i done -------- murphy -------- # basic loop for i in 1 2 3 4 5 6 7 8 9 10 do echo $i done -------- native -------- # basic loop for i in 1 2 3 4 5 6 7 8 9 10 do  echo $i done -------- paraiso-dark -------- # basic loop for i in 1 2 3 4 5 6 7 8 9 10 do  echo $i done -------- paraiso-light -------- # basic loop for i in 1 2 3 4 5 6 7 8 9 10 do  echo $i done -------- pastie -------- # basic loop for i in 1 2 3 4 5 6 7 8 9 10 do echo $i done -------- perldoc -------- # basic loop for i in 1 2 3 4 5 6 7 8 9 10 do echo $i done -------- rainbow_dash -------- # basic loop for i in 1 2 3 4 5 6 7 8 9 10 do  echo $i done -------- rrt -------- # basic loop for i in 1 2 3 4 5 6 7 8 9 10 do echo $i done -------- tango -------- # basic loop for i in 1 2 3 4 5 6 7 8 9 10 do echo $i done -------- trac -------- # basic loop for i in 1 2 3 4 5 6 7 8 9 10 do echo $i done -------- vim -------- # basic loop for i in 1 2 3 4 5 6 7 8 9 10 do  echo $i done -------- vs -------- # basic loop for i in 1 2 3 4 5 6 7 8 9 10 do echo $i done -------- xcode -------- # basic loop for i in 1 2 3 4 5 6 7 8 9 10 do echo $i done ------------------------------------ You can test color styles with other cheat sheets using this script: c="python/Advanced" for s in $(curl -s cheat.sh/:styles); do echo echo -------- echo $s echo -------- curl -s cheat.sh/$c?style=$s | head -5 done ================================================ FILE: share/vim/.vimrc ================================================ filetype plugin on set expandtab set sts=4 set ts=4 set sw=4 set smartindent syn on colorscheme desert map ds :.,/^--/-1 d set nojs set viminfo='10,\"100,:20,%,n~/.viminfo let g:NERDSpaceDelims = 1 let g:NERDCommentEmptyLines = 1 ================================================ FILE: share/vim.txt ================================================ " To use cheat.sh from your vim, install the cheat.sh-vim plugin from David Beniamine " located at https://github.com/dbeniamine/cheat.sh-vim. " See the page for the configuration options and the magic key combinations. " " ## Vizardry " " If you have Vizardry installed, you can run from vim: " " Invoke -u dbeniamine cheat.sh-vim " " ## Vundle " " Add the following to your Vundle Plugin list and do InstallPlugins: " " Plugin 'dbeniamine/cheat.sh-vim' " " ## Pathogen " " If you are using Pathogen, Run the following command in shell: " " git clone https://github.com/dbeniamine/cheat.sh-vim.git ~/.vim/bundle/cheat.sh-vim " " ## Quick install " " git clone https://github.com/dbeniamine/cheat.sh-vim.git " cd cheat.sh-vim/ " cp -r ./* ~/.vim ================================================ FILE: share/zsh.txt ================================================ #compdef cht.sh __CHTSH_LANGS=($(curl -s cheat.sh/:list)) _arguments -C \ '--help[show this help message and exit]: :->noargs' \ '--shell[enter shell repl]: :->noargs' \ '1:Cheat Sheet:->lang' \ '*::: :->noargs' && return 0 if [[ CURRENT -ge 1 ]]; then case $state in noargs) _message "nothing to complete";; lang) compadd -X "Cheat Sheets" ${__CHTSH_LANGS[@]};; *) _message "Unknown state, error in autocomplete";; esac return fi ================================================ FILE: tests/README.md ================================================ To run unit tests. python3 -m pytest -v ../lib/ To run input/output tests. ./run-tests.sh ================================================ FILE: tests/results/1 ================================================ 1_Inheritance 1line 2_Multiple_Inheritance :learn :list Advanced Classes Comments Control_Flow_and_Iterables Functions Modules Primitive_Datatypes_and_Operators Variables_and_Collections doc func hello lambda list_comprehension loops recursion rosetta/ ================================================ FILE: tests/results/10 ================================================ # How do I copy a file in Python? #  # shutil (http://docs.python.org/3/library/shutil.html) has many methods # you can use. One of which is: from shutil import copyfile copyfile(src, dst) # Copy the contents of the file named src to a file named dst. The # destination location must be writable; otherwise, an IOError exception # will be raised. If dst already exists, it will be replaced. Special # files such as character or block devices and pipes cannot be copied # with this function. src and dst are path names given as strings. #  # [Swati] [so/q/123198] [cc by-sa 3.0] ================================================ FILE: tests/results/11 ================================================ from shutil import copyfile copyfile(src, dst) ================================================ FILE: tests/results/12 ================================================ from shutil import copyfile copyfile(src, dst) ================================================ FILE: tests/results/13 ================================================ _ _ _ __    ___| |__ ___ __ _| |_ ___| |__ \ \      The only cheat sheet you need    / __| '_ \ / _ \/ _` | __| / __| '_ \ \ \  Unified access to the best     | (__| | | | __/ (_| | |_ _\__ \ | | |/ /  community driven documentation \___|_| |_|\___|\__,_|\__(_)___/_| |_/_/   repositories of the world      +------------------------+ +------------------------+ +------------------------+ | $ curl cheat.sh/ls     | | $ cht.sh btrfs         | | $ cht.sh lua/:learn | | $ curl cht.sh/btrfs    | | $ cht.sh tar~list      | | Learn any* programming | | $ curl cht.sh/tar~list | | | | language not leaving | | $ curl https://cht.sh  | | | | your shell | | | | | | *) any of 60 | | | | | | | +-- queries with curl ---+ +- own optional client --+ +- learn, learn, learn! -+ +------------------------+ +------------------------+ +------------------------+ | $ cht.sh go/f<tab><tab>| | $ cht.sh --shell | | $ cht.sh go zip lists | | go/for go/func | | cht.sh> help | | Ask any question using | | $ cht.sh go/for | | ... | | cht.sh or curl cht.sh: | | ... | | | | /go/zip+lists | | | | | | (use /,+ when curling) | | | | | | | +---- TAB-completion ----+ +-- interactive shell ---+ +- programming questions-+ +------------------------+ +------------------------+ +------------------------+ | $ curl cht.sh/:help | | $ vim prg.py | | $ time curl cht.sh/ | | see /:help and /:intro | | ... | | ... | | for usage information | | zip lists _ | | real 0m0.075s | | and README.md on GitHub| | <leader>KK | | | | for the details | | *awesome* | | | | *start here*| | | | | +--- self-documented ----+ +- queries from editor! -+ +---- instant answers ---+ [Follow @igor_chubin for updates][github.com/chubin/cheat.sh] ================================================ FILE: tests/results/14 ================================================ _ _ _ __    ___| |__ ___ __ _| |_ ___| |__ \ \      The only cheat sheet you need    / __| '_ \ / _ \/ _` | __| / __| '_ \ \ \  Unified access to the best     | (__| | | | __/ (_| | |_ _\__ \ | | |/ /  community driven documentation \___|_| |_|\___|\__,_|\__(_)___/_| |_/_/   repositories of the world      +------------------------+ +------------------------+ +------------------------+ | $ curl cheat.sh/ls     | | $ cht.sh btrfs         | | $ cht.sh lua/:learn | | $ curl cht.sh/btrfs    | | $ cht.sh tar~list      | | Learn any* programming | | $ curl cht.sh/tar~list | | | | language not leaving | | $ curl https://cht.sh  | | | | your shell | | | | | | *) any of 60 | | | | | | | +-- queries with curl ---+ +- own optional client --+ +- learn, learn, learn! -+ +------------------------+ +------------------------+ +------------------------+ | $ cht.sh go/f<tab><tab>| | $ cht.sh --shell | | $ cht.sh go zip lists | | go/for go/func | | cht.sh> help | | Ask any question using | | $ cht.sh go/for | | ... | | cht.sh or curl cht.sh: | | ... | | | | /go/zip+lists | | | | | | (use /,+ when curling) | | | | | | | +---- TAB-completion ----+ +-- interactive shell ---+ +- programming questions-+ +------------------------+ +------------------------+ +------------------------+ | $ curl cht.sh/:help | | $ vim prg.py | | $ time curl cht.sh/ | | see /:help and /:intro | | ... | | ... | | for usage information | | zip lists _ | | real 0m0.075s | | and README.md on GitHub| | <leader>KK | | | | for the details | | *awesome* | | | | *start here*| | | | | +--- self-documented ----+ +- queries from editor! -+ +---- instant answers ---+ [Follow @igor_chubin for updates][github.com/chubin/cheat.sh] ================================================ FILE: tests/results/15 ================================================ # Single line comments start with a number symbol. """ Multiline strings can be written  using three "s, and are often used  as documentation. """ #################################################### ## 1. Primitive Datatypes and Operators #################################################### # You have numbers 3 # => 3 # Math is what you would expect 1 + 1 # => 2 8 - 1 # => 7 10 * 2 # => 20 35 / 5 # => 7.0 # Integer division rounds down for both positive and negative numbers. 5 // 3 # => 1 -5 // 3 # => -2 5.0 // 3.0 # => 1.0 # works on floats too -5.0 // 3.0 # => -2.0 # The result of division is always a float 10.0 / 3 # => 3.3333333333333335 # Modulo operation 7 % 3 # => 1 # i % j have the same sign as j, unlike C -7 % 3 # => 2 # Exponentiation (x**y, x to the yth power) 2**3 # => 8 # Enforce precedence with parentheses 1 + 3 * 2 # => 7 (1 + 3) * 2 # => 8 # Boolean values are primitives (Note: the capitalization) True # => True False # => False # negate with not not True # => False not False # => True # Boolean Operators # Note "and" and "or" are case-sensitive True and False # => False False or True # => True # True and False are actually 1 and 0 but with different keywords True + True # => 2 True * 8 # => 8 False - 5 # => -5 # Comparison operators look at the numerical value of True and False 0 == False # => True 1 == True # => True 2 == True # => False -5 != False # => True # Using boolean logical operators on ints casts them to booleans for evaluation, but their non-cast value is returned # Don't mix up with bool(ints) and bitwise and/or (&,|) bool(0) # => False bool(4) # => True bool(-6) # => True 0 and 2 # => 0 -5 or 0 # => -5 # Equality is == 1 == 1 # => True 2 == 1 # => False # Inequality is != 1 != 1 # => False 2 != 1 # => True # More comparisons 1 < 10 # => True 1 > 10 # => False 2 <= 2 # => True 2 >= 2 # => True # Seeing whether a value is in a range 1 < 2 and 2 < 3 # => True 2 < 3 and 3 < 2 # => False # Chaining makes this look nicer 1 < 2 < 3 # => True 2 < 3 < 2 # => False # (is vs. ==) is checks if two variables refer to the same object, but == checks # if the objects pointed to have the same values. a = [1, 2, 3, 4] # Point a at a new list, [1, 2, 3, 4] b = a # Point b at what a is pointing to b is a # => True, a and b refer to the same object b == a # => True, a's and b's objects are equal b = [1, 2, 3, 4] # Point b at a new list, [1, 2, 3, 4] b is a # => False, a and b do not refer to the same object b == a # => True, a's and b's objects are equal # Strings are created with " or ' "This is a string." 'This is also a string.' # Strings can be added too "Hello " + "world!" # => "Hello world!" # String literals (but not variables) can be concatenated without using '+' "Hello " "world!" # => "Hello world!" # A string can be treated like a list of characters "Hello world!"[0] # => 'H' # You can find the length of a string len("This is a string") # => 16 # You can also format using f-strings or formatted string literals (in Python 3.6+) name = "Reiko" f"She said her name is {name}." # => "She said her name is Reiko" # You can basically put any Python expression inside the braces and it will be output in the string. f"{name} is {len(name)} characters long." # => "Reiko is 5 characters long." # None is an object None # => None # Don't use the equality "==" symbol to compare objects to None # Use "is" instead. This checks for equality of object identity. "etc" is None # => False None is None # => True # None, 0, and empty strings/lists/dicts/tuples all evaluate to False. # All other values are True bool(0) # => False bool("") # => False bool([]) # => False bool({}) # => False bool(()) # => False #################################################### ## 2. Variables and Collections #################################################### # Python has a print function print("I'm Python. Nice to meet you!") # => I'm Python. Nice to meet you! # By default the print function also prints out a newline at the end. # Use the optional argument end to change the end string. print("Hello, World", end="!") # => Hello, World! # Simple way to get input data from console input_string_var = input("Enter some data: ") # Returns the data as a string # There are no declarations, only assignments. # Convention is to use lower_case_with_underscores some_var = 5 some_var # => 5 # Accessing a previously unassigned variable is an exception. # See Control Flow to learn more about exception handling. some_unknown_var # Raises a NameError # if can be used as an expression # Equivalent of C's '?:' ternary operator "yay!" if 0 > 1 else "nay!" # => "nay!" # Lists store sequences li = [] # You can start with a prefilled list other_li = [4, 5, 6] # Add stuff to the end of a list with append li.append(1) # li is now [1] li.append(2) # li is now [1, 2] li.append(4) # li is now [1, 2, 4] li.append(3) # li is now [1, 2, 4, 3] # Remove from the end with pop li.pop() # => 3 and li is now [1, 2, 4] # Let's put it back li.append(3) # li is now [1, 2, 4, 3] again. # Access a list like you would any array li[0] # => 1 # Look at the last element li[-1] # => 3 # Looking out of bounds is an IndexError li[4] # Raises an IndexError # You can look at ranges with slice syntax. # The start index is included, the end index is not # (It's a closed/open range for you mathy types.) li[1:3] # Return list from index 1 to 3 => [2, 4] li[2:] # Return list starting from index 2 => [4, 3] li[:3] # Return list from beginning until index 3 => [1, 2, 4] li[::2] # Return list selecting every second entry => [1, 4] li[::-1] # Return list in reverse order => [3, 4, 2, 1] # Use any combination of these to make advanced slices # li[start:end:step] # Make a one layer deep copy using slices li2 = li[:] # => li2 = [1, 2, 4, 3] but (li2 is li) will result in false. # Remove arbitrary elements from a list with "del" del li[2] # li is now [1, 2, 3] # Remove first occurrence of a value li.remove(2) # li is now [1, 3] li.remove(2) # Raises a ValueError as 2 is not in the list # Insert an element at a specific index li.insert(1, 2) # li is now [1, 2, 3] again # Get the index of the first item found matching the argument li.index(2) # => 1 li.index(4) # Raises a ValueError as 4 is not in the list # You can add lists # Note: values for li and for other_li are not modified. li + other_li # => [1, 2, 3, 4, 5, 6] # Concatenate lists with "extend()" li.extend(other_li) # Now li is [1, 2, 3, 4, 5, 6] # Check for existence in a list with "in" 1 in li # => True # Examine the length with "len()" len(li) # => 6 # Tuples are like lists but are immutable. tup = (1, 2, 3) tup[0] # => 1 tup[0] = 3 # Raises a TypeError # Note that a tuple of length one has to have a comma after the last element but # tuples of other lengths, even zero, do not. type((1)) # =>  type((1,)) # =>  type(()) # =>  # You can do most of the list operations on tuples too len(tup) # => 3 tup + (4, 5, 6) # => (1, 2, 3, 4, 5, 6) tup[:2] # => (1, 2) 2 in tup # => True # You can unpack tuples (or lists) into variables a, b, c = (1, 2, 3) # a is now 1, b is now 2 and c is now 3 # You can also do extended unpacking a, *b, c = (1, 2, 3, 4) # a is now 1, b is now [2, 3] and c is now 4 # Tuples are created by default if you leave out the parentheses d, e, f = 4, 5, 6 # tuple 4, 5, 6 is unpacked into variables d, e and f # respectively such that d = 4, e = 5 and f = 6 # Now look how easy it is to swap two values e, d = d, e # d is now 5 and e is now 4 # Dictionaries store mappings from keys to values empty_dict = {} # Here is a prefilled dictionary filled_dict = {"one": 1, "two": 2, "three": 3} # Note keys for dictionaries have to be immutable types. This is to ensure that # the key can be converted to a constant hash value for quick look-ups. # Immutable types include ints, floats, strings, tuples. invalid_dict = {[1,2,3]: "123"} # => Raises a TypeError: unhashable type: 'list' valid_dict = {(1,2,3):[1,2,3]} # Values can be of any type, however. # Look up values with [] filled_dict["one"] # => 1 # Get all keys as an iterable with "keys()". We need to wrap the call in list() # to turn it into a list. We'll talk about those later. Note - for Python # versions <3.7, dictionary key ordering is not guaranteed. Your results might # not match the example below exactly. However, as of Python 3.7, dictionary # items maintain the order at which they are inserted into the dictionary. list(filled_dict.keys()) # => ["three", "two", "one"] in Python <3.7 list(filled_dict.keys()) # => ["one", "two", "three"] in Python 3.7+ # Get all values as an iterable with "values()". Once again we need to wrap it # in list() to get it out of the iterable. Note - Same as above regarding key # ordering. list(filled_dict.values()) # => [3, 2, 1] in Python <3.7 list(filled_dict.values()) # => [1, 2, 3] in Python 3.7+ # Check for existence of keys in a dictionary with "in" "one" in filled_dict # => True 1 in filled_dict # => False # Looking up a non-existing key is a KeyError filled_dict["four"] # KeyError # Use "get()" method to avoid the KeyError filled_dict.get("one") # => 1 filled_dict.get("four") # => None # The get method supports a default argument when the value is missing filled_dict.get("one", 4) # => 1 filled_dict.get("four", 4) # => 4 # "setdefault()" inserts into a dictionary only if the given key isn't present filled_dict.setdefault("five", 5) # filled_dict["five"] is set to 5 filled_dict.setdefault("five", 6) # filled_dict["five"] is still 5 # Adding to a dictionary filled_dict.update({"four":4}) # => {"one": 1, "two": 2, "three": 3, "four": 4} filled_dict["four"] = 4 # another way to add to dict # Remove keys from a dictionary with del del filled_dict["one"] # Removes the key "one" from filled dict # From Python 3.5 you can also use the additional unpacking options {'a': 1, **{'b': 2}} # => {'a': 1, 'b': 2} {'a': 1, **{'a': 2}} # => {'a': 2} # Sets store ... well sets empty_set = set() # Initialize a set with a bunch of values. Yeah, it looks a bit like a dict. Sorry. some_set = {1, 1, 2, 2, 3, 4} # some_set is now {1, 2, 3, 4} # Similar to keys of a dictionary, elements of a set have to be immutable. invalid_set = {[1], 1} # => Raises a TypeError: unhashable type: 'list' valid_set = {(1,), 1} # Add one more item to the set filled_set = some_set filled_set.add(5) # filled_set is now {1, 2, 3, 4, 5} # Sets do not have duplicate elements filled_set.add(5) # it remains as before {1, 2, 3, 4, 5} # Do set intersection with & other_set = {3, 4, 5, 6} filled_set & other_set # => {3, 4, 5} # Do set union with | filled_set | other_set # => {1, 2, 3, 4, 5, 6} # Do set difference with - {1, 2, 3, 4} - {2, 3, 5} # => {1, 4} # Do set symmetric difference with ^ {1, 2, 3, 4} ^ {2, 3, 5} # => {1, 4, 5} # Check if set on the left is a superset of set on the right {1, 2} >= {1, 2, 3} # => False # Check if set on the left is a subset of set on the right {1, 2} <= {1, 2, 3} # => True # Check for existence in a set with in 2 in filled_set # => True 10 in filled_set # => False # Make a one layer deep copy filled_set = some_set.copy() # filled_set is {1, 2, 3, 4, 5} filled_set is some_set # => False #################################################### ## 3. Control Flow and Iterables #################################################### # Let's just make a variable some_var = 5 # Here is an if statement. Indentation is significant in Python! # Convention is to use four spaces, not tabs. # This prints "some_var is smaller than 10" if some_var > 10:  print("some_var is totally bigger than 10.") elif some_var < 10: # This elif clause is optional.  print("some_var is smaller than 10.") else: # This is optional too.  print("some_var is indeed 10.") """ For loops iterate over lists prints:  dog is a mammal  cat is a mammal  mouse is a mammal """ for animal in ["dog", "cat", "mouse"]:  # You can use format() to interpolate formatted strings  print("{} is a mammal".format(animal)) """ "range(number)" returns an iterable of numbers from zero to the given number prints:  0  1  2  3 """ for i in range(4):  print(i) """ "range(lower, upper)" returns an iterable of numbers from the lower number to the upper number prints:  4  5  6  7 """ for i in range(4, 8):  print(i) """ "range(lower, upper, step)" returns an iterable of numbers from the lower number to the upper number, while incrementing by step. If step is not indicated, the default value is 1. prints:  4  6 """ for i in range(4, 8, 2):  print(i) """ To loop over a list, and retrieve both the index and the value of each item in the list prints:  0 dog  1 cat  2 mouse """ animals = ["dog", "cat", "mouse"] for i, value in enumerate(animals):  print(i, value) """ While loops go until a condition is no longer met. prints:  0  1  2  3 """ x = 0 while x < 4:  print(x)  x += 1 # Shorthand for x = x + 1 # Handle exceptions with a try/except block try:  # Use "raise" to raise an error  raise IndexError("This is an index error") except IndexError as e:  pass # Pass is just a no-op. Usually you would do recovery here. except (TypeError, NameError):  pass # Multiple exceptions can be handled together, if required. else: # Optional clause to the try/except block. Must follow all except blocks  print("All good!") # Runs only if the code in try raises no exceptions finally: # Execute under all circumstances  print("We can clean up resources here") # Instead of try/finally to cleanup resources you can use a with statement with open("myfile.txt") as f:  for line in f:  print(line) # Writing to a file contents = {"aa": 12, "bb": 21} with open("myfile1.txt", "w+") as file:  file.write(str(contents)) # writes a string to a file with open("myfile2.txt", "w+") as file:  file.write(json.dumps(contents)) # writes an object to a file # Reading from a file with open('myfile1.txt', "r+") as file:  contents = file.read() # reads a string from a file print(contents) # print: {"aa": 12, "bb": 21} with open('myfile2.txt', "r+") as file:  contents = json.load(file) # reads a json object from a file print(contents) # print: {"aa": 12, "bb": 21} # Python offers a fundamental abstraction called the Iterable. # An iterable is an object that can be treated as a sequence. # The object returned by the range function, is an iterable. filled_dict = {"one": 1, "two": 2, "three": 3} our_iterable = filled_dict.keys() print(our_iterable) # => dict_keys(['one', 'two', 'three']). This is an object that implements our Iterable interface. # We can loop over it. for i in our_iterable:  print(i) # Prints one, two, three # However we cannot address elements by index. our_iterable[1] # Raises a TypeError # An iterable is an object that knows how to create an iterator. our_iterator = iter(our_iterable) # Our iterator is an object that can remember the state as we traverse through it. # We get the next object with "next()". next(our_iterator) # => "one" # It maintains state as we iterate. next(our_iterator) # => "two" next(our_iterator) # => "three" # After the iterator has returned all of its data, it raises a StopIteration exception next(our_iterator) # Raises StopIteration # We can also loop over it, in fact, "for" does this implicitly! our_iterator = iter(our_iterable) for i in our_iterator:  print(i) # Prints one, two, three # You can grab all the elements of an iterable or iterator by calling list() on it. list(our_iterable) # => Returns ["one", "two", "three"] list(our_iterator) # => Returns [] because state is saved #################################################### ## 4. Functions #################################################### # Use "def" to create new functions def add(x, y):  print("x is {} and y is {}".format(x, y))  return x + y # Return values with a return statement # Calling functions with parameters add(5, 6) # => prints out "x is 5 and y is 6" and returns 11 # Another way to call functions is with keyword arguments add(y=6, x=5) # Keyword arguments can arrive in any order. # You can define functions that take a variable number of # positional arguments def varargs(*args):  return args varargs(1, 2, 3) # => (1, 2, 3) # You can define functions that take a variable number of # keyword arguments, as well def keyword_args(**kwargs):  return kwargs # Let's call it to see what happens keyword_args(big="foot", loch="ness") # => {"big": "foot", "loch": "ness"} # You can do both at once, if you like def all_the_args(*args, **kwargs):  print(args)  print(kwargs) """ all_the_args(1, 2, a=3, b=4) prints:  (1, 2)  {"a": 3, "b": 4} """ # When calling functions, you can do the opposite of args/kwargs! # Use * to expand tuples and use ** to expand kwargs. args = (1, 2, 3, 4) kwargs = {"a": 3, "b": 4} all_the_args(*args) # equivalent to all_the_args(1, 2, 3, 4) all_the_args(**kwargs) # equivalent to all_the_args(a=3, b=4) all_the_args(*args, **kwargs) # equivalent to all_the_args(1, 2, 3, 4, a=3, b=4) # Returning multiple values (with tuple assignments) def swap(x, y):  return y, x # Return multiple values as a tuple without the parenthesis.  # (Note: parenthesis have been excluded but can be included) x = 1 y = 2 x, y = swap(x, y) # => x = 2, y = 1 # (x, y) = swap(x,y) # Again parenthesis have been excluded but can be included. # Function Scope x = 5 def set_x(num):  # Local var x not the same as global variable x  x = num # => 43  print(x) # => 43 def set_global_x(num):  global x  print(x) # => 5  x = num # global var x is now set to 6  print(x) # => 6 set_x(43) set_global_x(6) # Python has first class functions def create_adder(x):  def adder(y):  return x + y  return adder add_10 = create_adder(10) add_10(3) # => 13 # There are also anonymous functions (lambda x: x > 2)(3) # => True (lambda x, y: x ** 2 + y ** 2)(2, 1) # => 5 # There are built-in higher order functions list(map(add_10, [1, 2, 3])) # => [11, 12, 13] list(map(max, [1, 2, 3], [4, 2, 1])) # => [4, 2, 3] list(filter(lambda x: x > 5, [3, 4, 5, 6, 7])) # => [6, 7] # We can use list comprehensions for nice maps and filters # List comprehension stores the output as a list which can itself be a nested list [add_10(i) for i in [1, 2, 3]] # => [11, 12, 13] [x for x in [3, 4, 5, 6, 7] if x > 5] # => [6, 7] # You can construct set and dict comprehensions as well. {x for x in 'abcddeef' if x not in 'abc'} # => {'d', 'e', 'f'} {x: x**2 for x in range(5)} # => {0: 0, 1: 1, 2: 4, 3: 9, 4: 16} #################################################### ## 5. Modules #################################################### # You can import modules import math print(math.sqrt(16)) # => 4.0 # You can get specific functions from a module from math import ceil, floor print(ceil(3.7)) # => 4.0 print(floor(3.7)) # => 3.0 # You can import all functions from a module. # Warning: this is not recommended from math import * # You can shorten module names import math as m math.sqrt(16) == m.sqrt(16) # => True # Python modules are just ordinary Python files. You # can write your own, and import them. The name of the # module is the same as the name of the file. # You can find out which functions and attributes # are defined in a module. import math dir(math) # If you have a Python script named math.py in the same # folder as your current script, the file math.py will # be loaded instead of the built-in Python module. # This happens because the local folder has priority # over Python's built-in libraries. #################################################### ## 6. Classes #################################################### # We use the "class" statement to create a class class Human:  # A class attribute. It is shared by all instances of this class  species = "H. sapiens"  # Basic initializer, this is called when this class is instantiated.  # Note that the double leading and trailing underscores denote objects  # or attributes that are used by Python but that live in user-controlled  # namespaces. Methods(or objects or attributes) like: __init__, __str__,  # __repr__ etc. are called special methods (or sometimes called dunder methods)  # You should not invent such names on your own.  def __init__(self, name):  # Assign the argument to the instance's name attribute  self.name = name  # Initialize property  self._age = 0  # An instance method. All methods take "self" as the first argument  def say(self, msg):  print("{name}: {message}".format(name=self.name, message=msg))  # Another instance method  def sing(self):  return 'yo... yo... microphone check... one two... one two...'  # A class method is shared among all instances  # They are called with the calling class as the first argument  @classmethod  def get_species(cls):  return cls.species  # A static method is called without a class or instance reference  @staticmethod  def grunt():  return "*grunt*"  # A property is just like a getter.  # It turns the method age() into a read-only attribute of the same name.  # There's no need to write trivial getters and setters in Python, though.  @property  def age(self):  return self._age  # This allows the property to be set  @age.setter  def age(self, age):  self._age = age  # This allows the property to be deleted  @age.deleter  def age(self):  del self._age # When a Python interpreter reads a source file it executes all its code. # This __name__ check makes sure this code block is only executed when this # module is the main program. if __name__ == '__main__':  # Instantiate a class  i = Human(name="Ian")  i.say("hi") # "Ian: hi"  j = Human("Joel")  j.say("hello") # "Joel: hello"  # i and j are instances of type Human, or in other words: they are Human objects  # Call our class method  i.say(i.get_species()) # "Ian: H. sapiens"  # Change the shared attribute  Human.species = "H. neanderthalensis"  i.say(i.get_species()) # => "Ian: H. neanderthalensis"  j.say(j.get_species()) # => "Joel: H. neanderthalensis"  # Call the static method  print(Human.grunt()) # => "*grunt*"  # Static methods can be called by instances too  print(i.grunt()) # => "*grunt*"  # Update the property for this instance  i.age = 42  # Get the property  i.say(i.age) # => "Ian: 42"  j.say(j.age) # => "Joel: 0"  # Delete the property  del i.age  # i.age # => this would raise an AttributeError #################################################### ## 6.1 Inheritance #################################################### # Inheritance allows new child classes to be defined that inherit methods and # variables from their parent class. # Using the Human class defined above as the base or parent class, we can # define a child class, Superhero, which inherits the class variables like # "species", "name", and "age", as well as methods, like "sing" and "grunt" # from the Human class, but can also have its own unique properties. # To take advantage of modularization by file you could place the classes above in their own files, # say, human.py # To import functions from other files use the following format # from "filename-without-extension" import "function-or-class" from human import Human # Specify the parent class(es) as parameters to the class definition class Superhero(Human):  # If the child class should inherit all of the parent's definitions without  # any modifications, you can just use the "pass" keyword (and nothing else)  # but in this case it is commented out to allow for a unique child class:  # pass  # Child classes can override their parents' attributes  species = 'Superhuman'  # Children automatically inherit their parent class's constructor including  # its arguments, but can also define additional arguments or definitions  # and override its methods such as the class constructor.  # This constructor inherits the "name" argument from the "Human" class and  # adds the "superpower" and "movie" arguments:  def __init__(self, name, movie=False,  superpowers=["super strength", "bulletproofing"]):  # add additional class attributes:  self.fictional = True  self.movie = movie  # be aware of mutable default values, since defaults are shared  self.superpowers = superpowers  # The "super" function lets you access the parent class's methods  # that are overridden by the child, in this case, the __init__ method.  # This calls the parent class constructor:  super().__init__(name)  # override the sing method  def sing(self):  return 'Dun, dun, DUN!'  # add an additional instance method  def boast(self):  for power in self.superpowers:  print("I wield the power of {pow}!".format(pow=power)) if __name__ == '__main__':  sup = Superhero(name="Tick")  # Instance type checks  if isinstance(sup, Human):  print('I am human')  if type(sup) is Superhero:  print('I am a superhero')  # Get the Method Resolution search Order used by both getattr() and super()  # This attribute is dynamic and can be updated  print(Superhero.__mro__) # => (,  # => , )  # Calls parent method but uses its own class attribute  print(sup.get_species()) # => Superhuman  # Calls overridden method  print(sup.sing()) # => Dun, dun, DUN!  # Calls method from Human  sup.say('Spoon') # => Tick: Spoon  # Call method that exists only in Superhero  sup.boast() # => I wield the power of super strength!  # => I wield the power of bulletproofing!  # Inherited class attribute  sup.age = 31  print(sup.age) # => 31  # Attribute that only exists within Superhero  print('Am I Oscar eligible? ' + str(sup.movie)) #################################################### ## 6.2 Multiple Inheritance #################################################### # Another class definition # bat.py class Bat:  species = 'Baty'  def __init__(self, can_fly=True):  self.fly = can_fly  # This class also has a say method  def say(self, msg):  msg = '... ... ...'  return msg  # And its own method as well  def sonar(self):  return '))) ... (((' if __name__ == '__main__':  b = Bat()  print(b.say('hello'))  print(b.fly) # And yet another class definition that inherits from Superhero and Bat # superhero.py from superhero import Superhero from bat import Bat # Define Batman as a child that inherits from both Superhero and Bat class Batman(Superhero, Bat):  def __init__(self, *args, **kwargs):  # Typically to inherit attributes you have to call super:  # super(Batman, self).__init__(*args, **kwargs)  # However we are dealing with multiple inheritance here, and super()  # only works with the next base class in the MRO list.  # So instead we explicitly call __init__ for all ancestors.  # The use of *args and **kwargs allows for a clean way to pass arguments,  # with each parent "peeling a layer of the onion".  Superhero.__init__(self, 'anonymous', movie=True,  superpowers=['Wealthy'], *args, **kwargs)  Bat.__init__(self, *args, can_fly=False, **kwargs)  # override the value for the name attribute  self.name = 'Sad Affleck'  def sing(self):  return 'nan nan nan nan nan batman!' if __name__ == '__main__':  sup = Batman()  # Get the Method Resolution search Order used by both getattr() and super().  # This attribute is dynamic and can be updated  print(Batman.__mro__) # => (,  # => ,  # => ,  # => , )  # Calls parent method but uses its own class attribute  print(sup.get_species()) # => Superhuman  # Calls overridden method  print(sup.sing()) # => nan nan nan nan nan batman!  # Calls method from Human, because inheritance order matters  sup.say('I agree') # => Sad Affleck: I agree  # Call method that exists only in 2nd ancestor  print(sup.sonar()) # => ))) ... (((  # Inherited class attribute  sup.age = 100  print(sup.age) # => 100  # Inherited attribute from 2nd ancestor whose default value was overridden.  print('Can I fly? ' + str(sup.fly)) # => Can I fly? False #################################################### ## 7. Advanced #################################################### # Generators help you make lazy code. def double_numbers(iterable):  for i in iterable:  yield i + i # Generators are memory-efficient because they only load the data needed to # process the next value in the iterable. This allows them to perform # operations on otherwise prohibitively large value ranges. # NOTE: `range` replaces `xrange` in Python 3. for i in double_numbers(range(1, 900000000)): # `range` is a generator.  print(i)  if i >= 30:  break # Just as you can create a list comprehension, you can create generator # comprehensions as well. values = (-x for x in [1,2,3,4,5]) for x in values:  print(x) # prints -1 -2 -3 -4 -5 to console/terminal # You can also cast a generator comprehension directly to a list. values = (-x for x in [1,2,3,4,5]) gen_to_list = list(values) print(gen_to_list) # => [-1, -2, -3, -4, -5] # Decorators # In this example `beg` wraps `say`. If say_please is True then it # will change the returned message. from functools import wraps def beg(target_function):  @wraps(target_function)  def wrapper(*args, **kwargs):  msg, say_please = target_function(*args, **kwargs)  if say_please:  return "{} {}".format(msg, "Please! I am poor :(")  return msg  return wrapper @beg def say(say_please=False):  msg = "Can you buy me a beer?"  return msg, say_please print(say()) # Can you buy me a beer? print(say(say_please=True)) # Can you buy me a beer? Please! I am poor :( ================================================ FILE: tests/results/16 ================================================ # Latency numbers every programmer should know  1ns Main memory reference: Send 2,000 bytes Read 1,000,000 bytes ▗▖ 100ns over commodity network: sequentially from SSD: ▗▖ 31ns 38.876us L1 cache reference: 1ns ▗ ▗  ▗▖ 1.0us ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ SSD random read: 16.0us Disk seek: 2.332582ms Branch mispredict: 3ns  ▗▖▗ ▗▖▗▖  ▗▖▗▖▗▖  Compress 1KB wth Snappy: Read 1,000,000 bytes Read 1,000,000 bytes L2 cache reference: 4ns 2.0us sequentially from memory: sequentially from disk: ▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ 2.355us 717.936us ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗ ▗  Mutex lock/unlock: 16ns   ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  Round trip Packet roundtrip ▗▖▗▖▗▖▗▖▗▖▗▖▗ 10.0us = ▗▖ in same datacenter: 500.0us CA to Netherlands: 150.0ms ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  100ns = ▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ 1.0ms = ▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖    ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  # [github.com/chubin/late.nz] [MIT License] ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  # Console port of "Jeff Dean's latency numbers" ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖   # from [github.com/colin-scott/interactive_latencies] ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖   ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  ================================================ FILE: tests/results/17 ================================================  cheat.sheets:az  # Microsoft Azure CLI 2.0 # Command-line tools for Azure # Install Azure CLI 2.0 with one curl command. curl -L https://aka.ms/InstallAzureCli | bash # create a resource group named "MyRG" in the 'westus2' region az group create -n MyRG -l westus2 # create a Linux VM using the UbuntuTLS image, # with two attached storage disks of 10 GB and 20 GB az vm create -n MyLinuxVM -g MyRG \  --ssh-key-value $HOME/.ssh/id_rsa.pub --image UbuntuLTS \  --data-disk-sizes-gb 10 20 # list VMs az vm list --output table # list only VMs having distinct state az vm list -d --query "[?powerState=='VM running']" --output table # delete VM (with the name MyLinuxVM in the group MyRG) az vm delete -g MyRG -n MyLinuxVM --yes # Delete all VMs in a resource group az vm delete --ids $(az vm list -g MyRG --query "[].id" -o tsv) # Create an Image based on a running VM az vm deallocate -g MyRG -n MyLinuxVM az vm generalize -g MyRG -n MyLinuxVM az image create --resource-group MyRG --name MyTestImage --source MyLinuxVM # Running VM based on a VHD az storage blob upload --account-name "${account_name}" \  --account-key "${account_key}" --container-name "${container}" --type page \  --file "${file}" --name "${vhd}" az disk create \  --resource-group "${resource_group}" \  --name myManagedDisk \  --source "https://${account_name}.blob.core.windows.net/${container}/${vhd}" # open port az vm open-port --resource-group MyRG --name MyLinuxVM --port 443 --priority 899 # Show storage accounts az storage account list --output table # Show containers for an account az storage container list --account-name mystorageaccount --output table # Show blobs in a container az storage blob list --account-name mystorageaccount \  --container-name mycontainer --output table # list account keys az storage account keys list --account-name STORAGE_NAME --resource-group RESOURCE_GROUP # Show own images az image list --output table # Configure default storage location az configure --defaults location=eastus2 # Show disks az disk list --output table # Copy blob az storage blob copy start \  --source-uri 'https://xxx.blob.core.windows.net/jzwuuuzzapn0/abcd?...' \  --account-key XXXXXXXXXX== \  --account-name destaccount \  --destination-container vms \  --destination-blob DESTINATION-blob.vhd # List virtual networks az network vnet list --output table # List virtual networks adapters az network nic list --output table # List public IP addresses used by the VMs az vm list-ip-addresses --output table # create snapshot az snapshot create --resource-group IC-EXASOL-001 --source vm1-disk1 -n vm1-snap1 # create SAS url for a snapshot az snapshot grant-access --resource-group IC-EXASOL-001 --name vm1-snap1\  --duration-in-seconds 36000 --query '[accessSas]' -o tsv # attach disk az vm disk attach --vm-name vm1 -g RESOURCE_GROUP --disk DISK1_ID # detach disk az vm disk detach --vm-name vm1 -g RESOURCE_GROUP --name DISK1_ID  tldr:az  # az # The official CLI tool for Microsoft Azure. # Some subcommands such as `az login` have their own usage documentation. # More information: . # Log in to Azure: az login # Manage azure subscription information: az account # List all Azure Managed Disks: az disk list # List all Azure virtual machines: az vm list # Manage Azure Kubernetes Services: az aks # Manage Azure Network resources: az network ================================================ FILE: tests/results/18 ================================================ >>> s = 'abcdefgh' >>> n, m, char, chars = 2, 3, 'd', 'cd' >>> # starting from n=2 characters in and m=3 in length; >>> s[n-1:n+m-1] 'bcd' >>> # starting from n characters in, up to the end of the string; >>> s[n-1:] 'bcdefgh' >>> # whole string minus last character; >>> s[:-1] 'abcdefg' >>> # starting from a known character char="d" within the string and of m length; >>> indx = s.index(char) >>> s[indx:indx+m] 'def' >>> # starting from a known substring chars="cd" within the string and of m length. >>> indx = s.index(chars) >>> s[indx:indx+m] 'cde' >>> ================================================ FILE: tests/results/19 ================================================ >>> s = 'abcdefgh' >>> n, m, char, chars = 2, 3, 'd', 'cd' >>> # starting from n=2 characters in and m=3 in length; >>> s[n-1:n+m-1] 'bcd' >>> # starting from n characters in, up to the end of the string; >>> s[n-1:] 'bcdefgh' >>> # whole string minus last character; >>> s[:-1] 'abcdefg' >>> # starting from a known character char="d" within the string and of m length; >>> indx = s.index(char) >>> s[indx:indx+m] 'def' >>> # starting from a known substring chars="cd" within the string and of m length. >>> indx = s.index(chars) >>> s[indx:indx+m] 'cde' >>> ================================================ FILE: tests/results/2 ================================================  cheat:ls  # To display everything in , excluding hidden files: ls <dir> # To display everything in , including hidden files: ls -a <dir> # To display all files, along with the size (with unit suffixes) and timestamp ls -lh <dir> # To display files, sorted by size: ls -S <dir> # To display directories only: ls -d */ <dir> # To display directories only, include hidden: ls -d .*/ */ <dir>  tldr:ls  # ls # List directory contents. # More information: . # List files one per line: ls -1 # List all files, including hidden files: ls -a # List all files, with trailing `/` added to directory names: ls -F # Long format list (permissions, ownership, size, and modification date) of all files: ls -la # Long format list with size displayed using human-readable units (KiB, MiB, GiB): ls -lh # Long format list sorted by size (descending): ls -lS # Long format list of all files, sorted by modification date (oldest first): ls -ltr # Only list directories: ls -d */ ================================================ FILE: tests/results/20 ================================================ 00DESCRIPTION 100-doors 24-game 24-game-Solve 9-billion-names-of-God-the-integer 99-Bottles-of-Beer A+B ABC-Problem AKS-test-for-primes Abstract-type Abundant,-deficient-and-perfect-number-classifications Accumulator-factory Ackermann-function Active-Directory-Connect Active-Directory-Search-for-a-user Active-object Add-a-variable-to-a-class-instance-at-runtime Address-of-a-variable Align-columns Aliquot-sequence-classifications Almost-prime Amb Amicable-pairs Anagrams Anagrams-Deranged-anagrams Animate-a-pendulum Animation Anonymous-recursion Append-a-record-to-the-end-of-a-text-file Apply-a-callback-to-an-array Arbitrary-precision-integers--included- Arithmetic-Complex Arithmetic-Integer Arithmetic-Rational Arithmetic-evaluation Arithmetic-geometric-mean Arithmetic-geometric-mean-Calculate-Pi Array-concatenation Arrays Assertions Associative-array-Creation Associative-array-Iteration Atomic-updates Average-loop-length Averages-Arithmetic-mean Averages-Mean-angle Averages-Mean-time-of-day Averages-Median Averages-Mode Averages-Pythagorean-means Averages-Root-mean-square Averages-Simple-moving-average Balanced-brackets Balanced-ternary Benfords-law Bernoulli-numbers Best-shuffle Binary-digits Binary-search Binary-strings Bitcoin-address-validation Bitcoin-public-point-to-address Bitmap-B-zier-curves-Cubic Bitmap-Bresenhams-line-algorithm Bitmap-Flood-fill Bitmap-Histogram Bitmap-Midpoint-circle-algorithm Bitmap-PPM-conversion-through-a-pipe Bitmap-Read-a-PPM-file Bitmap-Read-an-image-through-a-pipe Bitmap-Write-a-PPM-file Bitwise-IO Bitwise-operations Boolean-values Box-the-compass Break-OO-privacy Brownian-tree Bulls-and-cows Bulls-and-cows-Player CRC-32 CSV-data-manipulation CSV-to-HTML-translation Caesar-cipher Calendar Calendar---for-REAL-programmers Call-a-foreign-language-function Call-a-function Call-a-function-in-a-shared-library Call-an-object-method Canny-edge-detector Carmichael-3-strong-pseudoprimes Case-sensitivity-of-identifiers Casting-out-nines Catalan-numbers Catalan-numbers-Pascals-triangle Catamorphism Catmull-Clark-subdivision-surface Character-codes Chat-server Check-Machin-like-formulas Check-that-file-exists Checkpoint-synchronization Chinese-remainder-theorem Cholesky-decomposition Circles-of-given-radius-through-two-points Classes Closest-pair-problem Closures-Value-capture Collections Color-of-a-screen-pixel Color-quantization Colour-bars-Display Colour-pinstripe-Display Colour-pinstripe-Printer Combinations Combinations-and-permutations Combinations-with-repetitions Comma-quibbling Command-line-arguments Comments Compare-sorting-algorithms-performance Compound-data-type Concurrent-computing Conditional-structures Conjugate-transpose Constrained-random-points-on-a-circle Continued-fraction Continued-fraction-Arithmetic-Construct-from-rational-number Continued-fraction-Arithmetic-G-matrix-NG,-Contined-Fraction-N- Convert-decimal-number-to-rational Conways-Game-of-Life Copy-a-string Count-in-factors Count-in-octal Count-occurrences-of-a-substring Count-the-coins Create-a-file Create-a-file-on-magnetic-tape Create-a-two-dimensional-array-at-runtime Create-an-HTML-table Currying Cut-a-rectangle Date-format Date-manipulation Day-of-the-week Deal-cards-for-FreeCell Death-Star Deconvolution-1D Deconvolution-2D+ Deepcopy Define-a-primitive-data-type Delegates Delete-a-file Detect-division-by-zero Determine-if-a-string-is-numeric Determine-if-only-one-instance-is-running Digital-root Digital-root-Multiplicative-digital-root Dinesmans-multiple-dwelling-problem Dining-philosophers Discordian-date Distributed-programming Documentation Dot-product Doubly-linked-list-Element-definition Doubly-linked-list-Element-insertion Doubly-linked-list-Traversal Dragon-curve Draw-a-clock Draw-a-cuboid Draw-a-sphere Dutch-national-flag-problem Dynamic-variable-names Echo-server Element-wise-operations Empty-directory Empty-string Enforced-immutability Entropy Enumerations Environment-variables Equilibrium-index Ethiopian-multiplication Euler-method Evaluate-binomial-coefficients Even-or-odd Events Evolutionary-algorithm Exceptions Exceptions-Catch-an-exception-thrown-in-a-nested-call Executable-library Execute-SNUSP Execute-a-Markov-algorithm Execute-a-system-command Exponentiation-operator Extensible-prime-generator Extreme-floating-point-values Factorial Factors-of-a-Mersenne-number Factors-of-an-integer Fast-Fourier-transform Fibonacci-n-step-number-sequences Fibonacci-sequence Fibonacci-word Fibonacci-word-fractal File-input-output File-modification-time File-size Filter Find-common-directory-path Find-largest-left-truncatable-prime-in-a-given-base Find-limit-of-recursion Find-the-last-Sunday-of-each-month Find-the-missing-permutation First-class-environments First-class-functions First-class-functions-Use-numbers-analogously Five-weekends FizzBuzz Flatten-a-list Flipping-bits-game Flow-control-structures Floyds-triangle Forest-fire Fork Formal-power-series Formatted-numeric-output Forward-difference Four-bit-adder Fractal-tree Fractran Function-composition Function-definition Function-frequency GUI-Maximum-window-dimensions GUI-component-interaction GUI-enabling-disabling-of-controls Galton-box-animation Gamma-function Gaussian-elimination Generate-Chess960-starting-position Generate-lower-case-ASCII-alphabet Generator-Exponential Generic-swap Globally-replace-text-in-several-files Gray-code Grayscale-image Greatest-common-divisor Greatest-element-of-a-list Greatest-subsequential-sum Greyscale-bars-Display Guess-the-number Guess-the-number-With-feedback Guess-the-number-With-feedback--player- HTTP HTTPS HTTPS-Authenticated HTTPS-Client-authenticated Hailstone-sequence Hamming-numbers Handle-a-signal Happy-numbers Harshad-or-Niven-series Hash-from-two-arrays Hash-join Haversine-formula Hello-world-Graphical Hello-world-Line-printer Hello-world-Newbie Hello-world-Newline-omission Hello-world-Standard-error Hello-world-Text Hello-world-Web-server Here-document Heronian-triangles Hickerson-series-of-almost-integers Higher-order-functions History-variables Hofstadter-Conway-$10,000-sequence Hofstadter-Figure-Figure-sequences Hofstadter-Q-sequence Holidays-related-to-Easter Horizontal-sundial-calculations Horners-rule-for-polynomial-evaluation Host-introspection Hostname Hough-transform Huffman-coding I-before-E-except-after-C IBAN Identity-matrix Image-convolution Image-noise Include-a-file Increment-a-numerical-string Infinity Inheritance-Multiple Inheritance-Single Input-loop Integer-comparison Integer-overflow Integer-sequence Interactive-programming Introspection Inverted-index Inverted-syntax Iterated-digits-squaring JSON Jensens-Device Josephus-problem Joystick-position Jump-anywhere K-d-tree K-means++-clustering Kaprekar-numbers Keyboard-input-Flush-the-keyboard-buffer Keyboard-input-Keypress-check Keyboard-input-Obtain-a-Y-or-N-response Keyboard-macros Knapsack-problem-0-1 Knapsack-problem-Bounded Knapsack-problem-Continuous Knights-tour Knuth-shuffle Knuths-algorithm-S LU-decomposition LZW-compression Langtons-ant Largest-int-from-concatenated-ints Last-Friday-of-each-month Last-letter-first-letter Leap-year Least-common-multiple Left-factorials Letter-frequency Levenshtein-distance Linear-congruential-generator List-comprehensions Literals-Floating-point Literals-Integer Literals-String Logical-operations Long-multiplication Longest-common-subsequence Longest-increasing-subsequence Longest-string-challenge Look-and-say-sequence Loop-over-multiple-arrays-simultaneously Loops-Break Loops-Continue Loops-Do-while Loops-Downward-for Loops-For Loops-For-with-a-specified-step Loops-Foreach Loops-Infinite Loops-N-plus-one-half Loops-Nested Loops-While Lucas-Lehmer-test Ludic-numbers Luhn-test-of-credit-card-numbers MD4 MD5 MD5-Implementation Machine-code Mad-Libs Magic-squares-of-odd-order Main-step-of-GOST-28147-89 Make-directory-path Man-or-boy-test Mandelbrot-set Map-range Matrix-arithmetic Matrix-exponentiation-operator Matrix-multiplication Matrix-transposition Maximum-triangle-path-sum Maze-generation Maze-solving Median-filter Memory-allocation Memory-layout-of-a-data-structure Menu Metaprogramming Metered-concurrency Metronome Middle-three-digits Minesweeper-game Modular-exponentiation Modular-inverse Monte-Carlo-methods Monty-Hall-problem Morse-code Mouse-position Move-to-front-algorithm Multifactorial Multiple-distinct-objects Multiple-regression Multiplication-tables Multiplicative-order Multisplit Munching-squares Mutual-recursion N-queens-problem Named-parameters Narcissist Narcissistic-decimal-number Natural-sorting Nautical-bell Non-continuous-subsequences Non-decimal-radices-Convert Non-decimal-radices-Input Non-decimal-radices-Output Nth Nth-root Null-object Number-reversal-game Numeric-error-propagation Numerical-integration Numerical-integration-Gauss-Legendre-Quadrature OLE-Automation Object-serialization Odd-word-problem Old-lady-swallowed-a-fly One-dimensional-cellular-automata One-of-n-lines-in-a-file OpenGL Optional-parameters Order-disjoint-list-items Order-two-numerical-lists Ordered-Partitions Ordered-words Palindrome-detection Pangram-checker Paraffins Parallel-calculations Parametrized-SQL-statement Parse-an-IP-Address Parsing-RPN-calculator-algorithm Parsing-RPN-to-infix-conversion Parsing-Shunting-yard-algorithm Partial-function-application Pascals-triangle Pascals-triangle-Puzzle Penneys-game Percentage-difference-between-images Percolation-Bond-percolation Percolation-Mean-cluster-density Percolation-Mean-run-density Percolation-Site-percolation Perfect-numbers Permutation-test Permutations Permutations-Derangements Permutations-Rank-of-a-permutation Permutations-by-swapping Pernicious-numbers Phrase-reversals Pi Pick-random-element Pig-the-dice-game Pig-the-dice-game-Player Pinstripe-Display Play-recorded-sounds Playing-cards Plot-coordinate-pairs Pointers-and-references Polymorphic-copy Polymorphism Polynomial-long-division Polynomial-regression Power-set Pragmatic-directives Price-fraction Primality-by-trial-division Prime-decomposition Priority-queue Probabilistic-choice Problem-of-Apollonius Program-name Program-termination Pythagorean-triples QR-decomposition Quaternion-type Queue-Definition Queue-Usage Quickselect-algorithm Quine README RIPEMD-160 RSA-code Random-number-generator--device- Random-numbers Range-expansion Range-extraction Ranking-methods Rate-counter Ray-casting-algorithm Read-a-configuration-file Read-a-file-line-by-line Read-a-specific-line-from-a-file Read-entire-file Real-constants-and-functions Record-sound Reduced-row-echelon-form Regular-expressions Remove-duplicate-elements Remove-lines-from-a-file Rename-a-file Rep-string Repeat-a-string Resistor-mesh Respond-to-an-unknown-method-call Return-multiple-values Reverse-a-string Reverse-words-in-a-string Rock-paper-scissors Roman-numerals-Decode Roman-numerals-Encode Roots-of-a-function Roots-of-a-quadratic-function Roots-of-unity Rot-13 Run-length-encoding Runge-Kutta-method Runtime-evaluation Runtime-evaluation-In-an-environment S-Expressions SEDOLs SHA-1 SHA-256 SOAP SQL-based-authentication Safe-addition Same-Fringe Scope-modifiers Search-a-list Secure-temporary-file Self-describing-numbers Self-referential-sequence Semiprime Semordnilap Send-an-unknown-method-call Send-email Sequence-of-non-squares Sequence-of-primes-by-Trial-Division Set Set-consolidation Set-of-real-numbers Set-puzzle Seven-sided-dice-from-five-sided-dice Shell-one-liner Short-circuit-evaluation Show-the-epoch Sierpinski-carpet Sierpinski-triangle Sierpinski-triangle-Graphical Sieve-of-Eratosthenes Simple-database Simple-windowed-application Simulate-input-Keyboard Simulate-input-Mouse Singleton Singly-linked-list-Element-definition Singly-linked-list-Element-insertion Singly-linked-list-Traversal Sleep Sockets Sokoban Solve-a-Hidato-puzzle Solve-a-Holy-Knights-tour Solve-a-Hopido-puzzle Solve-a-Numbrix-puzzle Solve-the-no-connection-puzzle Sort-an-array-of-composite-structures Sort-an-integer-array Sort-disjoint-sublist Sort-using-a-custom-comparator Sorting-algorithms-Bead-sort Sorting-algorithms-Bogosort Sorting-algorithms-Bubble-sort Sorting-algorithms-Cocktail-sort Sorting-algorithms-Comb-sort Sorting-algorithms-Counting-sort Sorting-algorithms-Gnome-sort Sorting-algorithms-Heapsort Sorting-algorithms-Insertion-sort Sorting-algorithms-Merge-sort Sorting-algorithms-Pancake-sort Sorting-algorithms-Permutation-sort Sorting-algorithms-Quicksort Sorting-algorithms-Radix-sort Sorting-algorithms-Selection-sort Sorting-algorithms-Shell-sort Sorting-algorithms-Sleep-sort Sorting-algorithms-Stooge-sort Sorting-algorithms-Strand-sort Soundex Sparkline-in-unicode Special-variables Speech-synthesis Spiral-matrix Stable-marriage-problem Stack Stack-traces Stair-climbing-puzzle State-name-puzzle Statistics-Basic Stem-and-leaf-plot Stern-Brocot-sequence String-append String-case String-comparison String-concatenation String-interpolation--included- String-length String-matching String-prepend Strip-a-set-of-characters-from-a-string Strip-block-comments Strip-comments-from-a-string Strip-control-codes-and-extended-characters-from-a-string Strip-whitespace-from-a-string-Top-and-tail Substring Substring-Top-and-tail Subtractive-generator Sudoku Sum-and-product-of-an-array Sum-digits-of-an-integer Sum-multiples-of-3-and-5 Sum-of-a-series Sum-of-squares Sutherland-Hodgman-polygon-clipping Symmetric-difference Synchronous-concurrency System-time Table-creation-Postal-addresses Take-notes-on-the-command-line Temperature-conversion Terminal-control-Clear-the-screen Terminal-control-Coloured-text Terminal-control-Cursor-movement Terminal-control-Cursor-positioning Terminal-control-Dimensions Terminal-control-Display-an-extended-character Terminal-control-Hiding-the-cursor Terminal-control-Inverse-video Terminal-control-Positional-read Terminal-control-Preserve-screen Terminal-control-Ringing-the-terminal-bell Terminal-control-Unicode-output Ternary-logic Test-a-function Text-processing-1 Text-processing-2 Text-processing-Max-licenses-in-use Textonyms The-ISAAC-Cipher The-Twelve-Days-of-Christmas Thieles-interpolation-formula Tic-tac-toe Time-a-function Tokenize-a-string Top-rank-per-group Topic-variable Topological-sort Topswops Total-circles-area Towers-of-Hanoi Trabb-Pardo-Knuth-algorithm Tree-traversal Trigonometric-functions Truncate-a-file Twelve-statements URL-decoding URL-encoding Ulam-spiral--for-primes- Unbias-a-random-generator Undefined-values Unicode-strings Unicode-variable-names Universal-Turing-machine Unix-ls Update-a-configuration-file Use-another-language-to-call-a-function User-input-Graphical User-input-Text Vampire-number Van-der-Corput-sequence Variable-length-quantity Variable-size-Get Variables Variadic-function Vector-products Verify-distribution-uniformity-Chi-squared-test Verify-distribution-uniformity-Naive Video-display-modes Vigen-re-cipher Vigen-re-cipher-Cryptanalysis Visualize-a-tree Vogels-approximation-method Voronoi-diagram Walk-a-directory-Non-recursively Walk-a-directory-Recursively Web-scraping Window-creation Window-creation-X11 Window-management Wireworld Word-wrap World-Cup-group-stage Write-float-arrays-to-a-text-file Write-language-name-in-3D-ASCII Write-to-Windows-event-log XML-DOM-serialization XML-Input XML-Output XML-XPath Xiaolin-Wus-line-algorithm Y-combinator Yahoo--search-interface Yin-and-yang Zebra-puzzle Zeckendorf-arithmetic Zeckendorf-number-representation Zero-to-the-zero-power Zhang-Suen-thinning-algorithm Zig-zag-matrix ================================================ FILE: tests/results/21 ================================================ // Single-line comments start with two slashes. /* Multiline comments start with slash-star,  and end with star-slash */ // Statements can be terminated by ; doStuff(); // ... but they don't have to be, as semicolons are automatically inserted // wherever there's a newline, except in certain cases. doStuff() // Because those cases can cause unexpected results, we'll keep on using // semicolons in this guide. /////////////////////////////////// // 1. Numbers, Strings and Operators // JavaScript has one number type (which is a 64-bit IEEE 754 double). // Doubles have a 52-bit mantissa, which is enough to store integers // up to about 9✕10¹⁵ precisely. 3; // = 3 1.5; // = 1.5 // Some basic arithmetic works as you'd expect. 1 + 1; // = 2 0.1 + 0.2; // = 0.30000000000000004 8 - 1; // = 7 10 * 2; // = 20 35 / 5; // = 7 // Including uneven division. 5 / 2; // = 2.5 // And modulo division. 10 % 2; // = 0 30 % 4; // = 2 18.5 % 7; // = 4.5 // Bitwise operations also work; when you perform a bitwise operation your float // is converted to a signed int *up to* 32 bits. 1 << 2; // = 4 // Precedence is enforced with parentheses. (1 + 3) * 2; // = 8 // There are three special not-a-real-number values: Infinity; // result of e.g. 1/0 -Infinity; // result of e.g. -1/0 NaN; // result of e.g. 0/0, stands for 'Not a Number' // There's also a boolean type. true; false; // Strings are created with ' or ". 'abc'; "Hello, world"; // Negation uses the ! symbol !true; // = false !false; // = true // Equality is === 1 === 1; // = true 2 === 1; // = false // Inequality is !== 1 !== 1; // = false 2 !== 1; // = true // More comparisons 1 < 10; // = true 1 > 10; // = false 2 <= 2; // = true 2 >= 2; // = true // Strings are concatenated with + "Hello " + "world!"; // = "Hello world!" // ... which works with more than just strings "1, 2, " + 3; // = "1, 2, 3" "Hello " + ["world", "!"]; // = "Hello world,!" // and are compared with < and > "a" < "b"; // = true // Type coercion is performed for comparisons with double equals... "5" == 5; // = true null == undefined; // = true // ...unless you use === "5" === 5; // = false null === undefined; // = false // ...which can result in some weird behaviour... 13 + !0; // 14 "13" + !0; // '13true' // You can access characters in a string with `charAt` "This is a string".charAt(0); // = 'T' // ...or use `substring` to get larger pieces. "Hello world".substring(0, 5); // = "Hello" // `length` is a property, so don't use (). "Hello".length; // = 5 // There's also `null` and `undefined`. null; // used to indicate a deliberate non-value undefined; // used to indicate a value is not currently present (although  // `undefined` is actually a value itself) // false, null, undefined, NaN, 0 and "" are falsy; everything else is truthy. // Note that 0 is falsy and "0" is truthy, even though 0 == "0". /////////////////////////////////// // 2. Variables, Arrays and Objects // Variables are declared with the `var` keyword. JavaScript is dynamically // typed, so you don't need to specify type. Assignment uses a single `=` // character. var someVar = 5; // If you leave the var keyword off, you won't get an error... someOtherVar = 10; // ...but your variable will be created in the global scope, not in the scope // you defined it in. // Variables declared without being assigned to are set to undefined. var someThirdVar; // = undefined // If you want to declare a couple of variables, then you could use a comma // separator var someFourthVar = 2, someFifthVar = 4; // There's shorthand for performing math operations on variables: someVar += 5; // equivalent to someVar = someVar + 5; someVar is 10 now someVar *= 10; // now someVar is 100 // and an even-shorter-hand for adding or subtracting 1 someVar++; // now someVar is 101 someVar--; // back to 100 // Arrays are ordered lists of values, of any type. var myArray = ["Hello", 45, true]; // Their members can be accessed using the square-brackets subscript syntax. // Array indices start at zero. myArray[1]; // = 45 // Arrays are mutable and of variable length. myArray.push("World"); myArray.length; // = 4 // Add/Modify at specific index myArray[3] = "Hello"; // Add and remove element from front or back end of an array myArray.unshift(3); // Add as the first element someVar = myArray.shift(); // Remove first element and return it myArray.push(3); // Add as the last element someVar = myArray.pop(); // Remove last element and return it // Join all elements of an array with semicolon var myArray0 = [32,false,"js",12,56,90]; myArray0.join(";"); // = "32;false;js;12;56;90" // Get subarray of elements from index 1 (include) to 4 (exclude) myArray0.slice(1,4); // = [false,"js",12] // Remove 4 elements starting from index 2, and insert there strings // "hi","wr" and "ld"; return removed subarray myArray0.splice(2,4,"hi","wr","ld"); // = ["js",12,56,90] // myArray0 === [32,false,"hi","wr","ld"] // JavaScript's objects are equivalent to "dictionaries" or "maps" in other // languages: an unordered collection of key-value pairs. var myObj = {key1: "Hello", key2: "World"}; // Keys are strings, but quotes aren't required if they're a valid // JavaScript identifier. Values can be any type. var myObj = {myKey: "myValue", "my other key": 4}; // Object attributes can also be accessed using the subscript syntax, myObj["my other key"]; // = 4 // ... or using the dot syntax, provided the key is a valid identifier. myObj.myKey; // = "myValue" // Objects are mutable; values can be changed and new keys added. myObj.myThirdKey = true; // If you try to access a value that's not yet set, you'll get undefined. myObj.myFourthKey; // = undefined /////////////////////////////////// // 3. Logic and Control Structures // The `if` structure works as you'd expect. var count = 1; if (count == 3){  // evaluated if count is 3 } else if (count == 4){  // evaluated if count is 4 } else {  // evaluated if it's not either 3 or 4 } // As does `while`. while (true){  // An infinite loop! } // Do-while loops are like while loops, except they always run at least once. var input; do {  input = getInput(); } while (!isValid(input)); // The `for` loop is the same as C and Java: // initialization; continue condition; iteration. for (var i = 0; i < 5; i++){  // will run 5 times } // Breaking out of labeled loops is similar to Java outer: for (var i = 0; i < 10; i++) {  for (var j = 0; j < 10; j++) {  if (i == 5 && j ==5) {  break outer;  // breaks out of outer loop instead of only the inner one  }  } } // The for/in statement allows iteration over properties of an object. var description = ""; var person = {fname:"Paul", lname:"Ken", age:18}; for (var x in person){  description += person[x] + " "; } // description = 'Paul Ken 18 ' // The for/of statement allows iteration over iterable objects (including the built-in String,  // Array, e.g. the Array-like arguments or NodeList objects, TypedArray, Map and Set,  // and user-defined iterables). var myPets = ""; var pets = ["cat", "dog", "hamster", "hedgehog"]; for (var pet of pets){  myPets += pet + " "; } // myPets = 'cat dog hamster hedgehog ' // && is logical and, || is logical or if (house.size == "big" && house.colour == "blue"){  house.contains = "bear"; } if (colour == "red" || colour == "blue"){  // colour is either red or blue } // && and || "short circuit", which is useful for setting default values. var name = otherName || "default"; // The `switch` statement checks for equality with `===`. // Use 'break' after each case // or the cases after the correct one will be executed too. grade = 'B'; switch (grade) {  case 'A':  console.log("Great job");  break;  case 'B':  console.log("OK job");  break;  case 'C':  console.log("You can do better");  break;  default:  console.log("Oy vey");  break; } /////////////////////////////////// // 4. Functions, Scope and Closures // JavaScript functions are declared with the `function` keyword. function myFunction(thing){  return thing.toUpperCase(); } myFunction("foo"); // = "FOO" // Note that the value to be returned must start on the same line as the // `return` keyword, otherwise you'll always return `undefined` due to // automatic semicolon insertion. Watch out for this when using Allman style. function myFunction(){  return // <- semicolon automatically inserted here  {thisIsAn: 'object literal'}; } myFunction(); // = undefined // JavaScript functions are first class objects, so they can be reassigned to // different variable names and passed to other functions as arguments - for // example, when supplying an event handler: function myFunction(){  // this code will be called in 5 seconds' time } setTimeout(myFunction, 5000); // Note: setTimeout isn't part of the JS language, but is provided by browsers // and Node.js. // Another function provided by browsers is setInterval function myFunction(){  // this code will be called every 5 seconds } setInterval(myFunction, 5000); // Function objects don't even have to be declared with a name - you can write // an anonymous function definition directly into the arguments of another. setTimeout(function(){  // this code will be called in 5 seconds' time }, 5000); // JavaScript has function scope; functions get their own scope but other blocks // do not. if (true){  var i = 5; } i; // = 5 - not undefined as you'd expect in a block-scoped language // This has led to a common pattern of "immediately-executing anonymous // functions", which prevent temporary variables from leaking into the global // scope. (function(){  var temporary = 5;  // We can access the global scope by assigning to the "global object", which  // in a web browser is always `window`. The global object may have a  // different name in non-browser environments such as Node.js.  window.permanent = 10; })(); temporary; // raises ReferenceError permanent; // = 10 // One of JavaScript's most powerful features is closures. If a function is // defined inside another function, the inner function has access to all the // outer function's variables, even after the outer function exits. function sayHelloInFiveSeconds(name){  var prompt = "Hello, " + name + "!";  // Inner functions are put in the local scope by default, as if they were  // declared with `var`.  function inner(){  alert(prompt);  }  setTimeout(inner, 5000);  // setTimeout is asynchronous, so the sayHelloInFiveSeconds function will  // exit immediately, and setTimeout will call inner afterwards. However,  // because inner is "closed over" sayHelloInFiveSeconds, inner still has  // access to the `prompt` variable when it is finally called. } sayHelloInFiveSeconds("Adam"); // will open a popup with "Hello, Adam!" in 5s /////////////////////////////////// // 5. More about Objects; Constructors and Prototypes // Objects can contain functions. var myObj = {  myFunc: function(){  return "Hello world!";  } }; myObj.myFunc(); // = "Hello world!" // When functions attached to an object are called, they can access the object // they're attached to using the `this` keyword. myObj = {  myString: "Hello world!",  myFunc: function(){  return this.myString;  } }; myObj.myFunc(); // = "Hello world!" // What this is set to has to do with how the function is called, not where // it's defined. So, our function doesn't work if it isn't called in the // context of the object. var myFunc = myObj.myFunc; myFunc(); // = undefined // Inversely, a function can be assigned to the object and gain access to it // through `this`, even if it wasn't attached when it was defined. var myOtherFunc = function(){  return this.myString.toUpperCase(); }; myObj.myOtherFunc = myOtherFunc; myObj.myOtherFunc(); // = "HELLO WORLD!" // We can also specify a context for a function to execute in when we invoke it // using `call` or `apply`. var anotherFunc = function(s){  return this.myString + s; }; anotherFunc.call(myObj, " And Hello Moon!"); // = "Hello World! And Hello Moon!" // The `apply` function is nearly identical, but takes an array for an argument // list. anotherFunc.apply(myObj, [" And Hello Sun!"]); // = "Hello World! And Hello Sun!" // This is useful when working with a function that accepts a sequence of // arguments and you want to pass an array. Math.min(42, 6, 27); // = 6 Math.min([42, 6, 27]); // = NaN (uh-oh!) Math.min.apply(Math, [42, 6, 27]); // = 6 // But, `call` and `apply` are only temporary. When we want it to stick, we can // use `bind`. var boundFunc = anotherFunc.bind(myObj); boundFunc(" And Hello Saturn!"); // = "Hello World! And Hello Saturn!" // `bind` can also be used to partially apply (curry) a function. var product = function(a, b){ return a * b; }; var doubler = product.bind(this, 2); doubler(8); // = 16 // When you call a function with the `new` keyword, a new object is created, and // made available to the function via the `this` keyword. Functions designed to be // called like that are called constructors. var MyConstructor = function(){  this.myNumber = 5; }; myNewObj = new MyConstructor(); // = {myNumber: 5} myNewObj.myNumber; // = 5 // Unlike most other popular object-oriented languages, JavaScript has no // concept of 'instances' created from 'class' blueprints; instead, JavaScript // combines instantiation and inheritance into a single concept: a 'prototype'. // Every JavaScript object has a 'prototype'. When you go to access a property // on an object that doesn't exist on the actual object, the interpreter will // look at its prototype. // Some JS implementations let you access an object's prototype on the magic // property `__proto__`. While this is useful for explaining prototypes it's not // part of the standard; we'll get to standard ways of using prototypes later. var myObj = {  myString: "Hello world!" }; var myPrototype = {  meaningOfLife: 42,  myFunc: function(){  return this.myString.toLowerCase();  } }; myObj.__proto__ = myPrototype; myObj.meaningOfLife; // = 42 // This works for functions, too. myObj.myFunc(); // = "hello world!" // Of course, if your property isn't on your prototype, the prototype's // prototype is searched, and so on. myPrototype.__proto__ = {  myBoolean: true }; myObj.myBoolean; // = true // There's no copying involved here; each object stores a reference to its // prototype. This means we can alter the prototype and our changes will be // reflected everywhere. myPrototype.meaningOfLife = 43; myObj.meaningOfLife; // = 43 // The for/in statement allows iteration over properties of an object, // walking up the prototype chain until it sees a null prototype. for (var x in myObj){  console.log(myObj[x]); } ///prints: // Hello world! // 43 // [Function: myFunc] // true // To only consider properties attached to the object itself // and not its prototypes, use the `hasOwnProperty()` check. for (var x in myObj){  if (myObj.hasOwnProperty(x)){  console.log(myObj[x]);  } } ///prints: // Hello world! // We mentioned that `__proto__` was non-standard, and there's no standard way to // change the prototype of an existing object. However, there are two ways to // create a new object with a given prototype. // The first is Object.create, which is a recent addition to JS, and therefore // not available in all implementations yet. var myObj = Object.create(myPrototype); myObj.meaningOfLife; // = 43 // The second way, which works anywhere, has to do with constructors. // Constructors have a property called prototype. This is *not* the prototype of // the constructor function itself; instead, it's the prototype that new objects // are given when they're created with that constructor and the new keyword. MyConstructor.prototype = {  myNumber: 5,  getMyNumber: function(){  return this.myNumber;  } }; var myNewObj2 = new MyConstructor(); myNewObj2.getMyNumber(); // = 5 myNewObj2.myNumber = 6; myNewObj2.getMyNumber(); // = 6 // Built-in types like strings and numbers also have constructors that create // equivalent wrapper objects. var myNumber = 12; var myNumberObj = new Number(12); myNumber == myNumberObj; // = true // Except, they aren't exactly equivalent. typeof myNumber; // = 'number' typeof myNumberObj; // = 'object' myNumber === myNumberObj; // = false if (0){  // This code won't execute, because 0 is falsy. } if (new Number(0)){  // This code will execute, because wrapped numbers are objects, and objects  // are always truthy. } // However, the wrapper objects and the regular builtins share a prototype, so // you can actually add functionality to a string, for instance. String.prototype.firstCharacter = function(){  return this.charAt(0); }; "abc".firstCharacter(); // = "a" // This fact is often used in "polyfilling", which is implementing newer // features of JavaScript in an older subset of JavaScript, so that they can be // used in older environments such as outdated browsers. // For instance, we mentioned that Object.create isn't yet available in all // implementations, but we can still use it with this polyfill: if (Object.create === undefined){ // don't overwrite it if it exists  Object.create = function(proto){  // make a temporary constructor with the right prototype  var Constructor = function(){};  Constructor.prototype = proto;  // then use it to create a new, appropriately-prototyped object  return new Constructor();  }; } // ES6 Additions // The "let" keyword allows you to define variables in a lexical scope,  // as opposed to a block scope like the var keyword does. let name = "Billy"; // Variables defined with let can be reassigned new values. name = "William"; // The "const" keyword allows you to define a variable in a lexical scope // like with let, but you cannot reassign the value once one has been assigned. const pi = 3.14; pi = 4.13; // You cannot do this. // There is a new syntax for functions in ES6 known as "lambda syntax". // This allows functions to be defined in a lexical scope like with variables // defined by const and let.  const isEven = (number) => {  return number % 2 === 0; }; isEven(7); // false // The "equivalent" of this function in the traditional syntax would look like this: function isEven(number) {  return number % 2 === 0; }; // I put the word "equivalent" in double quotes because a function defined // using the lambda syntax cannnot be called before the definition. // The following is an example of invalid usage: add(1, 8); const add = (firstNumber, secondNumber) => {  return firstNumber + secondNumber; }; ================================================ FILE: tests/results/22 ================================================ // Single-line comments start with two slashes. /* Multiline comments start with slash-star,  and end with star-slash */ // Statements can be terminated by ; doStuff(); // ... but they don't have to be, as semicolons are automatically inserted // wherever there's a newline, except in certain cases. doStuff() // Because those cases can cause unexpected results, we'll keep on using // semicolons in this guide. /////////////////////////////////// // 1. Numbers, Strings and Operators // JavaScript has one number type (which is a 64-bit IEEE 754 double). // Doubles have a 52-bit mantissa, which is enough to store integers // up to about 9✕10¹⁵ precisely. 3; // = 3 1.5; // = 1.5 // Some basic arithmetic works as you'd expect. 1 + 1; // = 2 0.1 + 0.2; // = 0.30000000000000004 8 - 1; // = 7 10 * 2; // = 20 35 / 5; // = 7 // Including uneven division. 5 / 2; // = 2.5 // And modulo division. 10 % 2; // = 0 30 % 4; // = 2 18.5 % 7; // = 4.5 // Bitwise operations also work; when you perform a bitwise operation your float // is converted to a signed int *up to* 32 bits. 1 << 2; // = 4 // Precedence is enforced with parentheses. (1 + 3) * 2; // = 8 // There are three special not-a-real-number values: Infinity; // result of e.g. 1/0 -Infinity; // result of e.g. -1/0 NaN; // result of e.g. 0/0, stands for 'Not a Number' // There's also a boolean type. true; false; // Strings are created with ' or ". 'abc'; "Hello, world"; // Negation uses the ! symbol !true; // = false !false; // = true // Equality is === 1 === 1; // = true 2 === 1; // = false // Inequality is !== 1 !== 1; // = false 2 !== 1; // = true // More comparisons 1 < 10; // = true 1 > 10; // = false 2 <= 2; // = true 2 >= 2; // = true // Strings are concatenated with + "Hello " + "world!"; // = "Hello world!" // ... which works with more than just strings "1, 2, " + 3; // = "1, 2, 3" "Hello " + ["world", "!"]; // = "Hello world,!" // and are compared with < and > "a" < "b"; // = true // Type coercion is performed for comparisons with double equals... "5" == 5; // = true null == undefined; // = true // ...unless you use === "5" === 5; // = false null === undefined; // = false // ...which can result in some weird behaviour... 13 + !0; // 14 "13" + !0; // '13true' // You can access characters in a string with `charAt` "This is a string".charAt(0); // = 'T' // ...or use `substring` to get larger pieces. "Hello world".substring(0, 5); // = "Hello" // `length` is a property, so don't use (). "Hello".length; // = 5 // There's also `null` and `undefined`. null; // used to indicate a deliberate non-value undefined; // used to indicate a value is not currently present (although  // `undefined` is actually a value itself) // false, null, undefined, NaN, 0 and "" are falsy; everything else is truthy. // Note that 0 is falsy and "0" is truthy, even though 0 == "0". /////////////////////////////////// // 2. Variables, Arrays and Objects // Variables are declared with the `var` keyword. JavaScript is dynamically // typed, so you don't need to specify type. Assignment uses a single `=` // character. var someVar = 5; // If you leave the var keyword off, you won't get an error... someOtherVar = 10; // ...but your variable will be created in the global scope, not in the scope // you defined it in. // Variables declared without being assigned to are set to undefined. var someThirdVar; // = undefined // If you want to declare a couple of variables, then you could use a comma // separator var someFourthVar = 2, someFifthVar = 4; // There's shorthand for performing math operations on variables: someVar += 5; // equivalent to someVar = someVar + 5; someVar is 10 now someVar *= 10; // now someVar is 100 // and an even-shorter-hand for adding or subtracting 1 someVar++; // now someVar is 101 someVar--; // back to 100 // Arrays are ordered lists of values, of any type. var myArray = ["Hello", 45, true]; // Their members can be accessed using the square-brackets subscript syntax. // Array indices start at zero. myArray[1]; // = 45 // Arrays are mutable and of variable length. myArray.push("World"); myArray.length; // = 4 // Add/Modify at specific index myArray[3] = "Hello"; // Add and remove element from front or back end of an array myArray.unshift(3); // Add as the first element someVar = myArray.shift(); // Remove first element and return it myArray.push(3); // Add as the last element someVar = myArray.pop(); // Remove last element and return it // Join all elements of an array with semicolon var myArray0 = [32,false,"js",12,56,90]; myArray0.join(";"); // = "32;false;js;12;56;90" // Get subarray of elements from index 1 (include) to 4 (exclude) myArray0.slice(1,4); // = [false,"js",12] // Remove 4 elements starting from index 2, and insert there strings // "hi","wr" and "ld"; return removed subarray myArray0.splice(2,4,"hi","wr","ld"); // = ["js",12,56,90] // myArray0 === [32,false,"hi","wr","ld"] // JavaScript's objects are equivalent to "dictionaries" or "maps" in other // languages: an unordered collection of key-value pairs. var myObj = {key1: "Hello", key2: "World"}; // Keys are strings, but quotes aren't required if they're a valid // JavaScript identifier. Values can be any type. var myObj = {myKey: "myValue", "my other key": 4}; // Object attributes can also be accessed using the subscript syntax, myObj["my other key"]; // = 4 // ... or using the dot syntax, provided the key is a valid identifier. myObj.myKey; // = "myValue" // Objects are mutable; values can be changed and new keys added. myObj.myThirdKey = true; // If you try to access a value that's not yet set, you'll get undefined. myObj.myFourthKey; // = undefined /////////////////////////////////// // 3. Logic and Control Structures // The `if` structure works as you'd expect. var count = 1; if (count == 3){  // evaluated if count is 3 } else if (count == 4){  // evaluated if count is 4 } else {  // evaluated if it's not either 3 or 4 } // As does `while`. while (true){  // An infinite loop! } // Do-while loops are like while loops, except they always run at least once. var input; do {  input = getInput(); } while (!isValid(input)); // The `for` loop is the same as C and Java: // initialization; continue condition; iteration. for (var i = 0; i < 5; i++){  // will run 5 times } // Breaking out of labeled loops is similar to Java outer: for (var i = 0; i < 10; i++) {  for (var j = 0; j < 10; j++) {  if (i == 5 && j ==5) {  break outer;  // breaks out of outer loop instead of only the inner one  }  } } // The for/in statement allows iteration over properties of an object. var description = ""; var person = {fname:"Paul", lname:"Ken", age:18}; for (var x in person){  description += person[x] + " "; } // description = 'Paul Ken 18 ' // The for/of statement allows iteration over iterable objects (including the built-in String,  // Array, e.g. the Array-like arguments or NodeList objects, TypedArray, Map and Set,  // and user-defined iterables). var myPets = ""; var pets = ["cat", "dog", "hamster", "hedgehog"]; for (var pet of pets){  myPets += pet + " "; } // myPets = 'cat dog hamster hedgehog ' // && is logical and, || is logical or if (house.size == "big" && house.colour == "blue"){  house.contains = "bear"; } if (colour == "red" || colour == "blue"){  // colour is either red or blue } // && and || "short circuit", which is useful for setting default values. var name = otherName || "default"; // The `switch` statement checks for equality with `===`. // Use 'break' after each case // or the cases after the correct one will be executed too. grade = 'B'; switch (grade) {  case 'A':  console.log("Great job");  break;  case 'B':  console.log("OK job");  break;  case 'C':  console.log("You can do better");  break;  default:  console.log("Oy vey");  break; } /////////////////////////////////// // 4. Functions, Scope and Closures // JavaScript functions are declared with the `function` keyword. function myFunction(thing){  return thing.toUpperCase(); } myFunction("foo"); // = "FOO" // Note that the value to be returned must start on the same line as the // `return` keyword, otherwise you'll always return `undefined` due to // automatic semicolon insertion. Watch out for this when using Allman style. function myFunction(){  return // <- semicolon automatically inserted here  {thisIsAn: 'object literal'}; } myFunction(); // = undefined // JavaScript functions are first class objects, so they can be reassigned to // different variable names and passed to other functions as arguments - for // example, when supplying an event handler: function myFunction(){  // this code will be called in 5 seconds' time } setTimeout(myFunction, 5000); // Note: setTimeout isn't part of the JS language, but is provided by browsers // and Node.js. // Another function provided by browsers is setInterval function myFunction(){  // this code will be called every 5 seconds } setInterval(myFunction, 5000); // Function objects don't even have to be declared with a name - you can write // an anonymous function definition directly into the arguments of another. setTimeout(function(){  // this code will be called in 5 seconds' time }, 5000); // JavaScript has function scope; functions get their own scope but other blocks // do not. if (true){  var i = 5; } i; // = 5 - not undefined as you'd expect in a block-scoped language // This has led to a common pattern of "immediately-executing anonymous // functions", which prevent temporary variables from leaking into the global // scope. (function(){  var temporary = 5;  // We can access the global scope by assigning to the "global object", which  // in a web browser is always `window`. The global object may have a  // different name in non-browser environments such as Node.js.  window.permanent = 10; })(); temporary; // raises ReferenceError permanent; // = 10 // One of JavaScript's most powerful features is closures. If a function is // defined inside another function, the inner function has access to all the // outer function's variables, even after the outer function exits. function sayHelloInFiveSeconds(name){  var prompt = "Hello, " + name + "!";  // Inner functions are put in the local scope by default, as if they were  // declared with `var`.  function inner(){  alert(prompt);  }  setTimeout(inner, 5000);  // setTimeout is asynchronous, so the sayHelloInFiveSeconds function will  // exit immediately, and setTimeout will call inner afterwards. However,  // because inner is "closed over" sayHelloInFiveSeconds, inner still has  // access to the `prompt` variable when it is finally called. } sayHelloInFiveSeconds("Adam"); // will open a popup with "Hello, Adam!" in 5s /////////////////////////////////// // 5. More about Objects; Constructors and Prototypes // Objects can contain functions. var myObj = {  myFunc: function(){  return "Hello world!";  } }; myObj.myFunc(); // = "Hello world!" // When functions attached to an object are called, they can access the object // they're attached to using the `this` keyword. myObj = {  myString: "Hello world!",  myFunc: function(){  return this.myString;  } }; myObj.myFunc(); // = "Hello world!" // What this is set to has to do with how the function is called, not where // it's defined. So, our function doesn't work if it isn't called in the // context of the object. var myFunc = myObj.myFunc; myFunc(); // = undefined // Inversely, a function can be assigned to the object and gain access to it // through `this`, even if it wasn't attached when it was defined. var myOtherFunc = function(){  return this.myString.toUpperCase(); }; myObj.myOtherFunc = myOtherFunc; myObj.myOtherFunc(); // = "HELLO WORLD!" // We can also specify a context for a function to execute in when we invoke it // using `call` or `apply`. var anotherFunc = function(s){  return this.myString + s; }; anotherFunc.call(myObj, " And Hello Moon!"); // = "Hello World! And Hello Moon!" // The `apply` function is nearly identical, but takes an array for an argument // list. anotherFunc.apply(myObj, [" And Hello Sun!"]); // = "Hello World! And Hello Sun!" // This is useful when working with a function that accepts a sequence of // arguments and you want to pass an array. Math.min(42, 6, 27); // = 6 Math.min([42, 6, 27]); // = NaN (uh-oh!) Math.min.apply(Math, [42, 6, 27]); // = 6 // But, `call` and `apply` are only temporary. When we want it to stick, we can // use `bind`. var boundFunc = anotherFunc.bind(myObj); boundFunc(" And Hello Saturn!"); // = "Hello World! And Hello Saturn!" // `bind` can also be used to partially apply (curry) a function. var product = function(a, b){ return a * b; }; var doubler = product.bind(this, 2); doubler(8); // = 16 // When you call a function with the `new` keyword, a new object is created, and // made available to the function via the `this` keyword. Functions designed to be // called like that are called constructors. var MyConstructor = function(){  this.myNumber = 5; }; myNewObj = new MyConstructor(); // = {myNumber: 5} myNewObj.myNumber; // = 5 // Unlike most other popular object-oriented languages, JavaScript has no // concept of 'instances' created from 'class' blueprints; instead, JavaScript // combines instantiation and inheritance into a single concept: a 'prototype'. // Every JavaScript object has a 'prototype'. When you go to access a property // on an object that doesn't exist on the actual object, the interpreter will // look at its prototype. // Some JS implementations let you access an object's prototype on the magic // property `__proto__`. While this is useful for explaining prototypes it's not // part of the standard; we'll get to standard ways of using prototypes later. var myObj = {  myString: "Hello world!" }; var myPrototype = {  meaningOfLife: 42,  myFunc: function(){  return this.myString.toLowerCase();  } }; myObj.__proto__ = myPrototype; myObj.meaningOfLife; // = 42 // This works for functions, too. myObj.myFunc(); // = "hello world!" // Of course, if your property isn't on your prototype, the prototype's // prototype is searched, and so on. myPrototype.__proto__ = {  myBoolean: true }; myObj.myBoolean; // = true // There's no copying involved here; each object stores a reference to its // prototype. This means we can alter the prototype and our changes will be // reflected everywhere. myPrototype.meaningOfLife = 43; myObj.meaningOfLife; // = 43 // The for/in statement allows iteration over properties of an object, // walking up the prototype chain until it sees a null prototype. for (var x in myObj){  console.log(myObj[x]); } ///prints: // Hello world! // 43 // [Function: myFunc] // true // To only consider properties attached to the object itself // and not its prototypes, use the `hasOwnProperty()` check. for (var x in myObj){  if (myObj.hasOwnProperty(x)){  console.log(myObj[x]);  } } ///prints: // Hello world! // We mentioned that `__proto__` was non-standard, and there's no standard way to // change the prototype of an existing object. However, there are two ways to // create a new object with a given prototype. // The first is Object.create, which is a recent addition to JS, and therefore // not available in all implementations yet. var myObj = Object.create(myPrototype); myObj.meaningOfLife; // = 43 // The second way, which works anywhere, has to do with constructors. // Constructors have a property called prototype. This is *not* the prototype of // the constructor function itself; instead, it's the prototype that new objects // are given when they're created with that constructor and the new keyword. MyConstructor.prototype = {  myNumber: 5,  getMyNumber: function(){  return this.myNumber;  } }; var myNewObj2 = new MyConstructor(); myNewObj2.getMyNumber(); // = 5 myNewObj2.myNumber = 6; myNewObj2.getMyNumber(); // = 6 // Built-in types like strings and numbers also have constructors that create // equivalent wrapper objects. var myNumber = 12; var myNumberObj = new Number(12); myNumber == myNumberObj; // = true // Except, they aren't exactly equivalent. typeof myNumber; // = 'number' typeof myNumberObj; // = 'object' myNumber === myNumberObj; // = false if (0){  // This code won't execute, because 0 is falsy. } if (new Number(0)){  // This code will execute, because wrapped numbers are objects, and objects  // are always truthy. } // However, the wrapper objects and the regular builtins share a prototype, so // you can actually add functionality to a string, for instance. String.prototype.firstCharacter = function(){  return this.charAt(0); }; "abc".firstCharacter(); // = "a" // This fact is often used in "polyfilling", which is implementing newer // features of JavaScript in an older subset of JavaScript, so that they can be // used in older environments such as outdated browsers. // For instance, we mentioned that Object.create isn't yet available in all // implementations, but we can still use it with this polyfill: if (Object.create === undefined){ // don't overwrite it if it exists  Object.create = function(proto){  // make a temporary constructor with the right prototype  var Constructor = function(){};  Constructor.prototype = proto;  // then use it to create a new, appropriately-prototyped object  return new Constructor();  }; } // ES6 Additions // The "let" keyword allows you to define variables in a lexical scope,  // as opposed to a block scope like the var keyword does. let name = "Billy"; // Variables defined with let can be reassigned new values. name = "William"; // The "const" keyword allows you to define a variable in a lexical scope // like with let, but you cannot reassign the value once one has been assigned. const pi = 3.14; pi = 4.13; // You cannot do this. // There is a new syntax for functions in ES6 known as "lambda syntax". // This allows functions to be defined in a lexical scope like with variables // defined by const and let.  const isEven = (number) => {  return number % 2 === 0; }; isEven(7); // false // The "equivalent" of this function in the traditional syntax would look like this: function isEven(number) {  return number % 2 === 0; }; // I put the word "equivalent" in double quotes because a function defined // using the lambda syntax cannnot be called before the definition. // The following is an example of invalid usage: add(1, 8); const add = (firstNumber, secondNumber) => {  return firstNumber + secondNumber; }; ================================================ FILE: tests/results/23 ================================================ :learn :list Arrays Axioms Channels Declarations Embedding Errors Interfaces Maps Operators Pointers Structs for func go hello http if packages print range rosetta/ slices switch types ================================================ FILE: tests/results/24 ================================================ Unknown topic. Do you mean one of these topics maybe? * mkfs.fat 84 * mkfs.vfat 80 * mkfs.exfat 76 ================================================ FILE: tests/results/25 ================================================ # Latency numbers every programmer should know  1ns Main memory reference: Send 2,000 bytes Read 1,000,000 bytes ▗▖ 100ns over commodity network: sequentially from SSD: 61us ▗▖ 62ns ▗  L1 cache reference: 1ns ▗  ▗▖ 1us Disk seek: 2ms ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ SSD random read: 16us ▗▖▗▖▗  Branch mispredict: 3ns  ▗▖  ▗▖▗▖▗▖  Read 1,000,000 bytes Compress 1KB with Snappy: Read 1,000,000 bytes sequentially from disk: L2 cache reference: 4ns 2us sequentially from memory: 947us ▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ 3us ▗  ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗  Mutex lock/unlock: 16ns   Packet roundtrip ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  Round trip CA to Netherlands: 150ms ▗▖▗▖▗▖▗▖▗▖▗▖▗ 10us = ▗▖ in same datacenter: 500us ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  100ns = ▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ 1ms = ▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖    ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖ ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  # [github.com/chubin/late.nz] [MIT License] ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖   # Console port of "Jeff Dean's latency numbers" ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  # from [github.com/colin-scott/interactive_latencies] ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖   ▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖▗▖  ================================================ FILE: tests/results/3 ================================================ #[cheat:ls] # To display everything in , excluding hidden files: ls # To display everything in , including hidden files: ls -a # To display all files, along with the size (with unit suffixes) and timestamp ls -lh # To display files, sorted by size: ls -S # To display directories only: ls -d */ # To display directories only, include hidden: ls -d .*/ */ #[tldr:ls] # ls # List directory contents. # More information: . # List files one per line: ls -1 # List all files, including hidden files: ls -a # List all files, with trailing `/` added to directory names: ls -F # Long format list (permissions, ownership, size, and modification date) of all files: ls -la # Long format list with size displayed using human-readable units (KiB, MiB, GiB): ls -lh # Long format list sorted by size (descending): ls -lS # Long format list of all files, sorted by modification date (oldest first): ls -ltr # Only list directories: ls -d */ ================================================ FILE: tests/results/4 ================================================  cheat.sheets:btrfs  # Create a btrfs file system on /dev/sdb, /dev/sdc, and /dev/sdd mkfs.btrfs /dev/sdb /dev/sdc /dev/sdd # btrfs with just one hard drive, metadata not redundant # (this is dangerous: if your metadata is lost, your data is lost as well) mkfs.btrfs -m single /dev/sdb # data to be redundant and metadata to be non-redundant: mkfs.btrfs -m raid0 -d raid1 /dev/sdb /dev/sdc /dev/sdd # both data and metadata to be redundant mkfs.btrfs -d raid1 /dev/sdb /dev/sdc /dev/sdd # To get a list of all btrfs file systems btrfs filesystem show # detailed df for a filesystem (mounted in /mnt) btrfs filesystem df /mnt # resize btrfs online (-2g decreases, +2g increases) btrfs filesystem resize -2g /mnt # use maximum space btrfs filesystem resize max /mnt # add new device to a filesystem btrfs device add /dev/sdf /mnt # remove devices from a filesystem btrfs device delete missing /mnt # create the subvolume /mnt/sv1 in the /mnt volume btrfs subvolume create /mnt/sv1 # list subvolumes btrfs subvolume list /mnt # mount subvolume without mounting the main filesystem mount -o subvol=sv1 /dev/sdb /mnt # delete subvolume btrfs subvolume delete /mnt/sv1 # taking snapshot of a subvolume btrfs subvolume snapshot /mnt/sv1 /mnt/sv1_snapshot # taking snapshot of a file (copy file by reference) cp --reflink /mnt/sv1/test1 /mnt/sv1/test3 # convert ext3/ext4 to btrfs btrfs-convert /dev/sdb1 # convert btrfs to ext3/ext4 btrfs-convert -r /dev/sdb1  tldr:btrfs  # btrfs # A filesystem based on the copy-on-write (COW) principle for Linux. # Some subcommands such as `btrfs device` have their own usage documentation. # More information: . # Create subvolume: sudo btrfs subvolume create path/to/subvolume # List subvolumes: sudo btrfs subvolume list path/to/mount_point # Show space usage information: sudo btrfs filesystem df path/to/mount_point # Enable quota: sudo btrfs quota enable path/to/subvolume # Show quota: sudo btrfs qgroup show path/to/subvolume ================================================ FILE: tests/results/5 ================================================  cheat.sheets:btrfs  # create the subvolume /mnt/sv1 in the /mnt volume btrfs subvolume create /mnt/sv1 # list subvolumes btrfs subvolume list /mnt # mount subvolume without mounting the main filesystem mount -o subvol=sv1 /dev/sdb /mnt # delete subvolume btrfs subvolume delete /mnt/sv1 # taking snapshot of a subvolume btrfs subvolume snapshot /mnt/sv1 /mnt/sv1_snapshot  tldr:btrfs  # Create subvolume: sudo btrfs subvolume create path/to/subvolume # List subvolumes: sudo btrfs subvolume list path/to/mount_point # Enable quota: sudo btrfs quota enable path/to/subvolume # Show quota: sudo btrfs qgroup show path/to/subvolume  tldr:btrfs-filesystem  # Defragment a directory recursively (does not cross subvolume boundaries): sudo btrfs filesystem defragment -v -r path/to/directory  tldr:btrfs-subvolume  # btrfs subvolume # Manage btrfs subvolumes and snapshots. # More information: . # Create a new empty subvolume: sudo btrfs subvolume create path/to/new_subvolume # List all subvolumes and snapshots in the specified filesystem: sudo btrfs subvolume list path/to/btrfs_filesystem # Delete a subvolume: sudo btrfs subvolume delete path/to/subvolume # Create a read-only snapshot of an existing subvolume: sudo btrfs subvolume snapshot -r path/to/source_subvolume path/to/target # Create a read-write snapshot of an existing subvolume: sudo btrfs subvolume snapshot path/to/source_subvolume path/to/target # Show detailed information about a subvolume: sudo btrfs subvolume show path/to/subvolume ================================================ FILE: tests/results/6 ================================================ ## curl cht.sh To access a cheat sheet you can simply issue a plain HTTP or HTTPS request specifying the topic name in the query URL: curl cheat.sh/tar curl https://cheat.sh/tar You can use the full service name, cheat.sh, or the shorter variant, cht.sh. They are equivalent: curl https://cht.sh/tar curl https://cheat.sh/tar The preferred access protocol is HTTPS, and you should use it always when possible. Cheat sheets in the root namespaces cover UNIX/Linux commands. Cheat sheets covering programming languages are located in subsections: curl cht.sh/go/func All cheat sheets in a subsection can be listed using a special query :list : curl cht.sh/go/:list There are several other special queries. All of them are starting with a colon. See /:help for the full list of the special queries. ## Search If a cheat sheet is too large, you can cut the needed part out using an additional search parameter. In this case, only the paragraph that contains the search term will be displayed: curl cht.sh/tar~extract If the name of the cheat sheet is omitted, and only the serch query is specified, all cheat sheets in the namespace are scanned, and the found occurrencies are displayed: curl cht.sh/~extract ## Options cheat.sh queries as well as search queries have many options. They can be specified as a part of the query string in the URL, after ?. Short single letter options could be written all jointly together, and long options are separated with &. For example, to switch syntax highlighting off the T switch is used: curl cht.sh/tar?T Full list of all available cheat.sh options as well as description of all modes of operation can be found in /:help, curl cht.sh/:help ## cht.sh client Though it's perfectly possible to access cheat.sh using curl (or any other HTTP client) alone, there is a special client, that has several advantages comparing to plain curling: cht.sh. To install the client in ~/bin: curl https://cht.sh/:cht.sh > ~/bin/cht.sh chmod +x ~/bin/cht.sh Queries look the same, but you can separate words in the query with spaces, instead of + as when using curl, what looks more natural: cht.sh python zip lists ## cht.sh shell If you always issuing queries about the same programming language, it's can be more convenient to run the client in the shell mode and specify the queries context: $ cht.sh --shell python cht.sh/python> zip lists Of course, you can start the shell without the context too: $ cht.sh --shell cht.sh> python zip lists cht.sh> go http query cht.sh> js iterate list If you use predominantly one language but sometime issuing queries about other, you may prepend the query with /: cht.sh/python> zip lists cht.sh/python> /go http query cht.sh/python> /js iterate list ## :learn If you are just start learning a new programming language, and you have no distinct queries for the moment, cheat.sh can be a good starting point too. As you know, it exports cheat sheets from the best cheat sheet repositories, and one of them is Learn X in Y, a repository of concise documentation devoted to learning programming languages from scratch (and not only them). If you want start learning a new programming language, do (use less -R because the output could be quite big): curl cht.sh/elixir/:learn | less -R Or simply :learn with cht.sh (you don't need less -R here, because cht.sh starts pager if needed automatically): cht.sh/elixir> :learn ## Programming languages questions One of the most important features of cheat.sh is that you can ask it any questions about programming languages and instantly get answers on them. You can use both direct HTTP queries or the cht.sh client for that: curl cht.sh/python/reverse+list cht.sh/python> reverse list In the latter case you don't need + to separate the words in the query, you can do it in a more natural way, with spaces. If context in the cht.sh shell is not specified, you have to write the programming language name as the first word in the query: cht.sh> python reverse list But if you are using only one programming language and all queries are about it, it's better to change the current context and ## Comments Text in the answers is syntactically formatted as comment in the correspondent programming language When using cht.sh, you can copy the result of the last query into the selection buffer (you may also call it "clibpoard") using C (or c, with text): cht.sh/python> reverse list ... cht.sh/python> C 1 lines copied ## bash TAB-completion for cht.sh One of the advantages of the cht.sh client comparing to plain curl is that you can use TAB completion when writing its queries in bash (other supported shells: zsh and fish). Install the TAB completion script for that. Assuming you use bash, you have to do: mkdir -p ~/.bash.d/ curl https://cht.sh/:bash_completion > ~/.bash.d/cht.sh echo 'source ~/.bash.d/cht.sh' >> ~/.bashrc source ~/.bash.d/cht.sh ## Editor You can access cheat.sh directly from editors: Vim and Emacs. It's a very important feature! You should absolutely like it. Imagine: instead of switching to your browser, googling, browsing Stack Overflow and eventually copying the code snippets you need and later pasting them into the editor, you can achieve the same instantly and without leaving the editor at all! Here is how it looks like: 1. In Vim, if you have a question while editing a program, you can just type your question directly in the buffer and press KK. You will get the answer to your question in pager. (with KB you'll get the answer in a separate buffer). 2. If you like the answer. You can manually paste it from the buffer or the pager, or if you are lazy you can use KP to paste it under your question (KR will replace your question). If you want the answer without the comments, KC replays the last query toggling them. You have to install cheat.sh Vim/Emacs plugins for the editor support. See /:vim or /:emacs with the detailed installation instructions. ## Feature requests, feedback and contribution If you want to submit a new community driver repository for cheat.sh please open a ticket on the project page on GitHub. If you want to modify an existing cheat sheet, please check the source of the cheat sheet (it is always displayed in the cheat sheet bottom line). If you want to add a new cheat sheet, add it here: https://github.com/chubin/cheat.sheets If you want to suggest a new feature for cheat.sh, or if you've found a bug, please open a new issue on github: https://github.com/chubin/cheat.sh If you want to get the major project updates, follow @igor_chubin in Twitter or this RSS feed: https://twitrss.me/twitter_user_to_rss/?user=igor_chubin ================================================ FILE: tests/results/7 ================================================ Usage: $ curl cheat.sh/TOPIC show cheat sheet on the TOPIC $ curl cheat.sh/TOPIC/SUB show cheat sheet on the SUB topic in TOPIC $ curl cheat.sh/~KEYWORD search cheat sheets for KEYWORD Options: ?OPTIONS q quiet mode, don't show github/twitter buttons T text only, no ANSI sequences style=STYLE color style c do not comment text, do not shift code (QUERY+ only) C do not comment text, shift code (QUERY+ only) Q code only, don't show text (QUERY+ only) Options can be combined together in this way: curl 'cheat.sh/for?qT&style=bw' (when using & in shell, don't forget to specify the quotes or escape & with \) Special pages: :help this page :list list all cheat sheets :post how to post new cheat sheet :cht.sh shell client (cht.sh) :bash_completion bash function for tab completion :styles list of color styles :styles-demo show color styles usage examples :random fetches a random cheat sheet Shell client: $ curl https://cht.sh/:cht.sh > ~/bin/cht.sh $ chmod +x ~/bin/cht.sh $ cht.sh python :learn $ cht.sh --shell Tab completion: $ mkdir -p ~/.bash.d/ $ curl cheat.sh/:bash_completion > ~/.bash.d/cht.sh $ . ~/.bash.d/cht.sh $ echo '. ~/.bash.d/cht.sh' >> ~/.bashrc Editor integration: :emacs see the page for the Emacs configuration :vim see the page for the Vim configuration Search: /~snapshot look for "snapshot" in the first level cheat sheets /~ssh~passphrase several keywords can be combined together using ~ /scala/~closure look for "closure" in scala cheat sheets /~snapshot/r look for "snapshot" in all cheat sheets recursively You can use special search options after the closing slash: /~shot/bi case insensitive (i), word boundaries (b) List of search options: b word boundaries i case insensitive search r recursive Programming languages topics: Each programming language topic has the following subtopics: hello hello world + how to start the program :learn big cheat sheet for learning language from scratch :list list of topics :random fetches a random cheat sheet belonging to the topic ================================================ FILE: tests/results/8 ================================================ #!/bin/bash # shellcheck disable=SC1117,SC2001 # # [X] open section # [X] one shot mode # [X] usage info # [X] dependencies check # [X] help # [X] yank/y/copy/c # [X] Y/C # [X] eof problem # [X] more # [X] stealth mode # # here are several examples for the stealth mode: # # zip lists # list permutation # random list element # reverse a list # read json from file # append string to a file # run process in background # count words in text counter # group elements list __CHTSH_VERSION=0.0.4 __CHTSH_DATETIME="2021-04-25 12:30:30 +0200" # cht.sh configuration loading # # configuration is stored in ~/.cht.sh/ (can be overridden with CHTSH env var.) # CHTSH_HOME=${CHTSH:-"$HOME"/.cht.sh} [ -z "$CHTSH_CONF" ] && CHTSH_CONF=$CHTSH_HOME/cht.sh.conf # shellcheck disable=SC1090,SC2002 [ -e "$CHTSH_CONF" ] && source "$CHTSH_CONF" [ -z "$CHTSH_URL" ] && CHTSH_URL=https://cht.sh # currently we support only two modes: # * lite = access the server using curl # * auto = try standalone usage first CHTSH_MODE="$(cat "$CHTSH_HOME"/mode 2> /dev/null)" [ "$CHTSH_MODE" != lite ] && CHTSH_MODE=auto CHEATSH_INSTALLATION="$(cat "$CHTSH_HOME/standalone" 2> /dev/null)" export LESSSECURE=1 STEALTH_MAX_SELECTION_LENGTH=5 case "$(uname -s)" in Darwin) is_macos=yes ;; *) is_macos=no ;; esac # for KSH93 # shellcheck disable=SC2034,SC2039,SC2168 if echo "$KSH_VERSION" | grep -q ' 93' && ! local foo 2>/dev/null; then alias local=typeset fi fatal() { echo "ERROR: $*" >&2 exit 1 } _say_what_i_do() { [ -n "$LOG" ] && echo "$(date '+[%Y-%m-%d %H:%M%S]') $*" >> "$LOG" local this_prompt="\033[0;1;4;32m>>\033[0m" printf "\n${this_prompt}%s\033[0m\n" " $* " } cheatsh_standalone_install() { # the function installs cheat.sh with the upstream repositories # in the standalone mode local installdir; installdir="$1" local default_installdir="$HOME/.cheat.sh" [ -z "$installdir" ] && installdir=${default_installdir} if [ "$installdir" = help ]; then cat </dev/null || \ { echo "DEPENDENCY: \"$dep\" is needed to install cheat.sh in the standalone mode" >&2; _exit_code=1; } done [ "$_exit_code" -ne 0 ] && return "$_exit_code" while true; do local _installdir echo -n "Where should cheat.sh be installed [$installdir]? "; read -r _installdir [ -n "$_installdir" ] && installdir=$_installdir if [ "$installdir" = y ] \ || [ "$installdir" = Y ] \ || [ "$(echo "$installdir" | tr "[:upper:]" "[:lower:]")" = yes ] then echo Please enter the directory name echo If it was the directory name already, please prepend it with \"./\": "./$installdir" else break fi done if [ -e "$installdir" ]; then echo "ERROR: Installation directory [$installdir] exists already" echo "Please remove it first before continuing" return 1 fi if ! mkdir -p "$installdir"; then echo "ERROR: Could not create the installation directory \"$installdir\"" echo "ERROR: Please check the permissions and start the script again" return 1 fi local space_needed=700 local space_available; space_available=$(($(df -k "$installdir" | awk '{print $4}' | tail -1)/1024)) if [ "$space_available" -lt "$space_needed" ]; then echo "ERROR: Installation directory has no enough space (needed: ${space_needed}M, available: ${space_available}M" echo "ERROR: Please clean up and start the script again" rmdir "$installdir" return 1 fi _say_what_i_do Cloning cheat.sh locally local url=https://github.com/chubin/cheat.sh rmdir "$installdir" git clone "$url" "$installdir" || fatal Could not clone "$url" with git into "$installdir" cd "$installdir" || fatal "Cannot cd into $installdir" # after the repository cloned, we may have the log directory # and we can write our installation log into it mkdir -p "log/" LOG="$PWD/log/install.log" # we use tee everywhere so we should set -o pipefail set -o pipefail # currently the script uses python 2, # but cheat.sh supports python 3 too # if you want to switch it to python 3 # set PYTHON2 to NO: # PYTHON2=NO # PYTHON2=NO if [[ $PYTHON2 = YES ]]; then python="python2" pip="pip" virtualenv_python3_option=() else python="python3" pip="pip3" virtualenv_python3_option=(-p python3) fi _say_what_i_do Creating virtual environment virtualenv "${virtualenv_python3_option[@]}" ve \ || fatal "Could not create virtual environment with 'virtualenv ve'" export CHEATSH_PATH_WORKDIR=$PWD # rapidfuzz does not support Python 2, # so if we are using Python 2, install fuzzywuzzy instead if [[ $PYTHON2 = YES ]]; then sed -i s/rapidfuzz/fuzzywuzzy/ requirements.txt echo "python-Levenshtein" >> requirements.txt fi _say_what_i_do Installing python requirements into the virtual environment ve/bin/"$pip" install -r requirements.txt > "$LOG" \ || { echo "ERROR:" echo "---" tail -n 10 "$LOG" echo "---" echo "See $LOG for more" fatal Could not install python dependencies into the virtual environment } echo "$(ve/bin/"$pip" freeze | wc -l) dependencies were successfully installed" _say_what_i_do Fetching the upstream cheat sheets repositories ve/bin/python lib/fetch.py fetch-all | tee -a "$LOG" _say_what_i_do Running self-tests ( cd tests || exit if CHEATSH_TEST_STANDALONE=YES \ CHEATSH_TEST_SKIP_ONLINE=NO \ CHEATSH_TEST_SHOW_DETAILS=NO \ PYTHON=../ve/bin/python bash run-tests.sh | tee -a "$LOG" then printf "\033[0;32m%s\033[0m\n" "SUCCESS" else printf "\033[0;31m%s\033[0m\n" "FAILED" echo "Some tests were failed. Run the tests manually for further investigation:" echo " cd $PWD; bash run-tests.sh)" fi ) mkdir -p "$CHTSH_HOME" echo "$installdir" > "$CHTSH_HOME/standalone" echo auto > "$CHTSH_HOME/mode" _say_what_i_do Done local v1; v1=$(printf "\033[0;1;32m") local v2; v2=$(printf "\033[0m") cat < "$CHTSH_HOME/mode" echo "Configured mode: $mode" fi else echo "Unknown mode: $mode" echo Supported modes: echo " auto use the standalone installation first" echo " lite use the cheat sheets server directly" fi } get_query_options() { local query="$*" if [ -n "$CHTSH_QUERY_OPTIONS" ]; then case $query in *\?*) query="$query&${CHTSH_QUERY_OPTIONS}";; *) query="$query?${CHTSH_QUERY_OPTIONS}";; esac fi printf "%s" "$query" } do_query() { local query="$*" local b_opts= local uri="${CHTSH_URL}/\"\$(get_query_options $query)\"" if [ -e "$CHTSH_HOME/id" ]; then b_opts="-b \"\$CHTSH_HOME/id\"" fi eval curl "$b_opts" -s "$uri" > "$TMP1" if [ -z "$lines" ] || [ "$(wc -l "$TMP1" | awk '{print $1}')" -lt "$lines" ]; then cat "$TMP1" else ${PAGER:-$defpager} "$TMP1" fi } prepare_query() { local section="$1"; shift local input="$1"; shift local arguments="$1" local query if [ -z "$section" ] || [ x"${input}" != x"${input#/}" ]; then query=$(printf %s "$input" | sed 's@ @/@; s@ @+@g') else query=$(printf %s "$section/$input" | sed 's@ @+@g') fi [ -n "$arguments" ] && arguments="?$arguments" printf %s "$query$arguments" } get_list_of_sections() { curl -s "${CHTSH_URL}"/:list | grep -v '/.*/' | grep '/$' | xargs } gen_random_str() ( len=$1 if command -v openssl >/dev/null; then openssl rand -base64 $((len*3/4)) | awk -v ORS='' // else rdev=/dev/urandom for d in /dev/{srandom,random,arandom}; do test -r "$d" && rdev=$d done if command -v hexdump >/dev/null; then hexdump -vn $((len/2)) -e '1/1 "%02X" 1 ""' "$rdev" elif command -v xxd >/dev/null; then xxd -l $((len/2)) -ps "$rdev" | awk -v ORS='' // else cd /tmp || { echo Cannot cd into /tmp >&2; exit 1; } s= # shellcheck disable=SC2000 while [ "$(echo "$s" | wc -c)" -lt "$len" ]; do s="$s$(mktemp -u XXXXXXXXXX)" done printf "%.${len}s" "$s" fi fi ) if [ "$CHTSH_MODE" = auto ] && [ -d "$CHEATSH_INSTALLATION" ]; then curl() { # ignoring all options # currently the standalone.py does not support them anyway local opt while getopts "b:s" opt; do : done shift $((OPTIND - 1)) local url; url="$1"; shift PYTHONIOENCODING=UTF-8 "$CHEATSH_INSTALLATION/ve/bin/python" "$CHEATSH_INSTALLATION/lib/standalone.py" "${url#"$CHTSH_URL"}" "$@" } elif [ "$(uname -s)" = OpenBSD ] && [ -x /usr/bin/ftp ]; then # any better test not involving either OS matching or actual query? curl() { local opt args="-o -" while getopts "b:s" opt; do case $opt in b) args="$args -c $OPTARG";; s) args="$args -M -V";; *) echo "internal error: unsupported cURL option '$opt'" >&2; exit 1;; esac done shift $((OPTIND - 1)) /usr/bin/ftp "$args" "$@" } else command -v curl >/dev/null || { echo 'DEPENDENCY: install "curl" to use cht.sh' >&2; exit 1; } _CURL=$(command -v curl) if [ x"$CHTSH_CURL_OPTIONS" != x ]; then curl() { $_CURL "${CHTSH_CURL_OPTIONS}" "$@" } fi fi if [ "$1" = --read ]; then read -r a || a="exit" printf "%s\n" "$a" exit 0 elif [ x"$1" = x--help ] || [ -z "$1" ]; then n=${0##*/} s=$(echo "$n" | sed "s/./ /"g) cat </dev/null || echo 'DEPENDENCY: please install "wl-copy" for "copy"' >&2 else command -v xsel >/dev/null || echo 'DEPENDENCY: please install "xsel" for "copy"' >&2 fi fi command -v rlwrap >/dev/null || { echo 'DEPENDENCY: install "rlwrap" to use cht.sh in the shell mode' >&2; exit 1; } mkdir -p "$CHTSH_HOME/" lines=$(tput lines) if command -v less >/dev/null; then defpager="less -R" elif command -v more >/dev/null; then defpager="more" else defpager="cat" fi cmd_cd() { if [ $# -eq 0 ]; then section="" else new_section=$(echo "$input" | sed 's/cd *//; s@/*$@@; s@^/*@@') if [ -z "$new_section" ] || [ ".." = "$new_section" ]; then section="" else valid_sections=$(get_list_of_sections) valid=no; for q in $valid_sections; do [ "$q" = "$new_section/" ] && { valid=yes; break; }; done if [ "$valid" = no ]; then echo "Invalid section: $new_section" echo "Valid sections:" echo "$valid_sections" \ | xargs printf "%-10s\n" \ | tr ' ' . \ | xargs -n 10 \ | sed 's/\./ /g; s/^/ /' else section="$new_section" fi fi fi } cmd_copy() { if [ -z "$DISPLAY" ]; then echo copy: supported only in the Desktop version elif [ -z "$input" ]; then echo copy: Make at least one query first. else curl -s "${CHTSH_URL}"/"$(get_query_options "$query"?T)" > "$TMP1" if [ "$is_macos" != yes ]; then if [ "$XDG_SESSION_TYPE" = wayland ]; then wl-copy < "$TMP1" else xsel -bi < "$TMP1" fi else pbcopy < "$TMP1" fi echo "copy: $(wc -l "$TMP1" | awk '{print $1}') lines copied to the selection" fi } cmd_ccopy() { if [ -z "$DISPLAY" ]; then echo copy: supported only in the Desktop version elif [ -z "$input" ]; then echo copy: Make at least one query first. else curl -s "${CHTSH_URL}"/"$(get_query_options "$query"?TQ)" > "$TMP1" if [ "$is_macos" != yes ]; then if [ "$XDG_SESSION_TYPE" = wayland ]; then wl-copy < "$TMP1" else xsel -bi < "$TMP1" fi else pbcopy < "$TMP1" fi echo "copy: $(wc -l "$TMP1" | awk '{print $1}') lines copied to the selection" fi } cmd_exit() { exit 0 } cmd_help() { cat < python zip list cht.sh/python> zip list cht.sh/go> /python zip list EOF } cmd_hush() { mkdir -p "$CHTSH_HOME/" && touch "$CHTSH_HOME/.hushlogin" && echo "Initial 'use help' message was disabled" } cmd_id() { id_file="$CHTSH_HOME/id" if [ id = "$input" ]; then new_id="" else new_id=$(echo "$input" | sed 's/id *//; s/ *$//; s/ /+/g') fi if [ "$new_id" = remove ]; then if [ -e "$id_file" ]; then rm -f -- "$id_file" && echo "id is removed" else echo "id was not set, so you can't remove it" fi return fi if [ -n "$new_id" ] && [ reset != "$new_id" ] && [ "$(/bin/echo -n "$new_id" | wc -c)" -lt 16 ]; then echo "ERROR: $new_id: Too short id. Minimal id length is 16. Use 'id reset' for a random id" return fi if [ -z "$new_id" ]; then # if new_id is not specified check if we have some id already # if yes, just show it # if not, generate a new id if [ -e "$id_file" ]; then awk '$6 == "id" {print $NF}' <"$id_file" | tail -n 1 return else new_id=reset fi fi if [ "$new_id" = reset ]; then new_id=$(gen_random_str 12) else echo WARNING: if someone gueses your id, he can read your cht.sh search history fi if [ -e "$id_file" ] && grep -q '\tid\t[^\t][^\t]*$' "$id_file" 2> /dev/null; then sed -i 's/\tid\t[^\t][^\t]*$/ id '"$new_id"'/' "$id_file" else if ! [ -e "$id_file" ]; then printf '#\n\n' > "$id_file" fi printf ".cht.sh\tTRUE\t/\tTRUE\t0\tid\t$new_id\n" >> "$id_file" fi echo "$new_id" } cmd_query() { query=$(prepare_query "$section" "$input") do_query "$query" } cmd_stealth() { if [ "$input" != stealth ]; then arguments=$(echo "$input" | sed 's/stealth //; s/ /\&/') fi trap break INT if [ "$is_macos" = yes ]; then past=$(pbpaste) else if [ "$XDG_SESSION_TYPE" = wayland ]; then past=$(wl-paste -p) else past=$(xsel -o) fi fi printf "\033[0;31mstealth:\033[0m you are in the stealth mode; select any text in any window for a query\n" printf "\033[0;31mstealth:\033[0m selections longer than $STEALTH_MAX_SELECTION_LENGTH words are ignored\n" if [ -n "$arguments" ]; then printf "\033[0;31mstealth:\033[0m query arguments: ?$arguments\n" fi printf "\033[0;31mstealth:\033[0m use ^C to leave this mode\n" while true; do if [ "$is_macos" = yes ]; then current=$(pbpaste) else if [ "$XDG_SESSION_TYPE" = wayland ]; then current=$(wl-paste -p) else current=$(xsel -o) fi fi if [ "$past" != "$current" ]; then past=$current current_text="$(echo $current | tr -c '[a-zA-Z0-9]' ' ')" if [ "$(echo "$current_text" | wc -w)" -gt "$STEALTH_MAX_SELECTION_LENGTH" ]; then printf "\033[0;31mstealth:\033[0m selection length is longer than $STEALTH_MAX_SELECTION_LENGTH words; ignoring\n" continue else printf "\n\033[0;31mstealth: \033[7m $current_text\033[0m\n" query=$(prepare_query "$section" "$current_text" "$arguments") do_query "$query" fi fi sleep 1; done trap - INT } cmd_update() { [ -w "$0" ] || { echo "The script is readonly; please update manually: curl -s ${CHTSH_URL}/:cht.sh | sudo tee $0"; return; } TMP2=$(mktemp /tmp/cht.sh.XXXXXXXXXXXXX) curl -s "${CHTSH_URL}"/:cht.sh > "$TMP2" if ! cmp "$0" "$TMP2" > /dev/null 2>&1; then if grep -q ^__CHTSH_VERSION= "$TMP2"; then # section was vaildated by us already args=(--shell "$section") cp "$TMP2" "$0" && echo "Updated. Restarting..." && rm "$TMP2" && CHEATSH_RESTART=1 exec "$0" "${args[@]}" else echo "Something went wrong. Please update manually" fi else echo "cht.sh is up to date. No update needed" fi rm -f "$TMP2" > /dev/null 2>&1 } cmd_version() { insttime=$(ls -l -- "$0" | sed 's/ */ /g' | cut -d ' ' -f 6-8) echo "cht.sh version $__CHTSH_VERSION of $__CHTSH_DATETIME; installed at: $insttime" TMP2=$(mktemp /tmp/cht.sh.XXXXXXXXXXXXX) if curl -s "${CHTSH_URL}"/:cht.sh > "$TMP2"; then if ! cmp "$0" "$TMP2" > /dev/null 2>&1; then echo "Update needed (type 'update' for that)". else echo "Up to date. No update needed" fi fi rm -f "$TMP2" > /dev/null 2>&1 } TMP1=$(mktemp /tmp/cht.sh.XXXXXXXXXXXXX) trap 'rm -f $TMP1 $TMP2' EXIT trap 'true' INT if ! [ -e "$CHTSH_HOME/.hushlogin" ] && [ -z "$this_query" ]; then echo "type 'help' for the cht.sh shell help" fi while true; do if [ "$section" != "" ]; then full_prompt="$prompt/$section> " else full_prompt="$prompt> " fi input=$( rlwrap -H "$CHTSH_HOME/history" -pgreen -C cht.sh -S "$full_prompt" bash "$0" --read | sed 's/ *#.*//' ) cmd_name=${input%% *} cmd_args=${input#* } case $cmd_name in "") continue;; # skip empty input lines '?'|h|help) cmd_name=help;; hush) cmd_name=hush;; cd) cmd_name="cd";; exit|quit) cmd_name="exit";; copy|yank|c|y) cmd_name=copy;; ccopy|cc|C|Y) cmd_name=ccopy;; id) cmd_name=id;; stealth) cmd_name=stealth;; update) cmd_name=update;; version) cmd_name=version;; *) cmd_name="query"; cmd_args="$input";; esac "cmd_$cmd_name" $cmd_args done ================================================ FILE: tests/results/9 ================================================ # How do I copy a file in Python? #  # shutil (http://docs.python.org/3/library/shutil.html) has many methods # you can use. One of which is: from shutil import copyfile copyfile(src, dst) # Copy the contents of the file named src to a file named dst. The # destination location must be writable; otherwise, an IOError exception # will be raised. If dst already exists, it will be replaced. Special # files such as character or block devices and pipes cannot be copied # with this function. src and dst are path names given as strings. #  # [Swati] [so/q/123198] [cc by-sa 3.0] ================================================ FILE: tests/run-tests.sh ================================================ #!/bin/bash # 1) start server: # without caching: # CHEATSH_CACHE_TYPE=none CHEATSH_PORT=50000 python bin/srv.py # (recommended) # with caching: # CHEATSH_REDIS_PREFIX=TEST1 CHEATSH_PORT=50000 python bin/srv.py # (for complex search queries + to test caching) # 2) configure CHTSH_URL # 3) run the script CHTSH_SCRIPT=$(dirname "$(dirname "$(realpath "$(readlink -f "$0")")")")/share/cht.sh.txt # work from script's dir cd "$(dirname "$0")" || exit # detect Python - if not set in env, try default virtualenv PYTHON="${PYTHON:-../ve/bin/python}" # if no virtalenv, try current python3 binary if ! command -v "$PYTHON" &> /dev/null; then PYTHON=$(command -v python3) fi python_version="$($PYTHON -c 'import sys; print(sys.version_info[0])')" echo "Using PYTHON $python_version: $PYTHON" skip_online="${CHEATSH_TEST_SKIP_ONLINE:-NO}" test_standalone="${CHEATSH_TEST_STANDALONE:-YES}" show_details="${CHEATSH_TEST_SHOW_DETAILS:-YES}" update_tests_results="${CHEATSH_UPDATE_TESTS_RESULTS:-NO}" CHTSH_URL="${CHTSH_URL:-http://localhost:8002}" TMP=$(mktemp /tmp/cht.sh.tests-XXXXXXXXXXXXXX) TMP2=$(mktemp /tmp/cht.sh.tests-XXXXXXXXXXXXXX) TMP3=$(mktemp /tmp/cht.sh.tests-XXXXXXXXXXXXXX) trap 'rm -rf $TMP $TMP2 $TMP3' EXIT echo "Using cht.sh client at $CHTSH_SCRIPT" export PYTHONIOENCODING=UTF-8 i=0 failed=0 { if [ -z "$1" ]; then cat -n tests.txt else cat -n tests.txt | sed -n "$(echo "$*" | sed 's/ /p; /g;s/$/p/')" fi } > "$TMP3" while read -r number test_line; do echo -e "\e[34mRunning $number: \e[36m$test_line\e[0m" if [ "$skip_online" = YES ]; then if [[ $test_line = *\[online\]* ]]; then echo "$number is [online]; skipping" continue fi fi if [[ "$python_version" = 2 ]] && [[ $test_line = *\[python3\]* ]]; then echo "$number is for Python 3; skipping" continue fi if [[ "$python_version" = 3 ]] && [[ $test_line = *\[python2\]* ]]; then echo "$number is for Python 2; skipping" continue fi #shellcheck disable=SC2001 test_line=$(echo "$test_line" | sed 's@ *#.*@@') if [ "$test_standalone" = YES ]; then test_line="${test_line//cht.sh /}" [[ $show_details == YES ]] && echo "${PYTHON} ../lib/standalone.py $test_line" "${PYTHON}" ../lib/standalone.py "$test_line" > "$TMP" elif [[ $test_line = "cht.sh "* ]]; then test_line="${test_line//cht.sh /}" [[ $show_details == YES ]] && echo "bash $CHTSH_SCRIPT $test_line" eval "bash $CHTSH_SCRIPT $test_line" > "$TMP" else [[ $show_details == YES ]] && echo "curl -s $CHTSH_URL/$test_line" eval "curl -s $CHTSH_URL/$test_line" > "$TMP" fi if ! diff -u3 results/"$number" "$TMP" > "$TMP2"; then if [[ $update_tests_results = NO ]]; then if [ "$show_details" = YES ]; then cat -t "$TMP2" fi echo "FAILED: [$number] $test_line" else cat "$TMP" > results/"$number" echo "UPDATED: [$number] $test_line" fi ((failed++)) fi ((i++)) done < "$TMP3" if [[ $update_tests_results = NO ]]; then echo TESTS/OK/FAILED "$i/$((i-failed))/$failed" else echo TESTS/OK/UPDATED "$i/$((i-failed))/$failed" fi test $failed -eq 0 ================================================ FILE: tests/tests.txt ================================================ python/:list ls ls?T btrfs btrfs~volume # search on page :intro :help :cht.sh cht.sh python copy file # [online] python/copy+file # [online] python/copy+file?Q # [online] python/copy+file?QT # [online] / // python/:learn latencies # [python3] az # chubin/cheat.sheets python/rosetta/Substring # rosetta python/rosetta/Substring?T # rosetta python/rosetta/:list # rosetta js/:learn # short names check javascript/:learn # short names check emacs:go-mode/:list # special editor names mkffs.ffatt # unknown latencies # [python2]