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:
commit
a4e36befe4
145
README.md
145
README.md
@ -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)
|
||||
|
||||
|
20
bin/srv.py
20
bin/srv.py
@ -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()
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
88
lib/colorize_internal.py
Normal 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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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]' ' ')"
|
||||
|
BIN
share/static/big-logo-v2-fixed.png
Normal file
BIN
share/static/big-logo-v2-fixed.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
@ -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>— 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>
|
||||
|
BIN
share/static/vscode-snippet-demo.gif
Normal file
BIN
share/static/vscode-snippet-demo.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 188 KiB |
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user