1
1
mirror of https://github.com/chubin/cheat.sh.git synced 2024-11-27 15:13:40 +03:00

Merge branch 'master' into master

This commit is contained in:
Joey Ciechanowicz 2018-07-19 12:00:16 +01:00 committed by GitHub
commit a4e36befe4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 430 additions and 239 deletions

145
README.md
View File

@ -1,21 +1,21 @@
![cheat.sh logo](http://cheat.sh/files/big-logo-v2.png)
![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.
How it should look like?
What should it look like?
What features should it have?
* **concise** — it should be concise; it should contain only things you need and nothing else;
* **fast** — it should be possible to use it instantly;
* **comprehensive** — it should contain answers for every question you could have;
* **universal** — it should be available everywhere, as soon as needed, without any preparations;
* **unobtrusive** — it does not distract you from your main task when you are using it;
* **tutoring** — it helps you to learn the subject;
* **inconspicuous** — it should be possible to use it completely unnoticed.
* **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 does exist.
Such a thing exists.
## Contents
@ -35,18 +35,18 @@ Such a thing does exist.
* [How to add a cheat sheet repository](#how-to-add-a-cheat-sheet-repository)
## Features
## Features:
**cheat.sh**
* has simple curl/browser interface;
* covers 55 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 as well as to StackOverflow;
* available everywhere, no installation needed;
* ultrafast, returns answers, as a rule, within 100 ms;
* 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 mode (stealth mode), where it can be used fully invisibly, not even touching a key and not making a sound.
* Has a simple curl/browser interface.
* Covers 55 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.
* 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 and making sounds.
<p align="center">
<img src='https://cheat.sh/files/demo-curl.gif'/>
@ -65,7 +65,7 @@ specifying the name of the command in the query:
```
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.
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:
@ -77,8 +77,7 @@ For example, to see how you can make `snapshots` of a filesystem/volume/somethin
<img src='https://cheat.sh/files/cht.sh-url-structure.jpg'/>
</p>
Programming languages cheat sheets are located not directly in the root namespace,
but in special namespaces, dedicated to them:
The programming language cheat sheets are located in special namespaces dedicated to them.
```
curl cht.sh/go/Pointers
@ -86,21 +85,21 @@ but in special namespaces, dedicated to them:
curl cht.sh/python/lambda
```
To get the list of available programming language cheat sheets, do a special query `:list`:
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`,
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.
It could be a good starting point if you've just started learning a language.
If there is no cheat sheet for some programming language query (and it is almost always the case),
it is generated on the fly, basing on available cheat sheets and answers on StackOverflow.
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, how the answers look like:
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
@ -109,7 +108,7 @@ Try these (and your own) queries to get the impression of that, how the answers
curl cht.sh/clojure/variadic+function
```
If you don't like an answer for some of your queries, you can pick another one: for that, repeat the query with an additional parameter `/1`, `/2` etc. appended:
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
@ -169,24 +168,23 @@ 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;
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.
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,
comparing to quering the service directly with `curl`,
has several useful features:
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);
* 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
@ -216,7 +214,7 @@ It is even more convenient to start the client in a special shell mode:
cht.sh> go reverse a list
```
If all your queries are supposed to be about the same language, you can change the context of the queries
If all your queries are about the same language, you can change the context
and spare repeating the programming language name:
```
$ cht.sh --shell
@ -278,7 +276,7 @@ Type `help` for other internal `cht.sh` commands.
The `cht.sh` client has its configuration file which is located at `~/.cht.sh/cht.sh.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
For example, to switch syntax highlighting off create the file with the following
content:
```
@ -296,6 +294,9 @@ QUERY_OPTIONS="style=native"
### Tab completion
#### Bash Tab completion
To activate tab completion support for `cht.sh`, add the `:bash_completion` script to your `~/.bashrc`:
```
@ -304,16 +305,25 @@ To activate tab completion support for `cht.sh`, add the `:bash_completion` scri
$ # 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`:
```
$ curl https://cheat.sh/:zsh > ~/.zsh.d/_cht
$ echo 'fpath=(~/.zsh.d/ $fpath)' >> ~/.zshrc
$ # Open a new shell to load the plugin
```
### Stealth mode
One of the important properties of any real cheat sheet,
is that it could be used fully unnoticed.
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 some cheat sheet.
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 it is added
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.
@ -368,7 +378,7 @@ 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)
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
@ -384,20 +394,20 @@ She: Yes! Perfect! Exactly what I wanted to see! |
```
Or course, it is just fun, and you should never cheat in your coding interviews,
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)
## Editors integration
You can use *cheat.sh* directly from the editor (*Vim* and *Emacs* are currently supported).
You can use *cheat.sh* directly from the editor (*Vim*, *Emacs* and *Visual Studio Code* are currently supported).
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 how it looks like in Vim:
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 `<leader>KK`. You will get
@ -472,10 +482,25 @@ Or, if you want to scroll and/or pause, the same on YouTube:
[![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 <kbd>⌘ Command</kbd> + <kbd>⇧ Shift</kbd> + <kbd>p</kbd>
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)
## Special pages
There are several special pages (their names are always starting with a colon),
that are not cheat sheets and have special meaning.
There are several special pages that are not cheat sheets.
Their names start with colon and have special meaning.
Getting started:
@ -519,7 +544,7 @@ To search for a keyword, use the query:
/~keyword
```
In this case search is not recursive — it is conducted only in a pages of the specified level.
In this case search is not recursive — it is conducted only in a page of the specified level.
For example:
```
@ -551,7 +576,7 @@ List of search options:
Cheat sheets related to programming languages
are organized in namespaces (subdirectories), that are named according
to the programming languages.
to the programming language.
For each supported programming language
there are several special cheat sheets: its own sheet, `hello`, `:list` and `:learn`.
@ -669,14 +694,14 @@ Pie diagram reflecting cheat sheets sources distribution (by number of cheat she
### 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 in the bottom of the page: the second one is the button
of the repository, whom belongs the current cheat sheet.
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 a edit button in the top right corner. If you click on it, an editor will be open.
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 upstrem repository is forked, your changes are
commited in the forked repository, a pull request to the upstream repository owner is sent).
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)

View File

@ -20,10 +20,10 @@ import requests
import jinja2
from flask import Flask, request, send_from_directory, redirect, Response
MYDIR = os.path.abspath(os.path.dirname(os.path.dirname('__file__')))
MYDIR = os.path.abspath(os.path.join(__file__, '..', '..'))
sys.path.append("%s/lib/" % MYDIR)
from globals import FILE_QUERIES_LOG, LOG_FILE, TEMPLATES, STATIC
from globals import FILE_QUERIES_LOG, LOG_FILE, TEMPLATES, STATIC, MALFORMED_RESPONSE_HTML_PAGE
from limits import Limits
from cheat_wrapper import cheat_wrapper
from post import process_post_request
@ -49,9 +49,10 @@ def is_html_needed(user_agent):
Basing on `user_agent`, return whether it needs HTML or ANSI
"""
plaintext_clients = ['curl', 'wget', 'fetch', 'httpie', 'lwp-request', 'python-requests']
if any([x in user_agent for x in plaintext_clients]):
return False
return True
return all([x not in user_agent for x in plaintext_clients])
def is_result_a_script(query):
return query in [':cht.sh']
@app.route('/files/<path:path>')
def send_static(path):
@ -193,10 +194,15 @@ def answer(topic=None):
if not_allowed:
return "429 %s\n" % not_allowed, 429
result, found = cheat_wrapper(topic, request_options=options, html=is_html_needed(user_agent))
html_is_needed = is_html_needed(user_agent) and not is_result_a_script(topic)
result, found = cheat_wrapper(topic, request_options=options, html=html_is_needed)
if 'Please come back in several hours' in result and html_is_needed:
return MALFORMED_RESPONSE_HTML_PAGE
log_query(ip_address, found, topic, user_agent)
return result
if html_is_needed:
return result
return Response(result, mimetype='text/plain')
SRV = WSGIServer(("", 8002), app) # log=None)
SRV.serve_forever()

View File

@ -7,8 +7,8 @@ Supports three modes of normalization and commenting:
2. Add comments
3. Remove text, leave code only
Since several operations are quite expensice,
actively uses caching.
Since several operations are quite expensive,
it actively uses caching.
Exported functions:
@ -33,7 +33,7 @@ from tempfile import NamedTemporaryFile
import redis
MYDIR = os.path.abspath(os.path.dirname(os.path.dirname('__file__')))
MYDIR = os.path.abspath(os.path.join(__file__, '..', '..'))
sys.path.append("%s/lib/" % MYDIR)
from languages_data import VIM_NAME
from globals import PATH_VIM_ENVIRONMENT
@ -41,35 +41,37 @@ from globals import PATH_VIM_ENVIRONMENT
REDIS = redis.StrictRedis(host='localhost', port=6379, db=1)
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 _cleanup_lines(lines):
"""
Cleanup `lines` a little bit: remove empty lines at the beginning
and at the end; remove to much empty lines in between.
"""
if lines == []:
return lines
# remove empty lines from the beginning
def _remove_empty_lines_from_beginning(lines):
start = 0
while start < len(lines) and lines[start].strip() == '':
start += 1
lines = lines[start:]
if lines == []:
return lines
return lines
# remove empty lines from the end
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 [''])
@ -78,7 +80,7 @@ def _cleanup_lines(lines):
return lines
def _classify_lines(lines):
def _line_type(line):
"""
Classify each line and say which of them
are text (0) and which of them are code (1).
@ -90,39 +92,40 @@ def _classify_lines(lines):
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 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
def _line_type(line):
if line.strip() == '':
return -1
# 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
# 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 0
if line.startswith(' '):
return CODE
return TEXT
if line.startswith(' '):
return 1
return 0
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] == 1 and line_types[i+1] == -1:
line_types[i+1] = -2
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] == -1 and line_types[i+1] == 1:
line_types[i] = -2
if line_types[i] == UNDEFINED and line_types[i+1] == CODE:
line_types[i] = CODE_WHITESPACE
changed = True
line_types = [1 if x == -2 else x for x in line_types]
line_types = [CODE if x == CODE_WHITESPACE else x for x in line_types]
# pass 3:
# fixing undefined line types (-1)
@ -133,51 +136,49 @@ def _classify_lines(lines):
# changing all lines types that are near the text
for i in range(len(line_types) - 1):
if line_types[i] == 0 and line_types[i+1] == -1:
line_types[i+1] = 0
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] == -1 and line_types[i+1] == 0:
line_types[i] = 0
if line_types[i] == UNDEFINED and line_types[i+1] == TEXT:
line_types[i] = TEXT
changed = True
# everything what is still undefined, change to 1
line_types = [1 if x == -1 else x for x in line_types]
# 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 line.startswith(' '):
# return line[4:]
if shift == -1 and line != '':
return ' ' + line
if shift > 0:
if line.startswith(' '*shift):
return line[shift:]
return line
def _wrap_lines(lines_classes, unindent_code=False):
"""
Wrap classified lines. Add the splitted lines to the stream.
Wrap classified lines. Add the split lines to the stream.
If `unindent_code` is True, remove leading four spaces.
"""
def _unindent_code(line, shift=0):
#if line.startswith(' '):
# return line[4:]
if shift == -1 and line != '':
return ' ' + line
if shift > 0:
if line.startswith(' '*shift):
return line[shift:]
return line
result = []
for line_tuple in lines_classes:
if line_tuple[0] == 1:
if unindent_code:
shift = 3 if unindent_code is True else unindent_code
else:
shift = -1
result.append((line_tuple[0], _unindent_code(line_tuple[1], shift=shift)))
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_tuple[1].strip() == "":
result.append((line_tuple[0], ""))
for line in textwrap.fill(line_tuple[1]).splitlines():
result.append((line_tuple[0], line))
if line_content.strip() == "":
result.append((line_type, ""))
for line in textwrap.fill(line_content).splitlines():
result.append((line_type, line))
return result

View File

@ -17,11 +17,11 @@ import colored
from pygments import highlight as pygments_highlight
from pygments.formatters import Terminal256Formatter # pylint: disable=no-name-in-module
MYDIR = os.path.abspath(os.path.dirname(os.path.dirname('__file__')))
MYDIR = os.path.abspath(os.path.join(__file__, '..', '..'))
sys.path.append("%s/lib/" % MYDIR)
from globals import error, ANSI2HTML, COLOR_STYLES
from buttons import TWITTER_BUTTON, GITHUB_BUTTON, GITHUB_BUTTON_FOOTER
from languages_data import LEXER
from languages_data import LEXER, LANGUAGE_ALIAS
from get_answer import get_topic_type, get_topics_list, get_answer, find_answer_by_keyword
from beautifier import code_blocks
# import beautifier
@ -92,13 +92,12 @@ def _colorize_ansi_answer(topic, answer, color_style, # pylint: di
color_style = color_style or "native"
lexer_class = LEXER['bash']
for lexer_name, lexer_value in LEXER.items():
if topic.startswith("%s/" % lexer_name):
# color_style = color_style or "monokai"
if lexer_name == 'php':
answer = "<?\n%s?>\n" % answer
lexer_class = lexer_value
break
if '/' in topic:
section_name = topic.split('/', 1)[0].lower()
section_name = LANGUAGE_ALIAS.get(section_name, section_name)
lexer_class = LEXER.get(section_name, lexer_class)
if section_name == 'php':
answer = "<?\n%s?>\n" % answer
if highlight_all:
highlight = lambda answer: pygments_highlight(
@ -179,7 +178,7 @@ def _render_html(query, result, editable, repository_button, request_options):
edit_button = ''
if editable:
# It's possible that topic directory starts with omited underscore
# 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
@ -258,17 +257,20 @@ def _visualize(query, keyword, answers, request_options, html=None): # pylint: d
else:
repository_button = _github_button(topic_type)
if html:
if html and query:
result = _render_html(
query, result, editable, repository_button, request_options)
return result, found
def _sanitize_query(query):
return re.sub('[<>"]', '', query)
def cheat_wrapper(query, request_options=None, html=False):
"""
Giant megafunction that delivers cheat sheet for `query`.
If `html` is True, the answer is formated as HTML.
If `html` is True, the answer is formatted as HTML.
Additional request options specified in `request_options`.
This function is really really bad, and should be rewritten
@ -297,6 +299,8 @@ def cheat_wrapper(query, request_options=None, html=False):
return topic, keyword, search_options
query = _sanitize_query(query)
# at the moment, we just remove trailing slashes
# so queries python/ and python are equal
query = _strip_hyperlink(query.rstrip('/'))

88
lib/colorize_internal.py Normal file
View File

@ -0,0 +1,88 @@
"""
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,
},
}
PALETTES_REVERSE = {
0: {
1: Back.WHITE + Fore.BLACK,
2: Style.DIM,
},
1: {
1: Back.CYAN + Fore.BLACK,
2: Style.DIM,
},
2: {
1: Back.RED + Fore.BLACK,
2: Style.DIM,
},
}
def colorize_internal(text, palette_number=1):
"""
Colorize `text`, use `palette`
"""
palette = PALETTES[palette_number]
palette_reverse = PALETTES_REVERSE[palette_number]
def _colorize_curlies_block(text):
text = text.group()[1:-1]
factor = 1
if text.startswith('-'):
text = text[1:]
factor = -1
stripped = text.lstrip('0123456789')
color_number = int(text[:len(text)-len(stripped)])*factor
if stripped.startswith('='):
stripped = stripped[1:]
reverse = False
if color_number < 0:
color_number = -color_number
reverse = True
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

View File

@ -20,7 +20,6 @@ import os
import re
import redis
from fuzzywuzzy import process, fuzz
from langdetect import detect
from polyglot.detect import Detector
from polyglot.detect.base import UnknownLanguage
import time
@ -56,48 +55,60 @@ INTERNAL_TOPICS = [
':share',
]
COLORIZED_INTERNAL_TOPICS = [
':intro',
]
def _get_filenames(path):
return [os.path.split(topic)[1] for topic in glob.glob(path)]
def _update_tldr_topics():
answer = []
for topic in glob.glob(PATH_TLDR_PAGES):
_, filename = os.path.split(topic)
if filename.endswith('.md'):
answer.append(filename[:-3])
return answer
TLDR_TOPICS = _update_tldr_topics()
return [filename[:-3]
for filename in _get_filenames(PATH_TLDR_PAGES) if filename.endswith('.md')]
def _update_cheat_topics():
answer = []
for topic in glob.glob(PATH_CHEAT_PAGES):
_, filename = os.path.split(topic)
answer.append(filename)
return answer
return _get_filenames(PATH_CHEAT_PAGES)
TLDR_TOPICS = _update_tldr_topics()
CHEAT_TOPICS = _update_cheat_topics()
def _remove_initial_underscore(filename):
if filename.startswith('_'):
filename = filename[1:]
return filename
def _sanitize_dirname(dirname):
dirname = os.path.basename(dirname)
dirname = _remove_initial_underscore(dirname)
return dirname
def _format_answer(dirname, filename):
return "%s/%s" % (_sanitize_dirname(dirname), filename)
def _get_answer_files_from_folder():
topics = map(os.path.split, glob.glob(PATH_CHEAT_SHEETS + "*/*"))
return [_format_answer(dirname, filename)
for dirname, filename in topics if filename not in ['_info.yaml']]
def _isdir(topic):
return os.path.isdir(topic)
def _get_answers_and_dirs():
topics = glob.glob(PATH_CHEAT_SHEETS + "*")
answer_dirs = [_remove_initial_underscore(os.path.split(topic)[1]).rstrip('/')+'/'
for topic in topics if _isdir(topic)]
answers = [os.path.split(topic)[1] for topic in topics if not _isdir(topic)]
return answer_dirs, answers
def _update_cheat_sheets_topics():
answer = []
answer_dirs = []
answers = _get_answer_files_from_folder()
cheatsheet_answers, cheatsheet_dirs = _get_answers_and_dirs()
return answers+cheatsheet_answers, cheatsheet_dirs
for topic in glob.glob(PATH_CHEAT_SHEETS + "*/*"):
dirname, filename = os.path.split(topic)
if filename in ['_info.yaml']:
continue
dirname = os.path.basename(dirname)
if dirname.startswith('_'):
dirname = dirname[1:]
answer.append("%s/%s" % (dirname, filename))
for topic in glob.glob(PATH_CHEAT_SHEETS + "*"):
_, filename = os.path.split(topic)
if os.path.isdir(topic):
if filename.startswith('_'):
filename = filename[1:]
answer_dirs.append(filename+'/')
else:
answer.append(filename)
return answer, answer_dirs
CHEAT_SHEETS_TOPICS, CHEAT_SHEETS_DIRS = _update_cheat_sheets_topics()
CACHED_TOPICS_LIST = [[]]
def get_topics_list(skip_dirs=False, skip_internal=False):
"""
List of topics returned on /:list
@ -158,10 +169,13 @@ def get_topic_type(topic): # pylint: disable=too-many-locals,too-many-branches,t
if '+' in topic_name:
result = 'question'
else:
if topic_type in _get_topics_dirs() and topic_name in [':list']:
#if topic_type in _get_topics_dirs() and topic_name in [':list']:
if topic_name in [':list']:
result = "internal"
elif is_valid_learnxy(topic):
result = 'learnxiny'
elif topic_name in [':learn']:
result = "internal"
else:
# let us activate the 'question' feature for all subsections
result = 'question'
@ -199,22 +213,19 @@ def _get_internal(topic):
if x.startswith(topic_type + "/")]
return "\n".join(topic_list)+"\n"
answer = ""
if topic == ":list":
return "\n".join(x for x in get_topics_list()) + "\n"
answer = "\n".join(x for x in get_topics_list()) + "\n"
elif topic == ':styles':
answer = "\n".join(COLOR_STYLES) + "\n"
elif topic == ":stat":
answer = _get_stat()+"\n"
elif topic in INTERNAL_TOPICS:
answer = open(os.path.join(MYDIR, "share", topic[1:]+".txt"), "r").read()
if topic in COLORIZED_INTERNAL_TOPICS:
answer = colorize_internal(answer)
if topic == ':styles':
return "\n".join(COLOR_STYLES) + "\n"
if topic == ":stat":
return _get_stat()+"\n"
if topic in INTERNAL_TOPICS:
if topic[1:] == 'intro':
return colorize_internal(open(os.path.join(MYDIR, "share", topic[1:]+".txt"), "r").read())
else:
return open(os.path.join(MYDIR, "share", topic[1:]+".txt"), "r").read()
return ""
return answer
def _get_tldr(topic):
cmd = ["tldr", topic]
@ -245,12 +256,15 @@ def _get_cheat(topic):
def _get_cheat_sheets(topic):
"""
Get the cheat sheet topic from the own repository (cheat.sheets).
It's possible that topic directory starts with omited underscore
It's possible that topic directory starts with omitted underscore
"""
filename = PATH_CHEAT_SHEETS + "%s" % topic
if not os.path.exists(filename):
filename = PATH_CHEAT_SHEETS + "_%s" % topic
return open(filename, "r").read().decode('utf-8')
if os.path.isdir(filename):
return ""
else:
return open(filename, "r").read().decode('utf-8')
def _get_cheat_sheets_dir(topic):
answer = []
@ -310,7 +324,7 @@ def _get_unknown(topic):
possible_topics_text = "\n".join([(" * %s %s" % x) for x in possible_topics])
return """
Unknown topic.
Do you mean one of these topics may be?
Do you mean one of these topics maybe?
%s
""" % possible_topics_text

View File

@ -1,6 +1,6 @@
"""
Global configuration of the project.
All hardcoded pathes should be (theoretically) here.
All hardcoded paths should be (theoretically) here.
"""
from __future__ import print_function
@ -8,7 +8,7 @@ import logging
import os
from pygments.styles import get_all_styles
MYDIR = os.path.abspath(os.path.dirname(os.path.dirname('__file__')))
MYDIR = os.path.abspath(os.path.join(__file__, '..', '..'))
ANSI2HTML = os.path.join(MYDIR, "share/ansi2html.sh")
@ -25,6 +25,8 @@ PATH_CHEAT_SHEETS_SPOOL = "/home/igor/cheat.sheets/spool/"
COLOR_STYLES = sorted(list(get_all_styles()))
MALFORMED_RESPONSE_HTML_PAGE = open(os.path.join(STATIC, 'malformed-response.html')).read()
def error(text):
"""
Log error `text` and produce a RuntimeError exception

View File

@ -62,6 +62,7 @@ LEXER = {
"tcsh" : pygments.lexers.TcshLexer,
"vb" : pygments.lexers.VbNetLexer,
"vbnet" : pygments.lexers.VbNetLexer,
"vim" : pygments.lexers.VimLexer,
# experimental
"arduino": pygments.lexers.ArduinoLexer,

View File

@ -6,7 +6,7 @@ import itertools
"""
After panela will be ready for it, it will be splitted out in a separate project,
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)
@ -206,7 +206,7 @@ class Panela:
"""
Paste <panela> starting at <x1>, <y1>.
If <extend> is True current panela space will be automatically extended
If <transparence> is True, then <panela> is overlayed and characters behind them are seen
If <transparence> is True, then <panela> is overlaid and characters behind them are seen
"""
# FIXME:
@ -278,7 +278,7 @@ class Panela:
def put_point(self, col, row, char=None, color=None, background=None):
"""
Puts charachter with color and background color on the field.
Puts character with color and background color on the field.
Char can be a Point or a character.
"""
@ -305,7 +305,7 @@ class Panela:
def put_line(self, x1, y1, x2, y2, char=None, color=None, background=None):
"""
Draw line (x1, y1) - (x2, y2) fith foreground color <color>, background color <background>
and charachter <char>, if specified.
and character <char>, if specified.
"""
def get_line(start, end):

View File

@ -453,7 +453,7 @@ function encode(string,start,end,i,ret,pos,sc,buf) {
}
if(dumpStatus==dsNew) {
# After moving/clearing we are now ready to write
# somthing to the screen so start recording now
# something to the screen so start recording now
ret=ret"\n"
dumpStatus=dsActive
}

View File

@ -29,6 +29,11 @@ __CHTSH_DATETIME="2018-07-08 22:26:46 +0200"
export LESSSECURE=1
STEALTH_MAX_SELECTION_LENGTH=5
case "$OSTYPE" in
darwin*) is_macos=yes ;;
*) is_macos=no ;;
esac
get_query_options()
{
local query="$*"
@ -141,7 +146,9 @@ else
fi
fi
which xsel >& /dev/null || { echo 'DEPENDENCY: please install "xsel" for "copy"' >&2; }
if [ "$is_macos" != yes ]; then
which xsel >& /dev/null || { echo 'DEPENDENCY: please install "xsel" for "copy"' >&2; }
fi
which rlwrap >& /dev/null || { echo 'DEPENDENCY: install "rlwrap" to use cht.sh in the shell mode' >&2; exit 1; }
which curl >& /dev/null || { echo 'DEPENDENCY: install "curl" to use cht.sh' >&2; exit 1; }
@ -149,7 +156,7 @@ mkdir -p "$HOME/.cht.sh/"
lines=$(tput lines)
TMP1=$(mktemp /tmp/cht.sh.XXXXXXXXXXXXX)
trap 'rm -rf $TMP1 $TMP2' EXIT
trap 'rm -f $TMP1 $TMP2' EXIT
trap 'true' INT
if ! [ -e "$HOME/.cht.sh/.hushlogin" ] && [ -z "$this_query" ]; then
@ -227,7 +234,11 @@ EOF
continue
else
curl -s https://cht.sh/"$(get_query_options "$query"?T)" > "$TMP1"
xsel -i < "$TMP1"
if [ "$is_macos" != yes ]; then
xsel -i < "$TMP1"
else
cat "$TMP1" | pbcopy
fi
echo "copy: $(wc -l "$TMP1" | awk '{print $1}') lines copied to the selection"
continue
fi
@ -242,7 +253,11 @@ EOF
continue
else
curl -s https://cht.sh/"$(get_query_options "$query"?TQ)" > "$TMP1"
xsel -i < "$TMP1"
if [ "$is_macos" != yes ]; then
xsel -i < "$TMP1"
else
cat "$TMP1" | pbcopy
fi
echo "copy: $(wc -l "$TMP1" | awk '{print $1}') lines copied to the selection"
continue
fi
@ -299,7 +314,11 @@ EOF
arguments=$(echo "$input" | sed 's/stealth //; s/ /\&/')
fi
trap break SIGINT
past=$(xsel -o)
if [ "$is_macos" == yes ]; then
past=$(pbpaste)
else
past=$(xsel -o)
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
@ -307,7 +326,11 @@ EOF
fi
printf "\033[0;31mstealth:\033[0m use ^C to leave this mode\n"
while true; do
current=$(xsel -o)
if [ $is_macos == yes ]; then
current=$(pbpaste)
else
current=$(xsel -o)
fi
if [ "$past" != "$current" ]; then
past=$current
current_text="$(echo $current | tr -c '[a-zA-Z0-9]' ' ')"

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,35 @@
<html>
<title>cheat.sh</title>
<head>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
</head>
<body style='background:black'>
<pre style='color:#bbbbbb'>
# 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
<a href="https://twitter.com/igor_chubin" class="twitter-follow-button" data-show-count="false" data-button="grey">Follow @igor_chubin</a> <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script><span style='position: relative; bottom: 6px;'>for the updates. </span>
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 <span style='position: relative; bottom: -6px;'><a aria-label="Star chubin/cheat.sh on GitHub" data-count-aria-label="# stargazers on GitHub" data-count-api="/repos/chubin/cheat.sh#stargazers_count" data-count-href="/chubin/cheat.sh/stargazers" data-icon="octicon-star" href="https://github.com/chubin/cheat.sh" class="github-button">cheat.sh</a></span> repository
to see how the scalability problem is (not yet) solved.
</pre>
<blockquote class="twitter-tweet" data-theme="dark" data-lang="en"><p lang="en" dir="ltr">How to get an instant answer to any question on (almost) any programming language from the command line:<br>$ curl <a href="https://t.co/9A97GQdoKB">https://t.co/9A97GQdoKB</a><br>$ curl <a href="https://t.co/F2XAJAIzDJ">https://t.co/F2XAJAIzDJ</a><br>$ curl <a href="https://t.co/XAKEDhzWjL">https://t.co/XAKEDhzWjL</a> <a href="https://t.co/HRr3mQGCJB">pic.twitter.com/HRr3mQGCJB</a></p>&mdash; Igor Chubin (@igor_chubin) <a href="https://twitter.com/igor_chubin/status/1014590497330028551?ref_src=twsrc%5Etfw">July 4, 2018</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<script async defer id="github-bjs" src="https://buttons.github.io/buttons.js"></script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

View File

@ -1,30 +1,22 @@
### Temporary disabled zsh support for cheat.sh
### This code has to be fixed
###
### # add this to ~/.zshrc or any other place that will be sourced by your .dotfiles script
###
### cheat.sh()
### {
### # replace native with the color scheme you want
### # curl cheat.sh/:styles-demo to show the available color schemes
### curl -s cheat.sh/"$1"?style=native
### }
###
### _cheatsh_complete_cheatsh()
### {
### local cur opts #prev
### _get_comp_words_by_ref -n : cur
###
### COMPREPLY=()
### prev="${COMP_WORDS[COMP_CWORD-1]}"
### opts="$(curl -s cheat.sh/:list)"
###
### #if [[ "${cur}" == ch ]] ; then
### COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
### __ltrim_colon_completions "$cur"
### return 0
### #fi
### }
###
### compdef _cheatsh_complete_cheatsh cheat.sh
#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