168 | --blockquote-text-color: var(--gray-be);
169 | // > note
170 | --blockquote-note-background-color: var(--dark-transparent-green);
171 | --blockquote-note-icon-color: var(--light-green);
172 | // > tip
173 | --blockquote-tip-background-color: var(--light-transparent-gray);
174 | --blockquote-tip-icon-color: var(--gray-be);
175 | // > warning
176 | --blockquote-warning-background-color: var(--dark-transparent-orange);
177 | --blockquote-warning-icon-color: var(--orange);
178 |
179 | // tags
180 | --tag-unsafe-color: var(--brand-color);
181 | --tag-deprecated-color: var(--gray-be);
182 |
183 | // buttons
184 | --button-hover-background-color: var(--gray-32);
185 |
186 | // search
187 | --search-button-background-color: var(--almost-black);
188 | --search-button-hover-border-color: var(--brand-color);
189 | --search-button-text-color: var(--gray-be);
190 | --search-input-placeholder-color: var(--gray-5a);
191 | --search-labels-text-color: var(--gray-5a);
192 | --search-result-active-background-color: var(--brand-color);
193 | --search-result-active-text-color: var(--text-color);
194 | --search-result-footer-hint-text-color: var(--gray-5a);
195 | --search-result-footer-hint-key-background-color: var(--gray-22);
196 | --search-result-footer-hint-keytext-color: var(--gray-be);
197 |
198 | // code-related
199 | --keyword-color: #cc7832;
200 | --comment-color: gray;
201 | --number-color: #2AACB8;
202 | --string-color: #6AAB73;
203 |
204 | // symbol and module references
205 | --symbol-ref-link-color: var(--keyword-color);
206 | --symbol-ref-underline: underline;
207 | --symbol-ref-hover-color: var(--keyword-color);
208 | --module-ref-link-color: var(--light-salat-green);
209 |
210 | // playground
211 | --playground-header-background-color: var(--gray-24);
212 | --tools-background-color: var(--dark-gray);
213 | }
214 |
--------------------------------------------------------------------------------
/www/styles/index.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sourceRoot":"","sources":["fonts.scss","colors.scss","close-button.scss","header.scss","tools.scss","tips.scss","scrollbar.scss","shortcut.scss","editor-themes/light.css","editor-themes/dark.css","playground_select_examples.scss","playground_help.scss","playground.scss"],"names":[],"mappings":"AAAQ;AAER;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AChDF;EAEE;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EAEA;EACA;EAEA;EACA;;;AAIF;EAEE;EAEA;EACA;EACA;EACA;EACA;EAEA;EAGA;EACA;EAGA;EAGA;EAGA;EACA;EACA;EACA;EAGA;EACA;EAGA;EACA;EACA;EAGA;EAEA;EACA;EAEA;EACA;EAEA;EACA;EAGA;EACA;EAGA;EAGA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;EAGA;EACA;;;AAIF;EAEE;EAEA;EACA;EACA;EACA;EACA;EAEA;EAGA;EACA;EAGA;EAGA;EAGA;EACA;EACA;EACA;EAGA;EACA;EAGA;EACA;EACA;EAGA;EAEA;EACA;EAEA;EACA;EAEA;EACA;EAGA;EACA;EAGA;EAGA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;EAGA;EACA;;;ACnNF;EACE;EACA;EAEA;EACA;EAEA;EACA;EACA;EAEA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;EACA;;AAEA;EACE;;;ACxBN;EACE;;;AAGF;EACE;;;AAKE;EACE;;AAKF;EACE;EACA;EACA;;AAKF;EACE;EACA;EACA;;AAEA;EALF;IAMI;IACA;IACA;;;AAMJ;EACE;;AAGF;EACE;;AAKF;EACE;;AAKF;EACE;EACA;;AAGF;EACE;;AAKF;EACE;IACE;;EAGF;IACE;IACA;;;AAMJ;EACE;IACE;;EAGF;IACE;IACA;;;;AAMR;EACE;EACA;EACA;EACA;EAEA;EACA;;AAEA;EATF;IAUI;IAEA;IACA;IACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAEA;EAPF;IAQI;;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAEA;EAPF;IAQI;;;AAGF;EACE;EACA;EAEA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;;AAGF;EACE;EACA;;AAGF;EACE;;AAIJ;EACE;;AAEA;EACE;EACA;EACA;EACA;EACA;EAEA;EAEA;EAEA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;AAEA;EACE;EACA;EACA;;AAGF;EACE;;AAGF;EACE;;AAIJ;EACE;;AAGF;EACE;EACA;EACA;EACA;;AAKN;EACE;EACA;EACA;EACA;EAEA;;AAEA;EARF;IASI;;;AAGF;EACE;EACA;;AAGF;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAIA;EADF;IAEI;;;AAGF;EACE;EACA;EACA;EACA;EAEA;EACA;EAEA;EACA;EACA;EACA;EACA;;AAGE;EACE;;AAIJ;EACE;;AAGE;EACE;;AAKN;EACE;EACA;;AAEA;EACE;;AAKN;EACE;;AAKF;EADF;IAEI;;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EAEA;;AAEA;EACE;;AAGF;EACE;EACA;;AAEA;EACE;;AAGF;EACE;;;AAQZ;EACE;;AAEA;EACE;EACA;;AAEA;EACE;EACA;;AAII;EACE;;AAMR;EACE;;AAGF;EACE;EACA;EACA;EACA;;AAEA;EANF;IAOI;;;AAGF;EACE;;AAEA;EACE;;AAON;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EAEA;EAEA;EACA;;AAEA;EACE;EACA;EACA;;AAGF;EACE;EACA;;AAEA;EACE;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;;AAQZ;EACE;EACA;EACA;EACA;EACA;EAEA;EAEA;EAEA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EAEA;;AAGE;EACE;EACA;EACA;;AAEA;EACE;;AAEA;EACE;;AAON;EACE;;AAIJ;EACE;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;;AAOV;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EAEA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EAEA;EACA;EACA;EACA;EAEA;EACA;EACA;EAEA;EACA;EAEA;;AAEA;EAnBF;IAoBI;IACA;IACA;IACA;IAEA;IACA;IAEA;;;AAGF;EACE;;AAEA;EAHF;IAII;IACA;IACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EARF;IASI;;;AAKN;EACE;EACA;EAEA;EAEA;EACA;EAEA;;AAGE;EACE;EAEA;EACA;;AAEA;EACE;;AAGF;EACE;;AAKN;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAIJ;EACE;EACA;EACA;EACA;EAEA;;AAEA;EACE;;AAKN;EACE;EACA;;AAEA;EACE;EACA;EAEA;;AAGF;EACE;EACA;EACA;EAEA;EACA;;AAEA;EARF;IASI;;;AAIA;EACE;;AAGF;EACE;;AAIA;EACE;;AAOV;EACE;EACA;EACA;EAEA;;AAEA;EAPF;IAQI;;;AAGF;EACE;EACA;;AAEA;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;;AAEA;EACE;;AAGF;EACE;;AAIJ;EACE;EAEA;EACA;EACA;;AAII;EACE;EACA;EAEA;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EAEA;EAEA;EACA;EAEA;;AAEA;EACE;;AAKE;EACE;;AAKN;EACE;;AAEA;EACE;EACA;;AAGF;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;;AAOV;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAMJ;EADF;IAEI;;;AAGF;EACE;;AAEA;EACE;EACA;EACA;EACA;EAEA;;AAEA;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;;AASd;EACE;EACA;EACA;;;ACx2BF;EACE;EACA;EACA;EAEA;EACA;EACA;EACA;EAEA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EAEA;;AAEA;EACE;EACA;EACA;EAEA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;EAEA;EACA;EACA;EACA;EAEA;EACA;;AAEA;EACE;;AAOR;EACE;EACA;EACA;EACA;EACA;EAEA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EASE;;AARA;EACE;;AAGF;EACE;;AAKF;EACE;;AAKN;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;AAKN;EACE;EACA;EACA;;AAEA;EACE;;AAII;EACE;;AAMR;EACE;;AAEA;EACE;;;AAOV;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;;;AAKN;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AC/PF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EAEA;EACA;EAEA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAEA;EACE;;AAIJ;EACE;;;ACjEN;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;ACnBA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;;ACnCV;EAAwD;;;AACxD;EAA0B;;;AAC1B;EAAuB;;;AACvB;EAAyB;;;AACzB;EAAgC;;;AAChC;EAAsB;;;AAGtB;EAA2B;;;AAE3B;EAA6B;;;AAC7B;EAAmD;;;AACnD;EAA0B;;;AAC1B;EAAyB;;;AACzB;EAA2B;;;AAC3B;EAAuB;;;AACvB;EAA4B;;;AAC5B;EAA0B;;;AAC1B;EAAsB;;;AACtB;EAA4B;;;AAC5B;EAAiC;;;AACjC;EAAqB;;;AACrB;EAAuB;;;AAEvB;EAAsC;;;AACtC;EAAoC;;;AACpC;EAA8B;;;AAE9B;EAAyD;;;AAEzD;EACI;EACA;EACA;;;AAEJ;EAA4D;;;AAE5D;EACI;EACA;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EAAmC;;;AACnC;EAAuD;;;AACvD;EAAoC;;;AACpC;EAAyH;;;AACzH;EAAwI;;;ACtDxI;EACI;;;AAGJ;EAAsD;;;AACtD;EAAyB;;;AACzB;EAAsB;;;AACtB;EAAwB;;;AACxB;EAA+B;;;AAC/B;EAAyC;;;AAGzC;EAA0B;;;AAE1B;EAA4B;;;AAC5B;EAAqC;;;AACrC;EAAyB;;;AACzB;EAAwB;;;AACxB;EAA0B;;;AAC1B;EAAsB;;;AACtB;EAA2B;;;AAC3B;EAAyB;;;AACzB;EAAqB;;;AACrB;EAA2B;;;AAC3B;EAAgC;;;AAChC;EAAoB;;;AACpB;EAAsB;;;AACtB;EAAkC;;;AAClC;EAAqC;;;AACrC;EAAmC;;;AACnC;EAA6B;;;AAE7B;EAAwD;;;AAExD;EACI;EACA;EACA;;;AAEJ;EAA2D;;;AAE3D;EACI;EACA;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EAAkC;;;AAClC;EAAsD;;;AACtD;EAAmC;;;AACnC;EAAwH;;;AACxH;EAAuI;;;AChEvI;EACI;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EAEA;EACA;EACA;;AAEA;EACI;EACA;;;AAIR;EACI;EACA;EACA;EACA;EAEA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EAEA;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;ACpHJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;;AAEA;EACI;;;AAIR;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;EAEA;EAEA;EAEA;EACA;;AAEA;EApBJ;IAqBQ;IACA;;;;AAIR;EACI;;;AAGJ;EACI;EACA;EACA;EACA;EAEA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;ACpHJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EAEA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EAEA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;EAEA;EACA;EAEA;EACA;;;AAGF;EACE;EACA;EACA;;AAGE;EACE;;;AASN;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAEA;EACE;;AAIJ;EACE;EAEA;EACA;EACA;EACA;EAEA;EACA;EACA;;AAEA;EACE;EACA;EAEA;;AAGF;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAKN;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAEA;EACE;;AAIJ;EACE;EAEA;EACA;EAEA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;EAEA;EAEA;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;EACA;;;AASd;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;EACA;EACA;EAEA;EACA;;AAEA;EACE;;AAEA;EACE;;AAIJ;EACE;;AAEA;EACE;EACA;;AAMR;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;EACA;;AAGF;EACE;;AAGF;EACE;;;AAKN;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EAEE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAIA;EACE;;AAKF;EACE;EACA;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAKN;EACE","file":"index.css"}
--------------------------------------------------------------------------------
/www/src/RunConfigurationManager/RunConfigurationManager.ts:
--------------------------------------------------------------------------------
1 | import {QueryParams} from "../QueryParams";
2 | import {SharedCodeRunConfiguration} from "../Repositories";
3 |
4 | export enum RunConfigurationType {
5 | Run = "Run",
6 | Test = "Test",
7 | Cgen = "Cgen",
8 | }
9 |
10 | export function toSharedRunConfiguration(runConfigurationType: string): SharedCodeRunConfiguration {
11 | switch (runConfigurationType) {
12 | case "Run":
13 | return SharedCodeRunConfiguration.Run
14 | case "Test":
15 | return SharedCodeRunConfiguration.Test
16 | case "Cgen":
17 | return SharedCodeRunConfiguration.Cgen
18 | default:
19 | throw new Error(`Unknown run configuration type: ${runConfigurationType}`)
20 | }
21 | }
22 |
23 | export function getRunConfigurationTypeByShared(sharedRunConfiguration: SharedCodeRunConfiguration): RunConfigurationType {
24 | switch (sharedRunConfiguration) {
25 | case SharedCodeRunConfiguration.Run:
26 | return RunConfigurationType.Run
27 | case SharedCodeRunConfiguration.Test:
28 | return RunConfigurationType.Test
29 | case SharedCodeRunConfiguration.Cgen:
30 | return RunConfigurationType.Cgen
31 | default:
32 | return RunConfigurationType.Run
33 | }
34 | }
35 |
36 | function getRunConfigurationTypeByString(runConfigurationType: string): RunConfigurationType {
37 | switch (runConfigurationType) {
38 | case "Run":
39 | return RunConfigurationType.Run
40 | case "Test":
41 | return RunConfigurationType.Test
42 | case "Cgen":
43 | return RunConfigurationType.Cgen
44 | default:
45 | throw new Error(`Unknown run configuration type: ${runConfigurationType}`)
46 | }
47 | }
48 |
49 | export class RunConfigurationManager {
50 | private static readonly QUERY_PARAM_NAME = "runConfiguration"
51 | private static readonly LOCAL_STORAGE_KEY = "run-configuration"
52 | private static readonly LOCAL_STORAGE_BUILD_ARGUMENTS_KEY = "build-arguments"
53 | private static readonly LOCAL_STORAGE_RUN_ARGUMENTS_KEY = "run-arguments"
54 |
55 | private readonly queryParams: QueryParams
56 | private currentConfiguration: RunConfigurationType = RunConfigurationType.Run
57 | private fromQueryParam: boolean = false
58 |
59 | private readonly runButton = document.querySelector(".js-run__action")!
60 | private readonly runButtonLabel = document.querySelector(".js-run__action .label")!
61 | private readonly openRunButton = document.querySelector(".js-open-run-select")!
62 | private readonly configurationsList = document.querySelector(".js-run-configurations-list")!
63 | private readonly configurationsOverlay = document.querySelector(".js-run-configurations-list-overlay")!
64 | private readonly configurations = document.querySelectorAll(".js-configuration")!
65 | private readonly buildArgumentsInput = document.querySelector(".js-build-arguments-input") as HTMLInputElement
66 | private readonly runArgumentsInput = document.querySelector(".js-run-arguments-input") as HTMLInputElement
67 |
68 | private onChange: (type: RunConfigurationType) => void = () => {
69 | }
70 | private onSelect: (type: RunConfigurationType) => void = () => {
71 | }
72 |
73 | constructor(queryParams: QueryParams) {
74 | this.queryParams = queryParams
75 |
76 | this.mount()
77 | }
78 |
79 | get configuration(): RunConfigurationType {
80 | return this.currentConfiguration
81 | }
82 |
83 | public registerOnChange(callback: (type: RunConfigurationType) => void): void {
84 | this.onChange = callback
85 | }
86 |
87 | public registerOnSelect(callback: (type: RunConfigurationType) => void): void {
88 | this.onSelect = callback
89 | }
90 |
91 | public toggleConfigurationsList() {
92 | this.configurationsList.classList.toggle("hidden")
93 | this.configurationsOverlay.classList.toggle("opened")
94 | }
95 |
96 | public closeConfigurationsList() {
97 | this.configurationsList.classList.add("hidden")
98 | this.configurationsOverlay.classList.remove("opened")
99 | }
100 |
101 | public setupConfiguration() {
102 | const configurationFromQuery = this.queryParams.getURLParameter(RunConfigurationManager.QUERY_PARAM_NAME)
103 | if (configurationFromQuery !== null && configurationFromQuery !== undefined) {
104 | this.fromQueryParam = true
105 | this.useConfiguration(getRunConfigurationTypeByString(configurationFromQuery))
106 | return
107 | }
108 |
109 | const buildArgumentsFromLocalStorage = window.localStorage.getItem(RunConfigurationManager.LOCAL_STORAGE_BUILD_ARGUMENTS_KEY)
110 | if (buildArgumentsFromLocalStorage !== null && buildArgumentsFromLocalStorage !== undefined) {
111 | this.buildArgumentsInput.value = buildArgumentsFromLocalStorage
112 | }
113 |
114 | const runArgumentsFromLocalStorage = window.localStorage.getItem(RunConfigurationManager.LOCAL_STORAGE_RUN_ARGUMENTS_KEY)
115 | if (runArgumentsFromLocalStorage !== null && runArgumentsFromLocalStorage !== undefined) {
116 | this.runArgumentsInput.value = runArgumentsFromLocalStorage
117 | }
118 |
119 | const configurationFromLocalStorage = window.localStorage.getItem(RunConfigurationManager.LOCAL_STORAGE_KEY)
120 | if (configurationFromLocalStorage !== null && configurationFromLocalStorage !== undefined) {
121 | this.useConfiguration(getRunConfigurationTypeByString(configurationFromLocalStorage))
122 | return
123 | }
124 |
125 | this.useConfiguration(RunConfigurationType.Run)
126 | }
127 |
128 | public useConfiguration(runConfigurationType: RunConfigurationType) {
129 | this.currentConfiguration = runConfigurationType
130 | this.onChange(runConfigurationType)
131 |
132 | const runConfigurationAsString = RunConfigurationType[runConfigurationType]
133 | this.runButton.setAttribute("data-type", runConfigurationAsString)
134 | this.runButtonLabel.textContent = runConfigurationAsString
135 |
136 | if (runConfigurationType == RunConfigurationType.Cgen) {
137 | this.runButtonLabel.textContent = "Show generated C code"
138 | }
139 |
140 | if (!this.fromQueryParam) {
141 | // Don't update saved theme state if we're loading from query param.
142 | window.localStorage.setItem(RunConfigurationManager.LOCAL_STORAGE_KEY, runConfigurationAsString)
143 | window.localStorage.setItem(RunConfigurationManager.LOCAL_STORAGE_BUILD_ARGUMENTS_KEY, this.buildArgumentsInput.value)
144 | window.localStorage.setItem(RunConfigurationManager.LOCAL_STORAGE_RUN_ARGUMENTS_KEY, this.runArgumentsInput.value)
145 | }
146 |
147 | if (this.fromQueryParam) {
148 | // We update the query param only if we loaded from it.
149 | // If we don't change, then the user can change the configuration and then reload the page.
150 | // In this case, the page will load with the configuration from the URL, and the user
151 | // will think that his configuration change has not been saved (and will not be saved
152 | // until he removes the configuration from the URL).
153 | // To avoid this, we update the URL if the user changes configuration.
154 | this.queryParams.updateURLParameter(RunConfigurationManager.QUERY_PARAM_NAME, runConfigurationAsString)
155 | }
156 | }
157 |
158 | get buildArguments(): string[] {
159 | return this.buildArgumentsInput.value.split(" ").filter(arg => arg.length > 0)
160 | }
161 |
162 | get runArguments(): string[] {
163 | return this.runArgumentsInput.value.split(" ").filter(arg => arg.length > 0)
164 | }
165 |
166 | public setBuildArguments(args: string) {
167 | this.buildArgumentsInput.value = args
168 | }
169 |
170 | public setRunArguments(args: string) {
171 | this.runArgumentsInput.value = args
172 | }
173 |
174 | private mount() {
175 | this.openRunButton.addEventListener("click", () => {
176 | this.toggleConfigurationsList()
177 | })
178 |
179 | this.buildArgumentsInput.addEventListener("input", () => {
180 | window.localStorage.setItem(RunConfigurationManager.LOCAL_STORAGE_BUILD_ARGUMENTS_KEY, this.buildArgumentsInput.value)
181 | })
182 |
183 | this.runArgumentsInput.addEventListener("input", () => {
184 | window.localStorage.setItem(RunConfigurationManager.LOCAL_STORAGE_RUN_ARGUMENTS_KEY, this.runArgumentsInput.value)
185 | })
186 |
187 | this.configurationsOverlay.addEventListener("click", () => {
188 | this.toggleConfigurationsList()
189 | })
190 |
191 | document.addEventListener("keydown", (event) => {
192 | if (event.key === "Escape") {
193 | this.closeConfigurationsList()
194 | }
195 | })
196 |
197 | this.configurations.forEach(configuration => {
198 | configuration.addEventListener("click", () => {
199 | const configurationTypeString = configuration.getAttribute("data-type") ?? "Run"
200 | const configurationType = getRunConfigurationTypeByString(configurationTypeString)
201 | this.useConfiguration(configurationType)
202 | this.onSelect(configurationType)
203 | })
204 | })
205 | }
206 | }
207 |
--------------------------------------------------------------------------------
/www/styles/playground.scss:
--------------------------------------------------------------------------------
1 | html {
2 | --tools-button-hover: #ecedf1;
3 | --configuration-item-hover: #cfdefd;
4 | --terminal-header-border: #ecedf1;
5 | --terminal-header: #f7f8fa;
6 | --terminal-header-text: #000;
7 | --terminal-close-button: #777b88;
8 | --terminal-close-button-hover: #ecedf1;
9 | --terminal-tab-hover: #ecedf1;
10 | --terminal-text: #000;
11 | --terminal-background: #fff;
12 | --terminal-ok: #26ad2c;
13 | --terminal-error: #c02929;
14 | --playground-link: #4f4f4f;
15 | --run-embeded-icon-color: #000000;
16 | --completion-variant-background: #cfdefd;
17 | --completion-variant-color: #000;
18 | --completion-module-icon: url("../public/images/module-light.svg");
19 | --completion-type-icon: url("../public/images/type-light.svg");
20 |
21 | --inline-button-text-color: var(--gray-7e);
22 | --inline-button-hover-text-color: var(--gray-be);
23 |
24 | --terminal-success-text-color: #26ad2c;
25 | --terminal-error-text-color: #CD0000;
26 | --terminal-warning-text-color: #A66F00;
27 | }
28 |
29 | html[data-theme='dark'] {
30 | --tools-button-hover: #393b41;
31 | --configuration-item-hover: #2f446e;
32 | --terminal-header-border: #323438;
33 | --terminal-header: #2a2a2d;
34 | --terminal-header-text: #f7f8fa;
35 | --terminal-close-button: #d9dbe2;
36 | --terminal-close-button-hover: #393b41;
37 | --terminal-tab-hover: #393b41;
38 | --terminal-text: #BBBBBB;
39 | --terminal-background: #000;
40 | --terminal-ok: #43c449;
41 | --terminal-error: #e04949;
42 | --playground-link: #8d8d8d;
43 | --run-embeded-icon-color: #fff;
44 | --completion-variant-background: #2e436e;
45 | --completion-variant-color: #b4b8c0;
46 | --completion-module-icon: url("../public/images/module.svg");
47 | --completion-type-icon: url("../public/images/type.svg");
48 |
49 | --inline-button-text-color: var(--gray-7e);
50 | --inline-button-hover-text-color: var(--gray-be);
51 |
52 | --terminal-success-text-color: #43c449;
53 | --terminal-error-text-color: #FF6B68;
54 | --terminal-warning-text-color: #ABC023;
55 | }
56 |
57 | * {
58 | box-sizing: border-box;
59 | }
60 |
61 | body {
62 | margin: 0;
63 | padding: 70px 0 0 0;
64 |
65 | font: 400 14px "FiraSans", Helvetica, Arial, sans-serif;
66 | -webkit-font-smoothing: antialiased;
67 |
68 | background-color: var(--background-color);
69 | color: var(--text-color);
70 | }
71 |
72 | body.dragging {
73 | -webkit-user-select: none;
74 | -ms-user-select: none;
75 | user-select: none;
76 |
77 | .playground__terminal {
78 | .header {
79 | cursor: row-resize;
80 | }
81 | }
82 | }
83 |
84 | header {
85 | //background: var(--playground-header-background-color) !important;
86 | }
87 |
88 | main {
89 | top: 70px;
90 | display: grid;
91 | grid-template-rows: 65px calc(100vh - 70px - 65px);
92 | }
93 |
94 | .v-playground {
95 | position: relative;
96 | height: 100%;
97 | display: grid;
98 | grid-template-rows: auto max-content;
99 |
100 | &.closed-terminal {
101 | display: block;
102 |
103 | .playground__terminal {
104 | display: none;
105 | }
106 | }
107 |
108 | .playground__help-wrapper {
109 | position: absolute;
110 |
111 | display: flex;
112 | flex-flow: row;
113 | column-gap: 15px;
114 | align-items: center;
115 |
116 | margin-top: -60px;
117 | right: 20px;
118 | bottom: 20px;
119 |
120 | .version-info {
121 | color: var(--inline-button-text-color);
122 | font-size: 13px;
123 |
124 | margin-right: 10px;
125 | }
126 |
127 | .inline-button {
128 | background-color: transparent;
129 | border: none;
130 | color: var(--inline-button-text-color);
131 | font-size: 13px;
132 |
133 | &:hover {
134 | cursor: pointer;
135 | color: var(--inline-button-hover-text-color);
136 | }
137 | }
138 | }
139 |
140 | .playground__editors {
141 | position: relative;
142 | display: grid;
143 | height: 100%;
144 | grid-template-columns: 100%;
145 | overflow: hidden;
146 |
147 | &.two-editors {
148 | grid-template-columns: 50% 50%;
149 | }
150 | }
151 |
152 | .playground__editor {
153 | display: grid;
154 | grid-template-rows: 1fr;
155 | position: relative;
156 | height: 100%;
157 | max-width: 100vw;
158 | overflow: hidden;
159 | flex: 1;
160 |
161 | &.with-tabs {
162 | grid-template-rows: 25px auto;
163 |
164 | .tabs {
165 | display: block;
166 | }
167 | }
168 |
169 | .tabs {
170 | display: none;
171 |
172 | width: 100%;
173 | height: 100%;
174 |
175 | background-color: var(--tools-background-color);
176 | border-top: 1px solid var(--tools-border);
177 | border-bottom: 1px solid var(--tools-border);
178 |
179 | ul {
180 | padding: 0;
181 | margin: 0;
182 | height: 100%;
183 |
184 | li.tab {
185 | display: grid;
186 | grid-template-columns: 15px auto;
187 | grid-column-gap: 7px;
188 | align-items: center;
189 |
190 | padding: 0 12px 0 7px;
191 | width: max-content;
192 | height: 100%;
193 | text-decoration: none;
194 |
195 | background-color: var(--select-background);
196 |
197 | border-right: 1px solid var(--tools-border);
198 |
199 | span {
200 | color: var(--text-color);
201 | font-size: 12px;
202 | line-height: 20px;
203 | margin-top: -1px;
204 | }
205 |
206 | .close-button {
207 | width: 18px;
208 | height: 18px;
209 | padding: 3px;
210 | border-radius: 5px;
211 | border: none;
212 | background-color: transparent;
213 | cursor: pointer;
214 |
215 | &:hover {
216 | background-color: var(--tools-button-hover);
217 | }
218 |
219 | svg {
220 | width: 10px;
221 | margin-top: 0;
222 | margin-left: 0;
223 | }
224 | }
225 | }
226 | }
227 | }
228 | }
229 | }
230 |
231 | .playground__terminal {
232 | height: 270px;
233 | min-height: 150px;
234 | max-height: 75vh;
235 | display: grid;
236 | grid-template-rows: 35px auto;
237 |
238 | .header {
239 | display: grid;
240 | grid-template-columns: max-content auto max-content;
241 |
242 | padding: 11px 10px 0 15px;
243 | font-size: 12px;
244 | font-weight: 600;
245 | color: var(--terminal-header-text);
246 | border-top: 1px solid var(--terminal-header-border);
247 | border-bottom: 1px solid var(--terminal-header-border);
248 | background-color: var(--terminal-header);
249 | line-height: 1.1;
250 |
251 | &:hover {
252 | cursor: row-resize;
253 | }
254 |
255 | .tabs {
256 | padding: 0;
257 | margin: -11px 0 0 20px;
258 | list-style: none;
259 | font-weight: 400;
260 |
261 | display: flex;
262 | flex-direction: row;
263 |
264 | input {
265 | display: none;
266 |
267 | &:checked + label {
268 | border-bottom: solid 3px var(--blue);
269 | }
270 | }
271 |
272 | label {
273 | padding: 11px 10px 0;
274 |
275 | &:hover {
276 | cursor: pointer;
277 | background-color: var(--terminal-tab-hover);
278 | }
279 | }
280 | }
281 | }
282 |
283 | .terminal__close-button {
284 | padding: 2px 3px 0;
285 | margin-top: -4px;
286 | border-radius: 5px;
287 | border: none;
288 | background-color: transparent;
289 | cursor: pointer;
290 | height: max-content;
291 |
292 | .close-terminal-button-rect {
293 | fill: var(--terminal-close-button);
294 | }
295 |
296 | &:hover {
297 | background-color: var(--terminal-close-button-hover);
298 | }
299 | }
300 |
301 | .terminal__output {
302 | width: 100%;
303 | margin: 0;
304 | overflow: scroll;
305 | padding: 5px 20px 20px 10px;
306 | font-family: ui-monospace, SFMono-Regular, SF Mono, Consolas, Liberation Mono, Menlo, monospace;
307 | font-size: 13.5px;
308 | line-height: 1.57;
309 | background-color: var(--terminal-background);
310 | color: var(--terminal-text);
311 |
312 | &.hidden {
313 | display: none;
314 | }
315 |
316 | &::-webkit-scrollbar-thumb {
317 | background: var(--scrollbar-thumb-color);
318 | border-radius: 5px;
319 | }
320 |
321 | &::-webkit-scrollbar-corner {
322 | background-color: var(--terminal-background);
323 | }
324 |
325 | &::-webkit-scrollbar-track-piece {
326 | background-color: var(--terminal-background);
327 | }
328 |
329 | .message-success {
330 | color: var(--terminal-success-text-color);
331 | }
332 |
333 | .message-error {
334 | color: var(--terminal-error-text-color);
335 | font-weight: bold;
336 | }
337 |
338 | .message-warning {
339 | color: var(--terminal-warning-text-color);
340 | }
341 |
342 | .message-notice {
343 | color: var(--terminal-warning-text-color);
344 | }
345 | }
346 | }
347 |
348 | .CodeMirror {
349 | width: 100%;
350 | height: auto;
351 | font-family: ui-monospace, SFMono-Regular, SF Mono, Consolas, Liberation Mono, Menlo, monospace;
352 | font-size: 13.5px;
353 | line-height: 1.57;
354 | background-color: var(--background-color);
355 |
356 | .CodeMirror-scroll {
357 | //padding-top: 20px;
358 | padding-bottom: 0;
359 | }
360 |
361 | .CodeMirror-scrollbar-filler {
362 | background-color: transparent !important;
363 | }
364 |
365 | .CodeMirror-line {
366 | padding-left: 15px !important;
367 | }
368 |
369 | .CodeMirror-gutters {
370 | padding-left: 5px;
371 | }
372 |
373 | .CodeMirror-linenumber {
374 | font-size: 0.9em;
375 | }
376 | }
377 |
378 | .CodeMirror-hints {
379 | position: absolute;
380 | z-index: 10;
381 | overflow: hidden;
382 | list-style: none;
383 | min-width: 200px;
384 | margin: 0;
385 | padding: 4px;
386 | -webkit-box-shadow: 2px 3px 5px rgba(0, 0, 0, .2);
387 | -moz-box-shadow: 2px 3px 5px rgba(0, 0, 0, .2);
388 | box-shadow: 2px 3px 5px rgba(0, 0, 0, .2);
389 | border-radius: 6px;
390 | border: 1px solid var(--select-header-border);
391 | background: var(--select-background);
392 | color: var(--completion-variant-color);
393 | font-size: 14px;
394 | font-family: ui-monospace, SFMono-Regular, SF Mono, Consolas, Liberation Mono, Menlo, monospace;
395 | max-height: 20em;
396 | overflow-y: auto;
397 |
398 | .CodeMirror-hint {
399 | padding: 3px 10px 3px 33px;
400 | border-radius: 5px;
401 | white-space: pre;
402 | cursor: pointer;
403 |
404 | &.completion-keyword {
405 | font-weight: 600;
406 | }
407 |
408 | &.completion-type {
409 | &::before {
410 | background-image: var(--completion-type-icon);
411 | }
412 | }
413 |
414 | &.completion-module {
415 | &::before {
416 | background-image: var(--completion-module-icon);
417 | opacity: 0.9;
418 | }
419 | }
420 |
421 | &::before {
422 | content: "";
423 | display: inline-block;
424 | position: absolute;
425 | left: 10px;
426 | width: 18px;
427 | height: 18px;
428 | margin-right: 5px;
429 | background-repeat: no-repeat;
430 | background-size: 18px;
431 | }
432 | }
433 | }
434 |
435 | li.CodeMirror-hint-active {
436 | background-color: var(--completion-variant-background);
437 | }
438 |
--------------------------------------------------------------------------------
/www/public/scripts/header-search.js:
--------------------------------------------------------------------------------
1 | const searchWindow = document.querySelector(".search-window");
2 | const searchWindowOverlay = document.querySelector(".js-search-overlay");
3 | const searchButtons = document.querySelectorAll(".js-search-button");
4 | const searchInput = document.querySelector(".js-search-input");
5 | const searchResults = document.querySelector(".search-results");
6 | const searchResultCaptionElement = searchResults.querySelector(".caption");
7 | const searchResultsList = document.querySelector(".search-results-list");
8 | const searchCloseButton = document.querySelector(".js-close-search-button");
9 | const searchClearButton = document.querySelector(".js-clear-input-button");
10 | const searchPlaceRadioBoxes = document.querySelectorAll(".search-place-item input");
11 |
12 | const localSearchCache = {};
13 |
14 | const pageIcon = ``
15 | const textIcon = ``
16 | const subHeaderIcon = ``;
17 | const moduleIcon = ``
18 |
19 | const resetSearch = () => {
20 | searchInput.value = "";
21 | searchResults.classList.add("with-caption");
22 | searchResultsList.innerHTML = "";
23 | searchResultCaptionElement.textContent = "Type something to search";
24 | searchClearButton.classList.remove("show");
25 | searchInput.focus();
26 | }
27 |
28 | const closeSearchWindow = () => {
29 | searchWindow.classList.remove("open");
30 | resetSearch()
31 | }
32 |
33 | const openSearchWindow = () => {
34 | searchWindow.classList.add("open");
35 | setTimeout(() => {
36 | searchInput.focus();
37 | }, 250)
38 | }
39 |
40 | const searchWindowIsOpen = () => {
41 | return searchWindow.classList.contains("open");
42 | }
43 |
44 | const resetPlaceholder = () => {
45 | searchInput.placeholder = `Search ${getSelectedSearchPlacePresentation()}...`;
46 | }
47 |
48 | searchPlaceRadioBoxes.forEach((radioBox) => {
49 | radioBox.addEventListener("change", () => {
50 | runSearch();
51 | searchInput.focus();
52 |
53 | resetPlaceholder();
54 | });
55 | })
56 |
57 | const getSelectedSearchPlace = () => {
58 | const selectedSearchPlace = document.querySelector(".search-place-item input:checked");
59 | return selectedSearchPlace.value ?? "All";
60 | }
61 |
62 | const getSelectedSearchPlacePresentation = () => {
63 | const selectedSearchPlace = document.querySelector(".search-place-item input:checked");
64 | let presentation = "";
65 | switch (selectedSearchPlace.value) {
66 | case "all":
67 | presentation = "everywhere";
68 | break;
69 | case "documentation":
70 | presentation = "in documentation";
71 | break;
72 | case "blog":
73 | presentation = "in blog";
74 | break;
75 | case "modules":
76 | presentation = "in modules";
77 | break;
78 | default:
79 | presentation = "everywhere";
80 | break;
81 | }
82 | return presentation
83 | }
84 |
85 | function clearResults() {
86 | searchResultsList.innerHTML = "";
87 | }
88 |
89 | function setResults(place, hits) {
90 | hits.forEach((result) => {
91 | if (place === "modules") {
92 | renderModuleResult(result);
93 | } else {
94 | renderResult(result);
95 | }
96 | });
97 | }
98 |
99 | function renderModuleResult(result) {
100 | const formatted = result['_formatted']
101 | const icon = moduleIcon
102 |
103 | const searchResult = document.createElement("li");
104 | searchResult.classList.add("search-result");
105 | searchResult.setAttribute("role", "option");
106 | searchResult.setAttribute("aria-selected", "false");
107 |
108 | const link = document.createElement("a");
109 | link.classList.add("search-result-content");
110 | link.setAttribute("href", result.url);
111 | link.innerHTML = `
112 |
113 | ${icon}
114 |
115 |
116 | ${formatted.fqn}
117 | ${formatted.description}
118 |
119 | `;
120 | searchResult.appendChild(link);
121 | searchResultsList.appendChild(searchResult);
122 | }
123 |
124 | function renderResult(result) {
125 | const formatted = result['_formatted']
126 | const icon = formatted.title.includes("search-highlight") ? pageIcon : (result.parent !== "" ? subHeaderIcon : textIcon);
127 |
128 | const searchResult = document.createElement("li");
129 | searchResult.classList.add("search-result");
130 | searchResult.setAttribute("role", "option");
131 | searchResult.setAttribute("aria-selected", "false");
132 |
133 | const link = document.createElement("a");
134 | link.classList.add("search-result-content");
135 | link.setAttribute("href", result.url.replace(".md", ".html"));
136 | link.innerHTML = `
137 |
138 | ${icon}
139 |
140 |
141 | ${formatted.title}
142 | ${formatted.body}
143 |
144 | `;
145 | searchResult.appendChild(link);
146 | searchResultsList.appendChild(searchResult);
147 | }
148 |
149 | function doSearch(searchQuery) {
150 | const place = getSelectedSearchPlace();
151 | const localSearchCacheElement = localSearchCache[searchQuery + place];
152 | if (localSearchCacheElement && localSearchCacheElement.length > 0) {
153 | setWithResults();
154 | clearResults();
155 | setResults(place, localSearchCacheElement);
156 | setSearchElementsListeners();
157 | return
158 | }
159 |
160 | const searchEndpoint = "https://play.vlang.io/search"
161 |
162 | fetch(searchEndpoint + "?" + new URLSearchParams({
163 | place: place,
164 | q: searchQuery,
165 | }))
166 | .then((response) => {
167 | return response.json();
168 | })
169 | .then((data) => {
170 | console.log(data);
171 | console.log(data[0]);
172 | clearResults();
173 |
174 | let hits = [];
175 | data.forEach((result) => {
176 | hits = hits.concat(result.hits);
177 | })
178 |
179 | if (hits.length > 0) {
180 | setWithResults();
181 | setResults(place, hits);
182 | setSearchElementsListeners();
183 |
184 | localSearchCache[searchQuery + place] = hits;
185 | } else {
186 | setNoResults();
187 | }
188 | })
189 | .catch((error) => {
190 | console.log(error);
191 | setNoResults();
192 | });
193 | }
194 |
195 | function runSearch() {
196 | const searchQuery = searchInput.value;
197 |
198 | if (searchQuery === "") {
199 | resetSearch()
200 | return
201 | }
202 |
203 | doSearch(searchQuery);
204 | }
205 |
206 | searchInput.addEventListener("input", () => {
207 | if (searchInput.value.length > 0) {
208 | searchClearButton.classList.add("show");
209 | } else {
210 | searchClearButton.classList.remove("show");
211 | }
212 |
213 | runSearch();
214 | })
215 |
216 | searchClearButton.addEventListener("click", () => {
217 | resetSearch();
218 | })
219 |
220 | searchCloseButton.addEventListener("click", () => {
221 | closeSearchWindow();
222 | })
223 |
224 | searchButtons.forEach((searchButton) => {
225 | searchButton.addEventListener("click", () => {
226 | openSearchWindow();
227 | });
228 | })
229 |
230 | searchWindowOverlay.addEventListener("click", () => {
231 | closeSearchWindow();
232 | });
233 |
234 | const selectSearchResultElement = (element, scroll = true) => {
235 | const searchResultsElements = document.querySelectorAll(".search-result");
236 | searchResultsElements.forEach((searchResultElement) => {
237 | searchResultElement.setAttribute("aria-selected", "false");
238 | });
239 | element.setAttribute("aria-selected", "true");
240 |
241 | if (scroll) {
242 | element.scrollIntoView({block: "nearest", inline: "nearest"})
243 | }
244 | }
245 |
246 | const setSearchElementsListeners = () => {
247 | const searchResultsElements = document.querySelectorAll(".search-result");
248 | searchResultsElements.forEach((searchResultElement) => {
249 | // on hover set aria-selected to true and remove from other elements
250 | searchResultElement.addEventListener("mouseenter", () => {
251 | selectSearchResultElement(searchResultElement, false);
252 | })
253 |
254 | // on touch set aria-selected to true and remove from other elements
255 | searchResultElement.addEventListener("touchstart", () => {
256 | selectSearchResultElement(searchResultElement, false);
257 | })
258 | })
259 | }
260 |
261 | document.addEventListener("keydown", (event) => {
262 | if (event.key === "Escape") {
263 | closeSearchWindow();
264 | return
265 | }
266 |
267 | if (!searchWindowIsOpen) {
268 | return;
269 | }
270 |
271 | const searchResultActiveElement = document.querySelector(".search-result[aria-selected='true']");
272 | if (searchResultActiveElement) {
273 | if (event.key === "ArrowUp") {
274 | const previousElement = searchResultActiveElement.previousElementSibling;
275 | if (previousElement) {
276 | selectSearchResultElement(previousElement)
277 | } else {
278 | selectSearchResultElement(searchResultsList.lastElementChild)
279 | }
280 |
281 | event.preventDefault()
282 | }
283 |
284 | if (event.key === "ArrowDown") {
285 | const nextElement = searchResultActiveElement.nextElementSibling;
286 | if (nextElement) {
287 | selectSearchResultElement(nextElement)
288 | } else {
289 | selectSearchResultElement(searchResultsList.firstElementChild)
290 | }
291 |
292 | event.preventDefault()
293 | }
294 | } else {
295 | if (event.key === "ArrowUp") {
296 | selectSearchResultElement(searchResultsList.lastElementChild)
297 | event.preventDefault()
298 | }
299 |
300 | if (event.key === "ArrowDown") {
301 | selectSearchResultElement(searchResultsList.firstElementChild)
302 | event.preventDefault()
303 | }
304 | }
305 |
306 | if (event.key === "Enter" && searchResultActiveElement) {
307 | searchResultActiveElement.querySelector("a").click();
308 | }
309 |
310 | const value = searchInput.value;
311 | if (value === "") {
312 | setEmptySearch();
313 | }
314 | })
315 |
316 | const setEmptySearch = () => {
317 | searchResults.classList.add("with-caption");
318 | searchResultCaptionElement.textContent = "Type something to search";
319 | }
320 |
321 | const removeEmptySearch = () => {
322 | searchResults.classList.remove("with-caption");
323 | }
324 |
325 | const setNoResults = () => {
326 | let inputValue = searchInput.value;
327 | if (inputValue === "") {
328 | setEmptySearch();
329 | return
330 | }
331 |
332 | searchResults.classList.add("with-caption");
333 | searchResultCaptionElement.textContent = `No results for "${inputValue}"`;
334 | }
335 |
336 | const setWithResults = () => {
337 | searchResults.classList.remove("with-caption");
338 | }
339 |
340 | document.addEventListener("keydown", (event) => {
341 | if (event.key === "k" && (event.metaKey || event.ctrlKey)) {
342 | event.preventDefault();
343 | openSearchWindow();
344 | }
345 | })
346 |
347 | document.addEventListener("DOMContentLoaded", () => {
348 | const ctrlKey = document.querySelector(".ctrl-key");
349 | const platform = navigator.platform.toLowerCase();
350 | if (platform.includes("mac") || platform.includes("ios")) {
351 | ctrlKey.textContent = "⌘";
352 | }
353 | });
354 |
355 | (function () {
356 | const place = searchInput.getAttribute("data-place");
357 | if (place) {
358 | const radioBox = document.querySelector(`.search-place-item input[value="${place}"]`);
359 | radioBox.checked = true;
360 |
361 | resetPlaceholder();
362 | }
363 | })()
364 |
--------------------------------------------------------------------------------
/www/src/v.ts:
--------------------------------------------------------------------------------
1 | import {EditorConfiguration, Mode, StringStream} from "codemirror"
2 | import {
3 | ifAttributesRegexp,
4 | keyValueAttributesRegexp,
5 | severalSingleKeyValueAttributesRegexp,
6 | simpleAttributesRegexp,
7 | singleKeyValueAttributesRegexp,
8 | } from "./v-hint"
9 |
10 | type Quota = "'" | "\"" | "`"
11 | type Tokenizer = (stream: StringStream, state: ModeState) => string | null
12 |
13 | interface ModeState {
14 | context: Context
15 |
16 | /**
17 | * Current tokenizer function or null.
18 | */
19 | tokenize: Tokenizer | null
20 |
21 | /**
22 | * Current indentation level.
23 | */
24 | indention: number
25 |
26 | /**
27 | * Whenever current position is a start of line.
28 | */
29 | startOfLine: boolean
30 | }
31 |
32 | class Context {
33 | constructor(
34 | public indentation: number,
35 | public column: number,
36 | public type: string,
37 | public align: boolean | null,
38 | public prev?: Context,
39 | /**
40 | * Set of imports in the current context.
41 | * Used for highlighting import names in code.
42 | */
43 | public knownImports: Set = new Set()) {
44 | }
45 |
46 | /**
47 | * Whenever current position inside a string.
48 | */
49 | insideString: boolean = false
50 |
51 | /**
52 | * Current quotation mark.
53 | * Valid only when insideString is true.
54 | */
55 | stringQuote: Quota | null = null
56 |
57 | /**
58 | * Whenever next token expected to be an import name.
59 | * Used for highlighting import names in import statements.
60 | */
61 | expectedImportName: boolean = false
62 | }
63 |
64 | export const keywords: Set = new Set([
65 | "as",
66 | "asm",
67 | "assert",
68 | "atomic",
69 | "break",
70 | "const",
71 | "continue",
72 | "defer",
73 | "else",
74 | "enum",
75 | "fn",
76 | "for",
77 | "go",
78 | "goto",
79 | "if",
80 | "implements",
81 | "import",
82 | "in",
83 | "interface",
84 | "is",
85 | "isreftype",
86 | "lock",
87 | "match",
88 | "module",
89 | "mut",
90 | "none",
91 | "or",
92 | "pub",
93 | "return",
94 | "rlock",
95 | "select",
96 | "shared",
97 | "sizeof",
98 | "static",
99 | "struct",
100 | "spawn",
101 | "type",
102 | "typeof",
103 | "union",
104 | "unsafe",
105 | "volatile",
106 | "__global",
107 | "__offsetof",
108 | ])
109 |
110 | export const pseudoKeywords: Set = new Set([
111 | "sql",
112 | "chan",
113 | "thread",
114 | ])
115 |
116 | export const hashDirectives: Set = new Set([
117 | "#flag",
118 | "#include",
119 | "#pkgconfig",
120 | ])
121 |
122 | export const atoms: Set = new Set([
123 | "true",
124 | "false",
125 | "nil",
126 | "print",
127 | "println",
128 | "exit",
129 | "panic",
130 | "error",
131 | "dump",
132 | ])
133 |
134 | export const builtinTypes: Set = new Set([
135 | "bool",
136 | "string",
137 | "i8",
138 | "i16",
139 | "int",
140 | "i32",
141 | "i64",
142 | "i128",
143 | "u8",
144 | "u16",
145 | "u32",
146 | "u64",
147 | "u128",
148 | "rune",
149 | "f32",
150 | "f64",
151 | "isize",
152 | "usize",
153 | "voidptr",
154 | "any",
155 | ])
156 |
157 | // @ts-ignore
158 | CodeMirror.defineMode("v", (config: EditorConfiguration): Mode => {
159 | const indentUnit = config.indentUnit ?? 0
160 |
161 | const isOperatorChar = /[+\-*&^%:=<>!?|\/]/
162 |
163 | let curPunc: string | null = null
164 |
165 | function eatIdentifier(stream: StringStream): string {
166 | stream.eatWhile(/[\w$_\xa1-\uffff]/)
167 | return stream.current()
168 | }
169 |
170 | function tokenBase(stream: StringStream, state: ModeState): string | null {
171 | const ch = stream.next()
172 | if (ch === null) {
173 | return null
174 | }
175 |
176 | if (state.context.insideString && ch === "}") {
177 | stream.eat("}")
178 | state.tokenize = tokenString(state.context.stringQuote)
179 | return "end-interpolation"
180 | }
181 |
182 | if (ch === "\"" || ch === "'" || ch === "`") {
183 | state.tokenize = tokenString(ch)
184 | return state.tokenize(stream, state)
185 | }
186 |
187 | // r'foo' or c'foo'
188 | // r"foo" or c"foo"
189 | if ((ch === "r" || ch === "c") && (stream.peek() == "\"" || stream.peek() == "'")) {
190 | const next = stream.next()
191 | if (next === null) {
192 | return "string"
193 | }
194 | state.tokenize = tokenRawString(next as Quota)
195 | return "string"
196 | }
197 |
198 | if (ch === ".") {
199 | if (!stream.match(/^[0-9]+([eE][\-+]?[0-9]+)?/)) {
200 | return "operator"
201 | }
202 | }
203 |
204 | // probably attribute
205 | // [attr]
206 | // [attr: value]
207 | // [attr1; attr2]
208 | if (ch === "[") {
209 | // [unsafe]
210 | if (stream.match(simpleAttributesRegexp)) {
211 | return "attribute"
212 | }
213 |
214 | // [sql: foo]
215 | if (stream.match(singleKeyValueAttributesRegexp)) {
216 | return "attribute"
217 | }
218 |
219 | // [sql; foo]
220 | if (stream.match(severalSingleKeyValueAttributesRegexp)) {
221 | return "attribute"
222 | }
223 |
224 | // [attr: value; attr: value]
225 | // [attr: value; attr]
226 | if (stream.match(keyValueAttributesRegexp)) {
227 | return "attribute"
228 | }
229 |
230 | // match `[if some ?]`
231 | if (stream.match(ifAttributesRegexp)) {
232 | return "attribute"
233 | }
234 | }
235 |
236 | if (/[\d.]/.test(ch)) {
237 | if (ch === "0") {
238 | stream.match(/^[xX][0-9a-fA-F_]+/) ||
239 | stream.match(/^o[0-7_]+/) ||
240 | stream.match(/^b[0-1_]+/)
241 | } else {
242 | stream.match(/^[0-9_]*\.?[0-9_]*([eE][\-+]?[0-9_]+)?/)
243 | }
244 | return "number"
245 | }
246 | if (/[\[\]{}(),;:.]/.test(ch)) {
247 | curPunc = ch
248 | return null
249 | }
250 | if (ch === "/") {
251 | if (stream.eat("*")) {
252 | state.tokenize = tokenComment
253 | return tokenComment(stream, state)
254 | }
255 | if (stream.eat("/")) {
256 | stream.skipToEnd()
257 | return "comment"
258 | }
259 | }
260 | if (isOperatorChar.test(ch)) {
261 | stream.eatWhile(isOperatorChar)
262 | return "operator"
263 | }
264 |
265 | if (ch === "@") {
266 | eatIdentifier(stream)
267 | return "at-identifier"
268 | }
269 |
270 | if (ch === "$") {
271 | const ident = eatIdentifier(stream).slice(1)
272 | if (keywords.has(ident)) {
273 | return "keyword"
274 | }
275 |
276 | return "compile-time-identifier"
277 | }
278 |
279 | stream.backUp(2)
280 | const wasDot = stream.next() === "."
281 | stream.next()
282 |
283 | const cur = eatIdentifier(stream)
284 | if (cur === "import") {
285 | state.context.expectedImportName = true
286 | }
287 |
288 | if (keywords.has(cur)) return "keyword"
289 | if (pseudoKeywords.has(cur)) return "keyword"
290 | if (atoms.has(cur)) return "atom"
291 | if (hashDirectives.has(cur)) return "hash-directive"
292 |
293 | if (!wasDot) {
294 | // don't highlight `foo.int()`
295 | // ^^^ as builtin
296 | if (builtinTypes.has(cur)) return "builtin"
297 | }
298 |
299 | if (cur.length > 0 && cur[0].toUpperCase() === cur[0]) {
300 | return "type"
301 | }
302 |
303 | const next = stream.peek()
304 | if (next === "(" || next === "<") {
305 | return "function"
306 | }
307 |
308 | if (next === "[") {
309 | stream.next()
310 | const after = stream.next()
311 | stream.backUp(2)
312 | if (after != null && after.match(/[A-Z]/i)) {
313 | return "function"
314 | }
315 | }
316 |
317 | // highlight only last part
318 | // example:
319 | // import foo.boo
320 | // ^^^ - only this part will be highlighted
321 | if (state.context.expectedImportName && stream.peek() !== ".") {
322 | state.context.expectedImportName = false
323 | if (state.context.knownImports === undefined) {
324 | state.context.knownImports = new Set()
325 | }
326 | state.context.knownImports.add(cur)
327 | return "import-name"
328 | }
329 |
330 | if (wasDot) {
331 | return "property"
332 | }
333 |
334 | // highlight only identifier with dot after it
335 | // example:
336 | // import foo
337 | // import bar
338 | //
339 | // foo.bar
340 | // ^^^ - only this part will be highlighted
341 | if (state.context.knownImports.has(cur) && stream.peek() == ".") {
342 | return "import-name"
343 | }
344 |
345 | return "variable"
346 | }
347 |
348 | function tokenLongInterpolation(stream: StringStream, state: ModeState) {
349 | if (stream.match("}")) {
350 | state.tokenize = tokenString(state.context.stringQuote)
351 | return "end-interpolation"
352 | }
353 | state.tokenize = tokenBase
354 | return state.tokenize(stream, state)
355 | }
356 |
357 | function tokenShortInterpolation(stream: StringStream, state: ModeState) {
358 | const ch = stream.next()
359 | if (ch === " ") {
360 | state.tokenize = tokenString(state.context.stringQuote)
361 | return state.tokenize(stream, state)
362 | }
363 | if (ch === ".") {
364 | return "operator"
365 | }
366 |
367 | const ident = eatIdentifier(stream)
368 | if (ident[0].toLowerCase() === ident[0].toUpperCase()) {
369 | state.tokenize = tokenString(state.context.stringQuote)
370 | return state.tokenize(stream, state)
371 | }
372 |
373 | const next = stream.next()
374 | stream.backUp(1)
375 | if (next === ".") {
376 | state.tokenize = tokenShortInterpolation
377 | } else {
378 | state.tokenize = tokenString(state.context.stringQuote)
379 | }
380 |
381 | return "variable"
382 | }
383 |
384 | function tokenNextInterpolation(stream: StringStream, state: ModeState) {
385 | let next = stream.next()
386 | if (next === "$" && stream.eat("{")) {
387 | state.tokenize = tokenLongInterpolation
388 | return "start-interpolation"
389 | }
390 | if (next === "$") {
391 | state.tokenize = tokenShortInterpolation
392 | return "start-interpolation"
393 | }
394 |
395 | return "string"
396 | }
397 |
398 | function tokenNextEscape(stream: StringStream, state: ModeState) {
399 | let next = stream.next()
400 | if (next === "\\") {
401 | stream.next()
402 | state.tokenize = tokenString(state.context.stringQuote)
403 | // we already know that next char is valid escape
404 | return "valid-escape"
405 | }
406 |
407 | return "string"
408 | }
409 |
410 | function isValidEscapeChar(ch: string) {
411 | return ch === "n" || ch === "t" || ch === "r" || ch === "\\" || ch === "\"" || ch === "\'" || ch === "0"
412 | }
413 |
414 | function tokenString(quote: Quota | null) {
415 | return function (stream: StringStream, state: ModeState) {
416 | state.context.insideString = true
417 | state.context.stringQuote = quote
418 |
419 | let next: string | null = ""
420 | let escaped = false
421 | let end = false
422 |
423 | while ((next = stream.next()) != null) {
424 | if (next === quote && !escaped) {
425 | end = true
426 | break
427 | }
428 | if (next === "$" && !escaped && stream.eat("{")) {
429 | state.tokenize = tokenNextInterpolation
430 | stream.backUp(2)
431 | return "string"
432 | }
433 | if (next === "$" && !escaped) {
434 | state.tokenize = tokenNextInterpolation
435 | stream.backUp(1)
436 | return "string"
437 | }
438 | if (escaped && isValidEscapeChar(next)) {
439 | stream.backUp(2)
440 | state.tokenize = tokenNextEscape
441 | return "string"
442 | }
443 | escaped = !escaped && next === "\\"
444 | }
445 |
446 | if (end || escaped) {
447 | state.tokenize = tokenBase
448 | }
449 |
450 | state.context.insideString = false
451 | state.context.stringQuote = null
452 | return "string"
453 | }
454 | }
455 |
456 | function tokenRawString(quote: Quota | null) {
457 | return function (stream: StringStream, state: ModeState) {
458 | state.context.insideString = true
459 | state.context.stringQuote = quote
460 |
461 | let next: string | null = ""
462 | let escaped = false
463 | let end = false
464 |
465 | while ((next = stream.next()) != null) {
466 | if (next === quote && !escaped) {
467 | end = true
468 | break
469 | }
470 | escaped = !escaped && next === "\\"
471 | }
472 |
473 | if (end || escaped) {
474 | state.tokenize = tokenBase
475 | }
476 |
477 | state.context.insideString = false
478 | state.context.stringQuote = null
479 | return "string"
480 | }
481 | }
482 |
483 | function tokenComment(stream: StringStream, state: ModeState) {
484 | let maybeEnd = false
485 | let ch: string | null
486 | while (ch = stream.next()) {
487 | if (ch === "/" && maybeEnd) {
488 | state.tokenize = tokenBase
489 | break
490 | }
491 | maybeEnd = (ch === "*")
492 | }
493 | return "comment"
494 | }
495 |
496 | function pushContext(state: ModeState, column: number, type: string) {
497 | return state.context = new Context(state.indention, column, type, null, state.context, state.context.knownImports)
498 | }
499 |
500 | function popContext(state: ModeState) {
501 | if (!state.context.prev) return
502 | const t = state.context.type
503 | if (t === ")" || t === "]" || t === "}")
504 | state.indention = state.context.indentation
505 | state.context = state.context.prev
506 | return state.context
507 | }
508 |
509 | return {
510 | startState: function (): ModeState {
511 | return {
512 | tokenize: null,
513 | context: new Context(0, 0, "top", false),
514 | indention: 0,
515 | startOfLine: true,
516 | }
517 | },
518 |
519 | token: function (stream: StringStream, state: ModeState): string | null {
520 | const ctx = state.context
521 | if (stream.sol()) {
522 | if (ctx.align == null) {
523 | ctx.align = false
524 | }
525 | state.indention = stream.indentation()
526 | state.startOfLine = true
527 | }
528 | if (stream.eatSpace()) {
529 | return null
530 | }
531 | curPunc = null
532 | const style = (state.tokenize || tokenBase)(stream, state)
533 | if (style === "comment") {
534 | return style
535 | }
536 | if (ctx.align == null) {
537 | ctx.align = true
538 | }
539 |
540 | if (curPunc === "{") pushContext(state, stream.column(), "}")
541 | else if (curPunc === "[") pushContext(state, stream.column(), "]")
542 | else if (curPunc === "(") pushContext(state, stream.column(), ")")
543 | else if (curPunc === "}" && ctx.type === "}") popContext(state)
544 | else if (curPunc === ctx.type) popContext(state)
545 | state.startOfLine = false
546 | return style
547 | },
548 |
549 | indent: function (state: ModeState, textAfter: string): number {
550 | if (state.tokenize !== tokenBase && state.tokenize != null) {
551 | return 0
552 | }
553 |
554 | if (state.context.type == "top") {
555 | return 0
556 | }
557 |
558 | const ctx = state.context
559 | const firstChar = textAfter.charAt(0)
560 |
561 | const closing = firstChar === ctx.type
562 | if (ctx.align) {
563 | return ctx.column + (closing ? 0 : 1)
564 | }
565 |
566 | return ctx.indentation + (closing ? 0 : indentUnit)
567 | },
568 |
569 | // @ts-ignore
570 | electricChars: "{}):",
571 | // @ts-ignore
572 | closeBrackets: "()[]{}''\"\"``",
573 | fold: "brace",
574 | blockCommentStart: "/*",
575 | blockCommentEnd: "*/",
576 | lineComment: "//",
577 | }
578 | })
579 |
580 | // @ts-ignore
581 | CodeMirror.defineMIME("text/x-v", "v")
582 |
--------------------------------------------------------------------------------