forked from Mirrors/helix
Compare commits
136 Commits
main
...
old-main-2
Author | SHA1 | Date |
---|---|---|
Trivernis | d623ac94a8 | 2 years ago |
trivernis | ab350b9b9f | 2 years ago |
Trivernis | 806b1a168d | 2 years ago |
trivernis | 8046381de4 | 2 years ago |
Trivernis | 696bf492b5 | 2 years ago |
trivernis | cd76c9fd2e | 2 years ago |
trivernis | 41b9e81f61 | 2 years ago |
Trivernis | b01774deef | 2 years ago |
trivernis | 8c87021a53 | 2 years ago |
trivernis | 7ccbea2a31 | 2 years ago |
trivernis | 82adbb35ab | 2 years ago |
LazyTanuki | cd8a7de454 | 2 years ago |
LazyTanuki | d9e342796e | 2 years ago |
LazyTanuki | 18945587ff | 2 years ago |
LazyTanuki | cfcf2ff4ff | 2 years ago |
LazyTanuki | 63051a7163 | 2 years ago |
LazyTanuki | 55de407681 | 2 years ago |
LazyTanuki | 7d6b2cbbf6 | 2 years ago |
LazyTanuki | d4c3609c43 | 2 years ago |
Trivernis | 27e965ef2f | 2 years ago |
trivernis | bd32cb3114 | 2 years ago |
trivernis | 432522e724 | 2 years ago |
Trivernis | bfb2ce8a7a | 2 years ago |
Trivernis | fd8c4eda69 | 2 years ago |
trivernis | 1d86a9bdcc | 2 years ago |
Trivernis | a40aa917c6 | 2 years ago |
trivernis | 2f169b172f | 2 years ago |
wongjiahau | cf9669f276 | 2 years ago |
wongjiahau | 88ac941407 | 2 years ago |
wongjiahau | f37c795c96 | 2 years ago |
wongjiahau | eebff622de | 2 years ago |
wongjiahau | a331e52971 | 2 years ago |
wongjiahau | f5aec54fe2 | 2 years ago |
wongjiahau | 33542e9ddb | 2 years ago |
wongjiahau | 898c1670d1 | 2 years ago |
wongjiahau | 404f950b09 | 2 years ago |
wongjiahau | ee34720a31 | 2 years ago |
wongjiahau | 1be2ac286b | 2 years ago |
wongjiahau | e5dfde2a9b | 2 years ago |
wongjiahau | afda68a11d | 2 years ago |
wongjiahau | f5af209f09 | 2 years ago |
wongjiahau | 52be2e0c43 | 2 years ago |
wongjiahau | 41ebc30ea6 | 2 years ago |
wongjiahau | 8b561e2e88 | 2 years ago |
wongjiahau | 9a1aff25bd | 2 years ago |
wongjiahau | 178086767f | 2 years ago |
wongjiahau | c4c3e8075e | 2 years ago |
wongjiahau | 54b16936db | 2 years ago |
wongjiahau | 20241fb256 | 2 years ago |
wongjiahau | eb9287d816 | 2 years ago |
WJH | 1108c883c4 | 2 years ago |
wongjiahau | d043ea4db4 | 2 years ago |
wongjiahau | 10032eb156 | 2 years ago |
wongjiahau | 7ccee10297 | 2 years ago |
wongjiahau | 9726ae7dbb | 2 years ago |
wongjiahau | e991ed9b17 | 2 years ago |
wongjiahau | d1e6a21016 | 2 years ago |
wongjiahau | d62b487321 | 2 years ago |
wongjiahau | 80a2f8642c | 2 years ago |
wongjiahau | aa6780e149 | 2 years ago |
wongjiahau | bc62b7615d | 2 years ago |
wongjiahau | 31c0e84461 | 2 years ago |
wongjiahau | d3db1b6204 | 2 years ago |
wongjiahau | 8ef95ee56a | 2 years ago |
wongjiahau | a4943a7226 | 2 years ago |
wongjiahau | c2e2f050da | 2 years ago |
wongjiahau | 43b226a2ab | 2 years ago |
wongjiahau | a2cb28d1d1 | 2 years ago |
wongjiahau | 19d436ee56 | 2 years ago |
wongjiahau | b18a9746e9 | 2 years ago |
wongjiahau | 8379669742 | 2 years ago |
wongjiahau | fae4990444 | 2 years ago |
wongjiahau | 7e4feb02ef | 2 years ago |
wongjiahau | 4a0c620b77 | 2 years ago |
wongjiahau | c0073edebf | 2 years ago |
WJH | c3b8be978e | 2 years ago |
wongjiahau | d578f8af61 | 2 years ago |
wongjiahau | 5d600fef0f | 2 years ago |
wongjiahau | ef1850295b | 2 years ago |
wongjiahau | 601f2c4e5f | 2 years ago |
wongjiahau | ba00a80037 | 2 years ago |
wongjiahau | 72b845da15 | 2 years ago |
wongjiahau | 24b50bb525 | 2 years ago |
wongjiahau | 38ef079099 | 2 years ago |
wongjiahau | b5d92aca45 | 2 years ago |
wongjiahau | 36769cb3f6 | 2 years ago |
wongjiahau | dffbc15067 | 2 years ago |
wongjiahau | cf9b60a3d1 | 2 years ago |
wongjiahau | 9205117505 | 2 years ago |
wongjiahau | 6af9a06e74 | 2 years ago |
wongjiahau | 899491ba25 | 2 years ago |
wongjiahau | f9ff01dd9c | 2 years ago |
wongjiahau | 7b63fda7d2 | 2 years ago |
wongjiahau | 6321dc9ade | 2 years ago |
wongjiahau | 78bb29732a | 2 years ago |
wongjiahau | bcb1672378 | 2 years ago |
wongjiahau | a259c205c0 | 2 years ago |
wongjiahau | 2e7709e505 | 2 years ago |
wongjiahau | 2e654a0775 | 2 years ago |
wongjiahau | 2a60662e8b | 2 years ago |
wongjiahau | 64059fba47 | 2 years ago |
wongjiahau | c88164f2fa | 2 years ago |
wongjiahau | 4dfa8696bd | 2 years ago |
wongjiahau | f0a4b109ad | 2 years ago |
wongjiahau | 70984fd148 | 2 years ago |
wongjiahau | 0f8e0a51ba | 2 years ago |
wongjiahau | ef73559a8e | 2 years ago |
wongjiahau | 30bac647ef | 2 years ago |
wongjiahau | c8578ba3cc | 2 years ago |
wongjiahau | 374b8ddd4e | 2 years ago |
wongjiahau | 94e2c2989b | 2 years ago |
wongjiahau | 72495363f1 | 2 years ago |
wongjiahau | 9bd534bb6f | 2 years ago |
wongjiahau | 85fa1c56b7 | 2 years ago |
wongjiahau | a079477a23 | 2 years ago |
wongjiahau | 56056e8556 | 2 years ago |
wongjiahau | b38a941955 | 2 years ago |
wongjiahau | 790192dfd4 | 2 years ago |
wongjiahau | 2c221f0af1 | 2 years ago |
wongjiahau | 35ffc6036d | 2 years ago |
wongjiahau | 2bafac0c4e | 2 years ago |
wongjiahau | ddb7564809 | 2 years ago |
wongjiahau | ec2059bf93 | 2 years ago |
wongjiahau | 52a26ff72c | 2 years ago |
wongjiahau | 5a5a1de4b8 | 2 years ago |
wongjiahau | 44b46dda6a | 2 years ago |
wongjiahau | 2af8b41007 | 2 years ago |
wongjiahau | 458fa1ca58 | 2 years ago |
wongjiahau | 0f8b641a5d | 2 years ago |
wongjiahau | 82fe4a309d | 2 years ago |
wongjiahau | bdab93e856 | 2 years ago |
wongjiahau | aa397ef801 | 2 years ago |
wongjiahau | d04a1ce214 | 2 years ago |
wongjiahau | c446c39645 | 2 years ago |
wongjiahau | d9d4daa87d | 2 years ago |
cossonleo | b652f96449 | 2 years ago |
@ -1,2 +1,5 @@
|
||||
# Things that we don't want ripgrep to search that we do want in git
|
||||
# https://github.com/BurntSushi/ripgrep/blob/master/GUIDE.md#automatic-filtering
|
||||
|
||||
# Minified JS vendored from mdbook
|
||||
book/theme/highlight.js
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,231 +0,0 @@
|
||||
html {
|
||||
font-family: "Inter", sans-serif;
|
||||
}
|
||||
|
||||
.sidebar .sidebar-scrollbox {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.chapter {
|
||||
margin: 0.25rem 0;
|
||||
}
|
||||
|
||||
.chapter li.chapter-item {
|
||||
line-height: initial;
|
||||
margin: 0;
|
||||
padding: 1rem 1.5rem;
|
||||
}
|
||||
|
||||
.chapter .section li.chapter-item {
|
||||
line-height: inherit;
|
||||
padding: .5rem .5rem 0 .5rem;
|
||||
}
|
||||
|
||||
.content {
|
||||
overflow-y: auto;
|
||||
padding: 0 15px;
|
||||
padding-bottom: 50px;
|
||||
}
|
||||
|
||||
/* 2 1.75 1.5 1.25 1 .875 */
|
||||
.content h1 { font-size: 2em }
|
||||
.content h2 { font-size: 1.75em }
|
||||
.content h3 { font-size: 1.5em }
|
||||
.content h4 { font-size: 1.25em }
|
||||
.content h5 { font-size: 1em }
|
||||
.content h6 { font-size: .875em }
|
||||
|
||||
.content h1,
|
||||
.content h2,
|
||||
.content h3,
|
||||
.content h4 {
|
||||
font-weight: 500;
|
||||
margin-top: 1.275em;
|
||||
margin-bottom: .875em;
|
||||
}
|
||||
|
||||
.content p,
|
||||
.content ol,
|
||||
.content ul,
|
||||
.content table {
|
||||
margin-top: 0;
|
||||
margin-bottom: .875em;
|
||||
}
|
||||
|
||||
.content ul li {
|
||||
margin-bottom: .25rem;
|
||||
}
|
||||
|
||||
.content ul {
|
||||
list-style-type: square;
|
||||
}
|
||||
|
||||
.content ul ul,
|
||||
.content ol ul {
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
|
||||
.content li p {
|
||||
margin-bottom: .5em;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 1.5rem 0;
|
||||
padding: 1rem 1.5rem;
|
||||
color: var(--fg);
|
||||
opacity: .9;
|
||||
background-color: var(--quote-bg);
|
||||
border-left: 4px solid var(--quote-border);
|
||||
border-top: none;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
blockquote *:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
table thead th {
|
||||
padding: .75rem;
|
||||
text-align: left;
|
||||
font-weight: 500;
|
||||
line-height: 1.5;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
table td {
|
||||
padding: .75rem;
|
||||
border: none;
|
||||
}
|
||||
|
||||
table thead tr {
|
||||
border: none;
|
||||
border-bottom: 2px var(--table-border-color) solid;
|
||||
}
|
||||
|
||||
table tbody tr {
|
||||
border-bottom: 1px var(--table-border-line) solid;
|
||||
}
|
||||
|
||||
table tbody tr:nth-child(2n) {
|
||||
background: unset;
|
||||
}
|
||||
|
||||
pre code.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
code.hljs {
|
||||
padding: 3px 5px;
|
||||
}
|
||||
|
||||
.colibri {
|
||||
--bg: #3b224c;
|
||||
--fg: #bcbdd0;
|
||||
--heading-fg: #fff;
|
||||
|
||||
--sidebar-bg: #281733;
|
||||
--sidebar-fg: #c8c9db;
|
||||
--sidebar-non-existent: #505274;
|
||||
--sidebar-active: #a4a0e8;
|
||||
--sidebar-spacer: #2d334f;
|
||||
|
||||
--scrollbar: var(--sidebar-fg);
|
||||
|
||||
--icons: #737480;
|
||||
--icons-hover: #b7b9cc;
|
||||
|
||||
/* --links: #a4a0e8; */
|
||||
--links: #ECCDBA;
|
||||
|
||||
--inline-code-color: hsl(48.7, 7.8%, 70%);
|
||||
|
||||
--theme-popup-bg: #161923;
|
||||
--theme-popup-border: #737480;
|
||||
--theme-hover: rgba(0, 0, 0, .2);
|
||||
|
||||
--quote-bg: #281733;
|
||||
--quote-border: hsl(226, 15%, 22%);
|
||||
|
||||
--table-border-color: hsl(226, 23%, 76%);
|
||||
--table-header-bg: hsla(226, 23%, 31%, 0);
|
||||
--table-alternate-bg: hsl(226, 23%, 14%);
|
||||
--table-border-line: hsla(201deg, 20%, 92%, 0.2);
|
||||
|
||||
--searchbar-border-color: #aaa;
|
||||
--searchbar-bg: #aeaec6;
|
||||
--searchbar-fg: #000;
|
||||
--searchbar-shadow-color: #aaa;
|
||||
--searchresults-header-fg: #5f5f71;
|
||||
--searchresults-border-color: #5c5c68;
|
||||
--searchresults-li-bg: #242430;
|
||||
--search-mark-bg: #acff5;
|
||||
}
|
||||
|
||||
.colibri .content .header {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* highlight.js theme, :where() is used to avoid increasing specificity */
|
||||
|
||||
:where(.colibri) .hljs {
|
||||
background: #2f1e2e;
|
||||
color: #a39e9b;
|
||||
}
|
||||
|
||||
:where(.colibri) .hljs-comment,
|
||||
:where(.colibri) .hljs-quote {
|
||||
color: #8d8687;
|
||||
}
|
||||
|
||||
:where(.colibri) .hljs-link,
|
||||
:where(.colibri) .hljs-meta,
|
||||
:where(.colibri) .hljs-name,
|
||||
:where(.colibri) .hljs-regexp,
|
||||
:where(.colibri) .hljs-selector-class,
|
||||
:where(.colibri) .hljs-selector-id,
|
||||
:where(.colibri) .hljs-tag,
|
||||
:where(.colibri) .hljs-template-variable,
|
||||
:where(.colibri) .hljs-variable {
|
||||
color: #ef6155;
|
||||
}
|
||||
|
||||
:where(.colibri) .hljs-built_in,
|
||||
:where(.colibri) .hljs-deletion,
|
||||
:where(.colibri) .hljs-literal,
|
||||
:where(.colibri) .hljs-number,
|
||||
:where(.colibri) .hljs-params,
|
||||
:where(.colibri) .hljs-type {
|
||||
color: #f99b15;
|
||||
}
|
||||
|
||||
:where(.colibri) .hljs-attribute,
|
||||
:where(.colibri) .hljs-section,
|
||||
:where(.colibri) .hljs-title {
|
||||
color: #fec418;
|
||||
}
|
||||
|
||||
:where(.colibri) .hljs-addition,
|
||||
:where(.colibri) .hljs-bullet,
|
||||
:where(.colibri) .hljs-string,
|
||||
:where(.colibri) .hljs-symbol {
|
||||
color: #48b685;
|
||||
}
|
||||
|
||||
:where(.colibri) .hljs-keyword,
|
||||
:where(.colibri) .hljs-selector-tag {
|
||||
color: #815ba4;
|
||||
}
|
||||
|
||||
:where(.colibri) .hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
:where(.colibri) .hljs-strong {
|
||||
font-weight: 700;
|
||||
}
|
@ -0,0 +1,140 @@
|
||||
# Icons
|
||||
|
||||
## Requirements
|
||||
|
||||
File-type and symbol-kind icons require a patched font such as [NerdFonts](https://www.nerdfonts.com/) to be installed and configured in your terminal emulator. These types of fonts are called *patched* fonts because they define arbitrary symbols for a range of Unicode values, which may vary from one font to another. Therefore, you need to use an icon flavor adapted to your configured terminal font, otherwise you may end up with undefined characters and mismatched icons.
|
||||
|
||||
To enable file-type and symbol-kind icons within the editor, see the `[editor.icons]` section of the [configuration file](./configuration.md).
|
||||
|
||||
To use an icon flavor add `icons = "<name>"` to your [`config.toml`](./configuration.md) at the very top of the file before the first section or select it during runtime using `:icons <name>`.
|
||||
|
||||
## Creating an icon flavor
|
||||
|
||||
Create a file with the name of your icon flavor as file name (i.e `myicons.toml`) and place it in your `icons` directory (i.e `~/.config/helix/icons`). The directory might have to be created beforehand.
|
||||
|
||||
The name "default" is reserved for the builtin icons and cannot be overridden by user defined icons.
|
||||
|
||||
The name of the icon flavor must be set using the `name` key.
|
||||
|
||||
The default icons.toml can be found [here](https://github.com/helix-editor/helix/blob/master/icons.toml), and user submitted icon flavors [here](https://github.com/helix-editor/helix/blob/master/runtime/icons).
|
||||
|
||||
Icons flavors have five sections:
|
||||
|
||||
- Diagnostics
|
||||
- Breakpoints
|
||||
- Diff
|
||||
- Symbol kinds
|
||||
- Mime types
|
||||
|
||||
Each line in these sections is specified as below:
|
||||
|
||||
```toml
|
||||
key = { icon = "…", color = "#ff0000" }
|
||||
```
|
||||
|
||||
where `key` represents what you want to style, `icon` specifies the character to show as the icon, and `color` specifies the foreground color of the icon. `color` can be omitted to defer to the defaults.
|
||||
|
||||
### Diagnostic icons
|
||||
|
||||
The `[diagnostic]` section defines four **required** diagnostic icons:
|
||||
|
||||
- `error`
|
||||
- `warning`
|
||||
- `info`
|
||||
- `hint`
|
||||
|
||||
These icons appear in the gutter, in the diagnostic pickers as well as in the status line diagnostic component.
|
||||
By default, they have the foreground color defined in the current theme's corresponding keys.
|
||||
|
||||
> An icon flavor TOML file must define all of these icons.
|
||||
|
||||
### Diff icons
|
||||
|
||||
The `[diff]` section defines three **required** diffing icons:
|
||||
|
||||
- `added`
|
||||
- `deleted`
|
||||
- `modified`
|
||||
|
||||
These icons appear in the gutter.
|
||||
By default, they have the foreground color defined in the current theme's corresponding keys.
|
||||
|
||||
> An icon flavor TOML file must define all of these icons.
|
||||
|
||||
### Breakpoint icons
|
||||
|
||||
The `[breakpoint]` section defines two **required** breakpoint icons:
|
||||
|
||||
- `verified`
|
||||
- `unverified`
|
||||
|
||||
These icons appear in the gutter while using the Debug Adapter Protocol (DAP). Their color depends on the breakpoint's condition and log message, it cannot be overridden by the `color` key.
|
||||
|
||||
> An icon flavor TOML file must define all of these icons.
|
||||
|
||||
### Symbol kinds icons
|
||||
|
||||
The `[symbol-kind]` section defines **optional** icons for the following required LSP-defined symbol kinds:
|
||||
|
||||
- `file` (this icon is also used on files for which the mime type has not been defined in the next section, as a "generic file" icon)
|
||||
- `module`
|
||||
- `namespace`
|
||||
- `package`
|
||||
- `class`
|
||||
- `method`
|
||||
- `property`
|
||||
- `field`
|
||||
- `constructor`
|
||||
- `enumeration`
|
||||
- `interface`
|
||||
- `variable`
|
||||
- `function`
|
||||
- `constant`
|
||||
- `string`
|
||||
- `number`
|
||||
- `boolean`
|
||||
- `array`
|
||||
- `object`
|
||||
- `key`
|
||||
- `null`
|
||||
- `enum-member`
|
||||
- `structure`
|
||||
- `event`
|
||||
- `operator`
|
||||
- `type-parameter`
|
||||
|
||||
By default, these icons have the same style as the loaded theme's `keyword` key. Their style can be customized using the `symbolkind` key in the theme configuration file, or it can individually be overridden by their `color` key.
|
||||
|
||||
> An icon flavor TOML file must define either none or all of these icons.
|
||||
|
||||
### Mime types icons
|
||||
|
||||
The `[mime-type]` section defines **optional** icons for mime types or filename, such as:
|
||||
|
||||
```toml
|
||||
[mime-type]
|
||||
".bashrc" = { icon = "…", color = "#…" }
|
||||
"LICENSE" = { icon = "…", color = "#…" }
|
||||
"rs" = { icon = "…", color = "#…" }
|
||||
```
|
||||
|
||||
These icons appear in the file picker, in the statusline `file-type-icon` component, and in the bufferline (when enabled).
|
||||
|
||||
> An icon flavor TOML file can define none, some or all of these icons.
|
||||
|
||||
### Inheritance
|
||||
|
||||
Extend upon other icon flavors by setting the `inherits` property to an existing theme.
|
||||
|
||||
```toml
|
||||
inherits = "nerdfonts"
|
||||
name = "custom_nerdfonts"
|
||||
|
||||
# Override the icon for generic files:
|
||||
[symbol-kind]
|
||||
file = {icon = "…"}
|
||||
|
||||
# Override the icon for Rust files
|
||||
[mime-type]
|
||||
"rs" = { icon = "…", color = "#…" }
|
||||
```
|
@ -0,0 +1,660 @@
|
||||
"use strict";
|
||||
|
||||
// Fix back button cache problem
|
||||
window.onunload = function () { };
|
||||
|
||||
// Global variable, shared between modules
|
||||
function playground_text(playground) {
|
||||
let code_block = playground.querySelector("code");
|
||||
|
||||
if (window.ace && code_block.classList.contains("editable")) {
|
||||
let editor = window.ace.edit(code_block);
|
||||
return editor.getValue();
|
||||
} else {
|
||||
return code_block.textContent;
|
||||
}
|
||||
}
|
||||
|
||||
(function codeSnippets() {
|
||||
function fetch_with_timeout(url, options, timeout = 6000) {
|
||||
return Promise.race([
|
||||
fetch(url, options),
|
||||
new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), timeout))
|
||||
]);
|
||||
}
|
||||
|
||||
var playgrounds = Array.from(document.querySelectorAll(".playground"));
|
||||
if (playgrounds.length > 0) {
|
||||
fetch_with_timeout("https://play.rust-lang.org/meta/crates", {
|
||||
headers: {
|
||||
'Content-Type': "application/json",
|
||||
},
|
||||
method: 'POST',
|
||||
mode: 'cors',
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(response => {
|
||||
// get list of crates available in the rust playground
|
||||
let playground_crates = response.crates.map(item => item["id"]);
|
||||
playgrounds.forEach(block => handle_crate_list_update(block, playground_crates));
|
||||
});
|
||||
}
|
||||
|
||||
function handle_crate_list_update(playground_block, playground_crates) {
|
||||
// update the play buttons after receiving the response
|
||||
update_play_button(playground_block, playground_crates);
|
||||
|
||||
// and install on change listener to dynamically update ACE editors
|
||||
if (window.ace) {
|
||||
let code_block = playground_block.querySelector("code");
|
||||
if (code_block.classList.contains("editable")) {
|
||||
let editor = window.ace.edit(code_block);
|
||||
editor.addEventListener("change", function (e) {
|
||||
update_play_button(playground_block, playground_crates);
|
||||
});
|
||||
// add Ctrl-Enter command to execute rust code
|
||||
editor.commands.addCommand({
|
||||
name: "run",
|
||||
bindKey: {
|
||||
win: "Ctrl-Enter",
|
||||
mac: "Ctrl-Enter"
|
||||
},
|
||||
exec: _editor => run_rust_code(playground_block)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// updates the visibility of play button based on `no_run` class and
|
||||
// used crates vs ones available on http://play.rust-lang.org
|
||||
function update_play_button(pre_block, playground_crates) {
|
||||
var play_button = pre_block.querySelector(".play-button");
|
||||
|
||||
// skip if code is `no_run`
|
||||
if (pre_block.querySelector('code').classList.contains("no_run")) {
|
||||
play_button.classList.add("hidden");
|
||||
return;
|
||||
}
|
||||
|
||||
// get list of `extern crate`'s from snippet
|
||||
var txt = playground_text(pre_block);
|
||||
var re = /extern\s+crate\s+([a-zA-Z_0-9]+)\s*;/g;
|
||||
var snippet_crates = [];
|
||||
var item;
|
||||
while (item = re.exec(txt)) {
|
||||
snippet_crates.push(item[1]);
|
||||
}
|
||||
|
||||
// check if all used crates are available on play.rust-lang.org
|
||||
var all_available = snippet_crates.every(function (elem) {
|
||||
return playground_crates.indexOf(elem) > -1;
|
||||
});
|
||||
|
||||
if (all_available) {
|
||||
play_button.classList.remove("hidden");
|
||||
} else {
|
||||
play_button.classList.add("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
function run_rust_code(code_block) {
|
||||
var result_block = code_block.querySelector(".result");
|
||||
if (!result_block) {
|
||||
result_block = document.createElement('code');
|
||||
result_block.className = 'result hljs language-bash';
|
||||
|
||||
code_block.append(result_block);
|
||||
}
|
||||
|
||||
let text = playground_text(code_block);
|
||||
let classes = code_block.querySelector('code').classList;
|
||||
let has_2018 = classes.contains("edition2018");
|
||||
let edition = has_2018 ? "2018" : "2015";
|
||||
|
||||
var params = {
|
||||
version: "stable",
|
||||
optimize: "0",
|
||||
code: text,
|
||||
edition: edition
|
||||
};
|
||||
|
||||
if (text.indexOf("#![feature") !== -1) {
|
||||
params.version = "nightly";
|
||||
}
|
||||
|
||||
result_block.innerText = "Running...";
|
||||
|
||||
fetch_with_timeout("https://play.rust-lang.org/evaluate.json", {
|
||||
headers: {
|
||||
'Content-Type': "application/json",
|
||||
},
|
||||
method: 'POST',
|
||||
mode: 'cors',
|
||||
body: JSON.stringify(params)
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(response => result_block.innerText = response.result)
|
||||
.catch(error => result_block.innerText = "Playground Communication: " + error.message);
|
||||
}
|
||||
|
||||
// Syntax highlighting Configuration
|
||||
hljs.configure({
|
||||
tabReplace: ' ', // 4 spaces
|
||||
languages: [], // Languages used for auto-detection
|
||||
});
|
||||
|
||||
let code_nodes = Array
|
||||
.from(document.querySelectorAll('code'))
|
||||
// Don't highlight `inline code` blocks in headers.
|
||||
.filter(function (node) {return !node.parentElement.classList.contains("header"); });
|
||||
|
||||
if (window.ace) {
|
||||
// language-rust class needs to be removed for editable
|
||||
// blocks or highlightjs will capture events
|
||||
Array
|
||||
.from(document.querySelectorAll('code.editable'))
|
||||
.forEach(function (block) { block.classList.remove('language-rust'); });
|
||||
|
||||
Array
|
||||
.from(document.querySelectorAll('code:not(.editable)'))
|
||||
.forEach(function (block) { hljs.highlightBlock(block); });
|
||||
} else {
|
||||
code_nodes.forEach(function (block) { hljs.highlightBlock(block); });
|
||||
}
|
||||
|
||||
// Adding the hljs class gives code blocks the color css
|
||||
// even if highlighting doesn't apply
|
||||
code_nodes.forEach(function (block) { block.classList.add('hljs'); });
|
||||
|
||||
Array.from(document.querySelectorAll("code.language-rust")).forEach(function (block) {
|
||||
|
||||
var lines = Array.from(block.querySelectorAll('.boring'));
|
||||
// If no lines were hidden, return
|
||||
if (!lines.length) { return; }
|
||||
block.classList.add("hide-boring");
|
||||
|
||||
var buttons = document.createElement('div');
|
||||
buttons.className = 'buttons';
|
||||
buttons.innerHTML = "<button class=\"fa fa-eye\" title=\"Show hidden lines\" aria-label=\"Show hidden lines\"></button>";
|
||||
|
||||
// add expand button
|
||||
var pre_block = block.parentNode;
|
||||
pre_block.insertBefore(buttons, pre_block.firstChild);
|
||||
|
||||
pre_block.querySelector('.buttons').addEventListener('click', function (e) {
|
||||
if (e.target.classList.contains('fa-eye')) {
|
||||
e.target.classList.remove('fa-eye');
|
||||
e.target.classList.add('fa-eye-slash');
|
||||
e.target.title = 'Hide lines';
|
||||
e.target.setAttribute('aria-label', e.target.title);
|
||||
|
||||
block.classList.remove('hide-boring');
|
||||
} else if (e.target.classList.contains('fa-eye-slash')) {
|
||||
e.target.classList.remove('fa-eye-slash');
|
||||
e.target.classList.add('fa-eye');
|
||||
e.target.title = 'Show hidden lines';
|
||||
e.target.setAttribute('aria-label', e.target.title);
|
||||
|
||||
block.classList.add('hide-boring');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (window.playground_copyable) {
|
||||
Array.from(document.querySelectorAll('pre code')).forEach(function (block) {
|
||||
var pre_block = block.parentNode;
|
||||
if (!pre_block.classList.contains('playground')) {
|
||||
var buttons = pre_block.querySelector(".buttons");
|
||||
if (!buttons) {
|
||||
buttons = document.createElement('div');
|
||||
buttons.className = 'buttons';
|
||||
pre_block.insertBefore(buttons, pre_block.firstChild);
|
||||
}
|
||||
|
||||
var clipButton = document.createElement('button');
|
||||
clipButton.className = 'fa fa-copy clip-button';
|
||||
clipButton.title = 'Copy to clipboard';
|
||||
clipButton.setAttribute('aria-label', clipButton.title);
|
||||
clipButton.innerHTML = '<i class=\"tooltiptext\"></i>';
|
||||
|
||||
buttons.insertBefore(clipButton, buttons.firstChild);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Process playground code blocks
|
||||
Array.from(document.querySelectorAll(".playground")).forEach(function (pre_block) {
|
||||
// Add play button
|
||||
var buttons = pre_block.querySelector(".buttons");
|
||||
if (!buttons) {
|
||||
buttons = document.createElement('div');
|
||||
buttons.className = 'buttons';
|
||||
pre_block.insertBefore(buttons, pre_block.firstChild);
|
||||
}
|
||||
|
||||
var runCodeButton = document.createElement('button');
|
||||
runCodeButton.className = 'fa fa-play play-button';
|
||||
runCodeButton.hidden = true;
|
||||
runCodeButton.title = 'Run this code';
|
||||
runCodeButton.setAttribute('aria-label', runCodeButton.title);
|
||||
|
||||
buttons.insertBefore(runCodeButton, buttons.firstChild);
|
||||
runCodeButton.addEventListener('click', function (e) {
|
||||
run_rust_code(pre_block);
|
||||
});
|
||||
|
||||
if (window.playground_copyable) {
|
||||
var copyCodeClipboardButton = document.createElement('button');
|
||||
copyCodeClipboardButton.className = 'fa fa-copy clip-button';
|
||||
copyCodeClipboardButton.innerHTML = '<i class="tooltiptext"></i>';
|
||||
copyCodeClipboardButton.title = 'Copy to clipboard';
|
||||
copyCodeClipboardButton.setAttribute('aria-label', copyCodeClipboardButton.title);
|
||||
|
||||
buttons.insertBefore(copyCodeClipboardButton, buttons.firstChild);
|
||||
}
|
||||
|
||||
let code_block = pre_block.querySelector("code");
|
||||
if (window.ace && code_block.classList.contains("editable")) {
|
||||
var undoChangesButton = document.createElement('button');
|
||||
undoChangesButton.className = 'fa fa-history reset-button';
|
||||
undoChangesButton.title = 'Undo changes';
|
||||
undoChangesButton.setAttribute('aria-label', undoChangesButton.title);
|
||||
|
||||
buttons.insertBefore(undoChangesButton, buttons.firstChild);
|
||||
|
||||
undoChangesButton.addEventListener('click', function () {
|
||||
let editor = window.ace.edit(code_block);
|
||||
editor.setValue(editor.originalCode);
|
||||
editor.clearSelection();
|
||||
});
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
(function themes() {
|
||||
var html = document.querySelector('html');
|
||||
var themeToggleButton = document.getElementById('theme-toggle');
|
||||
var themePopup = document.getElementById('theme-list');
|
||||
var themeColorMetaTag = document.querySelector('meta[name="theme-color"]');
|
||||
var stylesheets = {
|
||||
ayuHighlight: document.querySelector("[href$='ayu-highlight.css']"),
|
||||
tomorrowNight: document.querySelector("[href$='tomorrow-night.css']"),
|
||||
highlight: document.querySelector("[href$='highlight.css']"),
|
||||
};
|
||||
|
||||
function showThemes() {
|
||||
themePopup.style.display = 'block';
|
||||
themeToggleButton.setAttribute('aria-expanded', true);
|
||||
themePopup.querySelector("button#" + get_theme()).focus();
|
||||
}
|
||||
|
||||
function hideThemes() {
|
||||
themePopup.style.display = 'none';
|
||||
themeToggleButton.setAttribute('aria-expanded', false);
|
||||
themeToggleButton.focus();
|
||||
}
|
||||
|
||||
function get_theme() {
|
||||
var theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch (e) { }
|
||||
if (theme === null || theme === undefined) {
|
||||
return default_theme;
|
||||
} else {
|
||||
return theme;
|
||||
}
|
||||
}
|
||||
|
||||
function set_theme(theme, store = true) {
|
||||
let ace_theme;
|
||||
|
||||
if (theme == 'coal' || theme == 'navy') {
|
||||
stylesheets.ayuHighlight.disabled = true;
|
||||
stylesheets.tomorrowNight.disabled = false;
|
||||
stylesheets.highlight.disabled = true;
|
||||
|
||||
ace_theme = "ace/theme/tomorrow_night";
|
||||
} else if (theme == 'ayu') {
|
||||
stylesheets.ayuHighlight.disabled = false;
|
||||
stylesheets.tomorrowNight.disabled = true;
|
||||
stylesheets.highlight.disabled = true;
|
||||
ace_theme = "ace/theme/tomorrow_night";
|
||||
} else {
|
||||
stylesheets.ayuHighlight.disabled = true;
|
||||
stylesheets.tomorrowNight.disabled = true;
|
||||
stylesheets.highlight.disabled = false;
|
||||
ace_theme = "ace/theme/dawn";
|
||||
}
|
||||
|
||||
setTimeout(function () {
|
||||
themeColorMetaTag.content = getComputedStyle(document.body).backgroundColor;
|
||||
}, 1);
|
||||
|
||||
if (window.ace && window.editors) {
|
||||
window.editors.forEach(function (editor) {
|
||||
editor.setTheme(ace_theme);
|
||||
});
|
||||
}
|
||||
|
||||
var previousTheme = get_theme();
|
||||
|
||||
if (store) {
|
||||
try { localStorage.setItem('mdbook-theme', theme); } catch (e) { }
|
||||
}
|
||||
|
||||
html.classList.remove(previousTheme);
|
||||
html.classList.add(theme);
|
||||
}
|
||||
|
||||
// Set theme
|
||||
var theme = get_theme();
|
||||
|
||||
set_theme(theme, false);
|
||||
|
||||
themeToggleButton.addEventListener('click', function () {
|
||||
if (themePopup.style.display === 'block') {
|
||||
hideThemes();
|
||||
} else {
|
||||
showThemes();
|
||||
}
|
||||
});
|
||||
|
||||
themePopup.addEventListener('click', function (e) {
|
||||
var theme = e.target.id || e.target.parentElement.id;
|
||||
set_theme(theme);
|
||||
});
|
||||
|
||||
themePopup.addEventListener('focusout', function(e) {
|
||||
// e.relatedTarget is null in Safari and Firefox on macOS (see workaround below)
|
||||
if (!!e.relatedTarget && !themeToggleButton.contains(e.relatedTarget) && !themePopup.contains(e.relatedTarget)) {
|
||||
hideThemes();
|
||||
}
|
||||
});
|
||||
|
||||
// Should not be needed, but it works around an issue on macOS & iOS: https://github.com/rust-lang/mdBook/issues/628
|
||||
document.addEventListener('click', function(e) {
|
||||
if (themePopup.style.display === 'block' && !themeToggleButton.contains(e.target) && !themePopup.contains(e.target)) {
|
||||
hideThemes();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('keydown', function (e) {
|
||||
if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
|
||||
if (!themePopup.contains(e.target)) { return; }
|
||||
|
||||
switch (e.key) {
|
||||
case 'Escape':
|
||||
e.preventDefault();
|
||||
hideThemes();
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
e.preventDefault();
|
||||
var li = document.activeElement.parentElement;
|
||||
if (li && li.previousElementSibling) {
|
||||
li.previousElementSibling.querySelector('button').focus();
|
||||
}
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
e.preventDefault();
|
||||
var li = document.activeElement.parentElement;
|
||||
if (li && li.nextElementSibling) {
|
||||
li.nextElementSibling.querySelector('button').focus();
|
||||
}
|
||||
break;
|
||||
case 'Home':
|
||||
e.preventDefault();
|
||||
themePopup.querySelector('li:first-child button').focus();
|
||||
break;
|
||||
case 'End':
|
||||
e.preventDefault();
|
||||
themePopup.querySelector('li:last-child button').focus();
|
||||
break;
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
(function sidebar() {
|
||||
var html = document.querySelector("html");
|
||||
var sidebar = document.getElementById("sidebar");
|
||||
var sidebarLinks = document.querySelectorAll('#sidebar a');
|
||||
var sidebarToggleButton = document.getElementById("sidebar-toggle");
|
||||
var sidebarResizeHandle = document.getElementById("sidebar-resize-handle");
|
||||
var firstContact = null;
|
||||
|
||||
function showSidebar() {
|
||||
html.classList.remove('sidebar-hidden')
|
||||
html.classList.add('sidebar-visible');
|
||||
Array.from(sidebarLinks).forEach(function (link) {
|
||||
link.setAttribute('tabIndex', 0);
|
||||
});
|
||||
sidebarToggleButton.setAttribute('aria-expanded', true);
|
||||
sidebar.setAttribute('aria-hidden', false);
|
||||
try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { }
|
||||
}
|
||||
|
||||
|
||||
var sidebarAnchorToggles = document.querySelectorAll('#sidebar a.toggle');
|
||||
|
||||
function toggleSection(ev) {
|
||||
ev.currentTarget.parentElement.classList.toggle('expanded');
|
||||
}
|
||||
|
||||
Array.from(sidebarAnchorToggles).forEach(function (el) {
|
||||
el.addEventListener('click', toggleSection);
|
||||
});
|
||||
|
||||
function hideSidebar() {
|
||||
html.classList.remove('sidebar-visible')
|
||||
html.classList.add('sidebar-hidden');
|
||||
Array.from(sidebarLinks).forEach(function (link) {
|
||||
link.setAttribute('tabIndex', -1);
|
||||
});
|
||||
sidebarToggleButton.setAttribute('aria-expanded', false);
|
||||
sidebar.setAttribute('aria-hidden', true);
|
||||
try { localStorage.setItem('mdbook-sidebar', 'hidden'); } catch (e) { }
|
||||
}
|
||||
|
||||
// Toggle sidebar
|
||||
sidebarToggleButton.addEventListener('click', function sidebarToggle() {
|
||||
if (html.classList.contains("sidebar-hidden")) {
|
||||
var current_width = parseInt(
|
||||
document.documentElement.style.getPropertyValue('--sidebar-width'), 10);
|
||||
if (current_width < 150) {
|
||||
document.documentElement.style.setProperty('--sidebar-width', '150px');
|
||||
}
|
||||
showSidebar();
|
||||
} else if (html.classList.contains("sidebar-visible")) {
|
||||
hideSidebar();
|
||||
} else {
|
||||
if (getComputedStyle(sidebar)['transform'] === 'none') {
|
||||
hideSidebar();
|
||||
} else {
|
||||
showSidebar();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
sidebarResizeHandle.addEventListener('mousedown', initResize, false);
|
||||
|
||||
function initResize(e) {
|
||||
window.addEventListener('mousemove', resize, false);
|
||||
window.addEventListener('mouseup', stopResize, false);
|
||||
html.classList.add('sidebar-resizing');
|
||||
}
|
||||
function resize(e) {
|
||||
var pos = (e.clientX - sidebar.offsetLeft);
|
||||
if (pos < 20) {
|
||||
hideSidebar();
|
||||
} else {
|
||||
if (html.classList.contains("sidebar-hidden")) {
|
||||
showSidebar();
|
||||
}
|
||||
pos = Math.min(pos, window.innerWidth - 100);
|
||||
document.documentElement.style.setProperty('--sidebar-width', pos + 'px');
|
||||
}
|
||||
}
|
||||
//on mouseup remove windows functions mousemove & mouseup
|
||||
function stopResize(e) {
|
||||
html.classList.remove('sidebar-resizing');
|
||||
window.removeEventListener('mousemove', resize, false);
|
||||
window.removeEventListener('mouseup', stopResize, false);
|
||||
}
|
||||
|
||||
document.addEventListener('touchstart', function (e) {
|
||||
firstContact = {
|
||||
x: e.touches[0].clientX,
|
||||
time: Date.now()
|
||||
};
|
||||
}, { passive: true });
|
||||
|
||||
document.addEventListener('touchmove', function (e) {
|
||||
if (!firstContact)
|
||||
return;
|
||||
|
||||
var curX = e.touches[0].clientX;
|
||||
var xDiff = curX - firstContact.x,
|
||||
tDiff = Date.now() - firstContact.time;
|
||||
|
||||
if (tDiff < 250 && Math.abs(xDiff) >= 150) {
|
||||
if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300))
|
||||
showSidebar();
|
||||
else if (xDiff < 0 && curX < 300)
|
||||
hideSidebar();
|
||||
|
||||
firstContact = null;
|
||||
}
|
||||
}, { passive: true });
|
||||
|
||||
// Scroll sidebar to current active section
|
||||
var activeSection = document.getElementById("sidebar").querySelector(".active");
|
||||
if (activeSection) {
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView
|
||||
activeSection.scrollIntoView({ block: 'center' });
|
||||
}
|
||||
})();
|
||||
|
||||
(function chapterNavigation() {
|
||||
document.addEventListener('keydown', function (e) {
|
||||
if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
|
||||
if (window.search && window.search.hasFocus()) { return; }
|
||||
|
||||
switch (e.key) {
|
||||
case 'ArrowRight':
|
||||
e.preventDefault();
|
||||
var nextButton = document.querySelector('.nav-chapters.next');
|
||||
if (nextButton) {
|
||||
window.location.href = nextButton.href;
|
||||
}
|
||||
break;
|
||||
case 'ArrowLeft':
|
||||
e.preventDefault();
|
||||
var previousButton = document.querySelector('.nav-chapters.previous');
|
||||
if (previousButton) {
|
||||
window.location.href = previousButton.href;
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
(function clipboard() {
|
||||
var clipButtons = document.querySelectorAll('.clip-button');
|
||||
|
||||
function hideTooltip(elem) {
|
||||
elem.firstChild.innerText = "";
|
||||
elem.className = 'fa fa-copy clip-button';
|
||||
}
|
||||
|
||||
function showTooltip(elem, msg) {
|
||||
elem.firstChild.innerText = msg;
|
||||
elem.className = 'fa fa-copy tooltipped';
|
||||
}
|
||||
|
||||
var clipboardSnippets = new ClipboardJS('.clip-button', {
|
||||
text: function (trigger) {
|
||||
hideTooltip(trigger);
|
||||
let playground = trigger.closest("pre");
|
||||
return playground_text(playground);
|
||||
}
|
||||
});
|
||||
|
||||
Array.from(clipButtons).forEach(function (clipButton) {
|
||||
clipButton.addEventListener('mouseout', function (e) {
|
||||
hideTooltip(e.currentTarget);
|
||||
});
|
||||
});
|
||||
|
||||
clipboardSnippets.on('success', function (e) {
|
||||
e.clearSelection();
|
||||
showTooltip(e.trigger, "Copied!");
|
||||
});
|
||||
|
||||
clipboardSnippets.on('error', function (e) {
|
||||
showTooltip(e.trigger, "Clipboard error!");
|
||||
});
|
||||
})();
|
||||
|
||||
(function scrollToTop () {
|
||||
var menuTitle = document.querySelector('.menu-title');
|
||||
|
||||
menuTitle.addEventListener('click', function () {
|
||||
document.scrollingElement.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
});
|
||||
})();
|
||||
|
||||
(function controlMenu() {
|
||||
var menu = document.getElementById('menu-bar');
|
||||
|
||||
(function controlPosition() {
|
||||
var scrollTop = document.scrollingElement.scrollTop;
|
||||
var prevScrollTop = scrollTop;
|
||||
var minMenuY = -menu.clientHeight - 50;
|
||||
// When the script loads, the page can be at any scroll (e.g. if you reforesh it).
|
||||
menu.style.top = scrollTop + 'px';
|
||||
// Same as parseInt(menu.style.top.slice(0, -2), but faster
|
||||
var topCache = menu.style.top.slice(0, -2);
|
||||
menu.classList.remove('sticky');
|
||||
var stickyCache = false; // Same as menu.classList.contains('sticky'), but faster
|
||||
document.addEventListener('scroll', function () {
|
||||
scrollTop = Math.max(document.scrollingElement.scrollTop, 0);
|
||||
// `null` means that it doesn't need to be updated
|
||||
var nextSticky = null;
|
||||
var nextTop = null;
|
||||
var scrollDown = scrollTop > prevScrollTop;
|
||||
var menuPosAbsoluteY = topCache - scrollTop;
|
||||
if (scrollDown) {
|
||||
nextSticky = false;
|
||||
if (menuPosAbsoluteY > 0) {
|
||||
nextTop = prevScrollTop;
|
||||
}
|
||||
} else {
|
||||
if (menuPosAbsoluteY > 0) {
|
||||
nextSticky = true;
|
||||
} else if (menuPosAbsoluteY < minMenuY) {
|
||||
nextTop = prevScrollTop + minMenuY;
|
||||
}
|
||||
}
|
||||
if (nextSticky === true && stickyCache === false) {
|
||||
menu.classList.add('sticky');
|
||||
stickyCache = true;
|
||||
} else if (nextSticky === false && stickyCache === true) {
|
||||
menu.classList.remove('sticky');
|
||||
stickyCache = false;
|
||||
}
|
||||
if (nextTop !== null) {
|
||||
menu.style.top = nextTop + 'px';
|
||||
topCache = nextTop;
|
||||
}
|
||||
prevScrollTop = scrollTop;
|
||||
}, { passive: true });
|
||||
})();
|
||||
(function controlBorder() {
|
||||
menu.classList.remove('bordered');
|
||||
document.addEventListener('scroll', function () {
|
||||
if (menu.offsetTop === 0) {
|
||||
menu.classList.remove('bordered');
|
||||
} else {
|
||||
menu.classList.add('bordered');
|
||||
}
|
||||
}, { passive: true });
|
||||
})();
|
||||
})();
|
@ -0,0 +1,499 @@
|
||||
/* CSS for UI elements (a.k.a. chrome) */
|
||||
|
||||
@import 'variables.css';
|
||||
|
||||
::-webkit-scrollbar {
|
||||
background: var(--bg);
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--scrollbar);
|
||||
}
|
||||
html {
|
||||
scrollbar-color: var(--scrollbar) var(--bg);
|
||||
}
|
||||
#searchresults a,
|
||||
.content a:link,
|
||||
a:visited,
|
||||
a > .hljs {
|
||||
color: var(--links);
|
||||
}
|
||||
.content a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Menu Bar */
|
||||
|
||||
#menu-bar,
|
||||
#menu-bar-hover-placeholder {
|
||||
z-index: 101;
|
||||
margin: auto calc(0px - var(--page-padding));
|
||||
}
|
||||
#menu-bar {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
background-color: var(--bg);
|
||||
border-bottom-color: var(--bg);
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-style: solid;
|
||||
}
|
||||
#menu-bar.sticky,
|
||||
.js #menu-bar-hover-placeholder:hover + #menu-bar,
|
||||
.js #menu-bar:hover,
|
||||
.js.sidebar-visible #menu-bar {
|
||||
position: -webkit-sticky;
|
||||
position: sticky;
|
||||
top: 0 !important;
|
||||
}
|
||||
#menu-bar-hover-placeholder {
|
||||
position: sticky;
|
||||
position: -webkit-sticky;
|
||||
top: 0;
|
||||
height: var(--menu-bar-height);
|
||||
}
|
||||
#menu-bar.bordered {
|
||||
border-bottom-color: var(--table-border-color);
|
||||
}
|
||||
#menu-bar i, #menu-bar .icon-button {
|
||||
position: relative;
|
||||
padding: 0 8px;
|
||||
z-index: 10;
|
||||
line-height: var(--menu-bar-height);
|
||||
cursor: pointer;
|
||||
transition: color 0.5s;
|
||||
}
|
||||
@media only screen and (max-width: 420px) {
|
||||
#menu-bar i, #menu-bar .icon-button {
|
||||
padding: 0 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-button {
|
||||
border: none;
|
||||
background: none;
|
||||
padding: 0;
|
||||
color: inherit;
|
||||
}
|
||||
.icon-button i {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.right-buttons {
|
||||
margin: 0 15px;
|
||||
}
|
||||
.right-buttons a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.left-buttons {
|
||||
display: flex;
|
||||
margin: 0 5px;
|
||||
}
|
||||
.no-js .left-buttons {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.menu-title {
|
||||
display: inline-block;
|
||||
font-weight: 200;
|
||||
font-size: 2.4rem;
|
||||
line-height: var(--menu-bar-height);
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
flex: 1;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.js .menu-title {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.menu-bar,
|
||||
.menu-bar:visited,
|
||||
.nav-chapters,
|
||||
.nav-chapters:visited,
|
||||
.mobile-nav-chapters,
|
||||
.mobile-nav-chapters:visited,
|
||||
.menu-bar .icon-button,
|
||||
.menu-bar a i {
|
||||
color: var(--icons);
|
||||
}
|
||||
|
||||
.menu-bar i:hover,
|
||||
.menu-bar .icon-button:hover,
|
||||
.nav-chapters:hover,
|
||||
.mobile-nav-chapters i:hover {
|
||||
color: var(--icons-hover);
|
||||
}
|
||||
|
||||
/* Nav Icons */
|
||||
|
||||
.nav-chapters {
|
||||
font-size: 2.5em;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
margin: 0;
|
||||
max-width: 150px;
|
||||
min-width: 90px;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
flex-direction: column;
|
||||
|
||||
transition: color 0.5s, background-color 0.5s;
|
||||
}
|
||||
|
||||
.nav-chapters:hover {
|
||||
text-decoration: none;
|
||||
background-color: var(--theme-hover);
|
||||
transition: background-color 0.15s, color 0.15s;
|
||||
}
|
||||
|
||||
.nav-wrapper {
|
||||
margin-top: 50px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mobile-nav-chapters {
|
||||
font-size: 2.5em;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
width: 90px;
|
||||
border-radius: 5px;
|
||||
background-color: var(--sidebar-bg);
|
||||
}
|
||||
|
||||
.previous {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.next {
|
||||
float: right;
|
||||
right: var(--page-padding);
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1080px) {
|
||||
.nav-wide-wrapper { display: none; }
|
||||
.nav-wrapper { display: block; }
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1380px) {
|
||||
.sidebar-visible .nav-wide-wrapper { display: none; }
|
||||
.sidebar-visible .nav-wrapper { display: block; }
|
||||
}
|
||||
|
||||
/* Inline code */
|
||||
|
||||
:not(pre) > .hljs {
|
||||
display: inline;
|
||||
padding: 0.1em 0.3em;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
:not(pre):not(a):not(td):not(p) > .hljs {
|
||||
color: var(--inline-code-color);
|
||||
overflow-x: initial;
|
||||
}
|
||||
|
||||
a:hover > .hljs {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
pre {
|
||||
position: relative;
|
||||
}
|
||||
pre > .buttons {
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
right: 5px;
|
||||
top: 5px;
|
||||
|
||||
color: var(--sidebar-fg);
|
||||
cursor: pointer;
|
||||
}
|
||||
pre > .buttons :hover {
|
||||
color: var(--sidebar-active);
|
||||
}
|
||||
pre > .buttons i {
|
||||
margin-left: 8px;
|
||||
}
|
||||
pre > .buttons button {
|
||||
color: inherit;
|
||||
background: transparent;
|
||||
border: none;
|
||||
cursor: inherit;
|
||||
}
|
||||
pre > .result {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* Search */
|
||||
|
||||
#searchresults a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
mark {
|
||||
border-radius: 2px;
|
||||
padding: 0 3px 1px 3px;
|
||||
margin: 0 -3px -1px -3px;
|
||||
background-color: var(--search-mark-bg);
|
||||
transition: background-color 300ms linear;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
mark.fade-out {
|
||||
background-color: rgba(0,0,0,0) !important;
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
.searchbar-outer {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: var(--content-max-width);
|
||||
}
|
||||
|
||||
#searchbar {
|
||||
width: 100%;
|
||||
margin: 5px auto 0px auto;
|
||||
padding: 10px 16px;
|
||||
transition: box-shadow 300ms ease-in-out;
|
||||
border: 1px solid var(--searchbar-border-color);
|
||||
border-radius: 3px;
|
||||
background-color: var(--searchbar-bg);
|
||||
color: var(--searchbar-fg);
|
||||
}
|
||||
#searchbar:focus,
|
||||
#searchbar.active {
|
||||
box-shadow: 0 0 3px var(--searchbar-shadow-color);
|
||||
}
|
||||
|
||||
.searchresults-header {
|
||||
font-weight: bold;
|
||||
font-size: 1em;
|
||||
padding: 18px 0 0 5px;
|
||||
color: var(--searchresults-header-fg);
|
||||
}
|
||||
|
||||
.searchresults-outer {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: var(--content-max-width);
|
||||
border-bottom: 1px dashed var(--searchresults-border-color);
|
||||
}
|
||||
|
||||
ul#searchresults {
|
||||
list-style: none;
|
||||
padding-left: 20px;
|
||||
}
|
||||
ul#searchresults li {
|
||||
margin: 10px 0px;
|
||||
padding: 2px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
ul#searchresults li.focus {
|
||||
background-color: var(--searchresults-li-bg);
|
||||
}
|
||||
ul#searchresults span.teaser {
|
||||
display: block;
|
||||
clear: both;
|
||||
margin: 5px 0 0 20px;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
ul#searchresults span.teaser em {
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* Sidebar */
|
||||
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: var(--sidebar-width);
|
||||
font-size: 0.875em;
|
||||
box-sizing: border-box;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
overscroll-behavior-y: contain;
|
||||
background-color: var(--sidebar-bg);
|
||||
color: var(--sidebar-fg);
|
||||
}
|
||||
.sidebar-resizing {
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.js:not(.sidebar-resizing) .sidebar {
|
||||
transition: transform 0.3s; /* Animation: slide away */
|
||||
}
|
||||
.sidebar code {
|
||||
line-height: 2em;
|
||||
}
|
||||
.sidebar .sidebar-scrollbox {
|
||||
overflow-y: auto;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
.sidebar .sidebar-resize-handle {
|
||||
position: absolute;
|
||||
cursor: col-resize;
|
||||
width: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
.js .sidebar .sidebar-resize-handle {
|
||||
cursor: col-resize;
|
||||
width: 5px;
|
||||
}
|
||||
.sidebar-hidden .sidebar {
|
||||
transform: translateX(calc(0px - var(--sidebar-width)));
|
||||
}
|
||||
.sidebar::-webkit-scrollbar {
|
||||
background: var(--sidebar-bg);
|
||||
}
|
||||
.sidebar::-webkit-scrollbar-thumb {
|
||||
background: var(--scrollbar);
|
||||
}
|
||||
|
||||
.sidebar-visible .page-wrapper {
|
||||
transform: translateX(var(--sidebar-width));
|
||||
}
|
||||
@media only screen and (min-width: 620px) {
|
||||
.sidebar-visible .page-wrapper {
|
||||
transform: none;
|
||||
margin-left: var(--sidebar-width);
|
||||
}
|
||||
}
|
||||
|
||||
.chapter {
|
||||
list-style: none outside none;
|
||||
padding-left: 0;
|
||||
margin: .25rem 0;
|
||||
}
|
||||
|
||||
.chapter ol {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.chapter li {
|
||||
display: flex;
|
||||
color: var(--sidebar-non-existent);
|
||||
}
|
||||
.chapter li a {
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
color: var(--sidebar-fg);
|
||||
}
|
||||
|
||||
.chapter li a:hover {
|
||||
color: var(--sidebar-active);
|
||||
}
|
||||
|
||||
.chapter li a.active {
|
||||
color: var(--sidebar-active);
|
||||
}
|
||||
|
||||
.chapter li > a.toggle {
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
padding: 0 10px;
|
||||
user-select: none;
|
||||
opacity: 0.68;
|
||||
}
|
||||
|
||||
.chapter li > a.toggle div {
|
||||
transition: transform 0.5s;
|
||||
}
|
||||
|
||||
/* collapse the section */
|
||||
.chapter li:not(.expanded) + li > ol {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.chapter li.chapter-item {
|
||||
padding: 1rem 1.5rem;
|
||||
}
|
||||
|
||||
.chapter .section li.chapter-item {
|
||||
padding: .5rem .5rem 0 .5rem;
|
||||
}
|
||||
|
||||
.chapter li.expanded > a.toggle div {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.spacer {
|
||||
width: 100%;
|
||||
height: 3px;
|
||||
margin: 5px 0px;
|
||||
}
|
||||
.chapter .spacer {
|
||||
background-color: var(--sidebar-spacer);
|
||||
}
|
||||
|
||||
@media (-moz-touch-enabled: 1), (pointer: coarse) {
|
||||
.chapter li a { padding: 5px 0; }
|
||||
.spacer { margin: 10px 0; }
|
||||
}
|
||||
|
||||
.section {
|
||||
list-style: none outside none;
|
||||
padding-left: 2rem;
|
||||
line-height: 1.9em;
|
||||
}
|
||||
|
||||
/* Theme Menu Popup */
|
||||
|
||||
.theme-popup {
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
top: var(--menu-bar-height);
|
||||
z-index: 1000;
|
||||
border-radius: 4px;
|
||||
font-size: 0.7em;
|
||||
color: var(--fg);
|
||||
background: var(--theme-popup-bg);
|
||||
border: 1px solid var(--theme-popup-border);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
display: none;
|
||||
}
|
||||
.theme-popup .default {
|
||||
color: var(--icons);
|
||||
}
|
||||
.theme-popup .theme {
|
||||
width: 100%;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
padding: 2px 10px;
|
||||
line-height: 25px;
|
||||
white-space: nowrap;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
color: inherit;
|
||||
background: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
.theme-popup .theme:hover {
|
||||
background-color: var(--theme-hover);
|
||||
}
|
||||
.theme-popup .theme:hover:first-child,
|
||||
.theme-popup .theme:hover:last-child {
|
||||
border-top-left-radius: inherit;
|
||||
border-top-right-radius: inherit;
|
||||
}
|
@ -0,0 +1,233 @@
|
||||
/* Base styles and content styles */
|
||||
|
||||
@import 'variables.css';
|
||||
|
||||
:root {
|
||||
/* Browser default font-size is 16px, this way 1 rem = 10px */
|
||||
font-size: 62.5%;
|
||||
}
|
||||
|
||||
/* TODO: replace with self hosted fonts */
|
||||
|
||||
html {
|
||||
font-family: "Inter", sans-serif;
|
||||
color: var(--fg);
|
||||
background-color: var(--bg);
|
||||
text-size-adjust: none;
|
||||
}
|
||||
|
||||
/* @supports (font-variation-settings: normal) { */
|
||||
/* html { font-family: 'Inter var', sans-serif; } */
|
||||
/* } */
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-size: 1.6rem;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: "Source Code Pro", Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace, monospace !important;
|
||||
font-size: 0.875em; /* please adjust the ace font size accordingly in editor.js */
|
||||
}
|
||||
|
||||
/* Don't change font size in headers. */
|
||||
h1 code, h2 code, h3 code, h4 code, h5 code, h6 code {
|
||||
font-size: unset;
|
||||
}
|
||||
|
||||
.left { float: left; }
|
||||
.right { float: right; }
|
||||
.boring { opacity: 0.6; }
|
||||
.hide-boring .boring { display: none; }
|
||||
.hidden { display: none !important; }
|
||||
|
||||
h2, h3 { margin-top: 2.5em; }
|
||||
h4, h5 { margin-top: 2em; }
|
||||
|
||||
.header + .header h3,
|
||||
.header + .header h4,
|
||||
.header + .header h5 {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
h1:target::before,
|
||||
h2:target::before,
|
||||
h3:target::before,
|
||||
h4:target::before,
|
||||
h5:target::before,
|
||||
h6:target::before {
|
||||
display: inline-block;
|
||||
content: "»";
|
||||
margin-left: -30px;
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
/* This is broken on Safari as of version 14, but is fixed
|
||||
in Safari Technology Preview 117 which I think will be Safari 14.2.
|
||||
https://bugs.webkit.org/show_bug.cgi?id=218076
|
||||
*/
|
||||
:target {
|
||||
scroll-margin-top: calc(var(--menu-bar-height) + 0.5em);
|
||||
}
|
||||
|
||||
.page {
|
||||
outline: 0;
|
||||
padding: 0 var(--page-padding);
|
||||
margin-top: calc(0px - var(--menu-bar-height)); /* Compensate for the #menu-bar-hover-placeholder */
|
||||
}
|
||||
.page-wrapper {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.js:not(.sidebar-resizing) .page-wrapper {
|
||||
transition: margin-left 0.3s ease, transform 0.3s ease; /* Animation: slide away */
|
||||
}
|
||||
|
||||
.content {
|
||||
overflow-y: auto;
|
||||
padding: 0 15px;
|
||||
padding-bottom: 50px;
|
||||
}
|
||||
.content main {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: var(--content-max-width);
|
||||
}
|
||||
|
||||
|
||||
/* 2 1.75 1.5 1.25 1 .875 */
|
||||
|
||||
.content h1 { font-size: 2em }
|
||||
.content h2 { font-size: 1.75em }
|
||||
.content h3 { font-size: 1.5em }
|
||||
.content h4 { font-size: 1.25em }
|
||||
.content h5 { font-size: 1em }
|
||||
.content h6 { font-size: .875em }
|
||||
|
||||
.content h1, .content h2, .content h3, .content h4 {
|
||||
font-weight: 500;
|
||||
margin-top: 1.275em;
|
||||
margin-bottom: .875em;
|
||||
}
|
||||
.content p, .content ol, .content ul, .content table {
|
||||
margin-top: 0;
|
||||
margin-bottom: .875em;
|
||||
}
|
||||
|
||||
.content ul li {
|
||||
margin-bottom: .25rem;
|
||||
}
|
||||
.content ul {
|
||||
list-style-type: square;
|
||||
}
|
||||
.content ul ul, .content ol ul {
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
.content li p {
|
||||
margin-bottom: .5em;
|
||||
}
|
||||
|
||||
.content p { line-height: 1.45em; }
|
||||
.content ol { line-height: 1.45em; }
|
||||
.content ul { line-height: 1.45em; }
|
||||
.content a { text-decoration: none; }
|
||||
.content a:hover { text-decoration: underline; }
|
||||
.content img { max-width: 100%; }
|
||||
.content .header:link,
|
||||
.content .header:visited {
|
||||
color: var(--fg);
|
||||
color: var(--heading-fg);
|
||||
}
|
||||
.content .header:link,
|
||||
.content .header:visited:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
table {
|
||||
margin: 0 auto;
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
table td {
|
||||
padding: .75rem;
|
||||
width: auto;
|
||||
}
|
||||
table thead {
|
||||
background: var(--table-header-bg);
|
||||
}
|
||||
table thead td {
|
||||
font-weight: 700;
|
||||
border: none;
|
||||
}
|
||||
table thead th {
|
||||
padding: .75rem;
|
||||
text-align: left;
|
||||
font-weight: 500;
|
||||
line-height: 1.5;
|
||||
width: auto;
|
||||
}
|
||||
table thead tr {
|
||||
border-bottom: 2px var(--table-border-color) solid;
|
||||
}
|
||||
table tbody tr {
|
||||
border-bottom: 1px var(--table-border-line) solid;
|
||||
}
|
||||
/* Alternate background colors for rows */
|
||||
table tbody tr:nth-child(2n) {
|
||||
/* background: var(--table-alternate-bg); */
|
||||
}
|
||||
|
||||
|
||||
blockquote {
|
||||
margin: 1.5rem 0;
|
||||
padding: 1rem 1.5rem;
|
||||
color: var(--fg);
|
||||
opacity: .9;
|
||||
background-color: var(--quote-bg);
|
||||
border-left: 4px solid var(--quote-border);
|
||||
}
|
||||
blockquote *:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
|
||||
:not(.footnote-definition) + .footnote-definition,
|
||||
.footnote-definition + :not(.footnote-definition) {
|
||||
margin-top: 2em;
|
||||
}
|
||||
.footnote-definition {
|
||||
font-size: 0.9em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.footnote-definition p {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.tooltiptext {
|
||||
position: absolute;
|
||||
visibility: hidden;
|
||||
color: #fff;
|
||||
background-color: #333;
|
||||
transform: translateX(-50%); /* Center by moving tooltip 50% of its width left */
|
||||
left: -8px; /* Half of the width of the icon */
|
||||
top: -35px;
|
||||
font-size: 0.8em;
|
||||
text-align: center;
|
||||
border-radius: 6px;
|
||||
padding: 5px 8px;
|
||||
margin: 5px;
|
||||
z-index: 1000;
|
||||
}
|
||||
.tooltipped .tooltiptext {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.chapter li.part-title {
|
||||
color: var(--sidebar-fg);
|
||||
margin: 5px 0px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.result-no-output {
|
||||
font-style: italic;
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
|
||||
#sidebar,
|
||||
#menu-bar,
|
||||
.nav-chapters,
|
||||
.mobile-nav-chapters {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#page-wrapper.page-wrapper {
|
||||
transform: none;
|
||||
margin-left: 0px;
|
||||
overflow-y: initial;
|
||||
}
|
||||
|
||||
#content {
|
||||
max-width: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.page {
|
||||
overflow-y: initial;
|
||||
}
|
||||
|
||||
code {
|
||||
background-color: #666666;
|
||||
border-radius: 5px;
|
||||
|
||||
/* Force background to be printed in Chrome */
|
||||
-webkit-print-color-adjust: exact;
|
||||
}
|
||||
|
||||
pre > .buttons {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
a, a:visited, a:active, a:hover {
|
||||
color: #4183c4;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
page-break-inside: avoid;
|
||||
page-break-after: avoid;
|
||||
}
|
||||
|
||||
pre, code {
|
||||
page-break-inside: avoid;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.fa {
|
||||
display: none !important;
|
||||
}
|
@ -0,0 +1,411 @@
|
||||
|
||||
/* Globals */
|
||||
|
||||
:root {
|
||||
--sidebar-width: 300px;
|
||||
--page-padding: 15px;
|
||||
--content-max-width: 750px;
|
||||
--menu-bar-height: 50px;
|
||||
}
|
||||
|
||||
/* Themes */
|
||||
|
||||
.ayu {
|
||||
--bg: hsl(210, 25%, 8%);
|
||||
--fg: #c5c5c5;
|
||||
|
||||
--sidebar-bg: #14191f;
|
||||
--sidebar-fg: #c8c9db;
|
||||
--sidebar-non-existent: #5c6773;
|
||||
--sidebar-active: #ffb454;
|
||||
--sidebar-spacer: #2d334f;
|
||||
|
||||
--scrollbar: var(--sidebar-fg);
|
||||
|
||||
--icons: #737480;
|
||||
--icons-hover: #b7b9cc;
|
||||
|
||||
--links: #0096cf;
|
||||
|
||||
--inline-code-color: #ffb454;
|
||||
|
||||
--theme-popup-bg: #14191f;
|
||||
--theme-popup-border: #5c6773;
|
||||
--theme-hover: #191f26;
|
||||
|
||||
--quote-bg: hsl(226, 15%, 17%);
|
||||
--quote-border: hsl(226, 15%, 22%);
|
||||
|
||||
--table-border-color: hsl(210, 25%, 13%);
|
||||
--table-header-bg: hsl(210, 25%, 28%);
|
||||
--table-alternate-bg: hsl(210, 25%, 11%);
|
||||
|
||||
--searchbar-border-color: #848484;
|
||||
--searchbar-bg: #424242;
|
||||
--searchbar-fg: #fff;
|
||||
--searchbar-shadow-color: #d4c89f;
|
||||
--searchresults-header-fg: #666;
|
||||
--searchresults-border-color: #888;
|
||||
--searchresults-li-bg: #252932;
|
||||
--search-mark-bg: #e3b171;
|
||||
--hljs-background: #191f26;
|
||||
--hljs-color: #e6e1cf;
|
||||
--hljs-quote: #5c6773;
|
||||
--hljs-variable: #ff7733;
|
||||
--hljs-type: #ffee99;
|
||||
--hljs-title: #b8cc52;
|
||||
--hljs-symbol: #ffb454;
|
||||
--hljs-selector-tag: #ff7733;
|
||||
--hljs-selector-tag: #36a3d9;
|
||||
--hljs-selector-tag: #00568d;
|
||||
--hljs-selector-tag: #91b362;
|
||||
--hljs-selector-tag: #d96c75;
|
||||
}
|
||||
|
||||
.coal {
|
||||
--bg: hsl(200, 7%, 8%);
|
||||
--fg: #98a3ad;
|
||||
|
||||
--sidebar-bg: #292c2f;
|
||||
--sidebar-fg: #a1adb8;
|
||||
--sidebar-non-existent: #505254;
|
||||
--sidebar-active: #3473ad;
|
||||
--sidebar-spacer: #393939;
|
||||
|
||||
--scrollbar: var(--sidebar-fg);
|
||||
|
||||
--icons: #43484d;
|
||||
--icons-hover: #b3c0cc;
|
||||
|
||||
--links: #2b79a2;
|
||||
|
||||
--inline-code-color: #c5c8c6;
|
||||
|
||||
--theme-popup-bg: #141617;
|
||||
--theme-popup-border: #43484d;
|
||||
--theme-hover: #1f2124;
|
||||
|
||||
--quote-bg: hsl(234, 21%, 18%);
|
||||
--quote-border: hsl(234, 21%, 23%);
|
||||
|
||||
--table-border-color: hsl(200, 7%, 13%);
|
||||
--table-header-bg: hsl(200, 7%, 28%);
|
||||
--table-alternate-bg: hsl(200, 7%, 11%);
|
||||
|
||||
--searchbar-border-color: #aaa;
|
||||
--searchbar-bg: #b7b7b7;
|
||||
--searchbar-fg: #000;
|
||||
--searchbar-shadow-color: #aaa;
|
||||
--searchresults-header-fg: #666;
|
||||
--searchresults-border-color: #98a3ad;
|
||||
--searchresults-li-bg: #2b2b2f;
|
||||
--search-mark-bg: #355c7d;
|
||||
--hljs-background: #969896;
|
||||
--hljs-color: #cc6666;
|
||||
--hljs-quote: #de935f;
|
||||
--hljs-variable: #f0c674;
|
||||
--hljs-type: #b5bd68;
|
||||
--hljs-title: #8abeb7;
|
||||
--hljs-symbol: #81a2be;
|
||||
--hljs-selector-tag: #b294bb;
|
||||
--hljs-selector-tag: #1d1f21;
|
||||
--hljs-selector-tag: #c5c8c6;
|
||||
--hljs-selector-tag: #718c00;
|
||||
--hljs-selector-tag: #c82829;
|
||||
}
|
||||
|
||||
.light {
|
||||
--bg: hsl(0, 0%, 100%);
|
||||
--fg: hsl(0, 0%, 0%);
|
||||
|
||||
--sidebar-bg: #fafafa;
|
||||
--sidebar-fg: hsl(0, 0%, 0%);
|
||||
--sidebar-non-existent: #aaaaaa;
|
||||
--sidebar-active: #1f1fff;
|
||||
--sidebar-spacer: #f4f4f4;
|
||||
|
||||
--scrollbar: #8F8F8F;
|
||||
|
||||
--icons: #747474;
|
||||
--icons-hover: #000000;
|
||||
|
||||
--links: #20609f;
|
||||
|
||||
--inline-code-color: #301900;
|
||||
|
||||
--theme-popup-bg: #fafafa;
|
||||
--theme-popup-border: #cccccc;
|
||||
--theme-hover: #e6e6e6;
|
||||
|
||||
--quote-bg: hsl(197, 37%, 96%);
|
||||
--quote-border: hsl(197, 37%, 91%);
|
||||
|
||||
--table-border-color: hsl(0, 0%, 95%);
|
||||
--table-header-bg: hsl(0, 0%, 80%);
|
||||
--table-alternate-bg: hsl(0, 0%, 97%);
|
||||
|
||||
--searchbar-border-color: #aaa;
|
||||
--searchbar-bg: #fafafa;
|
||||
--searchbar-fg: #000;
|
||||
--searchbar-shadow-color: #aaa;
|
||||
--searchresults-header-fg: #666;
|
||||
--searchresults-border-color: #888;
|
||||
--searchresults-li-bg: #e4f2fe;
|
||||
--search-mark-bg: #a2cff5;
|
||||
--hljs-background: #f6f7f6;
|
||||
--hljs-color: #000;
|
||||
--hljs-quote: #575757;
|
||||
--hljs-variable: #d70025;
|
||||
--hljs-type: #b21e00;
|
||||
--hljs-title: #0030f2;
|
||||
--hljs-symbol: #008200;
|
||||
--hljs-selector-tag: #9d00ec;
|
||||
}
|
||||
|
||||
.navy {
|
||||
--bg: hsl(226, 23%, 11%);
|
||||
--fg: #bcbdd0;
|
||||
|
||||
--sidebar-bg: #282d3f;
|
||||
--sidebar-fg: #c8c9db;
|
||||
--sidebar-non-existent: #505274;
|
||||
--sidebar-active: #2b79a2;
|
||||
--sidebar-spacer: #2d334f;
|
||||
|
||||
--scrollbar: var(--sidebar-fg);
|
||||
|
||||
--icons: #737480;
|
||||
--icons-hover: #b7b9cc;
|
||||
|
||||
--links: #2b79a2;
|
||||
|
||||
--inline-code-color: #c5c8c6;
|
||||
|
||||
--theme-popup-bg: #161923;
|
||||
--theme-popup-border: #737480;
|
||||
--theme-hover: #282e40;
|
||||
|
||||
--quote-bg: hsl(226, 15%, 17%);
|
||||
--quote-border: hsl(226, 15%, 22%);
|
||||
|
||||
--table-border-color: hsl(226, 23%, 16%);
|
||||
--table-header-bg: hsl(226, 23%, 31%);
|
||||
--table-alternate-bg: hsl(226, 23%, 14%);
|
||||
|
||||
--searchbar-border-color: #aaa;
|
||||
--searchbar-bg: #aeaec6;
|
||||
--searchbar-fg: #000;
|
||||
--searchbar-shadow-color: #aaa;
|
||||
--searchresults-header-fg: #5f5f71;
|
||||
--searchresults-border-color: #5c5c68;
|
||||
--searchresults-li-bg: #242430;
|
||||
--search-mark-bg: #a2cff5;
|
||||
|
||||
--hljs-background: #969896;
|
||||
--hljs-color: #cc6666;
|
||||
--hljs-quote: #de935f;
|
||||
--hljs-variable: #f0c674;
|
||||
--hljs-type: #b5bd68;
|
||||
--hljs-title: #8abeb7;
|
||||
--hljs-symbol: #81a2be;
|
||||
--hljs-selector-tag: #b294bb;
|
||||
--hljs-selector-tag: #1d1f21;
|
||||
--hljs-selector-tag: #c5c8c6;
|
||||
--hljs-selector-tag: #718c00;
|
||||
--hljs-selector-tag: #c82829;
|
||||
}
|
||||
|
||||
.rust {
|
||||
--bg: hsl(60, 9%, 87%);
|
||||
--fg: #262625;
|
||||
|
||||
--sidebar-bg: #3b2e2a;
|
||||
--sidebar-fg: #c8c9db;
|
||||
--sidebar-non-existent: #505254;
|
||||
--sidebar-active: #e69f67;
|
||||
--sidebar-spacer: #45373a;
|
||||
|
||||
--scrollbar: var(--sidebar-fg);
|
||||
|
||||
--icons: #737480;
|
||||
--icons-hover: #262625;
|
||||
|
||||
--links: #2b79a2;
|
||||
|
||||
--inline-code-color: #6e6b5e;
|
||||
|
||||
--theme-popup-bg: #e1e1db;
|
||||
--theme-popup-border: #b38f6b;
|
||||
--theme-hover: #99908a;
|
||||
|
||||
--quote-bg: hsl(60, 5%, 75%);
|
||||
--quote-border: hsl(60, 5%, 70%);
|
||||
|
||||
--table-border-color: hsl(60, 9%, 82%);
|
||||
--table-header-bg: #b3a497;
|
||||
--table-alternate-bg: hsl(60, 9%, 84%);
|
||||
|
||||
--searchbar-border-color: #aaa;
|
||||
--searchbar-bg: #fafafa;
|
||||
--searchbar-fg: #000;
|
||||
--searchbar-shadow-color: #aaa;
|
||||
--searchresults-header-fg: #666;
|
||||
--searchresults-border-color: #888;
|
||||
--searchresults-li-bg: #dec2a2;
|
||||
--search-mark-bg: #e69f67;
|
||||
--hljs-background: #f6f7f6;
|
||||
--hljs-color: #000;
|
||||
--hljs-quote: #575757;
|
||||
--hljs-variable: #d70025;
|
||||
--hljs-type: #b21e00;
|
||||
--hljs-title: #0030f2;
|
||||
--hljs-symbol: #008200;
|
||||
--hljs-selector-tag: #9d00ec;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.light.no-js {
|
||||
--bg: hsl(200, 7%, 8%);
|
||||
--fg: #98a3ad;
|
||||
|
||||
--sidebar-bg: #292c2f;
|
||||
--sidebar-fg: #a1adb8;
|
||||
--sidebar-non-existent: #505254;
|
||||
--sidebar-active: #3473ad;
|
||||
--sidebar-spacer: #393939;
|
||||
|
||||
--scrollbar: var(--sidebar-fg);
|
||||
|
||||
--icons: #43484d;
|
||||
--icons-hover: #b3c0cc;
|
||||
|
||||
--links: #2b79a2;
|
||||
|
||||
--inline-code-color: #c5c8c6;
|
||||
|
||||
--theme-popup-bg: #141617;
|
||||
--theme-popup-border: #43484d;
|
||||
--theme-hover: #1f2124;
|
||||
|
||||
--quote-bg: hsl(234, 21%, 18%);
|
||||
--quote-border: hsl(234, 21%, 23%);
|
||||
|
||||
--table-border-color: hsl(200, 7%, 13%);
|
||||
--table-header-bg: hsl(200, 7%, 28%);
|
||||
--table-alternate-bg: hsl(200, 7%, 11%);
|
||||
|
||||
--searchbar-border-color: #aaa;
|
||||
--searchbar-bg: #b7b7b7;
|
||||
--searchbar-fg: #000;
|
||||
--searchbar-shadow-color: #aaa;
|
||||
--searchresults-header-fg: #666;
|
||||
--searchresults-border-color: #98a3ad;
|
||||
--searchresults-li-bg: #2b2b2f;
|
||||
--search-mark-bg: #355c7d;
|
||||
}
|
||||
}
|
||||
|
||||
.colibri {
|
||||
--bg: #3b224c;
|
||||
--fg: #bcbdd0;
|
||||
--heading-fg: #fff;
|
||||
|
||||
--sidebar-bg: #281733;
|
||||
--sidebar-fg: #c8c9db;
|
||||
--sidebar-non-existent: #505274;
|
||||
--sidebar-active: #a4a0e8;
|
||||
--sidebar-spacer: #2d334f;
|
||||
|
||||
--scrollbar: var(--sidebar-fg);
|
||||
|
||||
--icons: #737480;
|
||||
--icons-hover: #b7b9cc;
|
||||
|
||||
/* --links: #a4a0e8; */
|
||||
--links: #ECCDBA;
|
||||
|
||||
--inline-code-color: hsl(48.7, 7.8%, 70%);
|
||||
|
||||
--theme-popup-bg: #161923;
|
||||
--theme-popup-border: #737480;
|
||||
--theme-hover: rgba(0,0,0, .2);
|
||||
|
||||
--quote-bg: #281733;
|
||||
--quote-border: hsl(226, 15%, 22%);
|
||||
|
||||
--table-border-color: hsl(226, 23%, 76%);
|
||||
--table-header-bg: hsla(226, 23%, 31%, 0);
|
||||
--table-alternate-bg: hsl(226, 23%, 14%);
|
||||
--table-border-line: hsla(201deg, 20%, 92%, 0.2);
|
||||
|
||||
--searchbar-border-color: #aaa;
|
||||
--searchbar-bg: #aeaec6;
|
||||
--searchbar-fg: #000;
|
||||
--searchbar-shadow-color: #aaa;
|
||||
--searchresults-header-fg: #5f5f71;
|
||||
--searchresults-border-color: #5c5c68;
|
||||
--searchresults-li-bg: #242430;
|
||||
--search-mark-bg: #acff5;
|
||||
--hljs-background: #2f1e2e;
|
||||
--hljs-color: #a39e9b;
|
||||
--hljs-quote: #8d8687;
|
||||
--hljs-variable: #ef6155;
|
||||
--hljs-type: #f99b15;
|
||||
--hljs-title: #fec418;
|
||||
--hljs-symbol: #48b685;
|
||||
--hljs-selector-tag: #815ba4;
|
||||
}
|
||||
|
||||
.colibri {
|
||||
/*
|
||||
--bg: #ffffff;
|
||||
--fg: #452859;
|
||||
--fg: #5a5977;
|
||||
--heading-fg: #281733;
|
||||
|
||||
--sidebar-bg: #281733;
|
||||
--sidebar-fg: #c8c9db;
|
||||
--sidebar-non-existent: #505274;
|
||||
--sidebar-active: #a4a0e8;
|
||||
--sidebar-spacer: #2d334f;
|
||||
|
||||
--scrollbar: var(--sidebar-fg);
|
||||
|
||||
--icons: #737480;
|
||||
--icons-hover: #b7b9cc;
|
||||
|
||||
--links: #6F44F0;
|
||||
|
||||
--inline-code-color: #a39e9b;
|
||||
|
||||
--theme-popup-bg: #161923;
|
||||
--theme-popup-border: #737480;
|
||||
--theme-hover: rgba(0,0,0, .2);
|
||||
|
||||
--quote-bg: rgba(0, 0, 0, 0);
|
||||
--quote-border: hsl(226, 15%, 75%);
|
||||
|
||||
--table-border-color: #5a5977;
|
||||
--table-border-color: hsl(201deg 10% 67%);
|
||||
--table-header-bg: hsl(0, 0%, 100%);
|
||||
--table-alternate-bg: hsl(0, 0%, 97%);
|
||||
--table-border-line: hsl(201deg, 20%, 92%);
|
||||
|
||||
--searchbar-border-color: #aaa;
|
||||
--searchbar-bg: #aeaec6;
|
||||
--searchbar-fg: #000;
|
||||
--searchbar-shadow-color: #aaa;
|
||||
--searchresults-header-fg: #5f5f71;
|
||||
--searchresults-border-color: #5c5c68;
|
||||
--searchresults-li-bg: #242430;
|
||||
--search-mark-bg: #a2cff5;
|
||||
--hljs-background: #TODO;
|
||||
--hljs-color: #TODO;
|
||||
--hljs-quote: #TODO;
|
||||
--hljs-variable: #TODO;
|
||||
--hljs-type: #TODO;
|
||||
--hljs-title: #TODO;
|
||||
--hljs-symbol: #TODO;
|
||||
--hljs-selector-tag: #TODO;
|
||||
*/
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
pre code.hljs {
|
||||
display:block;
|
||||
overflow-x:auto;
|
||||
padding:1em
|
||||
}
|
||||
code.hljs {
|
||||
padding:3px 5px
|
||||
}
|
||||
.hljs {
|
||||
background: var(--hljs-background);
|
||||
color: var(--hljs-color);
|
||||
}
|
||||
.hljs-comment,
|
||||
.hljs-quote {
|
||||
color: var(--hljs-quote)
|
||||
}
|
||||
.hljs-link,
|
||||
.hljs-meta,
|
||||
.hljs-name,
|
||||
.hljs-regexp,
|
||||
.hljs-selector-class,
|
||||
.hljs-selector-id,
|
||||
.hljs-tag,
|
||||
.hljs-template-variable,
|
||||
.hljs-variable {
|
||||
color: var(--hljs-variable)
|
||||
}
|
||||
.hljs-built_in,
|
||||
.hljs-deletion,
|
||||
.hljs-literal,
|
||||
.hljs-number,
|
||||
.hljs-params,
|
||||
.hljs-type {
|
||||
color: var(--hljs-type)
|
||||
}
|
||||
.hljs-attribute,
|
||||
.hljs-section,
|
||||
.hljs-title {
|
||||
color: var(--hljs-title)
|
||||
}
|
||||
.hljs-addition,
|
||||
.hljs-bullet,
|
||||
.hljs-string,
|
||||
.hljs-symbol {
|
||||
color: var(--hljs-symbol)
|
||||
}
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag {
|
||||
color: var(--hljs-selector-tag)
|
||||
}
|
||||
.hljs-emphasis {
|
||||
font-style:italic
|
||||
}
|
||||
.hljs-strong {
|
||||
font-weight:700
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -1,43 +0,0 @@
|
||||
use std::ops::DerefMut;
|
||||
|
||||
use nucleo::pattern::{Atom, AtomKind, CaseMatching};
|
||||
use nucleo::Config;
|
||||
use parking_lot::Mutex;
|
||||
|
||||
pub struct LazyMutex<T> {
|
||||
inner: Mutex<Option<T>>,
|
||||
init: fn() -> T,
|
||||
}
|
||||
|
||||
impl<T> LazyMutex<T> {
|
||||
pub const fn new(init: fn() -> T) -> Self {
|
||||
Self {
|
||||
inner: Mutex::new(None),
|
||||
init,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lock(&self) -> impl DerefMut<Target = T> + '_ {
|
||||
parking_lot::MutexGuard::map(self.inner.lock(), |val| val.get_or_insert_with(self.init))
|
||||
}
|
||||
}
|
||||
|
||||
pub static MATCHER: LazyMutex<nucleo::Matcher> = LazyMutex::new(nucleo::Matcher::default);
|
||||
|
||||
/// convenience function to easily fuzzy match
|
||||
/// on a (relatively small list of inputs). This is not recommended for building a full tui
|
||||
/// application that can match large numbers of matches as all matching is done on the current
|
||||
/// thread, effectively blocking the UI
|
||||
pub fn fuzzy_match<T: AsRef<str>>(
|
||||
pattern: &str,
|
||||
items: impl IntoIterator<Item = T>,
|
||||
path: bool,
|
||||
) -> Vec<(T, u16)> {
|
||||
let mut matcher = MATCHER.lock();
|
||||
matcher.config = Config::DEFAULT;
|
||||
if path {
|
||||
matcher.config.set_match_paths();
|
||||
}
|
||||
let pattern = Atom::new(pattern, CaseMatching::Smart, AtomKind::Fuzzy, false);
|
||||
pattern.match_list(items, &mut matcher)
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Register {
|
||||
name: char,
|
||||
values: Vec<String>,
|
||||
}
|
||||
|
||||
impl Register {
|
||||
pub const fn new(name: char) -> Self {
|
||||
Self {
|
||||
name,
|
||||
values: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_values(name: char, values: Vec<String>) -> Self {
|
||||
Self { name, values }
|
||||
}
|
||||
|
||||
pub const fn name(&self) -> char {
|
||||
self.name
|
||||
}
|
||||
|
||||
pub fn read(&self) -> &[String] {
|
||||
&self.values
|
||||
}
|
||||
|
||||
pub fn write(&mut self, values: Vec<String>) {
|
||||
self.values = values;
|
||||
}
|
||||
|
||||
pub fn push(&mut self, value: String) {
|
||||
self.values.push(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// Currently just wraps a `HashMap` of `Register`s
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Registers {
|
||||
inner: HashMap<char, Register>,
|
||||
}
|
||||
|
||||
impl Registers {
|
||||
pub fn get(&self, name: char) -> Option<&Register> {
|
||||
self.inner.get(&name)
|
||||
}
|
||||
|
||||
pub fn read(&self, name: char) -> Option<&[String]> {
|
||||
self.get(name).map(|reg| reg.read())
|
||||
}
|
||||
|
||||
pub fn write(&mut self, name: char, values: Vec<String>) {
|
||||
if name != '_' {
|
||||
self.inner
|
||||
.insert(name, Register::new_with_values(name, values));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push(&mut self, name: char, value: String) {
|
||||
if name != '_' {
|
||||
if let Some(r) = self.inner.get_mut(&name) {
|
||||
r.push(value);
|
||||
} else {
|
||||
self.write(name, vec![value]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn first(&self, name: char) -> Option<&String> {
|
||||
self.read(name).and_then(|entries| entries.first())
|
||||
}
|
||||
|
||||
pub fn last(&self, name: char) -> Option<&String> {
|
||||
self.read(name).and_then(|entries| entries.last())
|
||||
}
|
||||
|
||||
pub fn inner(&self) -> &HashMap<char, Register> {
|
||||
&self.inner
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
use std::io;
|
||||
|
||||
use ropey::iter::Chunks;
|
||||
use ropey::RopeSlice;
|
||||
|
||||
pub struct RopeReader<'a> {
|
||||
current_chunk: &'a [u8],
|
||||
chunks: Chunks<'a>,
|
||||
}
|
||||
|
||||
impl<'a> RopeReader<'a> {
|
||||
pub fn new(rope: RopeSlice<'a>) -> RopeReader<'a> {
|
||||
RopeReader {
|
||||
current_chunk: &[],
|
||||
chunks: rope.chunks(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl io::Read for RopeReader<'_> {
|
||||
fn read(&mut self, mut buf: &mut [u8]) -> io::Result<usize> {
|
||||
let buf_len = buf.len();
|
||||
loop {
|
||||
let read_bytes = self.current_chunk.read(buf)?;
|
||||
buf = &mut buf[read_bytes..];
|
||||
if buf.is_empty() {
|
||||
return Ok(buf_len);
|
||||
}
|
||||
|
||||
if let Some(next_chunk) = self.chunks.next() {
|
||||
self.current_chunk = next_chunk.as_bytes();
|
||||
} else {
|
||||
return Ok(buf_len - buf.len());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
std::vector<std::string>
|
||||
fn_with_many_parameters(int parm1, long parm2, float parm3, double parm4,
|
||||
char* parm5, bool parm6);
|
||||
|
||||
std::vector<std::string>
|
||||
fn_with_many_parameters(int parm1, long parm2, float parm3, double parm4,
|
||||
char* parm5, bool parm6) {
|
||||
auto lambda = []() {
|
||||
return 0;
|
||||
};
|
||||
auto lambda_with_a_really_long_name_that_uses_a_whole_line
|
||||
= [](int some_more_aligned_parameters,
|
||||
std::string parm2) {
|
||||
do_smth();
|
||||
};
|
||||
if (brace_on_same_line) {
|
||||
do_smth();
|
||||
} else if (brace_on_next_line)
|
||||
{
|
||||
do_smth();
|
||||
} else if (another_condition) {
|
||||
do_smth();
|
||||
}
|
||||
else {
|
||||
do_smth();
|
||||
}
|
||||
if (inline_if_statement)
|
||||
do_smth();
|
||||
if (another_inline_if_statement)
|
||||
return [](int parm1, char* parm2) {
|
||||
this_is_a_really_pointless_lambda();
|
||||
};
|
||||
|
||||
switch (var) {
|
||||
case true:
|
||||
return -1;
|
||||
case false:
|
||||
return 42;
|
||||
}
|
||||
}
|
||||
|
||||
class MyClass : public MyBaseClass {
|
||||
public:
|
||||
MyClass();
|
||||
void public_fn();
|
||||
private:
|
||||
super_secret_private_fn();
|
||||
}
|
@ -0,0 +1 @@
|
||||
../../../src/indent.rs
|
@ -1,15 +0,0 @@
|
||||
[package]
|
||||
name = "helix-event"
|
||||
version = "0.6.0"
|
||||
authors = ["Blaž Hrastnik <blaz@mxxn.io>"]
|
||||
edition = "2021"
|
||||
license = "MPL-2.0"
|
||||
categories = ["editor"]
|
||||
repository = "https://github.com/helix-editor/helix"
|
||||
homepage = "https://helix-editor.com"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "1", features = ["rt", "rt-multi-thread", "time", "sync", "parking_lot"] }
|
||||
parking_lot = { version = "0.12", features = ["send_guard"] }
|
@ -1,8 +0,0 @@
|
||||
//! `helix-event` contains systems that allow (often async) communication between
|
||||
//! different editor components without strongly coupling them. Currently this
|
||||
//! crate only contains some smaller facilities but the intend is to add more
|
||||
//! functionality in the future ( like a generic hook system)
|
||||
|
||||
pub use redraw::{lock_frame, redraw_requested, request_redraw, start_frame, RenderLockGuard};
|
||||
|
||||
mod redraw;
|
@ -1,49 +0,0 @@
|
||||
//! Signals that control when/if the editor redraws
|
||||
|
||||
use std::future::Future;
|
||||
|
||||
use parking_lot::{RwLock, RwLockReadGuard};
|
||||
use tokio::sync::Notify;
|
||||
|
||||
/// A `Notify` instance that can be used to (asynchronously) request
|
||||
/// the editor the render a new frame.
|
||||
static REDRAW_NOTIFY: Notify = Notify::const_new();
|
||||
|
||||
/// A `RwLock` that prevents the next frame from being
|
||||
/// drawn until an exclusive (write) lock can be acquired.
|
||||
/// This allows asynchsonous tasks to acquire `non-exclusive`
|
||||
/// locks (read) to prevent the next frame from being drawn
|
||||
/// until a certain computation has finished.
|
||||
static RENDER_LOCK: RwLock<()> = RwLock::new(());
|
||||
|
||||
pub type RenderLockGuard = RwLockReadGuard<'static, ()>;
|
||||
|
||||
/// Requests that the editor is redrawn. The redraws are debounced (currently to
|
||||
/// 30FPS) so this can be called many times without causing a ton of frames to
|
||||
/// be rendered.
|
||||
pub fn request_redraw() {
|
||||
REDRAW_NOTIFY.notify_one();
|
||||
}
|
||||
|
||||
/// Returns a future that will yield once a redraw has been asynchronously
|
||||
/// requested using [`request_redraw`].
|
||||
pub fn redraw_requested() -> impl Future<Output = ()> {
|
||||
REDRAW_NOTIFY.notified()
|
||||
}
|
||||
|
||||
/// Wait until all locks acquired with [`lock_frame`] have been released.
|
||||
/// This function is called before rendering and is intended to allow the frame
|
||||
/// to wait for async computations that should be included in the current frame.
|
||||
pub fn start_frame() {
|
||||
drop(RENDER_LOCK.write());
|
||||
// exhaust any leftover redraw notifications
|
||||
let notify = REDRAW_NOTIFY.notified();
|
||||
tokio::pin!(notify);
|
||||
notify.enable();
|
||||
}
|
||||
|
||||
/// Acquires the render lock which will prevent the next frame from being drawn
|
||||
/// until the returned guard is dropped.
|
||||
pub fn lock_frame() -> RenderLockGuard {
|
||||
RENDER_LOCK.read()
|
||||
}
|
@ -1,193 +0,0 @@
|
||||
use std::{collections::HashMap, path::PathBuf, sync::Weak};
|
||||
|
||||
use globset::{GlobBuilder, GlobSetBuilder};
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use crate::{lsp, Client};
|
||||
|
||||
enum Event {
|
||||
FileChanged {
|
||||
path: PathBuf,
|
||||
},
|
||||
Register {
|
||||
client_id: usize,
|
||||
client: Weak<Client>,
|
||||
registration_id: String,
|
||||
options: lsp::DidChangeWatchedFilesRegistrationOptions,
|
||||
},
|
||||
Unregister {
|
||||
client_id: usize,
|
||||
registration_id: String,
|
||||
},
|
||||
RemoveClient {
|
||||
client_id: usize,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct ClientState {
|
||||
client: Weak<Client>,
|
||||
registered: HashMap<String, globset::GlobSet>,
|
||||
}
|
||||
|
||||
/// The Handler uses a dedicated tokio task to respond to file change events by
|
||||
/// forwarding changes to LSPs that have registered for notifications with a
|
||||
/// matching glob.
|
||||
///
|
||||
/// When an LSP registers for the DidChangeWatchedFiles notification, the
|
||||
/// Handler is notified by sending the registration details in addition to a
|
||||
/// weak reference to the LSP client. This is done so that the Handler can have
|
||||
/// access to the client without preventing the client from being dropped if it
|
||||
/// is closed and the Handler isn't properly notified.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Handler {
|
||||
tx: mpsc::UnboundedSender<Event>,
|
||||
}
|
||||
|
||||
impl Default for Handler {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Handler {
|
||||
pub fn new() -> Self {
|
||||
let (tx, rx) = mpsc::unbounded_channel();
|
||||
tokio::spawn(Self::run(rx));
|
||||
Self { tx }
|
||||
}
|
||||
|
||||
pub fn register(
|
||||
&self,
|
||||
client_id: usize,
|
||||
client: Weak<Client>,
|
||||
registration_id: String,
|
||||
options: lsp::DidChangeWatchedFilesRegistrationOptions,
|
||||
) {
|
||||
let _ = self.tx.send(Event::Register {
|
||||
client_id,
|
||||
client,
|
||||
registration_id,
|
||||
options,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn unregister(&self, client_id: usize, registration_id: String) {
|
||||
let _ = self.tx.send(Event::Unregister {
|
||||
client_id,
|
||||
registration_id,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn file_changed(&self, path: PathBuf) {
|
||||
let _ = self.tx.send(Event::FileChanged { path });
|
||||
}
|
||||
|
||||
pub fn remove_client(&self, client_id: usize) {
|
||||
let _ = self.tx.send(Event::RemoveClient { client_id });
|
||||
}
|
||||
|
||||
async fn run(mut rx: mpsc::UnboundedReceiver<Event>) {
|
||||
let mut state: HashMap<usize, ClientState> = HashMap::new();
|
||||
while let Some(event) = rx.recv().await {
|
||||
match event {
|
||||
Event::FileChanged { path } => {
|
||||
log::debug!("Received file event for {:?}", &path);
|
||||
|
||||
state.retain(|id, client_state| {
|
||||
if !client_state
|
||||
.registered
|
||||
.values()
|
||||
.any(|glob| glob.is_match(&path))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
let Some(client) = client_state.client.upgrade() else {
|
||||
log::warn!("LSP client was dropped: {id}");
|
||||
return false;
|
||||
};
|
||||
let Ok(uri) = lsp::Url::from_file_path(&path) else {
|
||||
return true;
|
||||
};
|
||||
log::debug!(
|
||||
"Sending didChangeWatchedFiles notification to client '{}'",
|
||||
client.name()
|
||||
);
|
||||
if let Err(err) = crate::block_on(client
|
||||
.did_change_watched_files(vec![lsp::FileEvent {
|
||||
uri,
|
||||
// We currently always send the CHANGED state
|
||||
// since we don't actually have more context at
|
||||
// the moment.
|
||||
typ: lsp::FileChangeType::CHANGED,
|
||||
}]))
|
||||
{
|
||||
log::warn!("Failed to send didChangeWatchedFiles notification to client: {err}");
|
||||
}
|
||||
true
|
||||
});
|
||||
}
|
||||
Event::Register {
|
||||
client_id,
|
||||
client,
|
||||
registration_id,
|
||||
options: ops,
|
||||
} => {
|
||||
log::debug!(
|
||||
"Registering didChangeWatchedFiles for client '{}' with id '{}'",
|
||||
client_id,
|
||||
registration_id
|
||||
);
|
||||
|
||||
let entry = state.entry(client_id).or_insert_with(ClientState::default);
|
||||
entry.client = client;
|
||||
|
||||
let mut builder = GlobSetBuilder::new();
|
||||
for watcher in ops.watchers {
|
||||
if let lsp::GlobPattern::String(pattern) = watcher.glob_pattern {
|
||||
if let Ok(glob) = GlobBuilder::new(&pattern).build() {
|
||||
builder.add(glob);
|
||||
}
|
||||
}
|
||||
}
|
||||
match builder.build() {
|
||||
Ok(globset) => {
|
||||
entry.registered.insert(registration_id, globset);
|
||||
}
|
||||
Err(err) => {
|
||||
// Remove any old state for that registration id and
|
||||
// remove the entire client if it's now empty.
|
||||
entry.registered.remove(®istration_id);
|
||||
if entry.registered.is_empty() {
|
||||
state.remove(&client_id);
|
||||
}
|
||||
log::warn!(
|
||||
"Unable to build globset for LSP didChangeWatchedFiles {err}"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Event::Unregister {
|
||||
client_id,
|
||||
registration_id,
|
||||
} => {
|
||||
log::debug!(
|
||||
"Unregistering didChangeWatchedFiles with id '{}' for client '{}'",
|
||||
registration_id,
|
||||
client_id
|
||||
);
|
||||
if let Some(client_state) = state.get_mut(&client_id) {
|
||||
client_state.registered.remove(®istration_id);
|
||||
if client_state.registered.is_empty() {
|
||||
state.remove(&client_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
Event::RemoveClient { client_id } => {
|
||||
log::debug!("Removing LSP client: {client_id}");
|
||||
state.remove(&client_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1 +1,4 @@
|
||||
/target
|
||||
|
||||
# This folder is used by `test_explorer` to create temporary folders needed for testing
|
||||
test_explorer
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,239 @@
|
||||
use fuzzy_matcher::skim::SkimMatcherV2 as Matcher;
|
||||
use fuzzy_matcher::FuzzyMatcher;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
|
||||
struct QueryAtom {
|
||||
kind: QueryAtomKind,
|
||||
atom: String,
|
||||
ignore_case: bool,
|
||||
inverse: bool,
|
||||
}
|
||||
impl QueryAtom {
|
||||
fn new(atom: &str) -> Option<QueryAtom> {
|
||||
let mut atom = atom.to_string();
|
||||
let inverse = atom.starts_with('!');
|
||||
if inverse {
|
||||
atom.remove(0);
|
||||
}
|
||||
|
||||
let mut kind = match atom.chars().next() {
|
||||
Some('^') => QueryAtomKind::Prefix,
|
||||
Some('\'') => QueryAtomKind::Substring,
|
||||
_ if inverse => QueryAtomKind::Substring,
|
||||
_ => QueryAtomKind::Fuzzy,
|
||||
};
|
||||
|
||||
if atom.starts_with(['^', '\'']) {
|
||||
atom.remove(0);
|
||||
}
|
||||
|
||||
if atom.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
if atom.ends_with('$') && !atom.ends_with("\\$") {
|
||||
atom.pop();
|
||||
kind = if kind == QueryAtomKind::Prefix {
|
||||
QueryAtomKind::Exact
|
||||
} else {
|
||||
QueryAtomKind::Postfix
|
||||
}
|
||||
}
|
||||
|
||||
Some(QueryAtom {
|
||||
kind,
|
||||
atom: atom.replace('\\', ""),
|
||||
// not ideal but fuzzy_matches only knows ascii uppercase so more consistent
|
||||
// to behave the same
|
||||
ignore_case: kind != QueryAtomKind::Fuzzy
|
||||
&& atom.chars().all(|c| c.is_ascii_lowercase()),
|
||||
inverse,
|
||||
})
|
||||
}
|
||||
|
||||
fn indices(&self, matcher: &Matcher, item: &str, indices: &mut Vec<usize>) -> bool {
|
||||
// for inverse there are no indices to return
|
||||
// just return whether we matched
|
||||
if self.inverse {
|
||||
return self.matches(matcher, item);
|
||||
}
|
||||
let buf;
|
||||
let item = if self.ignore_case {
|
||||
buf = item.to_ascii_lowercase();
|
||||
&buf
|
||||
} else {
|
||||
item
|
||||
};
|
||||
let off = match self.kind {
|
||||
QueryAtomKind::Fuzzy => {
|
||||
if let Some((_, fuzzy_indices)) = matcher.fuzzy_indices(item, &self.atom) {
|
||||
indices.extend_from_slice(&fuzzy_indices);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
QueryAtomKind::Substring => {
|
||||
if let Some(off) = item.find(&self.atom) {
|
||||
off
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
QueryAtomKind::Prefix if item.starts_with(&self.atom) => 0,
|
||||
QueryAtomKind::Postfix if item.ends_with(&self.atom) => item.len() - self.atom.len(),
|
||||
QueryAtomKind::Exact if item == self.atom => 0,
|
||||
_ => return false,
|
||||
};
|
||||
|
||||
indices.extend(off..(off + self.atom.len()));
|
||||
true
|
||||
}
|
||||
|
||||
fn matches(&self, matcher: &Matcher, item: &str) -> bool {
|
||||
let buf;
|
||||
let item = if self.ignore_case {
|
||||
buf = item.to_ascii_lowercase();
|
||||
&buf
|
||||
} else {
|
||||
item
|
||||
};
|
||||
let mut res = match self.kind {
|
||||
QueryAtomKind::Fuzzy => matcher.fuzzy_match(item, &self.atom).is_some(),
|
||||
QueryAtomKind::Substring => item.contains(&self.atom),
|
||||
QueryAtomKind::Prefix => item.starts_with(&self.atom),
|
||||
QueryAtomKind::Postfix => item.ends_with(&self.atom),
|
||||
QueryAtomKind::Exact => item == self.atom,
|
||||
};
|
||||
if self.inverse {
|
||||
res = !res;
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
enum QueryAtomKind {
|
||||
/// Item is a fuzzy match of this behaviour
|
||||
///
|
||||
/// Usage: `foo`
|
||||
Fuzzy,
|
||||
/// Item contains query atom as a continuous substring
|
||||
///
|
||||
/// Usage `'foo`
|
||||
Substring,
|
||||
/// Item starts with query atom
|
||||
///
|
||||
/// Usage: `^foo`
|
||||
Prefix,
|
||||
/// Item ends with query atom
|
||||
///
|
||||
/// Usage: `foo$`
|
||||
Postfix,
|
||||
/// Item is equal to query atom
|
||||
///
|
||||
/// Usage `^foo$`
|
||||
Exact,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct FuzzyQuery {
|
||||
first_fuzzy_atom: Option<String>,
|
||||
query_atoms: Vec<QueryAtom>,
|
||||
}
|
||||
|
||||
fn query_atoms(query: &str) -> impl Iterator<Item = &str> + '_ {
|
||||
let mut saw_backslash = false;
|
||||
query.split(move |c| {
|
||||
saw_backslash = match c {
|
||||
' ' if !saw_backslash => return true,
|
||||
'\\' => true,
|
||||
_ => false,
|
||||
};
|
||||
false
|
||||
})
|
||||
}
|
||||
|
||||
impl FuzzyQuery {
|
||||
pub fn refine(&self, query: &str, old_query: &str) -> (FuzzyQuery, bool) {
|
||||
// TODO: we could be a lot smarter about this
|
||||
let new_query = Self::new(query);
|
||||
let mut is_refinement = query.starts_with(old_query);
|
||||
|
||||
// if the last atom is an inverse atom adding more text to it
|
||||
// will actually increase the number of matches and we can not refine
|
||||
// the matches.
|
||||
if is_refinement && !self.query_atoms.is_empty() {
|
||||
let last_idx = self.query_atoms.len() - 1;
|
||||
if self.query_atoms[last_idx].inverse
|
||||
&& self.query_atoms[last_idx].atom != new_query.query_atoms[last_idx].atom
|
||||
{
|
||||
is_refinement = false;
|
||||
}
|
||||
}
|
||||
|
||||
(new_query, is_refinement)
|
||||
}
|
||||
|
||||
pub fn new(query: &str) -> FuzzyQuery {
|
||||
let mut first_fuzzy_query = None;
|
||||
let query_atoms = query_atoms(query)
|
||||
.filter_map(|atom| {
|
||||
let atom = QueryAtom::new(atom)?;
|
||||
if atom.kind == QueryAtomKind::Fuzzy && first_fuzzy_query.is_none() {
|
||||
first_fuzzy_query = Some(atom.atom);
|
||||
None
|
||||
} else {
|
||||
Some(atom)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
FuzzyQuery {
|
||||
first_fuzzy_atom: first_fuzzy_query,
|
||||
query_atoms,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fuzzy_match(&self, item: &str, matcher: &Matcher) -> Option<i64> {
|
||||
// use the rank of the first fuzzzy query for the rank, because merging ranks is not really possible
|
||||
// this behaviour matches fzf and skim
|
||||
let score = self
|
||||
.first_fuzzy_atom
|
||||
.as_ref()
|
||||
.map_or(Some(0), |atom| matcher.fuzzy_match(item, atom))?;
|
||||
if self
|
||||
.query_atoms
|
||||
.iter()
|
||||
.any(|atom| !atom.matches(matcher, item))
|
||||
{
|
||||
return None;
|
||||
}
|
||||
Some(score)
|
||||
}
|
||||
|
||||
pub fn fuzzy_indices(&self, item: &str, matcher: &Matcher) -> Option<(i64, Vec<usize>)> {
|
||||
let (score, mut indices) = self.first_fuzzy_atom.as_ref().map_or_else(
|
||||
|| Some((0, Vec::new())),
|
||||
|atom| matcher.fuzzy_indices(item, atom),
|
||||
)?;
|
||||
|
||||
// fast path for the common case of just a single atom
|
||||
if self.query_atoms.is_empty() {
|
||||
return Some((score, indices));
|
||||
}
|
||||
|
||||
for atom in &self.query_atoms {
|
||||
if !atom.indices(matcher, item, &mut indices) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
// deadup and remove duplicate matches
|
||||
indices.sort_unstable();
|
||||
indices.dedup();
|
||||
|
||||
Some((score, indices))
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
use crate::ui::fuzzy_match::FuzzyQuery;
|
||||
use crate::ui::fuzzy_match::Matcher;
|
||||
|
||||
fn run_test<'a>(query: &str, items: &'a [&'a str]) -> Vec<String> {
|
||||
let query = FuzzyQuery::new(query);
|
||||
let matcher = Matcher::default();
|
||||
items
|
||||
.iter()
|
||||
.filter_map(|item| {
|
||||
let (_, indices) = query.fuzzy_indices(item, &matcher)?;
|
||||
let matched_string = indices
|
||||
.iter()
|
||||
.map(|&pos| item.chars().nth(pos).unwrap())
|
||||
.collect();
|
||||
Some(matched_string)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn match_single_value() {
|
||||
let matches = run_test("foo", &["foobar", "foo", "bar"]);
|
||||
assert_eq!(matches, &["foo", "foo"])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn match_multiple_values() {
|
||||
let matches = run_test(
|
||||
"foo bar",
|
||||
&["foo bar", "foo bar", "bar foo", "bar", "foo"],
|
||||
);
|
||||
assert_eq!(matches, &["foobar", "foobar", "barfoo"])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn space_escape() {
|
||||
let matches = run_test(r"foo\ bar", &["bar foo", "foo bar", "foobar"]);
|
||||
assert_eq!(matches, &["foo bar"])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trim() {
|
||||
let matches = run_test(r" foo bar ", &["bar foo", "foo bar", "foobar"]);
|
||||
assert_eq!(matches, &["barfoo", "foobar", "foobar"]);
|
||||
let matches = run_test(r" foo bar\ ", &["bar foo", "foo bar", "foobar"]);
|
||||
assert_eq!(matches, &["bar foo"])
|
||||
}
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue