mirror of
https://github.com/danbulant/monaco-yaml
synced 2026-06-24 17:11:59 +00:00
Compare commits
196 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ab8e671e6 | ||
|
|
0e42b40a65 | ||
|
|
45d8d1ae57 | ||
|
|
629aaaa2a8 | ||
|
|
5efe47e449 | ||
|
|
e28f7fec9f | ||
|
|
c4ae02d2ef | ||
|
|
18cfa0a008 | ||
|
|
d35b72e1fe | ||
|
|
0518312cac | ||
|
|
326a253813 | ||
|
|
928d33cdc3 | ||
|
|
ef35d1be2b | ||
|
|
a959e028ce | ||
|
|
38e61fc7be | ||
|
|
f42c5e97e0 | ||
|
|
af88581fbb | ||
|
|
a18a75d137 | ||
|
|
9a2be95116 | ||
|
|
bd0ae90018 | ||
|
|
934eb8d1e3 | ||
|
|
3a873e083c | ||
|
|
ccfbbbe9d8 | ||
|
|
5bb55b6e96 | ||
|
|
90d895a214 | ||
|
|
72fc069c6d | ||
|
|
545e6a6bc4 | ||
|
|
f44c4e8e34 | ||
|
|
56372c1c2a | ||
|
|
c008952c87 | ||
|
|
2a80dc8913 | ||
|
|
e0d2f4ade6 | ||
|
|
f2948443e1 | ||
|
|
ed001fdc1c | ||
|
|
786cdb5dbb | ||
|
|
edf8623b78 | ||
|
|
5507d53dee | ||
|
|
4f06c90e34 | ||
|
|
d15459091c | ||
|
|
44e5c7b6b5 | ||
|
|
f3decc20dd | ||
|
|
7fd3bc973b | ||
|
|
eb0ce6dd0a | ||
|
|
fc405ef1b2 | ||
|
|
1b5fa06cc9 | ||
|
|
c5d83c4543 | ||
|
|
90a7bcda0a | ||
|
|
f7fce45714 | ||
|
|
554699887d | ||
|
|
4e30f4cf16 | ||
|
|
8d75a459ee | ||
|
|
0f2e5536bf | ||
|
|
9987fbc7d5 | ||
|
|
4affa0963c | ||
|
|
11f4465ee8 | ||
|
|
da51564fbb | ||
|
|
7313780510 | ||
|
|
3a0b164c51 | ||
|
|
979ed62d6f | ||
|
|
65e91a1d43 | ||
|
|
7bf8137107 | ||
|
|
2bece31f56 | ||
|
|
7a96d822b1 | ||
|
|
8d278d3d19 | ||
|
|
3d037a15a6 | ||
|
|
96abaa5bce | ||
|
|
04ba90907b | ||
|
|
9506aef618 | ||
|
|
9739d143d3 | ||
|
|
3541f69831 | ||
|
|
5445cd8437 | ||
|
|
99dcdff6fe | ||
|
|
f101c2fc1d | ||
|
|
26173cdbad | ||
|
|
b4ffefc00f | ||
|
|
3361bfe233 | ||
|
|
24b116419d | ||
|
|
307ae10623 | ||
|
|
28928ca314 | ||
|
|
888ac94808 | ||
|
|
67d9aecfb6 | ||
|
|
b79134d080 | ||
|
|
cf4b843ce5 | ||
|
|
74803276b6 | ||
|
|
020c123830 | ||
|
|
c99e1c3db6 | ||
|
|
0da9f2cdfd | ||
|
|
109332e5e7 | ||
|
|
a95fdc4463 | ||
|
|
d1c499b224 | ||
|
|
13a21cba5d | ||
|
|
1f476ad558 | ||
|
|
2228054e58 | ||
|
|
ecd189685e | ||
|
|
6860281373 | ||
|
|
8b0e02a4a6 | ||
|
|
8f6cb81147 | ||
|
|
3774841f7c | ||
|
|
5d059ce47c | ||
|
|
fff16c993e | ||
|
|
ba395c9d23 | ||
|
|
57e820ed0c | ||
|
|
40a38e2d2d | ||
|
|
40243d4e83 | ||
|
|
8fa3ca4252 | ||
|
|
bdfa1ef4e7 | ||
|
|
d4c6747667 | ||
|
|
df88461b0c | ||
|
|
d7e5aa7744 | ||
|
|
d4ddc80b51 | ||
|
|
fd9efe8060 | ||
|
|
62998a0b38 | ||
|
|
dd64c96d59 | ||
|
|
3dc04e2150 | ||
|
|
cdef13351a | ||
|
|
288c34cd37 | ||
|
|
26b0d6ea96 | ||
|
|
612604e254 | ||
|
|
970179a272 | ||
|
|
b533a0af0a | ||
|
|
0d107449ad | ||
|
|
95fd123191 | ||
|
|
c2e6af4198 | ||
|
|
f16e46331e | ||
|
|
41d2b2e0c9 | ||
|
|
b42b0b6af4 | ||
|
|
2912c4b287 | ||
|
|
f5fc412497 | ||
|
|
0d46780695 | ||
|
|
260f0da919 | ||
|
|
11030e5599 | ||
|
|
249771b770 | ||
|
|
72d77fdc33 | ||
|
|
ee45815857 | ||
|
|
4bbdb7e5b1 | ||
|
|
92b103ee8d | ||
|
|
b6195b1563 | ||
|
|
9962474f4d | ||
|
|
cff2665b7c | ||
|
|
3094ebef91 | ||
|
|
6271f31cd3 | ||
|
|
e4c13afe52 | ||
|
|
e95737f7a2 | ||
|
|
13c46f5de6 | ||
|
|
c1310d88ff | ||
|
|
2ea154af85 | ||
|
|
090302534a | ||
|
|
bd831b1e03 | ||
|
|
7174aff026 | ||
|
|
9436e1f034 | ||
|
|
7a6fcf0c64 | ||
|
|
ce58d4b6d7 | ||
|
|
249de5dc34 | ||
|
|
b276fd26d1 | ||
|
|
ac7b6fe307 | ||
|
|
9a107bf7cc | ||
|
|
f327e73f66 | ||
|
|
872423db4b | ||
|
|
668b53af11 | ||
|
|
296968c33f | ||
|
|
67e2accd3b | ||
|
|
bb3d8bd941 | ||
|
|
c12db2ef5b | ||
|
|
ec4de1f50c | ||
|
|
52c457a196 | ||
|
|
5a0de63544 | ||
|
|
c4a95e2dd5 | ||
|
|
0e0555455c | ||
|
|
4e00c72636 | ||
|
|
09e2418046 | ||
|
|
5fd08c73ed | ||
|
|
3762a98af0 | ||
|
|
3da78b4f01 | ||
|
|
1b173c35a9 | ||
|
|
9f10ccc9f9 | ||
|
|
c8d9e1cc58 | ||
|
|
04907bd623 | ||
|
|
97106b71c9 | ||
|
|
f4130f38a3 | ||
|
|
1c532f6815 | ||
|
|
26317d314a | ||
|
|
a5a49756be | ||
|
|
7f49a21a56 | ||
|
|
8d3e1626f3 | ||
|
|
e784f842c3 | ||
|
|
36e4f908e7 | ||
|
|
0889a43871 | ||
|
|
ba85acba70 | ||
|
|
6ba5b6d180 | ||
|
|
5b6cae33b2 | ||
|
|
4dd5405431 | ||
|
|
3fb057d250 | ||
|
|
660155eec5 | ||
|
|
d7bbebe8fc | ||
|
|
5134cc99bd | ||
|
|
fa72f5d6be |
166 changed files with 10473 additions and 34163 deletions
|
|
@ -3,12 +3,12 @@ root = true
|
||||||
|
|
||||||
[*]
|
[*]
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
indent_style = space
|
|
||||||
indent_size = 2
|
|
||||||
insert_final_newline = true
|
|
||||||
trim_trailing_whitespace = true
|
|
||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
|
indent_size = 2
|
||||||
|
indent_style = space
|
||||||
|
insert_final_newline = true
|
||||||
|
max_line_length = 100
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
[*.md]
|
[COMMIT_EDITMSG]
|
||||||
max_line_length = off
|
max_line_length = 72
|
||||||
trim_trailing_whitespace = false
|
|
||||||
|
|
|
||||||
19
.eslintrc.yaml
Normal file
19
.eslintrc.yaml
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
extends:
|
||||||
|
- remcohaszing
|
||||||
|
- remcohaszing/typechecking
|
||||||
|
rules:
|
||||||
|
no-restricted-globals: off
|
||||||
|
|
||||||
|
'@typescript-eslint/no-misused-promises': off
|
||||||
|
'@typescript-eslint/no-shadow': off
|
||||||
|
'@typescript-eslint/no-unnecessary-condition': off
|
||||||
|
|
||||||
|
import/extensions: off
|
||||||
|
import/no-extraneous-dependencies: off
|
||||||
|
import/no-unresolved: off
|
||||||
|
|
||||||
|
jsdoc/require-jsdoc: off
|
||||||
|
|
||||||
|
node/no-extraneous-import: off
|
||||||
|
node/no-unsupported-features/es-syntax: off
|
||||||
|
node/no-unsupported-features/node-builtins: off
|
||||||
66
.github/workflows/ci.yaml
vendored
Normal file
66
.github/workflows/ci.yaml
vendored
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
name: ci
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
tags: ['*']
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
eslint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-node@v2
|
||||||
|
with: { node-version: 16 }
|
||||||
|
- run: npm ci
|
||||||
|
- run: npx eslint .
|
||||||
|
|
||||||
|
examples:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-node@v2
|
||||||
|
with: { node-version: 16 }
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm run prepack
|
||||||
|
- run: npm run build --workspaces
|
||||||
|
|
||||||
|
pack:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-node@v2
|
||||||
|
with: { node-version: 16 }
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm pack
|
||||||
|
|
||||||
|
prettier:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-node@v2
|
||||||
|
with: { node-version: 16 }
|
||||||
|
- run: npm ci
|
||||||
|
- run: npx prettier --check .
|
||||||
|
|
||||||
|
tsc:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-node@v2
|
||||||
|
with: { node-version: 16 }
|
||||||
|
- run: npm ci
|
||||||
|
- run: npx tsc
|
||||||
|
# release:
|
||||||
|
# runs-on: ubuntu-latest
|
||||||
|
# needs: [eslint, pack, prettier, tsc]
|
||||||
|
# if: startsWith(github.ref, 'refs/tags/')
|
||||||
|
# steps:
|
||||||
|
# - uses: actions/checkout@v2
|
||||||
|
# - uses: actions/setup-node@v2
|
||||||
|
# with: { node-version: 16 }
|
||||||
|
# - run: npm ci
|
||||||
|
# - run: npm publish
|
||||||
|
# env:
|
||||||
|
# NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
10
.gitignore
vendored
10
.gitignore
vendored
|
|
@ -1,5 +1,7 @@
|
||||||
/node_modules/
|
dist/
|
||||||
/out/
|
node_modules/
|
||||||
/lib/
|
/index.js
|
||||||
|
/yaml.worker.js
|
||||||
*.log
|
*.log
|
||||||
|
*.map
|
||||||
|
*.tgz
|
||||||
|
|
|
||||||
3
.husky/pre-commit
Executable file
3
.husky/pre-commit
Executable file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
npx lint-staged
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
/.vscode/
|
|
||||||
/out/
|
|
||||||
/scripts/
|
|
||||||
/src/
|
|
||||||
/test/
|
|
||||||
/.gitignore
|
|
||||||
/.npmignore
|
|
||||||
1
.npmrc
Normal file
1
.npmrc
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
lockfile-version = 3
|
||||||
1
.nvmrc
Normal file
1
.nvmrc
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
16
|
||||||
3
.prettierignore
Normal file
3
.prettierignore
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
dist/
|
||||||
|
/index.js
|
||||||
|
/yaml.worker.js
|
||||||
3
.prettierrc.yaml
Normal file
3
.prettierrc.yaml
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
proseWrap: always
|
||||||
|
singleQuote: true
|
||||||
|
trailingComma: all
|
||||||
18
.vscode/launch.json
vendored
18
.vscode/launch.json
vendored
|
|
@ -1,18 +0,0 @@
|
||||||
{
|
|
||||||
// Use IntelliSense to learn about possible attributes.
|
|
||||||
// Hover to view descriptions of existing attributes.
|
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"type": "node",
|
|
||||||
"name": "vscode-jest-tests",
|
|
||||||
"request": "launch",
|
|
||||||
"args": ["--runInBand"],
|
|
||||||
"cwd": "${workspaceFolder}",
|
|
||||||
"console": "integratedTerminal",
|
|
||||||
"internalConsoleOptions": "neverOpen",
|
|
||||||
"program": "${workspaceFolder}/node_modules/jest/bin/jest"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
11
.vscode/settings.json
vendored
11
.vscode/settings.json
vendored
|
|
@ -1,11 +0,0 @@
|
||||||
// Place your settings in this file to overwrite default and user settings.
|
|
||||||
{
|
|
||||||
"files.trimTrailingWhitespace": true,
|
|
||||||
"search.exclude": {
|
|
||||||
"**/node_modules": true,
|
|
||||||
"**/lib": true,
|
|
||||||
"**/out": true
|
|
||||||
},
|
|
||||||
"javascript.preferences.quoteStyle": "single",
|
|
||||||
"typescript.preferences.quoteStyle": "single"
|
|
||||||
}
|
|
||||||
36
CONTRIBUTING.md
Normal file
36
CONTRIBUTING.md
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
# Contributing
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
The following are required to start working on this project:
|
||||||
|
|
||||||
|
- [Git](https://git-scm.com)
|
||||||
|
- [NodeJS](https://nodejs.org) 16 or higher
|
||||||
|
- [npm](https://github.com/npm/cli) 8.1.2 or higher
|
||||||
|
|
||||||
|
## Getting started
|
||||||
|
|
||||||
|
To get started with contributing, clone the repository and install its dependencies.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone https://github.com/remcohaszing/monaco-yaml
|
||||||
|
cd monaco-yaml
|
||||||
|
npm ci
|
||||||
|
```
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
To build the repository, run:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm run prepack
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running
|
||||||
|
|
||||||
|
To test it, run one of the
|
||||||
|
[examples](https://github.com/remcohaszing/monaco-yaml/tree/main/examples).
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm --workspace demo start
|
||||||
|
```
|
||||||
25
LICENSE.md
25
LICENSE.md
|
|
@ -2,20 +2,17 @@ The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) Microsoft Corporation
|
Copyright (c) Microsoft Corporation
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||||
in the Software without restriction, including without limitation the rights
|
including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
The above copyright notice and this permission notice shall be included in all copies or substantial
|
||||||
copies or substantial portions of the Software.
|
portions of the Software.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
|
|
|
||||||
235
README.md
235
README.md
|
|
@ -1,42 +1,219 @@
|
||||||
# Monaco YAML
|
# Monaco YAML
|
||||||
|
|
||||||
YAML language plugin for the Monaco Editor. It provides the following features when editing YAML files:
|
[](https://github.com/remcohaszing/monaco-yaml/actions/workflows/ci.yaml)
|
||||||
* Code completion, based on JSON schemas or by looking at similar objects in the same file
|
[](https://www.npmjs.com/package/monaco-yaml)
|
||||||
* Hovers, based on JSON schemas
|
[](https://prettier.io)
|
||||||
* Validation: Syntax errors and schema validation
|
[](https://monaco-yaml.js.org)
|
||||||
* Formatting
|
[](https://app.netlify.com/sites/monaco-yaml/deploys)
|
||||||
* Document Symbols
|
|
||||||
* Syntax highlighting
|
|
||||||
* Automatically load remote schema files (by enabling DiagnosticsOptions.enableSchemaRequest)
|
|
||||||
|
|
||||||
Schemas can also be provided by configuration. See [here](https://github.com/Microsoft/monaco-json/blob/master/src/monaco.d.ts)
|
YAML language plugin for the Monaco Editor. It provides the following features when editing YAML
|
||||||
for the API that the JSON plugin offers to configure the JSON language support.
|
files:
|
||||||
|
|
||||||
## Installing
|
- Code completion, based on JSON schemas or by looking at similar objects in the same file
|
||||||
|
- Hovers, based on JSON schemas
|
||||||
|
- Validation: Syntax errors and schema validation
|
||||||
|
- Formatting using Prettier
|
||||||
|
- Document Symbols
|
||||||
|
- Automatically load remote schema files (by enabling DiagnosticsOptions.enableSchemaRequest)
|
||||||
|
- Links from JSON references.
|
||||||
|
- Links and hover effects from YAML anchors.
|
||||||
|
|
||||||
`yarn add monaco-yaml`
|
Schemas can also be provided by configuration. See
|
||||||
Both vs loader and ESM are supported.
|
[here](https://github.com/remcohaszing/monaco-yaml/blob/main/index.d.ts) for the API that the plugin
|
||||||
See `examples` directory for esm and umd examples.
|
offers to configure the YAML language support.
|
||||||
|
|
||||||
## Development
|
## Installation
|
||||||
|
|
||||||
* `git clone https://github.com/pengx17/monaco-yaml`
|
```sh
|
||||||
* `cd monaco-yaml`
|
npm install monaco-yaml
|
||||||
* `yarn`
|
```
|
||||||
* open `$/monaco-yaml/demo/index.html` in your favorite browser.
|
|
||||||
|
|
||||||
A running example:
|
## Usage
|
||||||

|
|
||||||
|
Import `monaco-yaml` and configure it before an editor instance is created.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { editor, Uri } from 'monaco-editor';
|
||||||
|
import { setDiagnosticsOptions } from 'monaco-yaml';
|
||||||
|
|
||||||
|
// The uri is used for the schema file match.
|
||||||
|
const modelUri = Uri.parse('a://b/foo.yaml');
|
||||||
|
|
||||||
|
setDiagnosticsOptions({
|
||||||
|
enableSchemaRequest: true,
|
||||||
|
hover: true,
|
||||||
|
completion: true,
|
||||||
|
validate: true,
|
||||||
|
format: true,
|
||||||
|
schemas: [
|
||||||
|
{
|
||||||
|
// Id of the first schema
|
||||||
|
uri: 'http://myserver/foo-schema.json',
|
||||||
|
// Associate with our model
|
||||||
|
fileMatch: [String(modelUri)],
|
||||||
|
schema: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
p1: {
|
||||||
|
enum: ['v1', 'v2'],
|
||||||
|
},
|
||||||
|
p2: {
|
||||||
|
// Reference the second schema
|
||||||
|
$ref: 'http://myserver/bar-schema.json',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Id of the first schema
|
||||||
|
uri: 'http://myserver/bar-schema.json',
|
||||||
|
schema: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
q1: {
|
||||||
|
enum: ['x1', 'x2'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
editor.create(document.createElement('editor'), {
|
||||||
|
// Monaco-yaml features should just work if the editor language is set to 'yaml'.
|
||||||
|
language: 'yaml',
|
||||||
|
model: editor.createModel('p1: \n', 'yaml', modelUri),
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Also make sure to register the web worker. When using Webpack 5, this looks like the code below.
|
||||||
|
Other bundlers may use a different syntax, but the idea is the same. Languages you don’t used can be
|
||||||
|
omitted.
|
||||||
|
|
||||||
|
```js
|
||||||
|
window.MonacoEnvironment = {
|
||||||
|
getWorker(moduleId, label) {
|
||||||
|
switch (label) {
|
||||||
|
case 'editorWorkerService':
|
||||||
|
return new Worker(new URL('monaco-editor/esm/vs/editor/editor.worker', import.meta.url));
|
||||||
|
case 'css':
|
||||||
|
case 'less':
|
||||||
|
case 'scss':
|
||||||
|
return new Worker(new URL('monaco-editor/esm/vs/language/css/css.worker', import.meta.url));
|
||||||
|
case 'handlebars':
|
||||||
|
case 'html':
|
||||||
|
case 'razor':
|
||||||
|
return new Worker(
|
||||||
|
new URL('monaco-editor/esm/vs/language/html/html.worker', import.meta.url),
|
||||||
|
);
|
||||||
|
case 'json':
|
||||||
|
return new Worker(
|
||||||
|
new URL('monaco-editor/esm/vs/language/json/json.worker', import.meta.url),
|
||||||
|
);
|
||||||
|
case 'javascript':
|
||||||
|
case 'typescript':
|
||||||
|
return new Worker(
|
||||||
|
new URL('monaco-editor/esm/vs/language/typescript/ts.worker', import.meta.url),
|
||||||
|
);
|
||||||
|
case 'yaml':
|
||||||
|
return new Worker(new URL('monaco-yaml/yaml.worker', import.meta.url));
|
||||||
|
default:
|
||||||
|
throw new Error(`Unknown label ${label}`);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
A demo is available on [monaco-yaml.js.org](https://monaco-yaml.js.org).
|
||||||
|
|
||||||
|
A running example: 
|
||||||
|
|
||||||
|
Some usage examples can be found in the
|
||||||
|
[examples](https://github.com/remcohaszing/monaco-yaml/tree/main/examples) directory.
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
### Does this work with the Monaco UMD bundle?
|
||||||
|
|
||||||
|
No. Only ESM is supported.
|
||||||
|
|
||||||
|
### Does this work with Monaco Editor from a CDN?
|
||||||
|
|
||||||
|
No, because these use a UMD bundle, which isn’t supported.
|
||||||
|
|
||||||
|
### Does this work with `@monaco-editor/loader` or `@monaco-editor/react`?
|
||||||
|
|
||||||
|
No. These packages pull in the Monaco UMD bundle from a CDN. Because UMD isn’t supported, neither
|
||||||
|
are these packages.
|
||||||
|
|
||||||
|
### Is the web worker necessary?
|
||||||
|
|
||||||
|
Yes. The web worker provides the core functionality of `monaco-yaml`.
|
||||||
|
|
||||||
|
### Does it work without a bundler?
|
||||||
|
|
||||||
|
No. `monaco-yaml` uses dependencies from `node_modules`, so they can be deduped and your bundle size
|
||||||
|
is decreased. This comes at the cost of not being able to use it without a bundler.
|
||||||
|
|
||||||
|
### How do I integrate `monaco-yaml` with a framework? (Angular, React, Vue, etc.)
|
||||||
|
|
||||||
|
`monaco-yaml` only uses the Monaco Editor. It’s not tied to a framework, all that’s needed is a DOM
|
||||||
|
node to attach the Monaco Editor to. See the
|
||||||
|
[Monaco Editor examples](https://github.com/microsoft/monaco-editor/tree/main/monaco-editor-samples)
|
||||||
|
for examples on how to integrate Monaco Editor in your project, then configure `monaco-yaml` as
|
||||||
|
described above.
|
||||||
|
|
||||||
|
### Does `monaco-yaml` work with `create-react-app`?
|
||||||
|
|
||||||
|
Yes, but you’ll have to eject. See
|
||||||
|
[#92 (comment)](https://github.com/remcohaszing/monaco-yaml/issues/92#issuecomment-905836058) for
|
||||||
|
details.
|
||||||
|
|
||||||
|
### Why isn’t `monaco-yaml` working? Official Monaco language extensions do work.
|
||||||
|
|
||||||
|
This is most likely due to the fact that `monaco-yaml` is using a different instance of the
|
||||||
|
`monaco-editor` package than you are. This is something you’ll want to avoid regardless of
|
||||||
|
`monaco-editor`, because it means your bundle is significantly larger than it needs to be. This is
|
||||||
|
likely caused by one of the following issues:
|
||||||
|
|
||||||
|
- A code splitting misconfiguration
|
||||||
|
|
||||||
|
To solve this, try inspecting your bundle using for example
|
||||||
|
[webpack-bundle-analyzer](https://github.com/webpack-contrib/webpack-bundle-analyzer). If
|
||||||
|
`monaco-editor` is in there twice, this is the issue. It’s up to you to solve this, as it’s
|
||||||
|
project-specific.
|
||||||
|
|
||||||
|
- You’re using a package which imports `monaco-editor` for you, but it’s using a different version.
|
||||||
|
|
||||||
|
You can find out why the `monaco-editor` is installed using `npm ls monaco-editor` or
|
||||||
|
`yarn why monaco-editor`. It should exist only once, but it’s ok if it’s deduped.
|
||||||
|
|
||||||
|
You may be able to solve this by deleting your `node_modules` folder and `package-lock.json` or
|
||||||
|
`yarn.lock`, then running `npm install` or `yarn install` respectively.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Please see our [contributing guidelines](CONTRIBUTING.md)
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
- https://github.com/redhat-developer/yaml-language-server
|
|
||||||
|
|
||||||
### Maintain
|
Originally [@kpdecker](https://github.com/kpdecker) forked this repository from
|
||||||
Manually clone dependencies list below and update the project files accordingly:
|
[`monaco-json`](https://github.com/microsoft/monaco-json) by
|
||||||
- `src/languageservice`: https://github.com/redhat-developer/yaml-language-server
|
[@microsoft](https://github.com/microsoft) and rewrote it to work with
|
||||||
- `cp yaml-language-server/src/languageservice monaco-yaml/src/languageservice`
|
[`yaml-language-server`](https://github.com/redhat-developer/yaml-language-server) instead. Later
|
||||||
- Modify the import paths, go to the test page and see if it still works
|
the repository maintenance was taken over by [@pengx17](https://github.com/pengx17). Eventually the
|
||||||
- `src/yaml-ast-parser-custom-tags`: https://github.com/JPinkney/yaml-ast-parser/tree/master/src
|
repository was tranferred to the account of [@remcohaszing](https://github.com/remcohaszing), who is
|
||||||
|
currently maintaining this repository with the help of [@fleon](https://github.com/fleon) and
|
||||||
|
[@yazaabed](https://github.com/yazaabed).
|
||||||
|
|
||||||
|
The heavy processing is done in
|
||||||
|
[`yaml-language-server`](https://github.com/redhat-developer/yaml-language-server), best known for
|
||||||
|
being the backbone for [`vscode-yaml`](https://github.com/redhat-developer/vscode-yaml). This
|
||||||
|
repository provides a thin layer to add functionality provided by `yaml-language-server` into
|
||||||
|
`monaco-editor`.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
[MIT](https://github.com/pengx17/monaco-yaml/blob/master/LICENSE.md)
|
|
||||||
|
[MIT](https://github.com/remcohaszing/monaco-yaml/blob/main/LICENSE.md)
|
||||||
|
|
|
||||||
58
build.js
Executable file
58
build.js
Executable file
|
|
@ -0,0 +1,58 @@
|
||||||
|
const { build } = require('esbuild');
|
||||||
|
|
||||||
|
const { dependencies, peerDependencies } = require('./package.json');
|
||||||
|
|
||||||
|
build({
|
||||||
|
entryPoints: ['src/index.ts', 'src/yaml.worker.ts'],
|
||||||
|
bundle: true,
|
||||||
|
external: Object.keys({ ...dependencies, ...peerDependencies }),
|
||||||
|
logLevel: 'info',
|
||||||
|
outdir: '.',
|
||||||
|
sourcemap: true,
|
||||||
|
format: 'esm',
|
||||||
|
target: ['es2019'],
|
||||||
|
plugins: [
|
||||||
|
{
|
||||||
|
name: 'alias',
|
||||||
|
setup({ onResolve }) {
|
||||||
|
// The file monaco-yaml/lib/esm/schemaSelectionHandlers.js imports code from the language
|
||||||
|
// server part that we don’t want.
|
||||||
|
onResolve({ filter: /\/schemaSelectionHandlers$/ }, () => ({
|
||||||
|
path: require.resolve('./src/fillers/schemaSelectionHandlers.ts'),
|
||||||
|
}));
|
||||||
|
// The yaml language service only imports re-exports of vscode-languageserver-types from
|
||||||
|
// vscode-languageserver.
|
||||||
|
onResolve({ filter: /^vscode-languageserver(\/node|-protocol)?$/ }, () => ({
|
||||||
|
path: 'vscode-languageserver-types',
|
||||||
|
external: true,
|
||||||
|
}));
|
||||||
|
// The yaml language service uses path. We can stub it using path-browserify.
|
||||||
|
onResolve({ filter: /^path$/ }, () => ({
|
||||||
|
path: 'path-browserify',
|
||||||
|
external: true,
|
||||||
|
}));
|
||||||
|
// The main prettier entry point contains all of Prettier.
|
||||||
|
// The standalone bundle is smaller and works fine for us.
|
||||||
|
onResolve({ filter: /^prettier/ }, ({ path }) => ({
|
||||||
|
path: path === 'prettier' ? 'prettier/standalone.js' : `${path}.js`,
|
||||||
|
external: true,
|
||||||
|
}));
|
||||||
|
// This tiny filler implementation serves all our needs.
|
||||||
|
onResolve({ filter: /vscode-nls/ }, () => ({
|
||||||
|
path: require.resolve('./src/fillers/vscode-nls.ts'),
|
||||||
|
}));
|
||||||
|
// The language server dependencies tend to write both ESM and UMD output alongside each
|
||||||
|
// other, then use UMD for imports. We prefer ESM.
|
||||||
|
onResolve({ filter: /\/umd\// }, (args) => ({
|
||||||
|
path: require.resolve(args.path.replace(/\/umd\//, '/esm/'), {
|
||||||
|
paths: [args.resolveDir],
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}).catch((error) => {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error(error);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
31
examples/demo/README.md
Normal file
31
examples/demo/README.md
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
# Demo
|
||||||
|
|
||||||
|
This demo is deployed to [monaco-yaml.js.org](https://monaco-yaml.js.org). It shows how
|
||||||
|
`monaco-editor` and `monaco-yaml` can be used with
|
||||||
|
[Webpack 5](https://webpack.js.org/concepts/entry-points).
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- [NodeJS](https://nodejs.org) 16 or higher
|
||||||
|
- [npm](https://github.com/npm/cli) 8.1.2 or higher
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
To run the project locally, clone the repository and set it up:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone https://github.com/remcohaszing/monaco-yaml
|
||||||
|
cd monaco-yaml
|
||||||
|
npm ci
|
||||||
|
npm run prepack
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running
|
||||||
|
|
||||||
|
To start it, simply run:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm --workspace demo start
|
||||||
|
```
|
||||||
|
|
||||||
|
The demo will open in your browser.
|
||||||
24
examples/demo/package.json
Normal file
24
examples/demo/package.json
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"name": "demo",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"start": "webpack serve --open --mode development",
|
||||||
|
"build": "webpack --mode production"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@fortawesome/fontawesome-free": "^6.0.0",
|
||||||
|
"@schemastore/schema-catalog": "^0.0.5",
|
||||||
|
"css-loader": "^6.0.0",
|
||||||
|
"css-minimizer-webpack-plugin": "^3.0.0",
|
||||||
|
"html-webpack-plugin": "^5.0.0",
|
||||||
|
"mini-css-extract-plugin": "^2.0.0",
|
||||||
|
"monaco-editor": "^0.31.0",
|
||||||
|
"monaco-yaml": "file:../..",
|
||||||
|
"ts-loader": "^9.0.0",
|
||||||
|
"typescript": "^4.0.0",
|
||||||
|
"webpack": "^5.0.0",
|
||||||
|
"webpack-cli": "^4.0.0",
|
||||||
|
"webpack-dev-server": "^4.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
14
examples/demo/src/icon.svg
Normal file
14
examples/demo/src/icon.svg
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 512 470.647">
|
||||||
|
<style>
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
.char {
|
||||||
|
fill: #ffffff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<polygon class="char" points="512 422.735 395.638 422.735 395.638 250.125 347.442 250.125 347.442 469.647 512 469.647 512 422.737 512 422.735" />
|
||||||
|
<polygon class="char" points="87.701 250.177 87.701 470.647 135.004 470.647 135.004 318.569 184.509 420.789 221.743 420.789 272.939 314.976 272.939 470.602 318.318 470.602 318.318 250.177 256.358 250.177 201.381 349.883 149.021 250.177 87.701 250.177 87.701 250.177" />
|
||||||
|
<path fill="#cb171e" d="M330.294,195.39H228.433l-20.717,50.024H162.61L257.99,20.465h46.137l91.51,224.949h-48.2L330.293,195.39Zm-16.92-44.911-31.226-82.55-34.837,82.55h66.063Z" transform="translate(0 -19.939)" />
|
||||||
|
<polygon class="char" points="235.793 0 143.978 137.674 143.978 224.949 87.702 224.949 87.702 137.674 0 0 63.25 0 119.018 88.646 175.243 0 235.793 0 235.793 0" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1 KiB |
122
examples/demo/src/index.css
Normal file
122
examples/demo/src/index.css
Normal file
|
|
@ -0,0 +1,122 @@
|
||||||
|
:root {
|
||||||
|
--background-color: hsl(0, 0%, 96%);
|
||||||
|
--editor-background: hsl(60, 100%, 100%);
|
||||||
|
--foreground-color: hsl(0, 0%, 0%);
|
||||||
|
--primary-color: hsl(189, 100%, 63%);
|
||||||
|
--shadow-color: hsla(0, 0%, 27%, 0.239);
|
||||||
|
--warning-color: hsl(49, 100%, 40%);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root {
|
||||||
|
--background-color: hsl(0, 0%, 23%);
|
||||||
|
--editor-background: hsl(0, 0%, 12%);
|
||||||
|
--foreground-color: hsl(0, 0%, 100%);
|
||||||
|
--shadow-color: hsl(0, 0%, 43%);
|
||||||
|
--warning-color: hsl(49, 100%, 40%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background: var(--background-color);
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
font-family: sans-serif;
|
||||||
|
height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin: 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav {
|
||||||
|
align-items: center;
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
box-shadow: 0px 5px 5px var(--shadow-color);
|
||||||
|
display: flex;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
height: 3rem;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-icon {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-icon > img {
|
||||||
|
height: 2rem;
|
||||||
|
margin-right: 1rem;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-wrapper {
|
||||||
|
background: var(--editor-background);
|
||||||
|
box-shadow: 0 0 10px var(--shadow-color);
|
||||||
|
display: flex;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
flex-flow: column;
|
||||||
|
margin: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#schema-selection {
|
||||||
|
background-color: var(--editor-background);
|
||||||
|
border: none;
|
||||||
|
border-bottom: 1px solid var(--shadow-color);
|
||||||
|
color: var(--foreground-color);
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#breadcrumbs {
|
||||||
|
border-bottom: 1px solid var(--shadow-color);
|
||||||
|
color: var(--foreground-color);
|
||||||
|
flex: 0 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#breadcrumbs::before,
|
||||||
|
.breadcrumb:not(:last-child)::after {
|
||||||
|
content: '›';
|
||||||
|
margin: 0 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb.array::before {
|
||||||
|
content: '[]';
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb.object::before {
|
||||||
|
content: '{}';
|
||||||
|
}
|
||||||
|
|
||||||
|
#editor {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#problems {
|
||||||
|
border-top: 1px solid var(--shadow-color);
|
||||||
|
flex: 0 0 20vh;
|
||||||
|
color: var(--foreground-color);
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
.problem {
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
padding: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.problem:hover {
|
||||||
|
background-color: var(--shadow-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.problem-text {
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.problem .codicon-warning {
|
||||||
|
color: var(--warning-color);
|
||||||
|
}
|
||||||
37
examples/demo/src/index.ejs
Normal file
37
examples/demo/src/index.ejs
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="<%= require('./icon.svg') %>" />
|
||||||
|
<title>Monaco YAML</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav>
|
||||||
|
<h1>Monaco YAML</h1>
|
||||||
|
<div>
|
||||||
|
<a href="https://npmjs.com/package/monaco-yaml" class="nav-icon">
|
||||||
|
<img
|
||||||
|
alt="npm icon"
|
||||||
|
src="<%= require('@fortawesome/fontawesome-free/svgs/brands/npm.svg') %>"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<a href="https://github.com/remcohaszing/monaco-yaml" class="nav-icon">
|
||||||
|
<img
|
||||||
|
alt="GitHub icon"
|
||||||
|
src="<%= require('@fortawesome/fontawesome-free/svgs/brands/github.svg') %>"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="editor-wrapper">
|
||||||
|
<div>
|
||||||
|
<select id="schema-selection">
|
||||||
|
<option value="monaco-yaml.yaml">Monaco YAML example</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div id="breadcrumbs"></div>
|
||||||
|
<div id="editor"></div>
|
||||||
|
<div id="problems"></div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
230
examples/demo/src/index.ts
Normal file
230
examples/demo/src/index.ts
Normal file
|
|
@ -0,0 +1,230 @@
|
||||||
|
import './index.css';
|
||||||
|
|
||||||
|
import { JSONSchemaForSchemaStoreOrgCatalogFiles } from '@schemastore/schema-catalog';
|
||||||
|
import { CancellationToken } from 'monaco-editor/esm/vs/base/common/cancellation';
|
||||||
|
import { getDocumentSymbols } from 'monaco-editor/esm/vs/editor/contrib/documentSymbols/documentSymbols';
|
||||||
|
import {
|
||||||
|
editor,
|
||||||
|
Environment,
|
||||||
|
languages,
|
||||||
|
Position,
|
||||||
|
Range,
|
||||||
|
Uri,
|
||||||
|
} from 'monaco-editor/esm/vs/editor/editor.api';
|
||||||
|
import { SchemasSettings, setDiagnosticsOptions } from 'monaco-yaml';
|
||||||
|
|
||||||
|
// NOTE: This will give you all editor featues. If you would prefer to limit to only the editor
|
||||||
|
// features you want to use, import them each individually. See this example: (https://github.com/microsoft/monaco-editor-samples/blob/main/browser-esm-webpack-small/index.js#L1-L91)
|
||||||
|
import 'monaco-editor';
|
||||||
|
|
||||||
|
import defaultSchemaUri from './schema.json';
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
MonacoEnvironment: Environment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.MonacoEnvironment = {
|
||||||
|
getWorker(moduleId, label) {
|
||||||
|
switch (label) {
|
||||||
|
case 'editorWorkerService':
|
||||||
|
return new Worker(new URL('monaco-editor/esm/vs/editor/editor.worker', import.meta.url));
|
||||||
|
case 'yaml':
|
||||||
|
return new Worker(new URL('monaco-yaml/yaml.worker', import.meta.url));
|
||||||
|
default:
|
||||||
|
throw new Error(`Unknown label ${label}`);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultSchema: SchemasSettings = {
|
||||||
|
uri: defaultSchemaUri,
|
||||||
|
fileMatch: ['monaco-yaml.yaml'],
|
||||||
|
};
|
||||||
|
|
||||||
|
setDiagnosticsOptions({
|
||||||
|
schemas: [defaultSchema],
|
||||||
|
});
|
||||||
|
|
||||||
|
const value = `
|
||||||
|
# Property descriptions are displayed when hovering over properties using your cursor
|
||||||
|
property: This property has a JSON schema description
|
||||||
|
|
||||||
|
|
||||||
|
# Titles work too!
|
||||||
|
titledProperty: Titles work too!
|
||||||
|
|
||||||
|
|
||||||
|
# Even markdown descriptions work
|
||||||
|
markdown: hover me to get a markdown based description 😮
|
||||||
|
|
||||||
|
|
||||||
|
# Enums can be autocompleted by placing the cursor after the colon and pressing Ctrl+Space
|
||||||
|
enum:
|
||||||
|
|
||||||
|
|
||||||
|
# Unused anchors will be reported
|
||||||
|
unused anchor: &unused anchor
|
||||||
|
|
||||||
|
|
||||||
|
# Of course numbers are supported!
|
||||||
|
number: 12
|
||||||
|
|
||||||
|
|
||||||
|
# As well as booleans!
|
||||||
|
boolean: true
|
||||||
|
|
||||||
|
|
||||||
|
# And strings
|
||||||
|
string: I am a string
|
||||||
|
|
||||||
|
|
||||||
|
# This property is using the JSON schema recursively
|
||||||
|
reference:
|
||||||
|
boolean: Not a boolean
|
||||||
|
|
||||||
|
|
||||||
|
# Also works in arrays
|
||||||
|
array:
|
||||||
|
- string: 12
|
||||||
|
enum: Mewtwo
|
||||||
|
reference:
|
||||||
|
reference:
|
||||||
|
boolean: true
|
||||||
|
|
||||||
|
|
||||||
|
# JSON referenses can be clicked for navigation
|
||||||
|
pointer:
|
||||||
|
$ref: '#/array'
|
||||||
|
|
||||||
|
|
||||||
|
# This anchor can be referenced
|
||||||
|
anchorRef: &anchor can be clicked as well
|
||||||
|
|
||||||
|
|
||||||
|
# Press control while hovering over the anchor
|
||||||
|
anchorPointer: *anchor
|
||||||
|
|
||||||
|
|
||||||
|
formatting: Formatting is supported too! Under the hood this is powered by Prettier. Just press Ctrl+Shift+I or right click and press Format to format this document.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`.replace(/:$/m, ': ');
|
||||||
|
|
||||||
|
const ed = editor.create(document.getElementById('editor'), {
|
||||||
|
automaticLayout: true,
|
||||||
|
model: editor.createModel(value, 'yaml', Uri.parse('monaco-yaml.yaml')),
|
||||||
|
theme: window.matchMedia('(prefers-color-scheme: dark)').matches ? 'vs-dark' : 'vs-light',
|
||||||
|
});
|
||||||
|
|
||||||
|
const select = document.getElementById('schema-selection') as HTMLSelectElement;
|
||||||
|
|
||||||
|
fetch('https://www.schemastore.org/api/json/catalog.json').then(async (response) => {
|
||||||
|
if (!response.ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const catalog = (await response.json()) as JSONSchemaForSchemaStoreOrgCatalogFiles;
|
||||||
|
const schemas = [defaultSchema];
|
||||||
|
catalog.schemas.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
for (const { fileMatch, name, url } of catalog.schemas) {
|
||||||
|
const match =
|
||||||
|
typeof name === 'string' && fileMatch?.find((filename) => /\.ya?ml$/i.test(filename));
|
||||||
|
if (!match) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = match;
|
||||||
|
|
||||||
|
option.textContent = name;
|
||||||
|
select.append(option);
|
||||||
|
schemas.push({
|
||||||
|
fileMatch: [match],
|
||||||
|
uri: url,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setDiagnosticsOptions({
|
||||||
|
validate: true,
|
||||||
|
enableSchemaRequest: true,
|
||||||
|
format: true,
|
||||||
|
hover: true,
|
||||||
|
completion: true,
|
||||||
|
schemas,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
select.addEventListener('change', () => {
|
||||||
|
const oldModel = ed.getModel();
|
||||||
|
const newModel = editor.createModel(oldModel.getValue(), 'yaml', Uri.parse(select.value));
|
||||||
|
ed.setModel(newModel);
|
||||||
|
oldModel.dispose();
|
||||||
|
});
|
||||||
|
|
||||||
|
function* iterateSymbols(
|
||||||
|
symbols: languages.DocumentSymbol[],
|
||||||
|
position: Position,
|
||||||
|
): Iterable<languages.DocumentSymbol> {
|
||||||
|
for (const symbol of symbols) {
|
||||||
|
if (Range.containsPosition(symbol.range, position)) {
|
||||||
|
yield symbol;
|
||||||
|
yield* iterateSymbols(symbol.children, position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ed.onDidChangeCursorPosition(async (event) => {
|
||||||
|
const breadcrumbs = document.getElementById('breadcrumbs');
|
||||||
|
const symbols = await getDocumentSymbols(ed.getModel(), false, CancellationToken.None);
|
||||||
|
while (breadcrumbs.lastChild) {
|
||||||
|
breadcrumbs.lastChild.remove();
|
||||||
|
}
|
||||||
|
for (const symbol of iterateSymbols(symbols, event.position)) {
|
||||||
|
const breadcrumb = document.createElement('span');
|
||||||
|
breadcrumb.setAttribute('role', 'button');
|
||||||
|
breadcrumb.classList.add('breadcrumb');
|
||||||
|
breadcrumb.textContent = symbol.name;
|
||||||
|
breadcrumb.title = symbol.detail;
|
||||||
|
if (symbol.kind === languages.SymbolKind.Array) {
|
||||||
|
breadcrumb.classList.add('array');
|
||||||
|
} else if (symbol.kind === languages.SymbolKind.Module) {
|
||||||
|
breadcrumb.classList.add('object');
|
||||||
|
}
|
||||||
|
breadcrumb.addEventListener('click', () => {
|
||||||
|
ed.setPosition({
|
||||||
|
lineNumber: symbol.range.startLineNumber,
|
||||||
|
column: symbol.range.startColumn,
|
||||||
|
});
|
||||||
|
ed.focus();
|
||||||
|
});
|
||||||
|
breadcrumbs.append(breadcrumb);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
editor.onDidChangeMarkers(([resource]) => {
|
||||||
|
const problems = document.getElementById('problems');
|
||||||
|
const markers = editor.getModelMarkers({ resource });
|
||||||
|
while (problems.lastChild) {
|
||||||
|
problems.lastChild.remove();
|
||||||
|
}
|
||||||
|
for (const marker of markers) {
|
||||||
|
const wrapper = document.createElement('div');
|
||||||
|
wrapper.setAttribute('role', 'button');
|
||||||
|
const codicon = document.createElement('div');
|
||||||
|
const text = document.createElement('div');
|
||||||
|
wrapper.classList.add('problem');
|
||||||
|
codicon.classList.add('codicon', 'codicon-warning');
|
||||||
|
text.classList.add('problem-text');
|
||||||
|
text.textContent = marker.message;
|
||||||
|
wrapper.append(codicon, text);
|
||||||
|
wrapper.addEventListener('click', () => {
|
||||||
|
ed.setPosition({ lineNumber: marker.startLineNumber, column: marker.startColumn });
|
||||||
|
ed.focus();
|
||||||
|
});
|
||||||
|
problems.append(wrapper);
|
||||||
|
}
|
||||||
|
});
|
||||||
41
examples/demo/src/schema.json
Normal file
41
examples/demo/src/schema.json
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"property": {
|
||||||
|
"description": "I have a description"
|
||||||
|
},
|
||||||
|
"titledProperty": {
|
||||||
|
"title": "I have a title",
|
||||||
|
"description": "I also have a description"
|
||||||
|
},
|
||||||
|
"markdown": {
|
||||||
|
"markdownDescription": "Even **markdown** _descriptions_ `are` ~~not~~ supported!"
|
||||||
|
},
|
||||||
|
"enum": {
|
||||||
|
"description": "Pick your starter",
|
||||||
|
"enum": ["Bulbasaur", "Squirtle", "Charmander", "Pikachu"]
|
||||||
|
},
|
||||||
|
"number": {
|
||||||
|
"description": "Numbers work!",
|
||||||
|
"minimum": 42,
|
||||||
|
"maximum": 1337
|
||||||
|
},
|
||||||
|
"boolean": {
|
||||||
|
"description": "Are boolean supported?",
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"string": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"reference": {
|
||||||
|
"description": "JSON schemas can be referenced, even recursively",
|
||||||
|
"$ref": "#"
|
||||||
|
},
|
||||||
|
"array": {
|
||||||
|
"description": "It also works in arrays",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
examples/demo/src/types.d.ts
vendored
Normal file
29
examples/demo/src/types.d.ts
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
declare module 'monaco-editor/esm/vs/base/common/cancellation' {
|
||||||
|
export enum CancellationToken {
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'monaco-editor/esm/vs/editor/contrib/documentSymbols/documentSymbols' {
|
||||||
|
import { ITextModel, languages } from 'monaco-editor';
|
||||||
|
import { CancellationToken } from 'monaco-editor/esm/vs/base/common/cancellation';
|
||||||
|
|
||||||
|
export function getDocumentSymbols(
|
||||||
|
model: ITextModel,
|
||||||
|
flat: boolean,
|
||||||
|
token: CancellationToken,
|
||||||
|
): Promise<languages.DocumentSymbol[]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'monaco-editor/esm/vs/editor/editor.worker.js' {
|
||||||
|
import { worker } from 'monaco-editor/esm/vs/editor/editor.api';
|
||||||
|
|
||||||
|
export function initialize(
|
||||||
|
fn: (ctx: worker.IWorkerContext, createData: unknown) => unknown,
|
||||||
|
): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '*.json' {
|
||||||
|
declare const uri: string;
|
||||||
|
export default uri;
|
||||||
|
}
|
||||||
39
examples/demo/webpack.config.js
Normal file
39
examples/demo/webpack.config.js
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
|
||||||
|
const HtmlWebPackPlugin = require('html-webpack-plugin');
|
||||||
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
output: {
|
||||||
|
filename: '[contenthash].js',
|
||||||
|
},
|
||||||
|
devtool: 'source-map',
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.mjs', '.js', '.ts'],
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.css$/,
|
||||||
|
use: [MiniCssExtractPlugin.loader, 'css-loader'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Monaco editor uses .ttf icons.
|
||||||
|
test: /\.(svg|ttf)$/,
|
||||||
|
type: 'asset/resource',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /schema\.json$/,
|
||||||
|
type: 'asset/resource',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.ts$/,
|
||||||
|
loader: 'ts-loader',
|
||||||
|
options: { transpileOnly: true },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
optimization: {
|
||||||
|
minimizer: ['...', new CssMinimizerPlugin()],
|
||||||
|
},
|
||||||
|
plugins: [new HtmlWebPackPlugin(), new MiniCssExtractPlugin({ filename: '[contenthash].css' })],
|
||||||
|
};
|
||||||
35
examples/monaco-editor-webpack-plugin/README.md
Normal file
35
examples/monaco-editor-webpack-plugin/README.md
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
# Monaco Editor Webpack Loader Plugin Example
|
||||||
|
|
||||||
|
This demo demonstrates how bundle `monaco-editor` and `monaco-yaml` with
|
||||||
|
[monaco-editor-webpack-plugin](https://github.com/microsoft/monaco-editor/tree/main/webpack-plugin).
|
||||||
|
The build output is
|
||||||
|
[esm library](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules). Example is
|
||||||
|
based on
|
||||||
|
[link](https://github.com/microsoft/monaco-editor/tree/main/samples/browser-esm-webpack-monaco-plugin).
|
||||||
|
To start it, simply run:
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- [NodeJS](https://nodejs.org) 16 or higher
|
||||||
|
- [npm](https://github.com/npm/cli) 8.1.2 or higher
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
To run the project locally, clone the repository and set it up:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone https://github.com/remcohaszing/monaco-yaml
|
||||||
|
cd monaco-yaml
|
||||||
|
npm ci
|
||||||
|
npm run prepack
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running
|
||||||
|
|
||||||
|
To start it, simply run:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm --workspace monaco-editor-webpack-plugin-example start
|
||||||
|
```
|
||||||
|
|
||||||
|
The demo will open in your browser.
|
||||||
4
examples/monaco-editor-webpack-plugin/editor.js
Normal file
4
examples/monaco-editor-webpack-plugin/editor.js
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js';
|
||||||
|
|
||||||
|
export { setDiagnosticsOptions } from 'monaco-yaml';
|
||||||
|
export default monaco;
|
||||||
20
examples/monaco-editor-webpack-plugin/index.html
Normal file
20
examples/monaco-editor-webpack-plugin/index.html
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Monaco Editor Webpack Plugin Example</title>
|
||||||
|
<style>
|
||||||
|
.editor {
|
||||||
|
width: 800px;
|
||||||
|
height: 600px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="editor"></div>
|
||||||
|
<script src="index.js" type="module"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
37
examples/monaco-editor-webpack-plugin/index.js
Normal file
37
examples/monaco-editor-webpack-plugin/index.js
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
const value = `
|
||||||
|
number: 0xfe
|
||||||
|
boolean: true
|
||||||
|
`;
|
||||||
|
|
||||||
|
async function create() {
|
||||||
|
// Dynamic import is possible
|
||||||
|
const { default: monaco } = await import('./main.js');
|
||||||
|
|
||||||
|
// Define schema first
|
||||||
|
monaco.languages.yaml.yamlDefaults.setDiagnosticsOptions({
|
||||||
|
schemas: [
|
||||||
|
{
|
||||||
|
fileMatch: ['*'],
|
||||||
|
uri: 'my-schema.json',
|
||||||
|
schema: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
number: {
|
||||||
|
description: 'number property',
|
||||||
|
type: 'number',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create editor
|
||||||
|
monaco.editor.create(document.querySelector('.editor'), {
|
||||||
|
language: 'yaml',
|
||||||
|
tabSize: 2,
|
||||||
|
value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
create();
|
||||||
20
examples/monaco-editor-webpack-plugin/package.json
Normal file
20
examples/monaco-editor-webpack-plugin/package.json
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"name": "monaco-editor-webpack-plugin-example",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"start": "webpack serve --open --mode development",
|
||||||
|
"build": "webpack --mode production"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"css-loader": "^6.0.0",
|
||||||
|
"monaco-editor": "^0.31.0",
|
||||||
|
"monaco-editor-webpack-plugin": "^7.0.0",
|
||||||
|
"monaco-yaml": "file:../..",
|
||||||
|
"style-loader": "^3.0.0",
|
||||||
|
"webpack": "^5.0.0",
|
||||||
|
"webpack-cli": "^4.0.0",
|
||||||
|
"webpack-dev-server": "^4.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
49
examples/monaco-editor-webpack-plugin/webpack.config.js
Normal file
49
examples/monaco-editor-webpack-plugin/webpack.config.js
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
import MonacoWebpackPlugin from 'monaco-editor-webpack-plugin';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
entry: './editor.js',
|
||||||
|
output: {
|
||||||
|
filename: '[name].js',
|
||||||
|
library: {
|
||||||
|
type: 'module',
|
||||||
|
},
|
||||||
|
clean: true,
|
||||||
|
},
|
||||||
|
target: 'es2020',
|
||||||
|
experiments: {
|
||||||
|
outputModule: true,
|
||||||
|
},
|
||||||
|
devtool: 'source-map',
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.css$/,
|
||||||
|
use: ['style-loader', 'css-loader'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.ttf$/,
|
||||||
|
type: 'asset',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new MonacoWebpackPlugin({
|
||||||
|
languages: [],
|
||||||
|
customLanguages: [
|
||||||
|
{
|
||||||
|
label: 'yaml',
|
||||||
|
entry: ['monaco-yaml', 'vs/basic-languages/yaml/yaml.contribution'],
|
||||||
|
worker: {
|
||||||
|
id: 'monaco-yaml/yamlWorker',
|
||||||
|
entry: 'monaco-yaml/yaml.worker',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
devServer: {
|
||||||
|
static: {
|
||||||
|
directory: './',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
{
|
|
||||||
"presets": ["@babel/preset-env", "@babel/preset-react"]
|
|
||||||
}
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
dist/
|
|
||||||
node_modules/
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
# Demo: React + Weback + Worker Loader + Babel
|
|
||||||
|
|
||||||
To run:
|
|
||||||
```
|
|
||||||
yarn && yarn start
|
|
||||||
```
|
|
||||||
|
|
||||||
The demo will open in your browser. See (index.jsx)[index.jsx#L34-L36] for the schema loaded.
|
|
||||||
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<title>React Example</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="react"></div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
@ -1,80 +0,0 @@
|
||||||
import React, { useEffect, useState } from 'react';
|
|
||||||
import ReactDOM from 'react-dom';
|
|
||||||
import MonacoEditor from 'react-monaco-editor';
|
|
||||||
import 'monaco-yaml/esm/monaco.contribution';
|
|
||||||
import { languages } from 'monaco-editor/esm/vs/editor/editor.api';
|
|
||||||
|
|
||||||
// NOTE: This will give you all editor featues. If you would prefer to limit to only the editor
|
|
||||||
// features you want to use, import them each individually. See this example: (https://github.com/microsoft/monaco-editor-samples/blob/master/browser-esm-webpack-small/index.js#L1-L91)
|
|
||||||
import 'monaco-editor';
|
|
||||||
|
|
||||||
// NOTE: using loader syntax becuase Yaml worker imports editor.worker directly and that
|
|
||||||
// import shouldn't go through loader syntax.
|
|
||||||
import EditorWorker from 'worker-loader!monaco-editor/esm/vs/editor/editor.worker';
|
|
||||||
import YamlWorker from 'worker-loader!monaco-yaml/esm/yaml.worker';
|
|
||||||
|
|
||||||
window.MonacoEnvironment = {
|
|
||||||
getWorker(workerId, label) {
|
|
||||||
if (label === 'yaml') {
|
|
||||||
return new YamlWorker();
|
|
||||||
}
|
|
||||||
return new EditorWorker();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const { yaml } = languages || {};
|
|
||||||
|
|
||||||
const Editor = () => {
|
|
||||||
const [value, setValue] = useState('p1: ');
|
|
||||||
useEffect(() => {
|
|
||||||
yaml &&
|
|
||||||
yaml.yamlDefaults.setDiagnosticsOptions({
|
|
||||||
validate: true,
|
|
||||||
enableSchemaRequest: true,
|
|
||||||
hover: true,
|
|
||||||
completion: true,
|
|
||||||
schemas: [
|
|
||||||
{
|
|
||||||
uri: 'http://myserver/foo-schema.json', // id of the first schema
|
|
||||||
fileMatch: ['*'], // associate with our model
|
|
||||||
schema: {
|
|
||||||
id: 'http://myserver/foo-schema.json', // id of the first schema
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
p1: {
|
|
||||||
enum: ['v1', 'v2'],
|
|
||||||
},
|
|
||||||
p2: {
|
|
||||||
$ref: 'http://myserver/bar-schema.json', // reference the second schema
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
uri: 'http://myserver/bar-schema.json', // id of the first schema
|
|
||||||
schema: {
|
|
||||||
id: 'http://myserver/bar-schema.json', // id of the first schema
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
q1: {
|
|
||||||
enum: ['x1', 'x2'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<MonacoEditor
|
|
||||||
width="800"
|
|
||||||
height="600"
|
|
||||||
language="yaml"
|
|
||||||
value={value}
|
|
||||||
onChange={setValue}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
ReactDOM.render(<Editor />, document.getElementById('react'));
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
{
|
|
||||||
"name": "react-webpack-worker-loader-example",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "",
|
|
||||||
"private": true,
|
|
||||||
"main": "index.js",
|
|
||||||
"scripts": {
|
|
||||||
"start": "webpack-dev-server --open --mode development",
|
|
||||||
"build": "webpack --mode production"
|
|
||||||
},
|
|
||||||
"author": "",
|
|
||||||
"devDependencies": {
|
|
||||||
"@babel/core": "^7.8.4",
|
|
||||||
"@babel/preset-env": "^7.8.4",
|
|
||||||
"@babel/preset-react": "^7.8.3",
|
|
||||||
"babel-loader": "^8.0.6",
|
|
||||||
"css-loader": "^3.4.2",
|
|
||||||
"file-loader": "^5.1.0",
|
|
||||||
"html-webpack-plugin": "^3.2.0",
|
|
||||||
"monaco-editor": "^0.20.0",
|
|
||||||
"monaco-yaml": "^2.4.0",
|
|
||||||
"react": "^16.12.0",
|
|
||||||
"react-dom": "^16.12.0",
|
|
||||||
"react-monaco-editor": "^0.34.0",
|
|
||||||
"style-loader": "^1.1.3",
|
|
||||||
"webpack": "^4.41.6",
|
|
||||||
"webpack-cli": "^3.3.11",
|
|
||||||
"webpack-dev-server": "^3.10.3",
|
|
||||||
"worker-loader": "^2.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
const HtmlWebPackPlugin = require('html-webpack-plugin');
|
|
||||||
const path = require('path');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
entry: {
|
|
||||||
main: './index.jsx',
|
|
||||||
},
|
|
||||||
output: {
|
|
||||||
globalObject: 'this',
|
|
||||||
filename: '[name].bundle.js',
|
|
||||||
path: path.resolve(__dirname, 'dist'),
|
|
||||||
},
|
|
||||||
module: {
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
test: /\.(js|jsx)$/,
|
|
||||||
exclude: /node_modules/,
|
|
||||||
use: {
|
|
||||||
loader: 'babel-loader',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.css$/,
|
|
||||||
use: ['style-loader', 'css-loader'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.ttf$/,
|
|
||||||
loader: 'file-loader',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
new HtmlWebPackPlugin({
|
|
||||||
template: './index.html',
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
};
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,3 +0,0 @@
|
||||||
{
|
|
||||||
"presets": ["@babel/preset-env", "@babel/preset-react"]
|
|
||||||
}
|
|
||||||
2
examples/react-webpack/.gitignore
vendored
2
examples/react-webpack/.gitignore
vendored
|
|
@ -1,2 +0,0 @@
|
||||||
dist/
|
|
||||||
node_modules/
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
# Demo: React + Weback + Babel
|
|
||||||
|
|
||||||
To run:
|
|
||||||
```
|
|
||||||
yarn && yarn start
|
|
||||||
```
|
|
||||||
|
|
||||||
The demo will open in your browser. See (index.jsx)[index.jsx#L34-L36] for the schema loaded.
|
|
||||||
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<title>React Example</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="react"></div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
import React, { useEffect, useState } from 'react';
|
|
||||||
import ReactDOM from 'react-dom';
|
|
||||||
import MonacoEditor from 'react-monaco-editor';
|
|
||||||
import 'monaco-yaml/esm/monaco.contribution';
|
|
||||||
import { languages } from 'monaco-editor/esm/vs/editor/editor.api';
|
|
||||||
|
|
||||||
// NOTE: This will give you all editor featues. If you would prefer to limit to only the editor
|
|
||||||
// features you want to use, import them each individually. See this example: (https://github.com/microsoft/monaco-editor-samples/blob/master/browser-esm-webpack-small/index.js#L1-L91)
|
|
||||||
import 'monaco-editor';
|
|
||||||
|
|
||||||
window.MonacoEnvironment = {
|
|
||||||
getWorkerUrl(moduleId, label) {
|
|
||||||
if (label === 'yaml') {
|
|
||||||
return './yaml.worker.bundle.js';
|
|
||||||
}
|
|
||||||
return './editor.worker.bundle.js';
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const { yaml } = languages || {};
|
|
||||||
|
|
||||||
const Editor = () => {
|
|
||||||
const [value, setValue] = useState('p1: ');
|
|
||||||
useEffect(() => {
|
|
||||||
yaml &&
|
|
||||||
yaml.yamlDefaults.setDiagnosticsOptions({
|
|
||||||
validate: true,
|
|
||||||
enableSchemaRequest: true,
|
|
||||||
hover: true,
|
|
||||||
completion: true,
|
|
||||||
schemas: [
|
|
||||||
{
|
|
||||||
uri: 'http://myserver/foo-schema.json', // id of the first schema
|
|
||||||
fileMatch: ['*'], // associate with our model
|
|
||||||
schema: {
|
|
||||||
id: 'http://myserver/foo-schema.json', // id of the first schema
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
p1: {
|
|
||||||
enum: ['v1', 'v2'],
|
|
||||||
},
|
|
||||||
p2: {
|
|
||||||
$ref: 'http://myserver/bar-schema.json', // reference the second schema
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
uri: 'http://myserver/bar-schema.json', // id of the first schema
|
|
||||||
schema: {
|
|
||||||
id: 'http://myserver/bar-schema.json', // id of the first schema
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
q1: {
|
|
||||||
enum: ['x1', 'x2'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<MonacoEditor
|
|
||||||
width="800"
|
|
||||||
height="600"
|
|
||||||
language="yaml"
|
|
||||||
value={value}
|
|
||||||
onChange={setValue}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
ReactDOM.render(<Editor />, document.getElementById('react'));
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
{
|
|
||||||
"name": "react-webpack-example",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "",
|
|
||||||
"private": true,
|
|
||||||
"main": "index.js",
|
|
||||||
"scripts": {
|
|
||||||
"start": "webpack-dev-server --open --mode development",
|
|
||||||
"build": "webpack --mode production"
|
|
||||||
},
|
|
||||||
"author": "",
|
|
||||||
"devDependencies": {
|
|
||||||
"@babel/core": "^7.8.4",
|
|
||||||
"@babel/preset-env": "^7.8.4",
|
|
||||||
"@babel/preset-react": "^7.8.3",
|
|
||||||
"babel-loader": "^8.0.6",
|
|
||||||
"css-loader": "^3.4.2",
|
|
||||||
"file-loader": "^5.1.0",
|
|
||||||
"html-webpack-plugin": "^3.2.0",
|
|
||||||
"monaco-editor": "^0.20.0",
|
|
||||||
"monaco-yaml": "^2.4.0",
|
|
||||||
"react": "^16.12.0",
|
|
||||||
"react-dom": "^16.12.0",
|
|
||||||
"react-monaco-editor": "^0.34.0",
|
|
||||||
"style-loader": "^1.1.3",
|
|
||||||
"webpack": "^4.41.6",
|
|
||||||
"webpack-cli": "^3.3.11",
|
|
||||||
"webpack-dev-server": "^3.10.3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
const HtmlWebPackPlugin = require('html-webpack-plugin');
|
|
||||||
const path = require('path');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
entry: {
|
|
||||||
main: './index.jsx',
|
|
||||||
'editor.worker': 'monaco-editor/esm/vs/editor/editor.worker.js',
|
|
||||||
'yaml.worker': 'monaco-yaml/esm/yaml.worker.js',
|
|
||||||
},
|
|
||||||
output: {
|
|
||||||
globalObject: 'this',
|
|
||||||
filename: '[name].bundle.js',
|
|
||||||
path: path.resolve(__dirname, 'dist'),
|
|
||||||
},
|
|
||||||
module: {
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
test: /\.(js|jsx)$/,
|
|
||||||
exclude: /node_modules/,
|
|
||||||
use: {
|
|
||||||
loader: 'babel-loader',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.css$/,
|
|
||||||
use: ['style-loader', 'css-loader'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.ttf$/,
|
|
||||||
loader: 'file-loader',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
new HtmlWebPackPlugin({
|
|
||||||
template: './index.html',
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
};
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,162 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
||||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
|
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
data-name="vs/editor/editor.main"
|
|
||||||
href="../node_modules/monaco-editor-core/dev/vs/editor/editor.main.css"
|
|
||||||
/>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<h2>Monaco Editor YAML test page</h2>
|
|
||||||
<code id="path"></code>
|
|
||||||
<div
|
|
||||||
id="container"
|
|
||||||
style="width:800px;height:600px;border:1px solid grey"
|
|
||||||
></div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.x-highlight-range {
|
|
||||||
background-color: lightblue;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// Loading basic-languages to get the YAML language definition
|
|
||||||
var paths = {
|
|
||||||
'vs/basic-languages': '../node_modules/monaco-languages/release/dev',
|
|
||||||
'vs/language/yaml': '../lib/dev',
|
|
||||||
vs: '../node_modules/monaco-editor-core/dev/vs',
|
|
||||||
prettier: '../node_modules/prettier',
|
|
||||||
};
|
|
||||||
if (document.location.protocol === 'http:') {
|
|
||||||
// Add support for running local http server
|
|
||||||
let testIndex = document.location.pathname.indexOf('/test/');
|
|
||||||
if (testIndex !== -1) {
|
|
||||||
let prefix = document.location.pathname.substr(0, testIndex);
|
|
||||||
paths['vs/language/yaml'] = prefix + '/lib/dev';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var require = {
|
|
||||||
paths: paths,
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
<script src="../node_modules/monaco-editor-core/dev/vs/loader.js"></script>
|
|
||||||
<script src="../node_modules/monaco-editor-core/dev/vs/editor/editor.main.nls.js"></script>
|
|
||||||
<script src="../node_modules/monaco-editor-core/dev/vs/editor/editor.main.js"></script>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
require([
|
|
||||||
'vs/basic-languages/monaco.contribution',
|
|
||||||
'vs/language/yaml/monaco.contribution',
|
|
||||||
'prettier/standalone',
|
|
||||||
'prettier/parser-yaml',
|
|
||||||
], function() {
|
|
||||||
const yaml = `p1: `;
|
|
||||||
const modelUri = monaco.Uri.parse('a://b/foo.json');
|
|
||||||
const editor = monaco.editor.create(
|
|
||||||
document.getElementById('container'),
|
|
||||||
{
|
|
||||||
language: 'yaml',
|
|
||||||
showFoldingControls: 'always',
|
|
||||||
model: monaco.editor.createModel(yaml, 'yaml', modelUri),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
monaco.languages.yaml.yamlDefaults.setDiagnosticsOptions({
|
|
||||||
enableSchemaRequest: true,
|
|
||||||
hover: true,
|
|
||||||
completion: true,
|
|
||||||
validate: true,
|
|
||||||
format: true,
|
|
||||||
schemas: [
|
|
||||||
{
|
|
||||||
uri: 'http://myserver/foo-schema.json', // id of the first schema
|
|
||||||
fileMatch: [modelUri.toString()], // associate with our model
|
|
||||||
schema: {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
p1: {
|
|
||||||
enum: ['v1', 'v2'],
|
|
||||||
},
|
|
||||||
p2: {
|
|
||||||
$ref: 'http://myserver/bar-schema.json', // reference the second schema
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
uri: 'http://myserver/bar-schema.json', // id of the first schema
|
|
||||||
schema: {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
q1: {
|
|
||||||
enum: ['x1', 'x2'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
require(['vs/editor/contrib/quickOpen/quickOpen'], async quickOpen => {
|
|
||||||
const NEVER_CANCEL_TOKEN = {
|
|
||||||
isCancellationRequested: false,
|
|
||||||
onCancellationRequested: () => Event.NONE,
|
|
||||||
};
|
|
||||||
|
|
||||||
let oldDecorations = [];
|
|
||||||
|
|
||||||
async function _getSymbolForPosition(model, position) {
|
|
||||||
const symbols = await quickOpen.getDocumentSymbols(
|
|
||||||
model,
|
|
||||||
false,
|
|
||||||
NEVER_CANCEL_TOKEN
|
|
||||||
);
|
|
||||||
|
|
||||||
function _recur(symbol) {
|
|
||||||
let target = symbol;
|
|
||||||
if (symbol && symbol.children && symbol.children.length) {
|
|
||||||
target =
|
|
||||||
_recur(
|
|
||||||
symbol.children.find(child =>
|
|
||||||
child.range.containsPosition(position)
|
|
||||||
)
|
|
||||||
) || symbol;
|
|
||||||
}
|
|
||||||
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
|
|
||||||
return _recur({ children: symbols });
|
|
||||||
}
|
|
||||||
|
|
||||||
editor.onDidChangeCursorSelection(async ({ selection }) => {
|
|
||||||
const model = editor.getModel();
|
|
||||||
const position = selection.getPosition();
|
|
||||||
const symbol = await _getSymbolForPosition(model, position);
|
|
||||||
|
|
||||||
console.log(`${symbol.name}: ${symbol.range}`);
|
|
||||||
if (symbol && symbol.range) {
|
|
||||||
const decoration = {
|
|
||||||
range: symbol.range,
|
|
||||||
options: {
|
|
||||||
isWholeLine: false,
|
|
||||||
className: 'x-highlight-range',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
oldDecorations = editor.deltaDecorations(
|
|
||||||
oldDecorations,
|
|
||||||
decoration ? [decoration] : []
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
29
examples/vite-example/README.md
Normal file
29
examples/vite-example/README.md
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
# Vite Example
|
||||||
|
|
||||||
|
This minimal example shows how `monaco-yaml` can be used with [Vite](https://vitejs.dev).
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- [NodeJS](https://nodejs.org) 16 or higher
|
||||||
|
- [npm](https://github.com/npm/cli) 8.1.2 or higher
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
To run the project locally, clone the repository and set it up:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone https://github.com/remcohaszing/monaco-yaml
|
||||||
|
cd monaco-yaml
|
||||||
|
npm ci
|
||||||
|
npm run prepack
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running
|
||||||
|
|
||||||
|
To start it, simply run:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm --workspace vite-example start
|
||||||
|
```
|
||||||
|
|
||||||
|
The demo will be available on http://localhost:3000.
|
||||||
11
examples/vite-example/index.html
Normal file
11
examples/vite-example/index.html
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Monaco YAML</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="editor" style="width: 800px; height: 600px;"></div>
|
||||||
|
<script type="module" src="/index.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
67
examples/vite-example/index.js
Normal file
67
examples/vite-example/index.js
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
import { editor, Uri } from 'monaco-editor';
|
||||||
|
import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
|
||||||
|
import { setDiagnosticsOptions } from 'monaco-yaml';
|
||||||
|
import YamlWorker from 'monaco-yaml/yaml.worker?worker';
|
||||||
|
|
||||||
|
window.MonacoEnvironment = {
|
||||||
|
getWorker(moduleId, label) {
|
||||||
|
switch (label) {
|
||||||
|
case 'editorWorkerService':
|
||||||
|
return new EditorWorker();
|
||||||
|
case 'yaml':
|
||||||
|
return new YamlWorker();
|
||||||
|
default:
|
||||||
|
throw new Error(`Unknown label ${label}`);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// The uri is used for the schema file match.
|
||||||
|
const modelUri = Uri.parse('a://b/foo.yaml');
|
||||||
|
|
||||||
|
setDiagnosticsOptions({
|
||||||
|
enableSchemaRequest: true,
|
||||||
|
hover: true,
|
||||||
|
completion: true,
|
||||||
|
validate: true,
|
||||||
|
format: true,
|
||||||
|
schemas: [
|
||||||
|
{
|
||||||
|
// Id of the first schema
|
||||||
|
uri: 'http://myserver/foo-schema.json',
|
||||||
|
// Associate with our model
|
||||||
|
fileMatch: [String(modelUri)],
|
||||||
|
schema: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
p1: {
|
||||||
|
enum: ['v1', 'v2'],
|
||||||
|
},
|
||||||
|
p2: {
|
||||||
|
// Reference the second schema
|
||||||
|
$ref: 'http://myserver/bar-schema.json',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Id of the first schema
|
||||||
|
uri: 'http://myserver/bar-schema.json',
|
||||||
|
schema: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
q1: {
|
||||||
|
enum: ['x1', 'x2'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const value = 'p1: \np2: \n';
|
||||||
|
|
||||||
|
editor.create(document.getElementById('editor'), {
|
||||||
|
automaticLayout: true,
|
||||||
|
model: editor.createModel(value, 'yaml', modelUri),
|
||||||
|
});
|
||||||
14
examples/vite-example/package.json
Normal file
14
examples/vite-example/package.json
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"name": "vite-example",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"start": "vite",
|
||||||
|
"build": "vite build"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"monaco-editor": "^0.31.0",
|
||||||
|
"monaco-yaml": "file:../..",
|
||||||
|
"vite": "^2.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
112
index.d.ts
vendored
Normal file
112
index.d.ts
vendored
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
import { JSONSchema4, JSONSchema6, JSONSchema7 } from 'json-schema';
|
||||||
|
import { IEvent, languages } from 'monaco-editor/esm/vs/editor/editor.api';
|
||||||
|
|
||||||
|
export interface SchemasSettings {
|
||||||
|
/**
|
||||||
|
* A `Uri` file match which will trigger the schema validation. This may be a glob or an exact
|
||||||
|
* path.
|
||||||
|
*
|
||||||
|
* @example '.gitlab-ci.yml'
|
||||||
|
* @example 'file://**\/.github/actions/*.yaml'
|
||||||
|
*/
|
||||||
|
fileMatch: string[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The JSON schema which will be used for validation. If not specified, it will be downloaded from
|
||||||
|
* `uri`.
|
||||||
|
*/
|
||||||
|
schema?: JSONSchema4 | JSONSchema6 | JSONSchema7;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The source URI of the JSON schema. The JSON schema will be downloaded from here if no schema
|
||||||
|
* was supplied. It will also be displayed as the source in hover tooltips.
|
||||||
|
*/
|
||||||
|
uri: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'monaco-editor/esm/vs/editor/editor.api' {
|
||||||
|
namespace languages.yaml {
|
||||||
|
export interface DiagnosticsOptions {
|
||||||
|
/**
|
||||||
|
* If set, enable schema based autocompletion.
|
||||||
|
*
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
|
readonly completion?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of custom tags.
|
||||||
|
*
|
||||||
|
* @default []
|
||||||
|
*/
|
||||||
|
readonly customTags?: string[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set, the schema service would load schema content on-demand with 'fetch' if available
|
||||||
|
*
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
readonly enableSchemaRequest?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If true, formatting using Prettier is enabled. Setting this to `false` does **not** exclude
|
||||||
|
* Prettier from the bundle.
|
||||||
|
*
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
|
readonly format?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set, enable hover typs based the JSON schema.
|
||||||
|
*
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
|
readonly hover?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If true, a different diffing algorithm is used to generate error messages.
|
||||||
|
*
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
readonly isKubernetes?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of known schemas and/or associations of schemas to file names.
|
||||||
|
*
|
||||||
|
* @default []
|
||||||
|
*/
|
||||||
|
readonly schemas?: SchemasSettings[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set, the validator will be enabled and perform syntax validation as well as schema
|
||||||
|
* based validation.
|
||||||
|
*
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
|
readonly validate?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The YAML version to use for parsing.
|
||||||
|
*
|
||||||
|
* @default '1.2'
|
||||||
|
*/
|
||||||
|
readonly yamlVersion?: '1.1' | '1.2';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LanguageServiceDefaults {
|
||||||
|
readonly onDidChange: IEvent<LanguageServiceDefaults>;
|
||||||
|
readonly languageId: string;
|
||||||
|
readonly diagnosticsOptions: DiagnosticsOptions;
|
||||||
|
setDiagnosticsOptions: (options: DiagnosticsOptions) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const yamlDefaults: LanguageServiceDefaults;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure `monaco-yaml` diagnostics options.
|
||||||
|
*
|
||||||
|
* @param options - The options to set.
|
||||||
|
*/
|
||||||
|
export function setDiagnosticsOptions(options?: languages.yaml.DiagnosticsOptions): void;
|
||||||
3
netlify.toml
Normal file
3
netlify.toml
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
[build]
|
||||||
|
publish = 'examples/demo/dist/'
|
||||||
|
command = 'npm ci && npm run prepack && npm --workspace demo run build'
|
||||||
8500
package-lock.json
generated
Normal file
8500
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
125
package.json
125
package.json
|
|
@ -1,104 +1,71 @@
|
||||||
{
|
{
|
||||||
"name": "monaco-yaml",
|
"name": "monaco-yaml",
|
||||||
"version": "2.4.1",
|
"version": "4.0.0-alpha.1",
|
||||||
"description": "YAML plugin for the Monaco Editor",
|
"description": "YAML plugin for the Monaco Editor",
|
||||||
|
"homepage": "https://monaco-yaml.js.org",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"watch": "tsc -p ./src --watch",
|
"prepack": "node build.js",
|
||||||
"compile": "rimraf ./out && yarn compile:umd && yarn compile:esm",
|
"prepare": "husky install"
|
||||||
"compile:umd": "tsc -p ./src/tsconfig.json",
|
|
||||||
"compile:esm": "tsc -p ./src/tsconfig.esm.json",
|
|
||||||
"bundle": "rimraf ./lib && yarn bundle:umd && yarn bundle:esm && mcopy ./src/monaco.d.ts ./lib/monaco.d.ts",
|
|
||||||
"bundle:umd": "node ./scripts/bundle-umd",
|
|
||||||
"bundle:esm": "node ./scripts/bundle-esm",
|
|
||||||
"build": "yarn compile && yarn bundle",
|
|
||||||
"prepare": "yarn build",
|
|
||||||
"lint": "prettier \"{src,test}/**/*.{json,scss,html,ts}\" --write",
|
|
||||||
"test": "jest --verbose"
|
|
||||||
},
|
|
||||||
"main": "./lib/esm/monaco.contribution.js",
|
|
||||||
"module": "./lib/esm/monaco.contribution.js",
|
|
||||||
"typings": "./lib/monaco.d.ts",
|
|
||||||
"directories": {
|
|
||||||
"lib": "./lib"
|
|
||||||
},
|
},
|
||||||
|
"typings": "./index.d.ts",
|
||||||
|
"files": [
|
||||||
|
"index.js",
|
||||||
|
"index.d.ts",
|
||||||
|
"yaml.worker.js"
|
||||||
|
],
|
||||||
|
"workspaces": [
|
||||||
|
"examples/*"
|
||||||
|
],
|
||||||
"author": "Kevin Decker <kpdecker@gmail.com> (http://incaseofstairs.com)",
|
"author": "Kevin Decker <kpdecker@gmail.com> (http://incaseofstairs.com)",
|
||||||
"maintainers": [
|
"maintainers": [
|
||||||
"kpdecker",
|
"Remco Haszing <remcohaszing@gmail.com> (https://github.com/remcohaszing)"
|
||||||
"pengx17"
|
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/pengx17/monaco-yaml"
|
"url": "https://github.com/remcohaszing/monaco-yaml"
|
||||||
},
|
},
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/pengx17/monaco-yaml/issues"
|
"url": "https://github.com/remcohaszing/monaco-yaml/issues"
|
||||||
},
|
|
||||||
"optionalDependencies": {
|
|
||||||
"prettier": "^1.19.1"
|
|
||||||
},
|
},
|
||||||
|
"keywords": [
|
||||||
|
"editor",
|
||||||
|
"frontend",
|
||||||
|
"front-end",
|
||||||
|
"monaco",
|
||||||
|
"monaco-editor",
|
||||||
|
"yaml"
|
||||||
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"js-yaml": "^3.12.0",
|
"@types/json-schema": "^7.0.0",
|
||||||
"vscode-json-languageservice": "^3.4.11",
|
"jsonc-parser": "^3.0.0",
|
||||||
"vscode-languageserver": "^5.2.1",
|
"path-browserify": "^1.0.0",
|
||||||
"vscode-uri": "^2.1.1"
|
"prettier": "2.0.5",
|
||||||
|
"vscode-languageserver-textdocument": "^1.0.0",
|
||||||
|
"vscode-languageserver-types": "^3.0.0",
|
||||||
|
"yaml": "2.0.0-10"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"monaco-editor": "^0.19.2"
|
"monaco-editor": ">=0.30"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "^23.3.10",
|
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
||||||
"@types/node": "^10.9.3",
|
"esbuild": "^0.14.0",
|
||||||
"husky": "^1.2.1",
|
"eslint": "^7.0.0",
|
||||||
"jest": "^23.6.0",
|
"eslint-config-remcohaszing": "^3.0.0",
|
||||||
"jsonc-parser": "^2.0.2",
|
"husky": "^7.0.0",
|
||||||
"lint-staged": "^8.1.0",
|
"lint-staged": "^12.0.0",
|
||||||
"monaco-editor": "^0.16.2",
|
"monaco-editor": "^0.31.0",
|
||||||
"monaco-editor-core": "0.16.1",
|
"type-fest": "^2.0.0",
|
||||||
"monaco-languages": "1.6.0",
|
"typescript": "^4.0.0",
|
||||||
"monaco-plugin-helpers": "^1.0.2",
|
"yaml-language-server": "^1.0.0"
|
||||||
"prettier": "^1.19.1",
|
|
||||||
"request-light": "^0.2.5",
|
|
||||||
"requirejs": "^2.3.5",
|
|
||||||
"rimraf": "^2.6.2",
|
|
||||||
"ts-jest": "^23.10.5",
|
|
||||||
"typescript": "^3.7.4",
|
|
||||||
"uglify-es": "^3.3.9",
|
|
||||||
"vscode-languageserver-types": "3.12.0"
|
|
||||||
},
|
|
||||||
"prettier": {
|
|
||||||
"singleQuote": true,
|
|
||||||
"trailingComma": "es5",
|
|
||||||
"semi": true
|
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"linters": {
|
"*.{css,json,md,html,yaml}": [
|
||||||
"*.{json,scss,html,ts,js,jsx}": [
|
"prettier --write"
|
||||||
"prettier --write",
|
|
||||||
"git add"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"husky": {
|
|
||||||
"hooks": {
|
|
||||||
"pre-commit": "lint-staged"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"jest": {
|
|
||||||
"globals": {
|
|
||||||
"ts-jest": {
|
|
||||||
"tsConfig": "./test/tsconfig.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"moduleFileExtensions": [
|
|
||||||
"js",
|
|
||||||
"ts"
|
|
||||||
],
|
],
|
||||||
"transform": {
|
"*.{js,ts}": [
|
||||||
"^.+\\.(ts|tsx)$": "ts-jest"
|
"eslint"
|
||||||
},
|
|
||||||
"testMatch": [
|
|
||||||
"**/test/*.test.+(ts|js)"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
const path = require("path");
|
|
||||||
const helpers = require("monaco-plugin-helpers");
|
|
||||||
|
|
||||||
const REPO_ROOT = path.join(__dirname, "../");
|
|
||||||
|
|
||||||
helpers.packageESM({
|
|
||||||
repoRoot: REPO_ROOT,
|
|
||||||
esmSource: "out/esm",
|
|
||||||
esmDestination: "lib/esm",
|
|
||||||
entryPoints: ["monaco.contribution.js", "yamlMode.js", "yaml.worker.js"],
|
|
||||||
resolveAlias: {
|
|
||||||
"vscode-nls": path.join(REPO_ROOT, "out/esm/fillers/vscode-nls.js"),
|
|
||||||
"vscode-json-languageservice/lib/umd/services/jsonValidation": path.join(REPO_ROOT, 'node_modules/vscode-json-languageservice/lib/esm/services/jsonValidation.js'),
|
|
||||||
"vscode-json-languageservice": path.join(REPO_ROOT, "node_modules/vscode-json-languageservice/lib/esm/jsonLanguageService.js"),
|
|
||||||
"vscode-json-languageservice/lib/umd/services/jsonHover": path.join(REPO_ROOT, 'node_modules/vscode-json-languageservice/lib/esm/services/jsonHover.js'),
|
|
||||||
"vscode-json-languageservice/lib/umd/services/jsonDocumentSymbols": path.join(REPO_ROOT, 'node_modules/vscode-json-languageservice/lib/esm/services/jsonDocumentSymbols.js'),
|
|
||||||
"vscode-json-languageservice/lib/umd/services/jsonSchemaService": path.join(REPO_ROOT, 'node_modules/vscode-json-languageservice/lib/esm/services/jsonSchemaService.js'),
|
|
||||||
},
|
|
||||||
resolveSkip: ["monaco-editor", "monaco-editor-core", "js-yaml"],
|
|
||||||
destinationFolderSimplification: {
|
|
||||||
node_modules: "_deps",
|
|
||||||
"jsonc-parser/lib/esm": "jsonc-parser",
|
|
||||||
"vscode-languageserver-types/lib/esm": "vscode-languageserver-types",
|
|
||||||
"vscode-uri/lib/esm": "vscode-uri",
|
|
||||||
// "vscode-json-languageservice/lib/umd": "vscode-json-languageservice/lib/esm",
|
|
||||||
// "js-yaml/dist": "js-yaml"
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
@ -1,92 +0,0 @@
|
||||||
const requirejs = require('requirejs');
|
|
||||||
const path = require('path');
|
|
||||||
const fs = require('fs');
|
|
||||||
const UglifyES = require("uglify-es");
|
|
||||||
const helpers = require('monaco-plugin-helpers');
|
|
||||||
|
|
||||||
const REPO_ROOT = path.resolve(__dirname, '..');
|
|
||||||
|
|
||||||
const sha1 = helpers.getGitVersion(REPO_ROOT);
|
|
||||||
const semver = require('../package.json').version;
|
|
||||||
const headerVersion = semver + '(' + sha1 + ')';
|
|
||||||
|
|
||||||
const BUNDLED_FILE_HEADER = [
|
|
||||||
'/*!-----------------------------------------------------------------------------',
|
|
||||||
' * Copyright (c) Microsoft Corporation. All rights reserved.',
|
|
||||||
' * monaco-yaml version: ' + headerVersion,
|
|
||||||
' * Released under the MIT license',
|
|
||||||
' * https://github.com/kpdecker/monaco-yaml/blob/master/LICENSE.md',
|
|
||||||
' *-----------------------------------------------------------------------------*/',
|
|
||||||
''
|
|
||||||
].join('\n');
|
|
||||||
|
|
||||||
bundleOne('monaco.contribution');
|
|
||||||
bundleOne('yamlMode');
|
|
||||||
bundleOne('yamlWorker');
|
|
||||||
|
|
||||||
function bundleOne(moduleId, exclude) {
|
|
||||||
requirejs.optimize({
|
|
||||||
baseUrl: 'out/amd/',
|
|
||||||
name: 'vs/language/yaml/' + moduleId,
|
|
||||||
out: 'lib/dev/' + moduleId + '.js',
|
|
||||||
exclude: exclude,
|
|
||||||
paths: {
|
|
||||||
'vs/language/yaml': REPO_ROOT + '/out/amd'
|
|
||||||
},
|
|
||||||
optimize: 'none',
|
|
||||||
packages: [
|
|
||||||
{
|
|
||||||
name: 'js-yaml',
|
|
||||||
location: path.join(REPO_ROOT, 'node_modules/js-yaml/dist'),
|
|
||||||
main: 'js-yaml'
|
|
||||||
},
|
|
||||||
// The following is required by YAML language service
|
|
||||||
{
|
|
||||||
name: 'vscode-json-languageservice',
|
|
||||||
location: path.join(REPO_ROOT, 'node_modules/vscode-json-languageservice'),
|
|
||||||
main: 'lib/umd/jsonLanguageService'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'vscode-languageserver-textdocument',
|
|
||||||
location: path.join(REPO_ROOT, 'node_modules/vscode-languageserver-textdocument'),
|
|
||||||
main: 'lib/umd/main'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'yaml-ast-parser-custom-tags',
|
|
||||||
location: path.join(REPO_ROOT, 'node_modules/yaml-ast-parser-custom-tags'),
|
|
||||||
main: 'dist/src/index'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'jsonc-parser',
|
|
||||||
location: path.join(REPO_ROOT, 'node_modules/jsonc-parser/lib/umd'),
|
|
||||||
main: 'main'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'vscode-languageserver-types',
|
|
||||||
location: path.join(REPO_ROOT, 'node_modules/vscode-languageserver-types/lib/umd'),
|
|
||||||
main: 'main'
|
|
||||||
}, {
|
|
||||||
name: 'vscode-uri',
|
|
||||||
location: path.join(REPO_ROOT, 'node_modules/vscode-uri/lib/umd'),
|
|
||||||
main: 'index'
|
|
||||||
}, {
|
|
||||||
name: 'vscode-nls',
|
|
||||||
location: path.join(REPO_ROOT, '/out/amd/fillers'),
|
|
||||||
main: 'vscode-nls'
|
|
||||||
}]
|
|
||||||
}, function () {
|
|
||||||
const devFilePath = path.join(REPO_ROOT, 'lib/dev/' + moduleId + '.js');
|
|
||||||
const minFilePath = path.join(REPO_ROOT, 'lib/min/' + moduleId + '.js');
|
|
||||||
const fileContents = fs.readFileSync(devFilePath).toString();
|
|
||||||
console.log();
|
|
||||||
console.log(`Minifying ${devFilePath}...`);
|
|
||||||
const result = UglifyES.minify(fileContents, {
|
|
||||||
output: {
|
|
||||||
comments: 'some'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
console.log(`Done.`);
|
|
||||||
try { fs.mkdirSync(path.join(REPO_ROOT, 'lib/min')) } catch (err) { }
|
|
||||||
fs.writeFileSync(minFilePath, BUNDLED_FILE_HEADER + result.code);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
1
src/constants.ts
Normal file
1
src/constants.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export const languageId = 'yaml';
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
export const EOL = '\n';
|
|
||||||
5
src/fillers/schemaSelectionHandlers.ts
Normal file
5
src/fillers/schemaSelectionHandlers.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
/**
|
||||||
|
* This is a stub for `monaco-yaml/lib/esm/schemaSelectionHandlers.js`.
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
|
export function JSONSchemaSelection(): void {}
|
||||||
|
|
@ -1,8 +1,3 @@
|
||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
export interface Options {
|
export interface Options {
|
||||||
locale?: string;
|
locale?: string;
|
||||||
cacheLanguageResolution?: boolean;
|
cacheLanguageResolution?: boolean;
|
||||||
|
|
@ -11,38 +6,30 @@ export interface LocalizeInfo {
|
||||||
key: string;
|
key: string;
|
||||||
comment: string[];
|
comment: string[];
|
||||||
}
|
}
|
||||||
export interface LocalizeFunc {
|
export type LocalizeFunc = (
|
||||||
(info: LocalizeInfo, message: string, ...args: any[]): string;
|
info: LocalizeInfo | string,
|
||||||
(key: string, message: string, ...args: any[]): string;
|
message: string,
|
||||||
}
|
...args: unknown[]
|
||||||
|
) => string;
|
||||||
export type LoadFunc = (file?: string) => LocalizeFunc;
|
export type LoadFunc = (file?: string) => LocalizeFunc;
|
||||||
|
|
||||||
function format(message: string, args: any[]): string {
|
function format(message: string, args: string[]): string {
|
||||||
let result: string;
|
return args.length === 0
|
||||||
|
? message
|
||||||
if (args.length === 0) {
|
: message.replace(/{(\d+)}/g, (match, rest: number[]) => {
|
||||||
result = message;
|
const [index] = rest;
|
||||||
} else {
|
return typeof args[index] === 'undefined' ? match : args[index];
|
||||||
result = message.replace(/\{(\d+)\}/g, (match, rest) => {
|
|
||||||
const index = rest[0];
|
|
||||||
return typeof args[index] !== 'undefined' ? args[index] : match;
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function localize(
|
function localize(key: LocalizeInfo | string, message: string, ...args: string[]): string {
|
||||||
key: string | LocalizeInfo,
|
|
||||||
message: string,
|
|
||||||
...args: any[]
|
|
||||||
): string {
|
|
||||||
return format(message, args);
|
return format(message, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loadMessageBundle(file?: string): LocalizeFunc {
|
export function loadMessageBundle(): LocalizeFunc {
|
||||||
return localize;
|
return localize;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function config(opt?: Options | string): LoadFunc {
|
export function config(): LoadFunc {
|
||||||
return loadMessageBundle;
|
return loadMessageBundle;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
78
src/index.ts
Normal file
78
src/index.ts
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
import { Emitter, languages } from 'monaco-editor/esm/vs/editor/editor.api.js';
|
||||||
|
|
||||||
|
import { languageId } from './constants';
|
||||||
|
import { setupMode } from './yamlMode';
|
||||||
|
|
||||||
|
// --- YAML configuration and defaults ---------
|
||||||
|
|
||||||
|
const diagnosticDefault: languages.yaml.DiagnosticsOptions = {
|
||||||
|
completion: true,
|
||||||
|
customTags: [],
|
||||||
|
enableSchemaRequest: false,
|
||||||
|
format: true,
|
||||||
|
isKubernetes: false,
|
||||||
|
hover: true,
|
||||||
|
schemas: [],
|
||||||
|
validate: true,
|
||||||
|
yamlVersion: '1.2',
|
||||||
|
};
|
||||||
|
|
||||||
|
export function createLanguageServiceDefaults(
|
||||||
|
initialDiagnosticsOptions: languages.yaml.DiagnosticsOptions,
|
||||||
|
): languages.yaml.LanguageServiceDefaults {
|
||||||
|
const onDidChange = new Emitter<languages.yaml.LanguageServiceDefaults>();
|
||||||
|
let diagnosticsOptions = initialDiagnosticsOptions;
|
||||||
|
|
||||||
|
const languageServiceDefaults: languages.yaml.LanguageServiceDefaults = {
|
||||||
|
get onDidChange() {
|
||||||
|
return onDidChange.event;
|
||||||
|
},
|
||||||
|
|
||||||
|
get languageId() {
|
||||||
|
return languageId;
|
||||||
|
},
|
||||||
|
|
||||||
|
get diagnosticsOptions() {
|
||||||
|
return diagnosticsOptions;
|
||||||
|
},
|
||||||
|
|
||||||
|
setDiagnosticsOptions(options) {
|
||||||
|
diagnosticsOptions = { ...diagnosticDefault, ...options };
|
||||||
|
onDidChange.fire(languageServiceDefaults);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return languageServiceDefaults;
|
||||||
|
}
|
||||||
|
|
||||||
|
const yamlDefaults = createLanguageServiceDefaults(diagnosticDefault);
|
||||||
|
|
||||||
|
// Export API
|
||||||
|
function createAPI(): typeof languages.yaml {
|
||||||
|
return {
|
||||||
|
yamlDefaults,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
languages.yaml = createAPI();
|
||||||
|
|
||||||
|
// --- Registration to monaco editor ---
|
||||||
|
|
||||||
|
languages.register({
|
||||||
|
id: languageId,
|
||||||
|
extensions: ['.yaml', '.yml'],
|
||||||
|
aliases: ['YAML', 'yaml', 'YML', 'yml'],
|
||||||
|
mimetypes: ['application/x-yaml'],
|
||||||
|
});
|
||||||
|
|
||||||
|
languages.onLanguage('yaml', () => {
|
||||||
|
setupMode(yamlDefaults);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure `monaco-yaml` diagnostics options.
|
||||||
|
*
|
||||||
|
* @param options - The options to set.
|
||||||
|
*/
|
||||||
|
export function setDiagnosticsOptions(options: languages.yaml.DiagnosticsOptions = {}): void {
|
||||||
|
languages.yaml.yamlDefaults.setDiagnosticsOptions(options);
|
||||||
|
}
|
||||||
|
|
@ -1,149 +1,49 @@
|
||||||
/*---------------------------------------------------------------------------------------------
|
import {
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
editor,
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
IDisposable,
|
||||||
*--------------------------------------------------------------------------------------------*/
|
languages,
|
||||||
'use strict';
|
MarkerSeverity,
|
||||||
|
MarkerTag,
|
||||||
|
Position,
|
||||||
|
Range,
|
||||||
|
Uri,
|
||||||
|
} from 'monaco-editor/esm/vs/editor/editor.api.js';
|
||||||
|
import * as ls from 'vscode-languageserver-types';
|
||||||
|
import { CustomFormatterOptions } from 'yaml-language-server/lib/esm/languageservice/yamlLanguageService.js';
|
||||||
|
|
||||||
import { LanguageServiceDefaultsImpl } from './monaco.contribution';
|
import { languageId } from './constants';
|
||||||
import { YAMLWorker } from './yamlWorker';
|
import { YAMLWorker } from './yamlWorker';
|
||||||
|
|
||||||
import * as ls from 'vscode-languageserver-types';
|
export type WorkerAccessor = (...more: Uri[]) => PromiseLike<YAMLWorker>;
|
||||||
|
|
||||||
import Uri = monaco.Uri;
|
|
||||||
import Position = monaco.Position;
|
|
||||||
import Range = monaco.Range;
|
|
||||||
import IRange = monaco.IRange;
|
|
||||||
import Thenable = monaco.Thenable;
|
|
||||||
import CancellationToken = monaco.CancellationToken;
|
|
||||||
import IDisposable = monaco.IDisposable;
|
|
||||||
import { CustomFormatterOptions } from './languageservice/yamlLanguageService';
|
|
||||||
|
|
||||||
export type WorkerAccessor = (...more: Uri[]) => Thenable<YAMLWorker>;
|
|
||||||
|
|
||||||
// --- diagnostics --- ---
|
// --- diagnostics --- ---
|
||||||
|
|
||||||
export class DiagnosticsAdapter {
|
function toSeverity(lsSeverity: ls.DiagnosticSeverity): MarkerSeverity {
|
||||||
private _disposables: IDisposable[] = [];
|
|
||||||
private _listener: { [uri: string]: IDisposable } = Object.create(null);
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private _languageId: string,
|
|
||||||
private _worker: WorkerAccessor,
|
|
||||||
defaults: LanguageServiceDefaultsImpl
|
|
||||||
) {
|
|
||||||
const onModelAdd = (model: monaco.editor.IModel): void => {
|
|
||||||
const modeId = model.getModeId();
|
|
||||||
if (modeId !== this._languageId) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let handle: NodeJS.Timer;
|
|
||||||
this._listener[model.uri.toString()] = model.onDidChangeContent(() => {
|
|
||||||
clearTimeout(handle);
|
|
||||||
handle = setTimeout(() => this._doValidate(model.uri, modeId), 500);
|
|
||||||
});
|
|
||||||
|
|
||||||
this._doValidate(model.uri, modeId);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onModelRemoved = (model: monaco.editor.IModel): void => {
|
|
||||||
monaco.editor.setModelMarkers(model, this._languageId, []);
|
|
||||||
const uriStr = model.uri.toString();
|
|
||||||
const listener = this._listener[uriStr];
|
|
||||||
if (listener) {
|
|
||||||
listener.dispose();
|
|
||||||
delete this._listener[uriStr];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this._disposables.push(monaco.editor.onDidCreateModel(onModelAdd));
|
|
||||||
this._disposables.push(
|
|
||||||
monaco.editor.onWillDisposeModel(model => {
|
|
||||||
onModelRemoved(model);
|
|
||||||
this._resetSchema(model.uri);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
this._disposables.push(
|
|
||||||
monaco.editor.onDidChangeModelLanguage(event => {
|
|
||||||
onModelRemoved(event.model);
|
|
||||||
onModelAdd(event.model);
|
|
||||||
this._resetSchema(event.model.uri);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
this._disposables.push(
|
|
||||||
defaults.onDidChange(_ => {
|
|
||||||
monaco.editor.getModels().forEach(model => {
|
|
||||||
if (model.getModeId() === this._languageId) {
|
|
||||||
onModelRemoved(model);
|
|
||||||
onModelAdd(model);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
this._disposables.push({
|
|
||||||
dispose: () => {
|
|
||||||
monaco.editor.getModels().forEach(onModelRemoved);
|
|
||||||
for (const key in this._listener) {
|
|
||||||
this._listener[key].dispose();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
monaco.editor.getModels().forEach(onModelAdd);
|
|
||||||
}
|
|
||||||
|
|
||||||
public dispose(): void {
|
|
||||||
this._disposables.forEach(d => d && d.dispose());
|
|
||||||
this._disposables = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
private _resetSchema(resource: Uri): void {
|
|
||||||
this._worker().then(worker => {
|
|
||||||
worker.resetSchema(resource.toString());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private _doValidate(resource: Uri, languageId: string): void {
|
|
||||||
this._worker(resource)
|
|
||||||
.then(worker => {
|
|
||||||
return worker.doValidation(resource.toString()).then(diagnostics => {
|
|
||||||
const markers = diagnostics.map(d => toDiagnostics(resource, d));
|
|
||||||
const model = monaco.editor.getModel(resource);
|
|
||||||
if (model.getModeId() === languageId) {
|
|
||||||
monaco.editor.setModelMarkers(model, languageId, markers);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.then(undefined, err => {
|
|
||||||
console.error(err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function toSeverity(lsSeverity: number): monaco.MarkerSeverity {
|
|
||||||
switch (lsSeverity) {
|
switch (lsSeverity) {
|
||||||
case ls.DiagnosticSeverity.Error:
|
case ls.DiagnosticSeverity.Error:
|
||||||
return monaco.MarkerSeverity.Error;
|
return MarkerSeverity.Error;
|
||||||
case ls.DiagnosticSeverity.Warning:
|
case ls.DiagnosticSeverity.Warning:
|
||||||
return monaco.MarkerSeverity.Warning;
|
return MarkerSeverity.Warning;
|
||||||
case ls.DiagnosticSeverity.Information:
|
case ls.DiagnosticSeverity.Information:
|
||||||
return monaco.MarkerSeverity.Info;
|
return MarkerSeverity.Info;
|
||||||
case ls.DiagnosticSeverity.Hint:
|
case ls.DiagnosticSeverity.Hint:
|
||||||
return monaco.MarkerSeverity.Hint;
|
return MarkerSeverity.Hint;
|
||||||
default:
|
default:
|
||||||
return monaco.MarkerSeverity.Info;
|
return MarkerSeverity.Info;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function toDiagnostics(
|
function toMarkerDataTag(tag: ls.DiagnosticTag): MarkerTag {
|
||||||
resource: Uri,
|
switch (tag) {
|
||||||
diag: ls.Diagnostic
|
case ls.DiagnosticTag.Deprecated:
|
||||||
): monaco.editor.IMarkerData {
|
return MarkerTag.Deprecated;
|
||||||
const code =
|
case ls.DiagnosticTag.Unnecessary:
|
||||||
typeof diag.code === 'number' ? String(diag.code) : (diag.code as string);
|
return MarkerTag.Unnecessary;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toDiagnostics(diag: ls.Diagnostic): editor.IMarkerData {
|
||||||
return {
|
return {
|
||||||
severity: toSeverity(diag.severity),
|
severity: toSeverity(diag.severity),
|
||||||
startLineNumber: diag.range.start.line + 1,
|
startLineNumber: diag.range.start.line + 1,
|
||||||
|
|
@ -151,48 +51,109 @@ function toDiagnostics(
|
||||||
endLineNumber: diag.range.end.line + 1,
|
endLineNumber: diag.range.end.line + 1,
|
||||||
endColumn: diag.range.end.character + 1,
|
endColumn: diag.range.end.character + 1,
|
||||||
message: diag.message,
|
message: diag.message,
|
||||||
code,
|
code: String(diag.code),
|
||||||
source: diag.source,
|
source: diag.source,
|
||||||
|
tags: diag.tags?.map(toMarkerDataTag),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createDiagnosticsAdapter(
|
||||||
|
getWorker: WorkerAccessor,
|
||||||
|
defaults: languages.yaml.LanguageServiceDefaults,
|
||||||
|
): void {
|
||||||
|
const listeners = new Map<string, IDisposable>();
|
||||||
|
|
||||||
|
const resetSchema = async (resource: Uri): Promise<void> => {
|
||||||
|
const worker = await getWorker();
|
||||||
|
worker.resetSchema(String(resource));
|
||||||
|
};
|
||||||
|
|
||||||
|
const doValidate = async (resource: Uri): Promise<void> => {
|
||||||
|
const worker = await getWorker(resource);
|
||||||
|
const diagnostics = await worker.doValidation(String(resource));
|
||||||
|
const markers = diagnostics.map(toDiagnostics);
|
||||||
|
const model = editor.getModel(resource);
|
||||||
|
// Return value from getModel can be null if model not found
|
||||||
|
// (e.g. if user navigates away from editor)
|
||||||
|
if (model && model.getLanguageId() === languageId) {
|
||||||
|
editor.setModelMarkers(model, languageId, markers);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onModelAdd = (model: editor.IModel): void => {
|
||||||
|
if (model.getLanguageId() !== languageId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let handle: ReturnType<typeof setTimeout>;
|
||||||
|
listeners.set(
|
||||||
|
String(model.uri),
|
||||||
|
model.onDidChangeContent(() => {
|
||||||
|
clearTimeout(handle);
|
||||||
|
handle = setTimeout(() => doValidate(model.uri), 500);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
doValidate(model.uri);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onModelRemoved = (model: editor.IModel): void => {
|
||||||
|
editor.setModelMarkers(model, languageId, []);
|
||||||
|
const uriStr = String(model.uri);
|
||||||
|
const listener = listeners.get(uriStr);
|
||||||
|
if (listener) {
|
||||||
|
listener.dispose();
|
||||||
|
listeners.delete(uriStr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
editor.onDidCreateModel(onModelAdd);
|
||||||
|
editor.onWillDisposeModel((model) => {
|
||||||
|
onModelRemoved(model);
|
||||||
|
resetSchema(model.uri);
|
||||||
|
});
|
||||||
|
editor.onDidChangeModelLanguage((event) => {
|
||||||
|
onModelRemoved(event.model);
|
||||||
|
onModelAdd(event.model);
|
||||||
|
resetSchema(event.model.uri);
|
||||||
|
});
|
||||||
|
defaults.onDidChange(() => {
|
||||||
|
for (const model of editor.getModels()) {
|
||||||
|
if (model.getLanguageId() === languageId) {
|
||||||
|
onModelRemoved(model);
|
||||||
|
onModelAdd(model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const model of editor.getModels()) {
|
||||||
|
onModelAdd(model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// --- completion ------
|
// --- completion ------
|
||||||
|
|
||||||
function fromPosition(position: Position): ls.Position {
|
function fromPosition(position: Position): ls.Position {
|
||||||
if (!position) {
|
if (!position) {
|
||||||
return void 0;
|
return;
|
||||||
}
|
}
|
||||||
return { character: position.column - 1, line: position.lineNumber - 1 };
|
return { character: position.column - 1, line: position.lineNumber - 1 };
|
||||||
}
|
}
|
||||||
|
|
||||||
function fromRange(range: IRange): ls.Range {
|
|
||||||
if (!range) {
|
|
||||||
return void 0;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
start: {
|
|
||||||
line: range.startLineNumber - 1,
|
|
||||||
character: range.startColumn - 1,
|
|
||||||
},
|
|
||||||
end: { line: range.endLineNumber - 1, character: range.endColumn - 1 },
|
|
||||||
};
|
|
||||||
}
|
|
||||||
function toRange(range: ls.Range): Range {
|
function toRange(range: ls.Range): Range {
|
||||||
if (!range) {
|
if (!range) {
|
||||||
return void 0;
|
return;
|
||||||
}
|
}
|
||||||
return new Range(
|
return new Range(
|
||||||
range.start.line + 1,
|
range.start.line + 1,
|
||||||
range.start.character + 1,
|
range.start.character + 1,
|
||||||
range.end.line + 1,
|
range.end.line + 1,
|
||||||
range.end.character + 1
|
range.end.character + 1,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function toCompletionItemKind(
|
function toCompletionItemKind(kind: ls.CompletionItemKind): languages.CompletionItemKind {
|
||||||
kind: number
|
const mItemKind = languages.CompletionItemKind;
|
||||||
): monaco.languages.CompletionItemKind {
|
|
||||||
const mItemKind = monaco.languages.CompletionItemKind;
|
|
||||||
|
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
case ls.CompletionItemKind.Text:
|
case ls.CompletionItemKind.Text:
|
||||||
|
|
@ -231,59 +192,14 @@ function toCompletionItemKind(
|
||||||
return mItemKind.File;
|
return mItemKind.File;
|
||||||
case ls.CompletionItemKind.Reference:
|
case ls.CompletionItemKind.Reference:
|
||||||
return mItemKind.Reference;
|
return mItemKind.Reference;
|
||||||
}
|
default:
|
||||||
return mItemKind.Property;
|
return mItemKind.Property;
|
||||||
}
|
|
||||||
|
|
||||||
function fromCompletionItemKind(
|
|
||||||
kind: monaco.languages.CompletionItemKind
|
|
||||||
): ls.CompletionItemKind {
|
|
||||||
const mItemKind = monaco.languages.CompletionItemKind;
|
|
||||||
|
|
||||||
switch (kind) {
|
|
||||||
case mItemKind.Text:
|
|
||||||
return ls.CompletionItemKind.Text;
|
|
||||||
case mItemKind.Method:
|
|
||||||
return ls.CompletionItemKind.Method;
|
|
||||||
case mItemKind.Function:
|
|
||||||
return ls.CompletionItemKind.Function;
|
|
||||||
case mItemKind.Constructor:
|
|
||||||
return ls.CompletionItemKind.Constructor;
|
|
||||||
case mItemKind.Field:
|
|
||||||
return ls.CompletionItemKind.Field;
|
|
||||||
case mItemKind.Variable:
|
|
||||||
return ls.CompletionItemKind.Variable;
|
|
||||||
case mItemKind.Class:
|
|
||||||
return ls.CompletionItemKind.Class;
|
|
||||||
case mItemKind.Interface:
|
|
||||||
return ls.CompletionItemKind.Interface;
|
|
||||||
case mItemKind.Module:
|
|
||||||
return ls.CompletionItemKind.Module;
|
|
||||||
case mItemKind.Property:
|
|
||||||
return ls.CompletionItemKind.Property;
|
|
||||||
case mItemKind.Unit:
|
|
||||||
return ls.CompletionItemKind.Unit;
|
|
||||||
case mItemKind.Value:
|
|
||||||
return ls.CompletionItemKind.Value;
|
|
||||||
case mItemKind.Enum:
|
|
||||||
return ls.CompletionItemKind.Enum;
|
|
||||||
case mItemKind.Keyword:
|
|
||||||
return ls.CompletionItemKind.Keyword;
|
|
||||||
case mItemKind.Snippet:
|
|
||||||
return ls.CompletionItemKind.Snippet;
|
|
||||||
case mItemKind.Color:
|
|
||||||
return ls.CompletionItemKind.Color;
|
|
||||||
case mItemKind.File:
|
|
||||||
return ls.CompletionItemKind.File;
|
|
||||||
case mItemKind.Reference:
|
|
||||||
return ls.CompletionItemKind.Reference;
|
|
||||||
}
|
}
|
||||||
return ls.CompletionItemKind.Property;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function toTextEdit(textEdit: ls.TextEdit): monaco.editor.ISingleEditOperation {
|
function toTextEdit(textEdit: ls.TextEdit): editor.ISingleEditOperation {
|
||||||
if (!textEdit) {
|
if (!textEdit) {
|
||||||
return void 0;
|
return;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
range: toRange(textEdit.range),
|
range: toRange(textEdit.range),
|
||||||
|
|
@ -291,34 +207,31 @@ function toTextEdit(textEdit: ls.TextEdit): monaco.editor.ISingleEditOperation {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CompletionAdapter
|
export function createCompletionItemProvider(
|
||||||
implements monaco.languages.CompletionItemProvider {
|
getWorker: WorkerAccessor,
|
||||||
constructor(private _worker: WorkerAccessor) {}
|
): languages.CompletionItemProvider {
|
||||||
|
return {
|
||||||
|
triggerCharacters: [' ', ':'],
|
||||||
|
|
||||||
public get triggerCharacters(): string[] {
|
async provideCompletionItems(model, position) {
|
||||||
return [' ', ':'];
|
|
||||||
}
|
|
||||||
|
|
||||||
public provideCompletionItems(
|
|
||||||
model: monaco.editor.IReadOnlyModel,
|
|
||||||
position: Position,
|
|
||||||
context: monaco.languages.CompletionContext,
|
|
||||||
token: CancellationToken
|
|
||||||
): Thenable<monaco.languages.CompletionList> {
|
|
||||||
const wordInfo = model.getWordUntilPosition(position);
|
|
||||||
const resource = model.uri;
|
const resource = model.uri;
|
||||||
|
|
||||||
return this._worker(resource)
|
const worker = await getWorker(resource);
|
||||||
.then(worker => {
|
const info = await worker.doComplete(String(resource), fromPosition(position));
|
||||||
return worker.doComplete(resource.toString(), fromPosition(position));
|
|
||||||
})
|
|
||||||
.then(info => {
|
|
||||||
if (!info) {
|
if (!info) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const items: monaco.languages.CompletionItem[] = info.items.map(
|
|
||||||
entry => {
|
const wordInfo = model.getWordUntilPosition(position);
|
||||||
const item: monaco.languages.CompletionItem = {
|
const wordRange = new Range(
|
||||||
|
position.lineNumber,
|
||||||
|
wordInfo.startColumn,
|
||||||
|
position.lineNumber,
|
||||||
|
wordInfo.endColumn,
|
||||||
|
);
|
||||||
|
|
||||||
|
const items = info.items.map((entry) => {
|
||||||
|
const item: languages.CompletionItem = {
|
||||||
label: entry.label,
|
label: entry.label,
|
||||||
insertText: entry.insertText || entry.label,
|
insertText: entry.insertText || entry.label,
|
||||||
sortText: entry.sortText,
|
sortText: entry.sortText,
|
||||||
|
|
@ -326,111 +239,75 @@ export class CompletionAdapter
|
||||||
documentation: entry.documentation,
|
documentation: entry.documentation,
|
||||||
detail: entry.detail,
|
detail: entry.detail,
|
||||||
kind: toCompletionItemKind(entry.kind),
|
kind: toCompletionItemKind(entry.kind),
|
||||||
range: {
|
range: wordRange,
|
||||||
startLineNumber: position.lineNumber,
|
|
||||||
endLineNumber: position.lineNumber,
|
|
||||||
...wordInfo,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
if (entry.textEdit) {
|
if (entry.textEdit) {
|
||||||
item.range = toRange(entry.textEdit.range);
|
item.range = toRange(
|
||||||
|
'range' in entry.textEdit ? entry.textEdit.range : entry.textEdit.replace,
|
||||||
|
);
|
||||||
item.insertText = entry.textEdit.newText;
|
item.insertText = entry.textEdit.newText;
|
||||||
}
|
}
|
||||||
if (entry.additionalTextEdits) {
|
if (entry.additionalTextEdits) {
|
||||||
item.additionalTextEdits = entry.additionalTextEdits.map(
|
item.additionalTextEdits = entry.additionalTextEdits.map(toTextEdit);
|
||||||
toTextEdit
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (entry.insertTextFormat === ls.InsertTextFormat.Snippet) {
|
if (entry.insertTextFormat === ls.InsertTextFormat.Snippet) {
|
||||||
item.insertTextRules =
|
item.insertTextRules = languages.CompletionItemInsertTextRule.InsertAsSnippet;
|
||||||
monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet;
|
|
||||||
}
|
}
|
||||||
return item;
|
return item;
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isIncomplete: info.isIncomplete,
|
incomplete: info.isIncomplete,
|
||||||
suggestions: items,
|
suggestions: items,
|
||||||
};
|
};
|
||||||
});
|
},
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function isMarkupContent(thing: any): thing is ls.MarkupContent {
|
// --- definition ------
|
||||||
return (
|
|
||||||
thing &&
|
|
||||||
typeof thing === 'object' &&
|
|
||||||
typeof (thing as ls.MarkupContent).kind === 'string'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function toMarkdownString(
|
export function createDefinitionProvider(getWorker: WorkerAccessor): languages.DefinitionProvider {
|
||||||
entry: ls.MarkupContent | ls.MarkedString
|
|
||||||
): monaco.IMarkdownString {
|
|
||||||
if (typeof entry === 'string') {
|
|
||||||
return {
|
return {
|
||||||
value: entry,
|
async provideDefinition(model, position) {
|
||||||
};
|
const resource = model.uri;
|
||||||
}
|
|
||||||
if (isMarkupContent(entry)) {
|
|
||||||
if (entry.kind === 'plaintext') {
|
|
||||||
return {
|
|
||||||
value: entry.value.replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&'),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
value: entry.value,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return { value: '```' + entry.language + '\n' + entry.value + '\n```\n' };
|
const worker = await getWorker(resource);
|
||||||
}
|
const definitions = await worker.doDefinition(String(resource), fromPosition(position));
|
||||||
|
|
||||||
function toMarkedStringArray(
|
return definitions?.map((definition) => ({
|
||||||
contents: ls.MarkupContent | ls.MarkedString | ls.MarkedString[]
|
originSelectionRange: definition.originSelectionRange,
|
||||||
): monaco.IMarkdownString[] {
|
range: toRange(definition.targetRange),
|
||||||
if (!contents) {
|
targetSelectionRange: definition.targetSelectionRange,
|
||||||
return void 0;
|
uri: Uri.parse(definition.targetUri),
|
||||||
}
|
}));
|
||||||
if (Array.isArray(contents)) {
|
},
|
||||||
return contents.map(toMarkdownString);
|
};
|
||||||
}
|
|
||||||
return [toMarkdownString(contents)];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- hover ------
|
// --- hover ------
|
||||||
|
|
||||||
export class HoverAdapter implements monaco.languages.HoverProvider {
|
export function createHoverProvider(getWorker: WorkerAccessor): languages.HoverProvider {
|
||||||
constructor(private _worker: WorkerAccessor) {}
|
return {
|
||||||
|
async provideHover(model, position) {
|
||||||
public provideHover(
|
|
||||||
model: monaco.editor.IReadOnlyModel,
|
|
||||||
position: Position,
|
|
||||||
token: CancellationToken
|
|
||||||
): Thenable<monaco.languages.Hover> {
|
|
||||||
const resource = model.uri;
|
const resource = model.uri;
|
||||||
|
|
||||||
return this._worker(resource)
|
const worker = await getWorker(resource);
|
||||||
.then(worker => {
|
const info = await worker.doHover(String(resource), fromPosition(position));
|
||||||
return worker.doHover(resource.toString(), fromPosition(position));
|
|
||||||
})
|
|
||||||
.then(info => {
|
|
||||||
if (!info) {
|
if (!info) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
range: toRange(info.range),
|
range: toRange(info.range),
|
||||||
contents: toMarkedStringArray(info.contents),
|
contents: [{ value: (info.contents as ls.MarkupContent).value }],
|
||||||
} as monaco.languages.Hover;
|
};
|
||||||
});
|
},
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- document symbols ------
|
// --- document symbols ------
|
||||||
|
|
||||||
function toSymbolKind(kind: ls.SymbolKind): monaco.languages.SymbolKind {
|
function toSymbolKind(kind: ls.SymbolKind): languages.SymbolKind {
|
||||||
const mKind = monaco.languages.SymbolKind;
|
const mKind = languages.SymbolKind;
|
||||||
|
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
case ls.SymbolKind.File:
|
case ls.SymbolKind.File:
|
||||||
|
|
@ -469,47 +346,43 @@ function toSymbolKind(kind: ls.SymbolKind): monaco.languages.SymbolKind {
|
||||||
return mKind.Boolean;
|
return mKind.Boolean;
|
||||||
case ls.SymbolKind.Array:
|
case ls.SymbolKind.Array:
|
||||||
return mKind.Array;
|
return mKind.Array;
|
||||||
}
|
default:
|
||||||
return mKind.Function;
|
return mKind.Function;
|
||||||
}
|
|
||||||
|
|
||||||
export class DocumentSymbolAdapter
|
|
||||||
implements monaco.languages.DocumentSymbolProvider {
|
|
||||||
constructor(private _worker: WorkerAccessor) {}
|
|
||||||
|
|
||||||
public provideDocumentSymbols(
|
|
||||||
model: monaco.editor.IReadOnlyModel,
|
|
||||||
token: CancellationToken
|
|
||||||
): Thenable<monaco.languages.DocumentSymbol[]> {
|
|
||||||
const resource = model.uri;
|
|
||||||
|
|
||||||
return this._worker(resource)
|
|
||||||
.then(worker => worker.findDocumentSymbols(resource.toString()))
|
|
||||||
.then(items => {
|
|
||||||
if (!items) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return items.map(item => toDocumentSymbol(item));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function toDocumentSymbol(
|
function toDocumentSymbol(item: ls.DocumentSymbol): languages.DocumentSymbol {
|
||||||
item: ls.DocumentSymbol
|
|
||||||
): monaco.languages.DocumentSymbol {
|
|
||||||
return {
|
return {
|
||||||
detail: '',
|
detail: item.detail || '',
|
||||||
range: toRange(item.range),
|
range: toRange(item.range),
|
||||||
name: item.name,
|
name: item.name,
|
||||||
kind: toSymbolKind(item.kind),
|
kind: toSymbolKind(item.kind),
|
||||||
selectionRange: toRange(item.selectionRange),
|
selectionRange: toRange(item.selectionRange),
|
||||||
children: item.children.map(child => toDocumentSymbol(child)),
|
children: item.children.map(toDocumentSymbol),
|
||||||
|
tags: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createDocumentSymbolProvider(
|
||||||
|
getWorker: WorkerAccessor,
|
||||||
|
): languages.DocumentSymbolProvider {
|
||||||
|
return {
|
||||||
|
async provideDocumentSymbols(model) {
|
||||||
|
const resource = model.uri;
|
||||||
|
|
||||||
|
const worker = await getWorker(resource);
|
||||||
|
const items = await worker.findDocumentSymbols(String(resource));
|
||||||
|
if (!items) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return items.map(toDocumentSymbol);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function fromFormattingOptions(
|
function fromFormattingOptions(
|
||||||
options: monaco.languages.FormattingOptions
|
options: languages.FormattingOptions,
|
||||||
): ls.FormattingOptions & CustomFormatterOptions {
|
): CustomFormatterOptions & ls.FormattingOptions {
|
||||||
return {
|
return {
|
||||||
tabSize: options.tabSize,
|
tabSize: options.tabSize,
|
||||||
insertSpaces: options.insertSpaces,
|
insertSpaces: options.insertSpaces,
|
||||||
|
|
@ -517,115 +390,42 @@ function fromFormattingOptions(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DocumentFormattingEditProvider
|
export function createDocumentFormattingEditProvider(
|
||||||
implements monaco.languages.DocumentFormattingEditProvider {
|
getWorker: WorkerAccessor,
|
||||||
constructor(private _worker: WorkerAccessor) {}
|
): languages.DocumentFormattingEditProvider {
|
||||||
|
return {
|
||||||
public provideDocumentFormattingEdits(
|
async provideDocumentFormattingEdits(model, options) {
|
||||||
model: monaco.editor.IReadOnlyModel,
|
|
||||||
options: monaco.languages.FormattingOptions,
|
|
||||||
token: CancellationToken
|
|
||||||
): Thenable<monaco.editor.ISingleEditOperation[]> {
|
|
||||||
const resource = model.uri;
|
const resource = model.uri;
|
||||||
|
|
||||||
return this._worker(resource).then(worker => {
|
const worker = await getWorker(resource);
|
||||||
return worker
|
const edits = await worker.format(String(resource), fromFormattingOptions(options));
|
||||||
.format(resource.toString(), null, fromFormattingOptions(options))
|
|
||||||
.then(edits => {
|
|
||||||
if (!edits || edits.length === 0) {
|
if (!edits || edits.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return edits.map(toTextEdit);
|
return edits.map(toTextEdit);
|
||||||
});
|
},
|
||||||
});
|
};
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DocumentRangeFormattingEditProvider
|
function toLink(link: ls.DocumentLink): languages.ILink {
|
||||||
implements monaco.languages.DocumentRangeFormattingEditProvider {
|
return {
|
||||||
constructor(private _worker: WorkerAccessor) {}
|
range: toRange(link.range),
|
||||||
|
tooltip: link.tooltip,
|
||||||
|
url: link.target,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public provideDocumentRangeFormattingEdits(
|
export function createLinkProvider(getWorker: WorkerAccessor): languages.LinkProvider {
|
||||||
model: monaco.editor.IReadOnlyModel,
|
return {
|
||||||
range: Range,
|
async provideLinks(model) {
|
||||||
options: monaco.languages.FormattingOptions,
|
|
||||||
token: CancellationToken
|
|
||||||
): Thenable<monaco.editor.ISingleEditOperation[]> {
|
|
||||||
const resource = model.uri;
|
const resource = model.uri;
|
||||||
|
|
||||||
return this._worker(resource).then(worker => {
|
const worker = await getWorker(resource);
|
||||||
return worker
|
const links = await worker.findLinks(String(resource));
|
||||||
.format(
|
|
||||||
resource.toString(),
|
return {
|
||||||
fromRange(range),
|
links: links.map(toLink),
|
||||||
fromFormattingOptions(options)
|
};
|
||||||
)
|
},
|
||||||
.then(edits => {
|
};
|
||||||
if (!edits || edits.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return edits.map(toTextEdit);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// export class DocumentColorAdapter
|
|
||||||
// implements monaco.languages.DocumentColorProvider {
|
|
||||||
// constructor(private _worker: WorkerAccessor) {}
|
|
||||||
|
|
||||||
// public provideDocumentColors(
|
|
||||||
// model: monaco.editor.IReadOnlyModel,
|
|
||||||
// token: CancellationToken
|
|
||||||
// ): Thenable<monaco.languages.IColorInformation[]> {
|
|
||||||
// const resource = model.uri;
|
|
||||||
|
|
||||||
// return this._worker(resource)
|
|
||||||
// .then(worker => worker.findDocumentColors(resource.toString()))
|
|
||||||
// .then(infos => {
|
|
||||||
// if (!infos) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// return infos.map(item => ({
|
|
||||||
// color: item.color,
|
|
||||||
// range: toRange(item.range),
|
|
||||||
// }));
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public provideColorPresentations(
|
|
||||||
// model: monaco.editor.IReadOnlyModel,
|
|
||||||
// info: monaco.languages.IColorInformation,
|
|
||||||
// token: CancellationToken
|
|
||||||
// ): Thenable<monaco.languages.IColorPresentation[]> {
|
|
||||||
// const resource = model.uri;
|
|
||||||
|
|
||||||
// return this._worker(resource)
|
|
||||||
// .then(worker =>
|
|
||||||
// worker.getColorPresentations(
|
|
||||||
// resource.toString(),
|
|
||||||
// info.color,
|
|
||||||
// fromRange(info.range)
|
|
||||||
// )
|
|
||||||
// )
|
|
||||||
// .then(presentations => {
|
|
||||||
// if (!presentations) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// return presentations.map(presentation => {
|
|
||||||
// const item: monaco.languages.IColorPresentation = {
|
|
||||||
// label: presentation.label,
|
|
||||||
// };
|
|
||||||
// if (presentation.textEdit) {
|
|
||||||
// item.textEdit = toTextEdit(presentation.textEdit);
|
|
||||||
// }
|
|
||||||
// if (presentation.additionalTextEdits) {
|
|
||||||
// item.additionalTextEdits = presentation.additionalTextEdits.map(
|
|
||||||
// toTextEdit
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// return item;
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
|
||||||
|
|
@ -1,63 +0,0 @@
|
||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
export type ASTNode =
|
|
||||||
| ObjectASTNode
|
|
||||||
| PropertyASTNode
|
|
||||||
| ArrayASTNode
|
|
||||||
| StringASTNode
|
|
||||||
| NumberASTNode
|
|
||||||
| BooleanASTNode
|
|
||||||
| NullASTNode;
|
|
||||||
|
|
||||||
export interface BaseASTNode {
|
|
||||||
readonly type:
|
|
||||||
| 'object'
|
|
||||||
| 'array'
|
|
||||||
| 'property'
|
|
||||||
| 'string'
|
|
||||||
| 'number'
|
|
||||||
| 'boolean'
|
|
||||||
| 'null';
|
|
||||||
readonly parent?: ASTNode;
|
|
||||||
readonly offset: number;
|
|
||||||
readonly length: number;
|
|
||||||
readonly children?: ASTNode[];
|
|
||||||
readonly value?: string | boolean | number | null;
|
|
||||||
}
|
|
||||||
export interface ObjectASTNode extends BaseASTNode {
|
|
||||||
readonly type: 'object';
|
|
||||||
readonly properties: PropertyASTNode[];
|
|
||||||
readonly children: ASTNode[];
|
|
||||||
}
|
|
||||||
export interface PropertyASTNode extends BaseASTNode {
|
|
||||||
readonly type: 'property';
|
|
||||||
readonly keyNode: StringASTNode;
|
|
||||||
readonly valueNode?: ASTNode;
|
|
||||||
readonly colonOffset?: number;
|
|
||||||
readonly children: ASTNode[];
|
|
||||||
}
|
|
||||||
export interface ArrayASTNode extends BaseASTNode {
|
|
||||||
readonly type: 'array';
|
|
||||||
readonly items: ASTNode[];
|
|
||||||
readonly children: ASTNode[];
|
|
||||||
}
|
|
||||||
export interface StringASTNode extends BaseASTNode {
|
|
||||||
readonly type: 'string';
|
|
||||||
readonly value: string;
|
|
||||||
}
|
|
||||||
export interface NumberASTNode extends BaseASTNode {
|
|
||||||
readonly type: 'number';
|
|
||||||
readonly value: number;
|
|
||||||
readonly isInteger: boolean;
|
|
||||||
}
|
|
||||||
export interface BooleanASTNode extends BaseASTNode {
|
|
||||||
readonly type: 'boolean';
|
|
||||||
readonly value: boolean;
|
|
||||||
}
|
|
||||||
export interface NullASTNode extends BaseASTNode {
|
|
||||||
readonly type: 'null';
|
|
||||||
readonly value: null;
|
|
||||||
}
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Red Hat, Inc. All rights reserved.
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
export interface JSONSchema {
|
|
||||||
id?: string;
|
|
||||||
$schema?: string;
|
|
||||||
type?: string | string[];
|
|
||||||
title?: string;
|
|
||||||
// tslint:disable-next-line: no-any
|
|
||||||
default?: any;
|
|
||||||
definitions?: JSONSchemaMap;
|
|
||||||
description?: string;
|
|
||||||
properties?: JSONSchemaMap;
|
|
||||||
patternProperties?: JSONSchemaMap;
|
|
||||||
// tslint:disable-next-line: no-any
|
|
||||||
additionalProperties?: any;
|
|
||||||
minProperties?: number;
|
|
||||||
maxProperties?: number;
|
|
||||||
dependencies?: JSONSchemaMap | string[];
|
|
||||||
// tslint:disable-next-line: no-any
|
|
||||||
items?: any;
|
|
||||||
minItems?: number;
|
|
||||||
maxItems?: number;
|
|
||||||
uniqueItems?: boolean;
|
|
||||||
additionalItems?: boolean;
|
|
||||||
pattern?: string;
|
|
||||||
minLength?: number;
|
|
||||||
maxLength?: number;
|
|
||||||
minimum?: number;
|
|
||||||
maximum?: number;
|
|
||||||
exclusiveMinimum?: boolean;
|
|
||||||
exclusiveMaximum?: boolean;
|
|
||||||
multipleOf?: number;
|
|
||||||
required?: string[];
|
|
||||||
$ref?: string;
|
|
||||||
anyOf?: JSONSchema[];
|
|
||||||
allOf?: JSONSchema[];
|
|
||||||
oneOf?: JSONSchema[];
|
|
||||||
not?: JSONSchema;
|
|
||||||
// tslint:disable-next-line: no-any
|
|
||||||
enum?: any[];
|
|
||||||
format?: string;
|
|
||||||
errorMessage?: string; // VSCode extension
|
|
||||||
patternErrorMessage?: string; // VSCode extension
|
|
||||||
deprecationMessage?: string; // VSCode extension
|
|
||||||
enumDescriptions?: string[]; // VSCode extension
|
|
||||||
|
|
||||||
schemaSequence?: JSONSchema[]; // extension for multiple schemas related to multiple documents in single yaml file
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface JSONSchemaMap {
|
|
||||||
[name: string]: JSONSchema;
|
|
||||||
}
|
|
||||||
|
|
@ -1,85 +0,0 @@
|
||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
export type JSONSchemaRef = JSONSchema | boolean;
|
|
||||||
|
|
||||||
export interface JSONSchema {
|
|
||||||
id?: string;
|
|
||||||
$id?: string;
|
|
||||||
$schema?: string;
|
|
||||||
type?: string | string[];
|
|
||||||
title?: string;
|
|
||||||
// tslint:disable-next-line: no-any
|
|
||||||
default?: any;
|
|
||||||
definitions?: { [name: string]: JSONSchema };
|
|
||||||
description?: string;
|
|
||||||
properties?: JSONSchemaMap;
|
|
||||||
patternProperties?: JSONSchemaMap;
|
|
||||||
additionalProperties?: boolean | JSONSchemaRef;
|
|
||||||
minProperties?: number;
|
|
||||||
maxProperties?: number;
|
|
||||||
dependencies?: JSONSchemaMap | { [prop: string]: string[] };
|
|
||||||
items?: JSONSchemaRef | JSONSchemaRef[];
|
|
||||||
minItems?: number;
|
|
||||||
maxItems?: number;
|
|
||||||
uniqueItems?: boolean;
|
|
||||||
additionalItems?: boolean | JSONSchemaRef;
|
|
||||||
pattern?: string;
|
|
||||||
minLength?: number;
|
|
||||||
maxLength?: number;
|
|
||||||
minimum?: number;
|
|
||||||
maximum?: number;
|
|
||||||
exclusiveMinimum?: boolean | number;
|
|
||||||
exclusiveMaximum?: boolean | number;
|
|
||||||
multipleOf?: number;
|
|
||||||
required?: string[];
|
|
||||||
$ref?: string;
|
|
||||||
anyOf?: JSONSchemaRef[];
|
|
||||||
allOf?: JSONSchemaRef[];
|
|
||||||
oneOf?: JSONSchemaRef[];
|
|
||||||
not?: JSONSchemaRef;
|
|
||||||
// tslint:disable-next-line: no-any
|
|
||||||
enum?: any[];
|
|
||||||
format?: string;
|
|
||||||
|
|
||||||
// schema draft 06
|
|
||||||
// tslint:disable-next-line: no-any
|
|
||||||
const?: any;
|
|
||||||
contains?: JSONSchemaRef;
|
|
||||||
propertyNames?: JSONSchemaRef;
|
|
||||||
// tslint:disable-next-line: no-any
|
|
||||||
examples?: any[];
|
|
||||||
|
|
||||||
// schema draft 07
|
|
||||||
$comment?: string;
|
|
||||||
if?: JSONSchemaRef;
|
|
||||||
then?: JSONSchemaRef;
|
|
||||||
else?: JSONSchemaRef;
|
|
||||||
|
|
||||||
// VSCode extensions
|
|
||||||
|
|
||||||
defaultSnippets?: {
|
|
||||||
label?: string;
|
|
||||||
description?: string;
|
|
||||||
markdownDescription?: string;
|
|
||||||
// tslint:disable-next-line: no-any
|
|
||||||
body?: any;
|
|
||||||
bodyText?: string;
|
|
||||||
}[]; // VSCode extension: body: a object that will be converted to a JSON string. bodyText: text with \t and \n
|
|
||||||
|
|
||||||
errorMessage?: string; // VSCode extension
|
|
||||||
patternErrorMessage?: string; // VSCode extension
|
|
||||||
deprecationMessage?: string; // VSCode extension
|
|
||||||
enumDescriptions?: string[]; // VSCode extension
|
|
||||||
markdownEnumDescriptions?: string[]; // VSCode extension
|
|
||||||
markdownDescription?: string; // VSCode extension
|
|
||||||
doNotSuggest?: boolean; // VSCode extension
|
|
||||||
allowComments?: boolean; // VSCode extension
|
|
||||||
|
|
||||||
schemaSequence?: JSONSchema[]; // extension for multiple schemas related to multiple documents in single yaml file
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface JSONSchemaMap {
|
|
||||||
[name: string]: JSONSchemaRef;
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,43 +0,0 @@
|
||||||
/**
|
|
||||||
* Parse a boolean according to the specification
|
|
||||||
*
|
|
||||||
* Return:
|
|
||||||
* true if its a true value
|
|
||||||
* false if its a false value
|
|
||||||
*/
|
|
||||||
export function parseYamlBoolean(input: string): boolean {
|
|
||||||
if (
|
|
||||||
[
|
|
||||||
'true',
|
|
||||||
'True',
|
|
||||||
'TRUE',
|
|
||||||
'y',
|
|
||||||
'Y',
|
|
||||||
'yes',
|
|
||||||
'Yes',
|
|
||||||
'YES',
|
|
||||||
'on',
|
|
||||||
'On',
|
|
||||||
'ON',
|
|
||||||
].lastIndexOf(input) >= 0
|
|
||||||
) {
|
|
||||||
return true;
|
|
||||||
} else if (
|
|
||||||
[
|
|
||||||
'false',
|
|
||||||
'False',
|
|
||||||
'FALSE',
|
|
||||||
'n',
|
|
||||||
'N',
|
|
||||||
'no',
|
|
||||||
'No',
|
|
||||||
'NO',
|
|
||||||
'off',
|
|
||||||
'Off',
|
|
||||||
'OFF',
|
|
||||||
].lastIndexOf(input) >= 0
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
throw `Invalid boolean "${input}"`;
|
|
||||||
}
|
|
||||||
|
|
@ -1,398 +0,0 @@
|
||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Red Hat, Inc. All rights reserved.
|
|
||||||
* Copyright (c) Adam Voss. All rights reserved.
|
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import {
|
|
||||||
ASTNode,
|
|
||||||
ErrorCode,
|
|
||||||
BooleanASTNode,
|
|
||||||
NullASTNode,
|
|
||||||
ArrayASTNode,
|
|
||||||
NumberASTNode,
|
|
||||||
ObjectASTNode,
|
|
||||||
PropertyASTNode,
|
|
||||||
StringASTNode,
|
|
||||||
JSONDocument,
|
|
||||||
} from './jsonParser04';
|
|
||||||
|
|
||||||
import * as nls from 'vscode-nls';
|
|
||||||
const localize = nls.loadMessageBundle();
|
|
||||||
|
|
||||||
import * as Yaml from '../../yaml-ast-parser-custom-tags/index';
|
|
||||||
import { Schema, Type } from 'js-yaml';
|
|
||||||
|
|
||||||
import {
|
|
||||||
getLineStartPositions,
|
|
||||||
getPosition,
|
|
||||||
} from '../utils/documentPositionCalculator';
|
|
||||||
import { parseYamlBoolean } from './scalar-type';
|
|
||||||
import { filterInvalidCustomTags } from '../utils/arrUtils';
|
|
||||||
|
|
||||||
export class SingleYAMLDocument extends JSONDocument {
|
|
||||||
private lines;
|
|
||||||
public root;
|
|
||||||
public errors;
|
|
||||||
public warnings;
|
|
||||||
|
|
||||||
constructor(lines: number[]) {
|
|
||||||
super(null, []);
|
|
||||||
this.lines = lines;
|
|
||||||
this.root = null;
|
|
||||||
this.errors = [];
|
|
||||||
this.warnings = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
public getSchemas(schema, doc, node) {
|
|
||||||
const matchingSchemas = [];
|
|
||||||
doc.validate(schema, matchingSchemas, node.start);
|
|
||||||
return matchingSchemas;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getNodeFromOffset(offset: number): ASTNode {
|
|
||||||
return this.getNodeFromOffsetEndInclusive(offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function recursivelyBuildAst(parent: ASTNode, node: Yaml.YAMLNode): ASTNode {
|
|
||||||
if (!node) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (node.kind) {
|
|
||||||
case Yaml.Kind.MAP: {
|
|
||||||
const instance = <Yaml.YamlMap>node;
|
|
||||||
|
|
||||||
const result = new ObjectASTNode(
|
|
||||||
parent,
|
|
||||||
null,
|
|
||||||
node.startPosition,
|
|
||||||
node.endPosition
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const mapping of instance.mappings) {
|
|
||||||
result.addProperty(
|
|
||||||
<PropertyASTNode>recursivelyBuildAst(result, mapping)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
case Yaml.Kind.MAPPING: {
|
|
||||||
const instance = <Yaml.YAMLMapping>node;
|
|
||||||
const key = instance.key;
|
|
||||||
|
|
||||||
// Technically, this is an arbitrary node in YAML
|
|
||||||
// I doubt we would get a better string representation by parsing it
|
|
||||||
const keyNode = new StringASTNode(
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
true,
|
|
||||||
key.startPosition,
|
|
||||||
key.endPosition
|
|
||||||
);
|
|
||||||
keyNode.value = key.value;
|
|
||||||
|
|
||||||
const result = new PropertyASTNode(parent, keyNode);
|
|
||||||
result.end = instance.endPosition;
|
|
||||||
|
|
||||||
const valueNode = instance.value
|
|
||||||
? recursivelyBuildAst(result, instance.value)
|
|
||||||
: new NullASTNode(
|
|
||||||
parent,
|
|
||||||
key.value,
|
|
||||||
instance.endPosition,
|
|
||||||
instance.endPosition
|
|
||||||
);
|
|
||||||
valueNode.location = key.value;
|
|
||||||
|
|
||||||
result.setValue(valueNode);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
case Yaml.Kind.SEQ: {
|
|
||||||
const instance = <Yaml.YAMLSequence>node;
|
|
||||||
|
|
||||||
const result = new ArrayASTNode(
|
|
||||||
parent,
|
|
||||||
null,
|
|
||||||
instance.startPosition,
|
|
||||||
instance.endPosition
|
|
||||||
);
|
|
||||||
|
|
||||||
let count = 0;
|
|
||||||
for (const item of instance.items) {
|
|
||||||
if (item === null && count === instance.items.length - 1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Be aware of https://github.com/nodeca/js-yaml/issues/321
|
|
||||||
// Cannot simply work around it here because we need to know if we are in Flow or Block
|
|
||||||
const itemNode =
|
|
||||||
item === null
|
|
||||||
? new NullASTNode(
|
|
||||||
parent,
|
|
||||||
null,
|
|
||||||
instance.endPosition,
|
|
||||||
instance.endPosition
|
|
||||||
)
|
|
||||||
: recursivelyBuildAst(result, item);
|
|
||||||
|
|
||||||
itemNode.location = count++;
|
|
||||||
result.addItem(itemNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
case Yaml.Kind.SCALAR: {
|
|
||||||
const instance = <Yaml.YAMLScalar>node;
|
|
||||||
const type = Yaml.determineScalarType(instance);
|
|
||||||
|
|
||||||
// The name is set either by the sequence or the mapping case.
|
|
||||||
const name = null;
|
|
||||||
const value = instance.value;
|
|
||||||
|
|
||||||
//This is a patch for redirecting values with these strings to be boolean nodes because its not supported in the parser.
|
|
||||||
const possibleBooleanValues = [
|
|
||||||
'y',
|
|
||||||
'Y',
|
|
||||||
'yes',
|
|
||||||
'Yes',
|
|
||||||
'YES',
|
|
||||||
'n',
|
|
||||||
'N',
|
|
||||||
'no',
|
|
||||||
'No',
|
|
||||||
'NO',
|
|
||||||
'on',
|
|
||||||
'On',
|
|
||||||
'ON',
|
|
||||||
'off',
|
|
||||||
'Off',
|
|
||||||
'OFF',
|
|
||||||
];
|
|
||||||
if (
|
|
||||||
instance.plainScalar &&
|
|
||||||
possibleBooleanValues.indexOf(value.toString()) !== -1
|
|
||||||
) {
|
|
||||||
return new BooleanASTNode(
|
|
||||||
parent,
|
|
||||||
name,
|
|
||||||
parseYamlBoolean(value),
|
|
||||||
node.startPosition,
|
|
||||||
node.endPosition
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case Yaml.ScalarType.null: {
|
|
||||||
return new StringASTNode(
|
|
||||||
parent,
|
|
||||||
name,
|
|
||||||
false,
|
|
||||||
instance.startPosition,
|
|
||||||
instance.endPosition
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case Yaml.ScalarType.bool: {
|
|
||||||
return new BooleanASTNode(
|
|
||||||
parent,
|
|
||||||
name,
|
|
||||||
Yaml.parseYamlBoolean(value),
|
|
||||||
node.startPosition,
|
|
||||||
node.endPosition
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case Yaml.ScalarType.int: {
|
|
||||||
const result = new NumberASTNode(
|
|
||||||
parent,
|
|
||||||
name,
|
|
||||||
node.startPosition,
|
|
||||||
node.endPosition
|
|
||||||
);
|
|
||||||
result.value = Yaml.parseYamlInteger(value);
|
|
||||||
result.isInteger = true;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
case Yaml.ScalarType.float: {
|
|
||||||
const result = new NumberASTNode(
|
|
||||||
parent,
|
|
||||||
name,
|
|
||||||
node.startPosition,
|
|
||||||
node.endPosition
|
|
||||||
);
|
|
||||||
result.value = Yaml.parseYamlFloat(value);
|
|
||||||
result.isInteger = false;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
case Yaml.ScalarType.string: {
|
|
||||||
const result = new StringASTNode(
|
|
||||||
parent,
|
|
||||||
name,
|
|
||||||
false,
|
|
||||||
node.startPosition,
|
|
||||||
node.endPosition
|
|
||||||
);
|
|
||||||
result.value = node.value;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Yaml.Kind.ANCHOR_REF: {
|
|
||||||
const instance = (<Yaml.YAMLAnchorReference>node).value;
|
|
||||||
|
|
||||||
return (
|
|
||||||
recursivelyBuildAst(parent, instance) ||
|
|
||||||
new NullASTNode(parent, null, node.startPosition, node.endPosition)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case Yaml.Kind.INCLUDE_REF: {
|
|
||||||
const result = new StringASTNode(
|
|
||||||
parent,
|
|
||||||
null,
|
|
||||||
false,
|
|
||||||
node.startPosition,
|
|
||||||
node.endPosition
|
|
||||||
);
|
|
||||||
result.value = node.value;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function convertError(e: Yaml.Error) {
|
|
||||||
return {
|
|
||||||
message: `${e.reason}`,
|
|
||||||
location: {
|
|
||||||
start: e.mark.position,
|
|
||||||
end: e.mark.position + e.mark.column,
|
|
||||||
code: ErrorCode.Undefined,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function createJSONDocument(
|
|
||||||
yamlDoc: Yaml.YAMLNode,
|
|
||||||
startPositions: number[],
|
|
||||||
text: string
|
|
||||||
) {
|
|
||||||
const _doc = new SingleYAMLDocument(startPositions);
|
|
||||||
_doc.root = recursivelyBuildAst(null, yamlDoc);
|
|
||||||
|
|
||||||
if (!_doc.root) {
|
|
||||||
// TODO: When this is true, consider not pushing the other errors.
|
|
||||||
_doc.errors.push({
|
|
||||||
message: localize(
|
|
||||||
'Invalid symbol',
|
|
||||||
'Expected a YAML object, array or literal'
|
|
||||||
),
|
|
||||||
code: ErrorCode.Undefined,
|
|
||||||
location: { start: yamlDoc.startPosition, end: yamlDoc.endPosition },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const duplicateKeyReason = 'duplicate key';
|
|
||||||
|
|
||||||
//Patch ontop of yaml-ast-parser to disable duplicate key message on merge key
|
|
||||||
const isDuplicateAndNotMergeKey = function(
|
|
||||||
error: Yaml.Error,
|
|
||||||
yamlText: string
|
|
||||||
) {
|
|
||||||
const errorConverted = convertError(error);
|
|
||||||
const errorStart = errorConverted.location.start;
|
|
||||||
const errorEnd = errorConverted.location.end;
|
|
||||||
if (
|
|
||||||
error.reason === duplicateKeyReason &&
|
|
||||||
yamlText.substring(errorStart, errorEnd).startsWith('<<')
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
const errors = yamlDoc.errors
|
|
||||||
.filter(e => e.reason !== duplicateKeyReason && !e.isWarning)
|
|
||||||
.map(e => convertError(e));
|
|
||||||
const warnings = yamlDoc.errors
|
|
||||||
.filter(
|
|
||||||
e =>
|
|
||||||
(e.reason === duplicateKeyReason &&
|
|
||||||
isDuplicateAndNotMergeKey(e, text)) ||
|
|
||||||
e.isWarning
|
|
||||||
)
|
|
||||||
.map(e => convertError(e));
|
|
||||||
|
|
||||||
errors.forEach(e => _doc.errors.push(e));
|
|
||||||
warnings.forEach(e => _doc.warnings.push(e));
|
|
||||||
|
|
||||||
return _doc;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class YAMLDocument {
|
|
||||||
public documents: JSONDocument[];
|
|
||||||
private errors;
|
|
||||||
private warnings;
|
|
||||||
|
|
||||||
constructor(documents: JSONDocument[]) {
|
|
||||||
this.documents = documents;
|
|
||||||
this.errors = [];
|
|
||||||
this.warnings = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function parse(text: string, customTags = []): YAMLDocument {
|
|
||||||
const startPositions = getLineStartPositions(text);
|
|
||||||
// This is documented to return a YAMLNode even though the
|
|
||||||
// typing only returns a YAMLDocument
|
|
||||||
const yamlDocs = [];
|
|
||||||
|
|
||||||
const filteredTags = filterInvalidCustomTags(customTags);
|
|
||||||
|
|
||||||
const schemaWithAdditionalTags = Schema.create(
|
|
||||||
filteredTags.map(tag => {
|
|
||||||
const typeInfo = tag.split(' ');
|
|
||||||
return new Type(typeInfo[0], {
|
|
||||||
kind: (typeInfo[1] && typeInfo[1].toLowerCase()) || 'scalar',
|
|
||||||
});
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Collect the additional tags into a map of string to possible tag types
|
|
||||||
*/
|
|
||||||
const tagWithAdditionalItems = new Map<string, string[]>();
|
|
||||||
filteredTags.forEach(tag => {
|
|
||||||
const typeInfo = tag.split(' ');
|
|
||||||
const tagName = typeInfo[0];
|
|
||||||
const tagType = (typeInfo[1] && typeInfo[1].toLowerCase()) || 'scalar';
|
|
||||||
if (tagWithAdditionalItems.has(tagName)) {
|
|
||||||
tagWithAdditionalItems.set(
|
|
||||||
tagName,
|
|
||||||
tagWithAdditionalItems.get(tagName).concat([tagType])
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
tagWithAdditionalItems.set(tagName, [tagType]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
tagWithAdditionalItems.forEach((additionalTagKinds, key) => {
|
|
||||||
const newTagType = new Type(key, {
|
|
||||||
kind: additionalTagKinds[0] || 'scalar',
|
|
||||||
});
|
|
||||||
newTagType.additionalKinds = additionalTagKinds;
|
|
||||||
schemaWithAdditionalTags.compiledTypeMap[key] = newTagType;
|
|
||||||
});
|
|
||||||
|
|
||||||
const additionalOptions: Yaml.LoadOptions = {
|
|
||||||
schema: schemaWithAdditionalTags,
|
|
||||||
};
|
|
||||||
|
|
||||||
Yaml.loadAll(text, doc => yamlDocs.push(doc), additionalOptions);
|
|
||||||
|
|
||||||
return new YAMLDocument(
|
|
||||||
yamlDocs.map(doc => createJSONDocument(doc, startPositions, text))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,389 +0,0 @@
|
||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Red Hat, Inc. All rights reserved.
|
|
||||||
* Copyright (c) Adam Voss. All rights reserved.
|
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import {
|
|
||||||
JSONDocument,
|
|
||||||
NullASTNodeImpl,
|
|
||||||
PropertyASTNodeImpl,
|
|
||||||
StringASTNodeImpl,
|
|
||||||
ObjectASTNodeImpl,
|
|
||||||
NumberASTNodeImpl,
|
|
||||||
ArrayASTNodeImpl,
|
|
||||||
BooleanASTNodeImpl,
|
|
||||||
} from './jsonParser07';
|
|
||||||
|
|
||||||
import * as nls from 'vscode-nls';
|
|
||||||
const localize = nls.loadMessageBundle();
|
|
||||||
|
|
||||||
import * as Yaml from '../../yaml-ast-parser-custom-tags/index';
|
|
||||||
import { Schema, Type } from 'js-yaml';
|
|
||||||
|
|
||||||
import { getLineStartPositions } from '../utils/documentPositionCalculator';
|
|
||||||
import { parseYamlBoolean } from './scalar-type';
|
|
||||||
import { filterInvalidCustomTags } from '../utils/arrUtils';
|
|
||||||
import { ASTNode } from '../jsonASTTypes';
|
|
||||||
import { ErrorCode } from 'vscode-json-languageservice';
|
|
||||||
|
|
||||||
export class SingleYAMLDocument extends JSONDocument {
|
|
||||||
private lines;
|
|
||||||
public root;
|
|
||||||
public errors;
|
|
||||||
public warnings;
|
|
||||||
public isKubernetes: boolean;
|
|
||||||
public currentDocIndex: number;
|
|
||||||
|
|
||||||
constructor(lines: number[]) {
|
|
||||||
super(null, []);
|
|
||||||
this.lines = lines;
|
|
||||||
this.root = null;
|
|
||||||
this.errors = [];
|
|
||||||
this.warnings = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
public getSchemas(schema, doc, node) {
|
|
||||||
const matchingSchemas = [];
|
|
||||||
doc.validate(schema, matchingSchemas, node.start);
|
|
||||||
return matchingSchemas;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function recursivelyBuildAst(parent: ASTNode, node: Yaml.YAMLNode): ASTNode {
|
|
||||||
if (!node) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (node.kind) {
|
|
||||||
case Yaml.Kind.MAP: {
|
|
||||||
const instance = <Yaml.YamlMap>node;
|
|
||||||
|
|
||||||
const result = new ObjectASTNodeImpl(
|
|
||||||
parent,
|
|
||||||
node.startPosition,
|
|
||||||
node.endPosition - node.startPosition
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const mapping of instance.mappings) {
|
|
||||||
result.properties.push(
|
|
||||||
<PropertyASTNodeImpl>recursivelyBuildAst(result, mapping)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
case Yaml.Kind.MAPPING: {
|
|
||||||
const instance = <Yaml.YAMLMapping>node;
|
|
||||||
const key = instance.key;
|
|
||||||
|
|
||||||
const result = new PropertyASTNodeImpl(
|
|
||||||
parent as ObjectASTNodeImpl,
|
|
||||||
instance.startPosition,
|
|
||||||
instance.endPosition - instance.startPosition
|
|
||||||
);
|
|
||||||
|
|
||||||
// Technically, this is an arbitrary node in YAML
|
|
||||||
// I doubt we would get a better string representation by parsing it
|
|
||||||
const keyNode = new StringASTNodeImpl(
|
|
||||||
result,
|
|
||||||
key.startPosition,
|
|
||||||
key.endPosition - key.startPosition
|
|
||||||
);
|
|
||||||
keyNode.value = key.value;
|
|
||||||
|
|
||||||
const valueNode = instance.value
|
|
||||||
? recursivelyBuildAst(result, instance.value)
|
|
||||||
: new NullASTNodeImpl(parent, instance.endPosition, 0);
|
|
||||||
//valueNode.location = key.value;
|
|
||||||
|
|
||||||
result.keyNode = keyNode;
|
|
||||||
result.valueNode = valueNode;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
case Yaml.Kind.SEQ: {
|
|
||||||
const instance = <Yaml.YAMLSequence>node;
|
|
||||||
|
|
||||||
const result = new ArrayASTNodeImpl(
|
|
||||||
parent,
|
|
||||||
instance.startPosition,
|
|
||||||
instance.endPosition - instance.startPosition
|
|
||||||
);
|
|
||||||
|
|
||||||
const count = 0;
|
|
||||||
for (const item of instance.items) {
|
|
||||||
if (item === null && count === instance.items.length - 1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Be aware of https://github.com/nodeca/js-yaml/issues/321
|
|
||||||
// Cannot simply work around it here because we need to know if we are in Flow or Block
|
|
||||||
const itemNode =
|
|
||||||
item === null
|
|
||||||
? new NullASTNodeImpl(parent, instance.endPosition, 0)
|
|
||||||
: recursivelyBuildAst(result, item);
|
|
||||||
|
|
||||||
// itemNode.location = count++;
|
|
||||||
result.children.push(itemNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
case Yaml.Kind.SCALAR: {
|
|
||||||
const instance = <Yaml.YAMLScalar>node;
|
|
||||||
const type = Yaml.determineScalarType(instance);
|
|
||||||
|
|
||||||
// The name is set either by the sequence or the mapping case.
|
|
||||||
const name = null;
|
|
||||||
const value = instance.value;
|
|
||||||
|
|
||||||
//This is a patch for redirecting values with these strings to be boolean nodes because its not supported in the parser.
|
|
||||||
const possibleBooleanValues = [
|
|
||||||
'y',
|
|
||||||
'Y',
|
|
||||||
'yes',
|
|
||||||
'Yes',
|
|
||||||
'YES',
|
|
||||||
'n',
|
|
||||||
'N',
|
|
||||||
'no',
|
|
||||||
'No',
|
|
||||||
'NO',
|
|
||||||
'on',
|
|
||||||
'On',
|
|
||||||
'ON',
|
|
||||||
'off',
|
|
||||||
'Off',
|
|
||||||
'OFF',
|
|
||||||
];
|
|
||||||
if (
|
|
||||||
instance.plainScalar &&
|
|
||||||
possibleBooleanValues.indexOf(value.toString()) !== -1
|
|
||||||
) {
|
|
||||||
return new BooleanASTNodeImpl(
|
|
||||||
parent,
|
|
||||||
parseYamlBoolean(value),
|
|
||||||
node.startPosition,
|
|
||||||
node.endPosition - node.startPosition
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case Yaml.ScalarType.null: {
|
|
||||||
return new StringASTNodeImpl(
|
|
||||||
parent,
|
|
||||||
instance.startPosition,
|
|
||||||
instance.endPosition - instance.startPosition
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case Yaml.ScalarType.bool: {
|
|
||||||
return new BooleanASTNodeImpl(
|
|
||||||
parent,
|
|
||||||
Yaml.parseYamlBoolean(value),
|
|
||||||
node.startPosition,
|
|
||||||
node.endPosition - node.startPosition
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case Yaml.ScalarType.int: {
|
|
||||||
const result = new NumberASTNodeImpl(
|
|
||||||
parent,
|
|
||||||
node.startPosition,
|
|
||||||
node.endPosition - node.startPosition
|
|
||||||
);
|
|
||||||
result.value = Yaml.parseYamlInteger(value);
|
|
||||||
result.isInteger = true;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
case Yaml.ScalarType.float: {
|
|
||||||
const result = new NumberASTNodeImpl(
|
|
||||||
parent,
|
|
||||||
node.startPosition,
|
|
||||||
node.endPosition - node.startPosition
|
|
||||||
);
|
|
||||||
result.value = Yaml.parseYamlFloat(value);
|
|
||||||
result.isInteger = false;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
case Yaml.ScalarType.string: {
|
|
||||||
const result = new StringASTNodeImpl(
|
|
||||||
parent,
|
|
||||||
node.startPosition,
|
|
||||||
node.endPosition - node.startPosition
|
|
||||||
);
|
|
||||||
result.value = node.value;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Yaml.Kind.ANCHOR_REF: {
|
|
||||||
const instance = (<Yaml.YAMLAnchorReference>node).value;
|
|
||||||
|
|
||||||
return (
|
|
||||||
recursivelyBuildAst(parent, instance) ||
|
|
||||||
new NullASTNodeImpl(
|
|
||||||
parent,
|
|
||||||
node.startPosition,
|
|
||||||
node.endPosition - node.startPosition
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case Yaml.Kind.INCLUDE_REF: {
|
|
||||||
const result = new StringASTNodeImpl(
|
|
||||||
parent,
|
|
||||||
node.startPosition,
|
|
||||||
node.endPosition - node.startPosition
|
|
||||||
);
|
|
||||||
result.value = node.value;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function convertError(e: Yaml.Error) {
|
|
||||||
const line = e.mark.line === 0 ? 0 : e.mark.line - 1;
|
|
||||||
const character =
|
|
||||||
e.mark.position + e.mark.column === 0
|
|
||||||
? 0
|
|
||||||
: e.mark.position + e.mark.column - 1;
|
|
||||||
|
|
||||||
return {
|
|
||||||
message: `${e.reason}`,
|
|
||||||
range: {
|
|
||||||
start: {
|
|
||||||
line,
|
|
||||||
character,
|
|
||||||
},
|
|
||||||
end: {
|
|
||||||
line,
|
|
||||||
character,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
severity: 2,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function createJSONDocument(
|
|
||||||
yamlDoc: Yaml.YAMLNode,
|
|
||||||
startPositions: number[],
|
|
||||||
text: string
|
|
||||||
) {
|
|
||||||
const _doc = new SingleYAMLDocument(startPositions);
|
|
||||||
_doc.root = recursivelyBuildAst(null, yamlDoc);
|
|
||||||
|
|
||||||
if (!_doc.root) {
|
|
||||||
// TODO: When this is true, consider not pushing the other errors.
|
|
||||||
_doc.errors.push({
|
|
||||||
message: localize(
|
|
||||||
'Invalid symbol',
|
|
||||||
'Expected a YAML object, array or literal'
|
|
||||||
),
|
|
||||||
code: ErrorCode.Undefined,
|
|
||||||
location: { start: yamlDoc.startPosition, end: yamlDoc.endPosition },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const duplicateKeyReason = 'duplicate key';
|
|
||||||
|
|
||||||
//Patch ontop of yaml-ast-parser to disable duplicate key message on merge key
|
|
||||||
const isDuplicateAndNotMergeKey = function(
|
|
||||||
error: Yaml.Error,
|
|
||||||
yamlText: string
|
|
||||||
) {
|
|
||||||
const errorConverted = convertError(error);
|
|
||||||
const errorStart = errorConverted.range.start.character;
|
|
||||||
const errorEnd = errorConverted.range.end.character;
|
|
||||||
if (
|
|
||||||
error.reason === duplicateKeyReason &&
|
|
||||||
yamlText.substring(errorStart, errorEnd).startsWith('<<')
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
const errors = yamlDoc.errors
|
|
||||||
.filter(e => e.reason !== duplicateKeyReason && !e.isWarning)
|
|
||||||
.map(e => convertError(e));
|
|
||||||
const warnings = yamlDoc.errors
|
|
||||||
.filter(
|
|
||||||
e =>
|
|
||||||
(e.reason === duplicateKeyReason &&
|
|
||||||
isDuplicateAndNotMergeKey(e, text)) ||
|
|
||||||
e.isWarning
|
|
||||||
)
|
|
||||||
.map(e => convertError(e));
|
|
||||||
|
|
||||||
errors.forEach(e => _doc.errors.push(e));
|
|
||||||
warnings.forEach(e => _doc.warnings.push(e));
|
|
||||||
|
|
||||||
return _doc;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class YAMLDocument {
|
|
||||||
public documents: SingleYAMLDocument[];
|
|
||||||
private errors;
|
|
||||||
private warnings;
|
|
||||||
|
|
||||||
constructor(documents: SingleYAMLDocument[]) {
|
|
||||||
this.documents = documents;
|
|
||||||
this.errors = [];
|
|
||||||
this.warnings = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function parse(text: string, customTags = []): YAMLDocument {
|
|
||||||
const startPositions = getLineStartPositions(text);
|
|
||||||
// This is documented to return a YAMLNode even though the
|
|
||||||
// typing only returns a YAMLDocument
|
|
||||||
const yamlDocs = [];
|
|
||||||
|
|
||||||
const filteredTags = filterInvalidCustomTags(customTags);
|
|
||||||
|
|
||||||
const schemaWithAdditionalTags = Schema.create(
|
|
||||||
filteredTags.map(tag => {
|
|
||||||
const typeInfo = tag.split(' ');
|
|
||||||
return new Type(typeInfo[0], {
|
|
||||||
kind: (typeInfo[1] && typeInfo[1].toLowerCase()) || 'scalar',
|
|
||||||
});
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Collect the additional tags into a map of string to possible tag types
|
|
||||||
*/
|
|
||||||
const tagWithAdditionalItems = new Map<string, string[]>();
|
|
||||||
filteredTags.forEach(tag => {
|
|
||||||
const typeInfo = tag.split(' ');
|
|
||||||
const tagName = typeInfo[0];
|
|
||||||
const tagType = (typeInfo[1] && typeInfo[1].toLowerCase()) || 'scalar';
|
|
||||||
if (tagWithAdditionalItems.has(tagName)) {
|
|
||||||
tagWithAdditionalItems.set(
|
|
||||||
tagName,
|
|
||||||
tagWithAdditionalItems.get(tagName).concat([tagType])
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
tagWithAdditionalItems.set(tagName, [tagType]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
tagWithAdditionalItems.forEach((additionalTagKinds, key) => {
|
|
||||||
const newTagType = new Type(key, {
|
|
||||||
kind: additionalTagKinds[0] || 'scalar',
|
|
||||||
});
|
|
||||||
newTagType.additionalKinds = additionalTagKinds;
|
|
||||||
schemaWithAdditionalTags.compiledTypeMap[key] = newTagType;
|
|
||||||
});
|
|
||||||
|
|
||||||
const additionalOptions: Yaml.LoadOptions = {
|
|
||||||
schema: schemaWithAdditionalTags,
|
|
||||||
};
|
|
||||||
|
|
||||||
Yaml.loadAll(text, doc => yamlDocs.push(doc), additionalOptions);
|
|
||||||
|
|
||||||
return new YAMLDocument(
|
|
||||||
yamlDocs.map(doc => createJSONDocument(doc, startPositions, text))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,62 +0,0 @@
|
||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Red Hat, Inc. All rights reserved.
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import { parse as parseYAML } from '../parser/yamlParser07';
|
|
||||||
|
|
||||||
import {
|
|
||||||
SymbolInformation,
|
|
||||||
TextDocument,
|
|
||||||
DocumentSymbol,
|
|
||||||
} from 'vscode-languageserver-types';
|
|
||||||
import { YAMLSchemaService } from './yamlSchemaService';
|
|
||||||
import { JSONDocumentSymbols } from 'vscode-json-languageservice/lib/umd/services/jsonDocumentSymbols';
|
|
||||||
|
|
||||||
export class YAMLDocumentSymbols {
|
|
||||||
private jsonDocumentSymbols;
|
|
||||||
|
|
||||||
constructor(schemaService: YAMLSchemaService) {
|
|
||||||
this.jsonDocumentSymbols = new JSONDocumentSymbols(schemaService);
|
|
||||||
}
|
|
||||||
|
|
||||||
public findDocumentSymbols(document: TextDocument): SymbolInformation[] {
|
|
||||||
const doc = parseYAML(document.getText());
|
|
||||||
if (!doc || doc['documents'].length === 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
let results = [];
|
|
||||||
for (const yamlDoc of doc['documents']) {
|
|
||||||
if (yamlDoc.root) {
|
|
||||||
results = results.concat(
|
|
||||||
this.jsonDocumentSymbols.findDocumentSymbols(document, yamlDoc)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
public findHierarchicalDocumentSymbols(
|
|
||||||
document: TextDocument
|
|
||||||
): DocumentSymbol[] {
|
|
||||||
const doc = parseYAML(document.getText());
|
|
||||||
if (!doc || doc['documents'].length === 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
let results = [];
|
|
||||||
for (const yamlDoc of doc['documents']) {
|
|
||||||
if (yamlDoc.root) {
|
|
||||||
results = results.concat(
|
|
||||||
this.jsonDocumentSymbols.findDocumentSymbols2(document, yamlDoc)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,56 +0,0 @@
|
||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Red Hat, Inc. All rights reserved.
|
|
||||||
* Copyright (c) Adam Voss. All rights reserved.
|
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import {
|
|
||||||
TextDocument,
|
|
||||||
Range,
|
|
||||||
Position,
|
|
||||||
TextEdit,
|
|
||||||
} from 'vscode-languageserver-types';
|
|
||||||
import {
|
|
||||||
CustomFormatterOptions,
|
|
||||||
LanguageSettings,
|
|
||||||
} from '../yamlLanguageService';
|
|
||||||
|
|
||||||
export class YAMLFormatter {
|
|
||||||
private formatterEnabled: boolean = true;
|
|
||||||
|
|
||||||
public configure(shouldFormat: LanguageSettings) {
|
|
||||||
if (shouldFormat) {
|
|
||||||
this.formatterEnabled = shouldFormat.format;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public format(
|
|
||||||
document: TextDocument,
|
|
||||||
options: CustomFormatterOptions
|
|
||||||
): TextEdit[] {
|
|
||||||
if (!this.formatterEnabled) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const prettier = require('prettier/standalone');
|
|
||||||
const parser = require('prettier/parser-yaml');
|
|
||||||
const text = document.getText();
|
|
||||||
|
|
||||||
const formatted = prettier.format(
|
|
||||||
text,
|
|
||||||
Object.assign(options, { parser: 'yaml', plugins: [parser] })
|
|
||||||
);
|
|
||||||
|
|
||||||
return [
|
|
||||||
TextEdit.replace(
|
|
||||||
Range.create(Position.create(0, 0), document.positionAt(text.length)),
|
|
||||||
formatted
|
|
||||||
),
|
|
||||||
];
|
|
||||||
} catch (error) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Red Hat, Inc. All rights reserved.
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import {
|
|
||||||
PromiseConstructor,
|
|
||||||
Thenable,
|
|
||||||
LanguageService,
|
|
||||||
} from 'vscode-json-languageservice';
|
|
||||||
import { Hover, TextDocument, Position } from 'vscode-languageserver-types';
|
|
||||||
import { matchOffsetToDocument2 } from '../utils/arrUtils';
|
|
||||||
import { LanguageSettings } from '../yamlLanguageService';
|
|
||||||
import { parse as parseYAML } from '../parser/yamlParser07';
|
|
||||||
import { YAMLSchemaService } from './yamlSchemaService';
|
|
||||||
import { JSONHover } from 'vscode-json-languageservice/lib/umd/services/jsonHover';
|
|
||||||
|
|
||||||
export class YAMLHover {
|
|
||||||
private promise: PromiseConstructor;
|
|
||||||
private shouldHover: boolean;
|
|
||||||
private jsonHover;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
schemaService: YAMLSchemaService,
|
|
||||||
promiseConstructor: PromiseConstructor
|
|
||||||
) {
|
|
||||||
this.promise = promiseConstructor || Promise;
|
|
||||||
this.shouldHover = true;
|
|
||||||
this.jsonHover = new JSONHover(schemaService, [], Promise);
|
|
||||||
}
|
|
||||||
|
|
||||||
public configure(languageSettings: LanguageSettings) {
|
|
||||||
if (languageSettings) {
|
|
||||||
this.shouldHover = languageSettings.hover;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public doHover(document: TextDocument, position: Position): Thenable<Hover> {
|
|
||||||
if (!this.shouldHover || !document) {
|
|
||||||
return this.promise.resolve(void 0);
|
|
||||||
}
|
|
||||||
const doc = parseYAML(document.getText());
|
|
||||||
const offset = document.offsetAt(position);
|
|
||||||
const currentDoc = matchOffsetToDocument2(offset, doc);
|
|
||||||
if (currentDoc === null) {
|
|
||||||
return this.promise.resolve(void 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentDocIndex = doc.documents.indexOf(currentDoc);
|
|
||||||
currentDoc.currentDocIndex = currentDocIndex;
|
|
||||||
return this.jsonHover.doHover(document, position, currentDoc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,389 +0,0 @@
|
||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Red Hat, Inc. All rights reserved.
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import { JSONSchema, JSONSchemaMap, JSONSchemaRef } from '../jsonSchema07';
|
|
||||||
import {
|
|
||||||
SchemaRequestService,
|
|
||||||
WorkspaceContextService,
|
|
||||||
PromiseConstructor,
|
|
||||||
Thenable,
|
|
||||||
} from '../yamlLanguageService';
|
|
||||||
import {
|
|
||||||
UnresolvedSchema,
|
|
||||||
ResolvedSchema,
|
|
||||||
JSONSchemaService,
|
|
||||||
SchemaDependencies,
|
|
||||||
ISchemaContributions,
|
|
||||||
} from 'vscode-json-languageservice/lib/umd/services/jsonSchemaService';
|
|
||||||
|
|
||||||
import * as nls from 'vscode-nls';
|
|
||||||
import { convertSimple2RegExpPattern } from '../utils/strings';
|
|
||||||
const localize = nls.loadMessageBundle();
|
|
||||||
|
|
||||||
export declare type CustomSchemaProvider = (uri: string) => Thenable<string>;
|
|
||||||
|
|
||||||
export class FilePatternAssociation {
|
|
||||||
private schemas: string[];
|
|
||||||
private patternRegExp: RegExp;
|
|
||||||
|
|
||||||
constructor(pattern: string) {
|
|
||||||
try {
|
|
||||||
this.patternRegExp = new RegExp(
|
|
||||||
convertSimple2RegExpPattern(pattern) + '$'
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
// invalid pattern
|
|
||||||
this.patternRegExp = null;
|
|
||||||
}
|
|
||||||
this.schemas = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
public addSchema(id: string) {
|
|
||||||
this.schemas.push(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public matchesPattern(fileName: string): boolean {
|
|
||||||
return this.patternRegExp && this.patternRegExp.test(fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public getSchemas() {
|
|
||||||
return this.schemas;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class YAMLSchemaService extends JSONSchemaService {
|
|
||||||
private customSchemaProvider: CustomSchemaProvider | undefined;
|
|
||||||
private filePatternAssociations: FilePatternAssociation[];
|
|
||||||
private contextService: WorkspaceContextService;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
requestService: SchemaRequestService,
|
|
||||||
contextService?: WorkspaceContextService,
|
|
||||||
promiseConstructor?: PromiseConstructor
|
|
||||||
) {
|
|
||||||
super(requestService, contextService, promiseConstructor);
|
|
||||||
this.customSchemaProvider = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
registerCustomSchemaProvider(customSchemaProvider: CustomSchemaProvider) {
|
|
||||||
this.customSchemaProvider = customSchemaProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
//tslint:disable
|
|
||||||
public resolveSchemaContent(
|
|
||||||
schemaToResolve: UnresolvedSchema,
|
|
||||||
schemaURL: string,
|
|
||||||
dependencies: SchemaDependencies
|
|
||||||
): Thenable<ResolvedSchema> {
|
|
||||||
let resolveErrors: string[] = schemaToResolve.errors.slice(0);
|
|
||||||
let schema = schemaToResolve.schema;
|
|
||||||
let contextService = this.contextService;
|
|
||||||
|
|
||||||
let findSection = (schema: JSONSchema, path: string): any => {
|
|
||||||
if (!path) {
|
|
||||||
return schema;
|
|
||||||
}
|
|
||||||
let current: any = schema;
|
|
||||||
if (path[0] === '/') {
|
|
||||||
path = path.substr(1);
|
|
||||||
}
|
|
||||||
path.split('/').some(part => {
|
|
||||||
current = current[part];
|
|
||||||
return !current;
|
|
||||||
});
|
|
||||||
return current;
|
|
||||||
};
|
|
||||||
|
|
||||||
let merge = (
|
|
||||||
target: JSONSchema,
|
|
||||||
sourceRoot: JSONSchema,
|
|
||||||
sourceURI: string,
|
|
||||||
path: string
|
|
||||||
): void => {
|
|
||||||
let section = findSection(sourceRoot, path);
|
|
||||||
if (section) {
|
|
||||||
for (let key in section) {
|
|
||||||
if (section.hasOwnProperty(key) && !target.hasOwnProperty(key)) {
|
|
||||||
target[key] = section[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
resolveErrors.push(
|
|
||||||
localize(
|
|
||||||
'json.schema.invalidref',
|
|
||||||
"$ref '{0}' in '{1}' can not be resolved.",
|
|
||||||
path,
|
|
||||||
sourceURI
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let resolveExternalLink = (
|
|
||||||
node: JSONSchema,
|
|
||||||
uri: string,
|
|
||||||
linkPath: string,
|
|
||||||
parentSchemaURL: string,
|
|
||||||
parentSchemaDependencies: SchemaDependencies
|
|
||||||
): Thenable<any> => {
|
|
||||||
if (contextService && !/^\w+:\/\/.*/.test(uri)) {
|
|
||||||
uri = contextService.resolveRelativePath(uri, parentSchemaURL);
|
|
||||||
}
|
|
||||||
uri = this.normalizeId(uri);
|
|
||||||
const referencedHandle = this.getOrAddSchemaHandle(uri);
|
|
||||||
return referencedHandle.getUnresolvedSchema().then(unresolvedSchema => {
|
|
||||||
parentSchemaDependencies[uri] = true;
|
|
||||||
if (unresolvedSchema.errors.length) {
|
|
||||||
let loc = linkPath ? uri + '#' + linkPath : uri;
|
|
||||||
resolveErrors.push(
|
|
||||||
localize(
|
|
||||||
'json.schema.problemloadingref',
|
|
||||||
"Problems loading reference '{0}': {1}",
|
|
||||||
loc,
|
|
||||||
unresolvedSchema.errors[0]
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
merge(node, unresolvedSchema.schema, uri, linkPath);
|
|
||||||
return resolveRefs(
|
|
||||||
node,
|
|
||||||
unresolvedSchema.schema,
|
|
||||||
uri,
|
|
||||||
referencedHandle.dependencies
|
|
||||||
);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
let resolveRefs = (
|
|
||||||
node: JSONSchema,
|
|
||||||
parentSchema: JSONSchema,
|
|
||||||
parentSchemaURL: string,
|
|
||||||
parentSchemaDependencies: SchemaDependencies
|
|
||||||
): Thenable<any> => {
|
|
||||||
if (!node || typeof node !== 'object') {
|
|
||||||
return Promise.resolve(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
let toWalk: JSONSchema[] = [node];
|
|
||||||
let seen: JSONSchema[] = [];
|
|
||||||
|
|
||||||
let openPromises: Thenable<any>[] = [];
|
|
||||||
|
|
||||||
let collectEntries = (...entries: JSONSchemaRef[]) => {
|
|
||||||
for (let entry of entries) {
|
|
||||||
if (typeof entry === 'object') {
|
|
||||||
toWalk.push(entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let collectMapEntries = (...maps: JSONSchemaMap[]) => {
|
|
||||||
for (let map of maps) {
|
|
||||||
if (typeof map === 'object') {
|
|
||||||
for (let key in map) {
|
|
||||||
let entry = map[key];
|
|
||||||
if (typeof entry === 'object') {
|
|
||||||
toWalk.push(entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let collectArrayEntries = (...arrays: JSONSchemaRef[][]) => {
|
|
||||||
for (let array of arrays) {
|
|
||||||
if (Array.isArray(array)) {
|
|
||||||
for (let entry of array) {
|
|
||||||
if (typeof entry === 'object') {
|
|
||||||
toWalk.push(entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let handleRef = (next: JSONSchema) => {
|
|
||||||
let seenRefs = [];
|
|
||||||
while (next.$ref) {
|
|
||||||
const ref = next.$ref;
|
|
||||||
let segments = ref.split('#', 2);
|
|
||||||
delete next.$ref;
|
|
||||||
if (segments[0].length > 0) {
|
|
||||||
openPromises.push(
|
|
||||||
resolveExternalLink(
|
|
||||||
next,
|
|
||||||
segments[0],
|
|
||||||
segments[1],
|
|
||||||
parentSchemaURL,
|
|
||||||
parentSchemaDependencies
|
|
||||||
)
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
if (seenRefs.indexOf(ref) === -1) {
|
|
||||||
merge(next, parentSchema, parentSchemaURL, segments[1]); // can set next.$ref again, use seenRefs to avoid circle
|
|
||||||
seenRefs.push(ref);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
collectEntries(
|
|
||||||
<JSONSchema>next.items,
|
|
||||||
<JSONSchema>next.additionalProperties,
|
|
||||||
next.not,
|
|
||||||
next.contains,
|
|
||||||
next.propertyNames,
|
|
||||||
next.if,
|
|
||||||
next.then,
|
|
||||||
next.else
|
|
||||||
);
|
|
||||||
collectMapEntries(
|
|
||||||
next.definitions,
|
|
||||||
next.properties,
|
|
||||||
next.patternProperties,
|
|
||||||
<JSONSchemaMap>next.dependencies
|
|
||||||
);
|
|
||||||
collectArrayEntries(
|
|
||||||
next.anyOf,
|
|
||||||
next.allOf,
|
|
||||||
next.oneOf,
|
|
||||||
<JSONSchema[]>next.items,
|
|
||||||
next.schemaSequence
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
while (toWalk.length) {
|
|
||||||
let next = toWalk.pop();
|
|
||||||
if (seen.indexOf(next) >= 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
seen.push(next);
|
|
||||||
handleRef(next);
|
|
||||||
}
|
|
||||||
return Promise.all(openPromises);
|
|
||||||
};
|
|
||||||
|
|
||||||
return resolveRefs(schema, schema, schemaURL, dependencies).then(
|
|
||||||
_ => new ResolvedSchema(schema, resolveErrors)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
//tslint:enable
|
|
||||||
|
|
||||||
public getSchemaForResource(
|
|
||||||
resource: string,
|
|
||||||
doc = undefined
|
|
||||||
): Thenable<ResolvedSchema> {
|
|
||||||
const resolveSchema = () => {
|
|
||||||
const seen: { [schemaId: string]: boolean } = Object.create(null);
|
|
||||||
const schemas: string[] = [];
|
|
||||||
for (const entry of this.filePatternAssociations) {
|
|
||||||
if (entry.matchesPattern(resource)) {
|
|
||||||
for (const schemaId of entry.getSchemas()) {
|
|
||||||
if (!seen[schemaId]) {
|
|
||||||
schemas.push(schemaId);
|
|
||||||
seen[schemaId] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (schemas.length > 0) {
|
|
||||||
return super
|
|
||||||
.createCombinedSchema(resource, schemas)
|
|
||||||
.getResolvedSchema()
|
|
||||||
.then(schema => {
|
|
||||||
if (
|
|
||||||
schema.schema &&
|
|
||||||
schema.schema.schemaSequence &&
|
|
||||||
schema.schema.schemaSequence[doc.currentDocIndex]
|
|
||||||
) {
|
|
||||||
return new ResolvedSchema(
|
|
||||||
schema.schema.schemaSequence[doc.currentDocIndex]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return schema;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return Promise.resolve(null);
|
|
||||||
};
|
|
||||||
if (this.customSchemaProvider) {
|
|
||||||
return this.customSchemaProvider(resource)
|
|
||||||
.then(schemaUri => {
|
|
||||||
if (!schemaUri) {
|
|
||||||
return resolveSchema();
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.loadSchema(schemaUri).then(unsolvedSchema =>
|
|
||||||
this.resolveSchemaContent(unsolvedSchema, schemaUri, []).then(
|
|
||||||
schema => {
|
|
||||||
if (
|
|
||||||
schema.schema &&
|
|
||||||
schema.schema.schemaSequence &&
|
|
||||||
schema.schema.schemaSequence[doc.currentDocIndex]
|
|
||||||
) {
|
|
||||||
return new ResolvedSchema(
|
|
||||||
schema.schema.schemaSequence[doc.currentDocIndex]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return schema;
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.then(
|
|
||||||
schema => schema,
|
|
||||||
err => resolveSchema()
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return resolveSchema();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Everything below here is needed because we're importing from vscode-json-languageservice umd and we need
|
|
||||||
* to provide a wrapper around the javascript methods we are calling since they have no type
|
|
||||||
*/
|
|
||||||
|
|
||||||
normalizeId(id: string) {
|
|
||||||
return super.normalizeId(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
getOrAddSchemaHandle(id: string, unresolvedSchemaContent?: JSONSchema) {
|
|
||||||
return super.getOrAddSchemaHandle(id, unresolvedSchemaContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
// tslint:disable-next-line: no-any
|
|
||||||
loadSchema(schemaUri: string): Thenable<any> {
|
|
||||||
return super.loadSchema(schemaUri);
|
|
||||||
}
|
|
||||||
|
|
||||||
registerExternalSchema(
|
|
||||||
uri: string,
|
|
||||||
filePatterns?: string[],
|
|
||||||
unresolvedSchema?: JSONSchema
|
|
||||||
) {
|
|
||||||
return super.registerExternalSchema(uri, filePatterns, unresolvedSchema);
|
|
||||||
}
|
|
||||||
|
|
||||||
clearExternalSchemas(): void {
|
|
||||||
super.clearExternalSchemas();
|
|
||||||
}
|
|
||||||
|
|
||||||
setSchemaContributions(schemaContributions: ISchemaContributions): void {
|
|
||||||
super.setSchemaContributions(schemaContributions);
|
|
||||||
}
|
|
||||||
|
|
||||||
getRegisteredSchemaIds(filter?: (scheme: any) => boolean): string[] {
|
|
||||||
return super.getRegisteredSchemaIds(filter);
|
|
||||||
}
|
|
||||||
|
|
||||||
getResolvedSchema(schemaId: string): Thenable<ResolvedSchema> {
|
|
||||||
return super.getResolvedSchema(schemaId);
|
|
||||||
}
|
|
||||||
|
|
||||||
onResourceChange(uri: string): boolean {
|
|
||||||
return super.onResourceChange(uri);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,94 +0,0 @@
|
||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Red Hat, Inc. All rights reserved.
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import { Diagnostic, TextDocument } from 'vscode-languageserver-types';
|
|
||||||
import { PromiseConstructor, LanguageSettings } from '../yamlLanguageService';
|
|
||||||
import { parse as parseYAML, YAMLDocument } from '../parser/yamlParser07';
|
|
||||||
import { SingleYAMLDocument } from '../parser/yamlParser04';
|
|
||||||
import { YAMLSchemaService } from './yamlSchemaService';
|
|
||||||
import { JSONValidation } from 'vscode-json-languageservice/lib/umd/services/jsonValidation';
|
|
||||||
|
|
||||||
export class YAMLValidation {
|
|
||||||
private promise: PromiseConstructor;
|
|
||||||
private validationEnabled: boolean;
|
|
||||||
private customTags: String[];
|
|
||||||
private jsonValidation;
|
|
||||||
|
|
||||||
private MATCHES_MULTIPLE =
|
|
||||||
'Matches multiple schemas when only one must validate.';
|
|
||||||
|
|
||||||
public constructor(
|
|
||||||
schemaService: YAMLSchemaService,
|
|
||||||
promiseConstructor: PromiseConstructor
|
|
||||||
) {
|
|
||||||
this.promise = promiseConstructor || Promise;
|
|
||||||
this.validationEnabled = true;
|
|
||||||
this.jsonValidation = new JSONValidation(schemaService, this.promise);
|
|
||||||
}
|
|
||||||
|
|
||||||
public configure(settings: LanguageSettings) {
|
|
||||||
if (settings) {
|
|
||||||
this.validationEnabled = settings.validate;
|
|
||||||
this.customTags = settings.customTags;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async doValidation(
|
|
||||||
textDocument: TextDocument,
|
|
||||||
isKubernetes: boolean = false
|
|
||||||
): Promise<Diagnostic[]> {
|
|
||||||
if (!this.validationEnabled) {
|
|
||||||
return this.promise.resolve([]);
|
|
||||||
}
|
|
||||||
const yamlDocument: YAMLDocument = parseYAML(
|
|
||||||
textDocument.getText(),
|
|
||||||
this.customTags
|
|
||||||
);
|
|
||||||
const validationResult: Diagnostic[] = [];
|
|
||||||
let index = 0;
|
|
||||||
for (const currentYAMLDoc of yamlDocument.documents) {
|
|
||||||
currentYAMLDoc.isKubernetes = isKubernetes;
|
|
||||||
currentYAMLDoc.currentDocIndex = index;
|
|
||||||
|
|
||||||
const validation = await this.jsonValidation.doValidation(
|
|
||||||
textDocument,
|
|
||||||
currentYAMLDoc
|
|
||||||
);
|
|
||||||
const syd = (currentYAMLDoc as unknown) as SingleYAMLDocument;
|
|
||||||
if (syd.errors.length > 0) {
|
|
||||||
validationResult.push(...syd.errors);
|
|
||||||
}
|
|
||||||
|
|
||||||
validationResult.push(...validation);
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
|
|
||||||
const foundSignatures = new Set();
|
|
||||||
const duplicateMessagesRemoved = [];
|
|
||||||
for (const err of validationResult as Diagnostic[]) {
|
|
||||||
/**
|
|
||||||
* A patch ontop of the validation that removes the
|
|
||||||
* 'Matches many schemas' error for kubernetes
|
|
||||||
* for a better user experience.
|
|
||||||
*/
|
|
||||||
if (isKubernetes && err.message === this.MATCHES_MULTIPLE) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const errSig =
|
|
||||||
err.range.start.line +
|
|
||||||
' ' +
|
|
||||||
err.range.start.character +
|
|
||||||
' ' +
|
|
||||||
err.message;
|
|
||||||
if (!foundSignatures.has(errSig)) {
|
|
||||||
duplicateMessagesRemoved.push(err);
|
|
||||||
foundSignatures.add(errSig);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return duplicateMessagesRemoved;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,107 +0,0 @@
|
||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Red Hat, Inc. All rights reserved.
|
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
import { SingleYAMLDocument } from '../parser/yamlParser04';
|
|
||||||
|
|
||||||
export function removeDuplicates(arr, prop) {
|
|
||||||
const new_arr = [];
|
|
||||||
const lookup = {};
|
|
||||||
|
|
||||||
for (const i in arr) {
|
|
||||||
lookup[arr[i][prop]] = arr[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const i in lookup) {
|
|
||||||
new_arr.push(lookup[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new_arr;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getLineOffsets(textDocString: String): number[] {
|
|
||||||
const lineOffsets: number[] = [];
|
|
||||||
const text = textDocString;
|
|
||||||
let isLineStart = true;
|
|
||||||
for (let i = 0; i < text.length; i++) {
|
|
||||||
if (isLineStart) {
|
|
||||||
lineOffsets.push(i);
|
|
||||||
isLineStart = false;
|
|
||||||
}
|
|
||||||
const ch = text.charAt(i);
|
|
||||||
isLineStart = ch === '\r' || ch === '\n';
|
|
||||||
if (ch === '\r' && i + 1 < text.length && text.charAt(i + 1) === '\n') {
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isLineStart && text.length > 0) {
|
|
||||||
lineOffsets.push(text.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
return lineOffsets;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function removeDuplicatesObj(objArray) {
|
|
||||||
const nonDuplicateSet = new Set();
|
|
||||||
const nonDuplicateArr = [];
|
|
||||||
for (const obj in objArray) {
|
|
||||||
const currObj = objArray[obj];
|
|
||||||
const stringifiedObj = JSON.stringify(currObj);
|
|
||||||
if (!nonDuplicateSet.has(stringifiedObj)) {
|
|
||||||
nonDuplicateArr.push(currObj);
|
|
||||||
nonDuplicateSet.add(stringifiedObj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nonDuplicateArr;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function matchOffsetToDocument(offset: number, jsonDocuments) {
|
|
||||||
for (const jsonDoc in jsonDocuments.documents) {
|
|
||||||
const currJsonDoc: SingleYAMLDocument = jsonDocuments.documents[jsonDoc];
|
|
||||||
if (
|
|
||||||
currJsonDoc.root &&
|
|
||||||
currJsonDoc.root.end >= offset &&
|
|
||||||
currJsonDoc.root.start <= offset
|
|
||||||
) {
|
|
||||||
return currJsonDoc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Fix this so that it returns the correct document
|
|
||||||
return jsonDocuments.documents[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function matchOffsetToDocument2(offset: number, jsonDocuments) {
|
|
||||||
for (const jsonDoc of jsonDocuments.documents) {
|
|
||||||
if (
|
|
||||||
jsonDoc.root &&
|
|
||||||
jsonDoc.root.offset <= offset &&
|
|
||||||
jsonDoc.root.length + jsonDoc.root.offset >= offset
|
|
||||||
) {
|
|
||||||
return jsonDoc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Fix this so that it returns the correct document
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function filterInvalidCustomTags(customTags: String[]): String[] {
|
|
||||||
const validCustomTags = ['mapping', 'scalar', 'sequence'];
|
|
||||||
|
|
||||||
return customTags.filter(tag => {
|
|
||||||
if (typeof tag === 'string') {
|
|
||||||
const typeInfo = tag.split(' ');
|
|
||||||
const type = (typeInfo[1] && typeInfo[1].toLowerCase()) || 'scalar';
|
|
||||||
|
|
||||||
// We need to check if map is a type because map will throw an error within the yaml-ast-parser
|
|
||||||
if (type === 'map') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return validCustomTags.indexOf(type) !== -1;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Red Hat, Inc. All rights reserved.
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
export function insertionPointReturnValue(pt: number) {
|
|
||||||
return -pt - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function binarySearch(array: number[], sought: number) {
|
|
||||||
let lower = 0;
|
|
||||||
let upper = array.length - 1;
|
|
||||||
|
|
||||||
while (lower <= upper) {
|
|
||||||
const idx = Math.floor((lower + upper) / 2);
|
|
||||||
const value = array[idx];
|
|
||||||
|
|
||||||
if (value === sought) {
|
|
||||||
return idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lower === upper) {
|
|
||||||
const insertionPoint = value < sought ? idx + 1 : idx;
|
|
||||||
return insertionPointReturnValue(insertionPoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sought > value) {
|
|
||||||
lower = idx + 1;
|
|
||||||
} else if (sought < value) {
|
|
||||||
upper = idx - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getLineStartPositions(text: string) {
|
|
||||||
const lineStartPositions = [0];
|
|
||||||
for (let i = 0; i < text.length; i++) {
|
|
||||||
const c = text[i];
|
|
||||||
|
|
||||||
if (c === '\r') {
|
|
||||||
// Check for Windows encoding, otherwise we are old Mac
|
|
||||||
if (i + 1 < text.length && text[i + 1] === '\n') {
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
lineStartPositions.push(i + 1);
|
|
||||||
} else if (c === '\n') {
|
|
||||||
lineStartPositions.push(i + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return lineStartPositions;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getPosition(pos: number, lineStartPositions: number[]) {
|
|
||||||
let line = binarySearch(lineStartPositions, pos);
|
|
||||||
|
|
||||||
if (line < 0) {
|
|
||||||
const insertionPoint = -1 * line - 1;
|
|
||||||
line = insertionPoint - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return { line, column: pos - lineStartPositions[line] };
|
|
||||||
}
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Red Hat, Inc. All rights reserved.
|
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
export class ErrorHandler {
|
|
||||||
private errorResultsList;
|
|
||||||
private textDocument;
|
|
||||||
|
|
||||||
constructor(textDocument) {
|
|
||||||
this.errorResultsList = [];
|
|
||||||
this.textDocument = textDocument;
|
|
||||||
}
|
|
||||||
|
|
||||||
public addErrorResult(errorNode, errorMessage, errorType) {
|
|
||||||
this.errorResultsList.push({
|
|
||||||
severity: errorType,
|
|
||||||
range: {
|
|
||||||
start: this.textDocument.positionAt(errorNode.startPosition),
|
|
||||||
end: this.textDocument.positionAt(errorNode.endPosition),
|
|
||||||
},
|
|
||||||
message: errorMessage,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public getErrorResultsList() {
|
|
||||||
return this.errorResultsList;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
// tslint:disable-next-line: no-any
|
|
||||||
export function stringifyObject(
|
|
||||||
obj: any,
|
|
||||||
indent: string,
|
|
||||||
stringifyLiteral: (val: any) => string
|
|
||||||
): string {
|
|
||||||
if (obj !== null && typeof obj === 'object') {
|
|
||||||
const newIndent = indent + '\t';
|
|
||||||
if (Array.isArray(obj)) {
|
|
||||||
if (obj.length === 0) {
|
|
||||||
return '[]';
|
|
||||||
}
|
|
||||||
let result = '[\n';
|
|
||||||
for (let i = 0; i < obj.length; i++) {
|
|
||||||
result +=
|
|
||||||
newIndent + stringifyObject(obj[i], newIndent, stringifyLiteral);
|
|
||||||
if (i < obj.length - 1) {
|
|
||||||
result += ',';
|
|
||||||
}
|
|
||||||
result += '\n';
|
|
||||||
}
|
|
||||||
result += indent + ']';
|
|
||||||
return result;
|
|
||||||
} else {
|
|
||||||
const keys = Object.keys(obj);
|
|
||||||
if (keys.length === 0) {
|
|
||||||
return '{}';
|
|
||||||
}
|
|
||||||
let result = '{\n';
|
|
||||||
for (let i = 0; i < keys.length; i++) {
|
|
||||||
const key = keys[i];
|
|
||||||
|
|
||||||
result +=
|
|
||||||
newIndent +
|
|
||||||
JSON.stringify(key) +
|
|
||||||
': ' +
|
|
||||||
stringifyObject(obj[key], newIndent, stringifyLiteral);
|
|
||||||
if (i < keys.length - 1) {
|
|
||||||
result += ',';
|
|
||||||
}
|
|
||||||
result += '\n';
|
|
||||||
}
|
|
||||||
result += indent + '}';
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return stringifyLiteral(obj);
|
|
||||||
}
|
|
||||||
|
|
@ -1,83 +0,0 @@
|
||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
// tslint:disable-next-line: no-any
|
|
||||||
export function equals(one: any, other: any): boolean {
|
|
||||||
if (one === other) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
one === null ||
|
|
||||||
one === undefined ||
|
|
||||||
other === null ||
|
|
||||||
other === undefined
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (typeof one !== typeof other) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (typeof one !== 'object') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (Array.isArray(one) !== Array.isArray(other)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let i: number, key: string;
|
|
||||||
|
|
||||||
if (Array.isArray(one)) {
|
|
||||||
if (one.length !== other.length) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (i = 0; i < one.length; i++) {
|
|
||||||
if (!equals(one[i], other[i])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const oneKeys: string[] = [];
|
|
||||||
|
|
||||||
for (key in one) {
|
|
||||||
oneKeys.push(key);
|
|
||||||
}
|
|
||||||
oneKeys.sort();
|
|
||||||
const otherKeys: string[] = [];
|
|
||||||
for (key in other) {
|
|
||||||
otherKeys.push(key);
|
|
||||||
}
|
|
||||||
otherKeys.sort();
|
|
||||||
if (!equals(oneKeys, otherKeys)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (i = 0; i < oneKeys.length; i++) {
|
|
||||||
if (!equals(one[oneKeys[i]], other[oneKeys[i]])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// tslint:disable-next-line: no-any
|
|
||||||
export function isNumber(val: any): val is number {
|
|
||||||
return typeof val === 'number';
|
|
||||||
}
|
|
||||||
|
|
||||||
// tslint:disable-next-line: no-any
|
|
||||||
export function isDefined(val: any): val is object {
|
|
||||||
return typeof val !== 'undefined';
|
|
||||||
}
|
|
||||||
|
|
||||||
// tslint:disable-next-line: no-any
|
|
||||||
export function isBoolean(val: any): val is boolean {
|
|
||||||
return typeof val === 'boolean';
|
|
||||||
}
|
|
||||||
|
|
||||||
// tslint:disable-next-line: no-any
|
|
||||||
export function isString(val: any): val is string {
|
|
||||||
return typeof val === 'string';
|
|
||||||
}
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
import { WorkspaceFolder } from 'vscode-languageserver';
|
|
||||||
import { join, normalize } from 'path';
|
|
||||||
import { URI } from 'vscode-uri';
|
|
||||||
|
|
||||||
export const isRelativePath = (path: string): boolean => {
|
|
||||||
const relativePathRegex = /^(((\.\.?)|([\w-\. ]+))(\/|\\\\?))*[\w-\. ]*\.[\w-]+$/i;
|
|
||||||
return relativePathRegex.test(path);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const relativeToAbsolutePath = (
|
|
||||||
workspaceFolders: WorkspaceFolder[],
|
|
||||||
workspaceRoot: URI,
|
|
||||||
uri: string
|
|
||||||
): string => {
|
|
||||||
// Iterate through all of the workspace root folders
|
|
||||||
for (const folder of workspaceFolders) {
|
|
||||||
// If the requested schema URI specifies a workspace root folder
|
|
||||||
// Convert it into an absolute path with the appropriate root folder path
|
|
||||||
if (uri.startsWith(folder.name)) {
|
|
||||||
const pathToFolder = URI.parse(folder.uri).fsPath;
|
|
||||||
const withoutFolderPrefix = uri.split(folder.name);
|
|
||||||
withoutFolderPrefix.shift();
|
|
||||||
|
|
||||||
return URI.file(
|
|
||||||
join(pathToFolder, withoutFolderPrefix.join())
|
|
||||||
).toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If a root folder was not specified, resolve the relative URI
|
|
||||||
// Against the location of the workspace file instead
|
|
||||||
if (workspaceRoot) {
|
|
||||||
return URI.file(join(workspaceRoot.fsPath, uri)).toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback in case nothing could be applied
|
|
||||||
return normalize(uri);
|
|
||||||
};
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
export function startsWith(haystack: string, needle: string): boolean {
|
|
||||||
if (haystack.length < needle.length) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < needle.length; i++) {
|
|
||||||
if (haystack[i] !== needle[i]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines if haystack ends with needle.
|
|
||||||
*/
|
|
||||||
export function endsWith(haystack: string, needle: string): boolean {
|
|
||||||
const diff = haystack.length - needle.length;
|
|
||||||
if (diff > 0) {
|
|
||||||
return haystack.lastIndexOf(needle) === diff;
|
|
||||||
} else if (diff === 0) {
|
|
||||||
return haystack === needle;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function convertSimple2RegExp(pattern: string): RegExp {
|
|
||||||
const match = pattern.match(new RegExp('^/(.*?)/([gimy]*)$'));
|
|
||||||
return match
|
|
||||||
? convertRegexString2RegExp(match[1], match[2])
|
|
||||||
: convertGlobalPattern2RegExp(pattern);
|
|
||||||
}
|
|
||||||
|
|
||||||
function convertGlobalPattern2RegExp(pattern: string): RegExp {
|
|
||||||
return new RegExp(
|
|
||||||
pattern
|
|
||||||
.replace(/[\-\\\{\}\+\?\|\^\$\.\,\[\]\(\)\#\s]/g, '\\$&')
|
|
||||||
.replace(/[\*]/g, '.*') + '$'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function convertRegexString2RegExp(pattern: string, flag: string): RegExp {
|
|
||||||
return new RegExp(pattern, flag);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function convertSimple2RegExpPattern(pattern: string): string {
|
|
||||||
return pattern
|
|
||||||
.replace(/[\-\\\{\}\+\?\|\^\$\.\,\[\]\(\)\#\s]/g, '\\$&')
|
|
||||||
.replace(/[\*]/g, '.*');
|
|
||||||
}
|
|
||||||
|
|
@ -1,210 +0,0 @@
|
||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Red Hat, Inc. All rights reserved.
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import {
|
|
||||||
YAMLSchemaService,
|
|
||||||
CustomSchemaProvider,
|
|
||||||
} from './services/yamlSchemaService';
|
|
||||||
import {
|
|
||||||
TextDocument,
|
|
||||||
Position,
|
|
||||||
CompletionList,
|
|
||||||
Diagnostic,
|
|
||||||
Hover,
|
|
||||||
SymbolInformation,
|
|
||||||
DocumentSymbol,
|
|
||||||
CompletionItem,
|
|
||||||
TextEdit,
|
|
||||||
} from 'vscode-languageserver-types';
|
|
||||||
import { JSONSchema } from './jsonSchema04';
|
|
||||||
import { YAMLDocumentSymbols } from './services/documentSymbols';
|
|
||||||
import { YAMLCompletion } from './services/yamlCompletion';
|
|
||||||
import { YAMLHover } from './services/yamlHover';
|
|
||||||
import { YAMLValidation } from './services/yamlValidation';
|
|
||||||
import { YAMLFormatter } from './services/yamlFormatter';
|
|
||||||
import {
|
|
||||||
LanguageService as JSONLanguageService,
|
|
||||||
getLanguageService as getJSONLanguageService,
|
|
||||||
JSONWorkerContribution,
|
|
||||||
} from 'vscode-json-languageservice';
|
|
||||||
|
|
||||||
export interface LanguageSettings {
|
|
||||||
validate?: boolean; //Setting for whether we want to validate the schema
|
|
||||||
hover?: boolean; //Setting for whether we want to have hover results
|
|
||||||
completion?: boolean; //Setting for whether we want to have completion results
|
|
||||||
format?: boolean; //Setting for whether we want to have the formatter or not
|
|
||||||
isKubernetes?: boolean; //If true then its validating against kubernetes
|
|
||||||
// tslint:disable-next-line: no-any
|
|
||||||
schemas?: any[]; //List of schemas,
|
|
||||||
customTags?: Array<String>; //Array of Custom Tags
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PromiseConstructor {
|
|
||||||
/**
|
|
||||||
* Creates a new Promise.
|
|
||||||
* @param executor A callback used to initialize the promise. This callback is passed two arguments:
|
|
||||||
* a resolve callback used resolve the promise with a value or the result of another promise,
|
|
||||||
* and a reject callback used to reject the promise with a provided reason or error.
|
|
||||||
*/
|
|
||||||
// tslint:disable-next-line: no-any
|
|
||||||
new <T>(
|
|
||||||
executor: (
|
|
||||||
resolve: (value?: T | Thenable<T>) => void,
|
|
||||||
reject: (reason?: any) => void
|
|
||||||
) => void
|
|
||||||
): Thenable<T>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a Promise that is resolved with an array of results when all of the provided Promises
|
|
||||||
* resolve, or rejected when any Promise is rejected.
|
|
||||||
* @param values An array of Promises.
|
|
||||||
* @returns A new Promise.
|
|
||||||
*/
|
|
||||||
all<T>(values: Array<T | Thenable<T>>): Thenable<T[]>;
|
|
||||||
/**
|
|
||||||
* Creates a new rejected promise for the provided reason.
|
|
||||||
* @param reason The reason the promise was rejected.
|
|
||||||
* @returns A new rejected Promise.
|
|
||||||
*/
|
|
||||||
// tslint:disable-next-line: no-any
|
|
||||||
reject<T>(reason: any): Thenable<T>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new resolved promise for the provided value.
|
|
||||||
* @param value A promise.
|
|
||||||
* @returns A promise whose internal state matches the provided promise.
|
|
||||||
*/
|
|
||||||
resolve<T>(value: T | Thenable<T>): Thenable<T>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Thenable<R> {
|
|
||||||
/**
|
|
||||||
* Attaches callbacks for the resolution and/or rejection of the Promise.
|
|
||||||
* @param onfulfilled The callback to execute when the Promise is resolved.
|
|
||||||
* @param onrejected The callback to execute when the Promise is rejected.
|
|
||||||
* @returns A Promise for the completion of which ever callback is executed.
|
|
||||||
*/
|
|
||||||
// tslint:disable-next-line: no-any
|
|
||||||
then<TResult>(
|
|
||||||
onfulfilled?: (value: R) => TResult | Thenable<TResult>,
|
|
||||||
onrejected?: (reason: any) => TResult | Thenable<TResult>
|
|
||||||
): Thenable<TResult>;
|
|
||||||
// tslint:disable-next-line: no-any
|
|
||||||
then<TResult>(
|
|
||||||
onfulfilled?: (value: R) => TResult | Thenable<TResult>,
|
|
||||||
onrejected?: (reason: any) => void
|
|
||||||
): Thenable<TResult>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface WorkspaceContextService {
|
|
||||||
resolveRelativePath(relativePath: string, resource: string): string;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* The schema request service is used to fetch schemas. The result should the schema file comment, or,
|
|
||||||
* in case of an error, a displayable error string
|
|
||||||
*/
|
|
||||||
export interface SchemaRequestService {
|
|
||||||
(uri: string): Thenable<string>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SchemaConfiguration {
|
|
||||||
/**
|
|
||||||
* The URI of the schema, which is also the identifier of the schema.
|
|
||||||
*/
|
|
||||||
uri: string;
|
|
||||||
/**
|
|
||||||
* A list of file names that are associated to the schema. The '*' wildcard can be used. For example '*.schema.json', 'package.json'
|
|
||||||
*/
|
|
||||||
fileMatch?: string[];
|
|
||||||
/**
|
|
||||||
* The schema for the given URI.
|
|
||||||
* If no schema is provided, the schema will be fetched with the schema request service (if available).
|
|
||||||
*/
|
|
||||||
schema?: JSONSchema;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CustomFormatterOptions {
|
|
||||||
singleQuote?: boolean;
|
|
||||||
bracketSpacing?: boolean;
|
|
||||||
proseWrap?: string;
|
|
||||||
printWidth?: number;
|
|
||||||
enable?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LanguageService {
|
|
||||||
configure(settings: LanguageSettings): void;
|
|
||||||
registerCustomSchemaProvider(schemaProvider: CustomSchemaProvider): void;
|
|
||||||
doComplete(
|
|
||||||
document: TextDocument,
|
|
||||||
position: Position,
|
|
||||||
isKubernetes: boolean
|
|
||||||
): Thenable<CompletionList>;
|
|
||||||
doValidation(
|
|
||||||
document: TextDocument,
|
|
||||||
isKubernetes: boolean
|
|
||||||
): Thenable<Diagnostic[]>;
|
|
||||||
doHover(document: TextDocument, position: Position): Thenable<Hover | null>;
|
|
||||||
findDocumentSymbols(document: TextDocument): SymbolInformation[];
|
|
||||||
findDocumentSymbols2(document: TextDocument): DocumentSymbol[];
|
|
||||||
doResolve(completionItem): Thenable<CompletionItem>;
|
|
||||||
resetSchema(uri: string): boolean;
|
|
||||||
doFormat(document: TextDocument, options: CustomFormatterOptions): TextEdit[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getLanguageService(
|
|
||||||
schemaRequestService: SchemaRequestService,
|
|
||||||
workspaceContext: WorkspaceContextService,
|
|
||||||
contributions: JSONWorkerContribution[],
|
|
||||||
promiseConstructor?: PromiseConstructor
|
|
||||||
): LanguageService {
|
|
||||||
const promise = promiseConstructor || Promise;
|
|
||||||
|
|
||||||
const schemaService = new YAMLSchemaService(
|
|
||||||
schemaRequestService,
|
|
||||||
workspaceContext
|
|
||||||
);
|
|
||||||
const completer = new YAMLCompletion(schemaService, contributions, promise);
|
|
||||||
const hover = new YAMLHover(schemaService, promise);
|
|
||||||
const yamlDocumentSymbols = new YAMLDocumentSymbols(schemaService);
|
|
||||||
const yamlValidation = new YAMLValidation(schemaService, promise);
|
|
||||||
const formatter = new YAMLFormatter();
|
|
||||||
|
|
||||||
return {
|
|
||||||
configure: settings => {
|
|
||||||
schemaService.clearExternalSchemas();
|
|
||||||
if (settings.schemas) {
|
|
||||||
settings.schemas.forEach(settings => {
|
|
||||||
schemaService.registerExternalSchema(
|
|
||||||
settings.uri,
|
|
||||||
settings.fileMatch,
|
|
||||||
settings.schema
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
yamlValidation.configure(settings);
|
|
||||||
hover.configure(settings);
|
|
||||||
const customTagsSetting =
|
|
||||||
settings && settings['customTags'] ? settings['customTags'] : [];
|
|
||||||
completer.configure(settings, customTagsSetting);
|
|
||||||
formatter.configure(settings);
|
|
||||||
},
|
|
||||||
registerCustomSchemaProvider: (schemaProvider: CustomSchemaProvider) => {
|
|
||||||
schemaService.registerCustomSchemaProvider(schemaProvider);
|
|
||||||
},
|
|
||||||
doComplete: completer.doComplete.bind(completer),
|
|
||||||
doResolve: completer.doResolve.bind(completer),
|
|
||||||
doValidation: yamlValidation.doValidation.bind(yamlValidation),
|
|
||||||
doHover: hover.doHover.bind(hover),
|
|
||||||
findDocumentSymbols: yamlDocumentSymbols.findDocumentSymbols.bind(
|
|
||||||
yamlDocumentSymbols
|
|
||||||
),
|
|
||||||
findDocumentSymbols2: yamlDocumentSymbols.findHierarchicalDocumentSymbols.bind(
|
|
||||||
yamlDocumentSymbols
|
|
||||||
),
|
|
||||||
resetSchema: (uri: string) => schemaService.onResourceChange(uri),
|
|
||||||
doFormat: formatter.format.bind(formatter),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import { setupMode } from './yamlMode';
|
|
||||||
|
|
||||||
import Emitter = monaco.Emitter;
|
|
||||||
import IEvent = monaco.IEvent;
|
|
||||||
|
|
||||||
declare var require: <T>(
|
|
||||||
moduleId: [string],
|
|
||||||
callback: (module: T) => void
|
|
||||||
) => void;
|
|
||||||
|
|
||||||
// --- YAML configuration and defaults ---------
|
|
||||||
|
|
||||||
export class LanguageServiceDefaultsImpl
|
|
||||||
implements monaco.languages.yaml.LanguageServiceDefaults {
|
|
||||||
private _onDidChange = new Emitter<
|
|
||||||
monaco.languages.yaml.LanguageServiceDefaults
|
|
||||||
>();
|
|
||||||
private _diagnosticsOptions: monaco.languages.yaml.DiagnosticsOptions;
|
|
||||||
private _languageId: string;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
languageId: string,
|
|
||||||
diagnosticsOptions: monaco.languages.yaml.DiagnosticsOptions
|
|
||||||
) {
|
|
||||||
this._languageId = languageId;
|
|
||||||
this.setDiagnosticsOptions(diagnosticsOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
get onDidChange(): IEvent<monaco.languages.yaml.LanguageServiceDefaults> {
|
|
||||||
return this._onDidChange.event;
|
|
||||||
}
|
|
||||||
|
|
||||||
get languageId(): string {
|
|
||||||
return this._languageId;
|
|
||||||
}
|
|
||||||
|
|
||||||
get diagnosticsOptions(): monaco.languages.yaml.DiagnosticsOptions {
|
|
||||||
return this._diagnosticsOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public setDiagnosticsOptions(
|
|
||||||
options: monaco.languages.yaml.DiagnosticsOptions
|
|
||||||
): void {
|
|
||||||
this._diagnosticsOptions = options || Object.create(null);
|
|
||||||
this._onDidChange.fire(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const diagnosticDefault: monaco.languages.yaml.DiagnosticsOptions = {
|
|
||||||
validate: true,
|
|
||||||
schemas: [],
|
|
||||||
enableSchemaRequest: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
const yamlDefaults = new LanguageServiceDefaultsImpl('yaml', diagnosticDefault);
|
|
||||||
|
|
||||||
// Export API
|
|
||||||
function createAPI(): typeof monaco.languages.yaml {
|
|
||||||
return {
|
|
||||||
yamlDefaults,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
monaco.languages.yaml = createAPI();
|
|
||||||
|
|
||||||
// --- Registration to monaco editor ---
|
|
||||||
|
|
||||||
monaco.languages.register({
|
|
||||||
id: 'yaml',
|
|
||||||
extensions: ['.yaml', '.yml'],
|
|
||||||
aliases: ['YAML', 'yaml', 'YML', 'yml'],
|
|
||||||
mimetypes: ['application/x-yaml'],
|
|
||||||
});
|
|
||||||
|
|
||||||
monaco.languages.onLanguage('yaml', () => {
|
|
||||||
setupMode(yamlDefaults);
|
|
||||||
});
|
|
||||||
54
src/monaco.d.ts
vendored
54
src/monaco.d.ts
vendored
|
|
@ -1,54 +0,0 @@
|
||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
declare namespace monaco.languages.yaml {
|
|
||||||
export interface DiagnosticsOptions {
|
|
||||||
/**
|
|
||||||
* If set, the validator will be enabled and perform syntax validation as well as schema based validation.
|
|
||||||
*/
|
|
||||||
readonly validate?: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A list of known schemas and/or associations of schemas to file names.
|
|
||||||
*/
|
|
||||||
readonly schemas?: Array<{
|
|
||||||
/**
|
|
||||||
* The URI of the schema, which is also the identifier of the schema.
|
|
||||||
*/
|
|
||||||
readonly uri: string;
|
|
||||||
/**
|
|
||||||
* A list of file names that are associated to the schema. The '*' wildcard can be used. For example '*.schema.json', 'package.json'
|
|
||||||
*/
|
|
||||||
readonly fileMatch?: string[];
|
|
||||||
/**
|
|
||||||
* The schema for the given URI.
|
|
||||||
*/
|
|
||||||
readonly schema?: any;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If set, the schema service would load schema content on-demand with 'fetch' if available
|
|
||||||
*/
|
|
||||||
readonly enableSchemaRequest?: boolean;
|
|
||||||
/**
|
|
||||||
* If specified, this prefix will be added to all on demand schema requests
|
|
||||||
*/
|
|
||||||
readonly prefix?: string;
|
|
||||||
/**
|
|
||||||
* Whether or not kubernetes yaml is supported
|
|
||||||
*/
|
|
||||||
readonly isKubernetes?: boolean;
|
|
||||||
|
|
||||||
readonly format?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LanguageServiceDefaults {
|
|
||||||
readonly onDidChange: IEvent<LanguageServiceDefaults>;
|
|
||||||
readonly diagnosticsOptions: DiagnosticsOptions;
|
|
||||||
setDiagnosticsOptions(options: DiagnosticsOptions): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const yamlDefaults: LanguageServiceDefaults;
|
|
||||||
}
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
import { NotificationType, RequestType } from 'vscode-languageserver';
|
|
||||||
|
|
||||||
export namespace SchemaAssociationNotification {
|
|
||||||
export const type: NotificationType<{}, {}> = new NotificationType(
|
|
||||||
'json/schemaAssociations'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export namespace DynamicCustomSchemaRequestRegistration {
|
|
||||||
export const type: NotificationType<{}, {}> = new NotificationType(
|
|
||||||
'yaml/registerCustomSchemaRequest'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export namespace VSCodeContentRequest {
|
|
||||||
export const type: RequestType<{}, {}, {}, {}> = new RequestType(
|
|
||||||
'vscode/content'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export namespace CustomSchemaContentRequest {
|
|
||||||
export const type: RequestType<{}, {}, {}, {}> = new RequestType(
|
|
||||||
'custom/schema/content'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export namespace CustomSchemaRequest {
|
|
||||||
export const type: RequestType<{}, {}, {}, {}> = new RequestType(
|
|
||||||
'custom/schema/request'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export namespace ColorSymbolRequest {
|
|
||||||
export const type: RequestType<{}, {}, {}, {}> = new RequestType(
|
|
||||||
'json/colorSymbols'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"module": "esnext",
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"outDir": "../out/esm",
|
|
||||||
"target": "es5",
|
|
||||||
"lib": [
|
|
||||||
"dom",
|
|
||||||
"es5",
|
|
||||||
"es2015.collection",
|
|
||||||
"es2015.promise",
|
|
||||||
"es2016",
|
|
||||||
"es2017.string"
|
|
||||||
],
|
|
||||||
"downlevelIteration": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"module": "umd",
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"outDir": "../out/amd",
|
|
||||||
"forceConsistentCasingInFileNames": true,
|
|
||||||
"target": "es5",
|
|
||||||
"lib": [
|
|
||||||
"dom",
|
|
||||||
"es5",
|
|
||||||
"es2015.collection",
|
|
||||||
"es2015.promise",
|
|
||||||
"es2016",
|
|
||||||
"es2017.string"
|
|
||||||
],
|
|
||||||
"downlevelIteration": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
5
src/typings/refs.d.ts
vendored
5
src/typings/refs.d.ts
vendored
|
|
@ -1,5 +0,0 @@
|
||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
/// <reference path='../../node_modules/monaco-editor-core/monaco.d.ts'/>
|
|
||||||
|
|
@ -1,96 +1,67 @@
|
||||||
/*---------------------------------------------------------------------------------------------
|
import { editor, languages } from 'monaco-editor/esm/vs/editor/editor.api.js';
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import { LanguageServiceDefaultsImpl } from './monaco.contribution';
|
import { WorkerAccessor } from './languageFeatures';
|
||||||
import { YAMLWorker } from './yamlWorker';
|
import { YAMLWorker } from './yamlWorker';
|
||||||
|
|
||||||
import IDisposable = monaco.IDisposable;
|
// 2min
|
||||||
import Uri = monaco.Uri;
|
const STOP_WHEN_IDLE_FOR = 2 * 60 * 1000;
|
||||||
|
|
||||||
const STOP_WHEN_IDLE_FOR = 2 * 60 * 1000; // 2min
|
export function createWorkerManager(
|
||||||
|
defaults: languages.yaml.LanguageServiceDefaults,
|
||||||
|
): WorkerAccessor {
|
||||||
|
let worker: editor.MonacoWebWorker<YAMLWorker>;
|
||||||
|
let client: Promise<YAMLWorker>;
|
||||||
|
let lastUsedTime = 0;
|
||||||
|
|
||||||
export class WorkerManager {
|
const stopWorker = (): void => {
|
||||||
private _defaults: LanguageServiceDefaultsImpl;
|
if (worker) {
|
||||||
private _idleCheckInterval: NodeJS.Timer;
|
worker.dispose();
|
||||||
private _lastUsedTime: number;
|
worker = undefined;
|
||||||
private _configChangeListener: IDisposable;
|
|
||||||
|
|
||||||
private _worker: monaco.editor.MonacoWebWorker<YAMLWorker>;
|
|
||||||
private _client: Promise<YAMLWorker>;
|
|
||||||
|
|
||||||
constructor(defaults: LanguageServiceDefaultsImpl) {
|
|
||||||
this._defaults = defaults;
|
|
||||||
this._worker = null;
|
|
||||||
this._idleCheckInterval = setInterval(() => this._checkIfIdle(), 30 * 1000);
|
|
||||||
this._lastUsedTime = 0;
|
|
||||||
this._configChangeListener = this._defaults.onDidChange(() =>
|
|
||||||
this._stopWorker()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
client = undefined;
|
||||||
|
};
|
||||||
|
|
||||||
public dispose(): void {
|
setInterval(() => {
|
||||||
clearInterval(this._idleCheckInterval);
|
if (!worker) {
|
||||||
this._configChangeListener.dispose();
|
|
||||||
this._stopWorker();
|
|
||||||
}
|
|
||||||
|
|
||||||
public getLanguageServiceWorker(...resources: Uri[]): Promise<YAMLWorker> {
|
|
||||||
let _client: YAMLWorker;
|
|
||||||
return this._getClient()
|
|
||||||
.then(client => {
|
|
||||||
_client = client;
|
|
||||||
})
|
|
||||||
.then(_ => {
|
|
||||||
return this._worker.withSyncedResources(resources);
|
|
||||||
})
|
|
||||||
.then(_ => _client);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _stopWorker(): void {
|
|
||||||
if (this._worker) {
|
|
||||||
this._worker.dispose();
|
|
||||||
this._worker = null;
|
|
||||||
}
|
|
||||||
this._client = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _checkIfIdle(): void {
|
|
||||||
if (!this._worker) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const timePassedSinceLastUsed = Date.now() - this._lastUsedTime;
|
const timePassedSinceLastUsed = Date.now() - lastUsedTime;
|
||||||
if (timePassedSinceLastUsed > STOP_WHEN_IDLE_FOR) {
|
if (timePassedSinceLastUsed > STOP_WHEN_IDLE_FOR) {
|
||||||
this._stopWorker();
|
stopWorker();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}, 30 * 1000);
|
||||||
|
|
||||||
private _getClient(): Promise<YAMLWorker> {
|
// This is necessary to have updated language options take effect (e.g. schema changes)
|
||||||
this._lastUsedTime = Date.now();
|
defaults.onDidChange(() => stopWorker());
|
||||||
|
|
||||||
if (!this._client) {
|
const getClient = (): Promise<YAMLWorker> => {
|
||||||
this._worker = monaco.editor.createWebWorker<YAMLWorker>({
|
lastUsedTime = Date.now();
|
||||||
// module that exports the create() method and returns a `YAMLWorker` instance
|
|
||||||
|
if (!client) {
|
||||||
|
worker = editor.createWebWorker<YAMLWorker>({
|
||||||
|
// Module that exports the create() method and returns a `YAMLWorker` instance
|
||||||
moduleId: 'vs/language/yaml/yamlWorker',
|
moduleId: 'vs/language/yaml/yamlWorker',
|
||||||
|
|
||||||
label: this._defaults.languageId,
|
label: defaults.languageId,
|
||||||
|
|
||||||
// passed in to the create() method
|
// Passed in to the create() method
|
||||||
createData: {
|
createData: {
|
||||||
languageSettings: this._defaults.diagnosticsOptions,
|
languageSettings: defaults.diagnosticsOptions,
|
||||||
languageId: this._defaults.languageId,
|
enableSchemaRequest: defaults.diagnosticsOptions.enableSchemaRequest,
|
||||||
enableSchemaRequest: this._defaults.diagnosticsOptions
|
isKubernetes: defaults.diagnosticsOptions.isKubernetes,
|
||||||
.enableSchemaRequest,
|
customTags: defaults.diagnosticsOptions.customTags,
|
||||||
prefix: this._defaults.diagnosticsOptions.prefix,
|
|
||||||
isKubernetes: this._defaults.diagnosticsOptions.isKubernetes,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
this._client = this._worker.getProxy();
|
client = worker.getProxy();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._client;
|
return client;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
return async (...resources) => {
|
||||||
|
const client = await getClient();
|
||||||
|
await worker.withSyncedResources(resources);
|
||||||
|
return client;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2
src/yaml-ast-parser-custom-tags/.gitignore
vendored
2
src/yaml-ast-parser-custom-tags/.gitignore
vendored
|
|
@ -1,2 +0,0 @@
|
||||||
/*.js
|
|
||||||
/*.js.map
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
export function isNothing(subject) {
|
|
||||||
return typeof subject === 'undefined' || null === subject;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isObject(subject) {
|
|
||||||
return typeof subject === 'object' && null !== subject;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function toArray(sequence) {
|
|
||||||
if (Array.isArray(sequence)) {
|
|
||||||
return sequence;
|
|
||||||
} else if (isNothing(sequence)) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
return [sequence];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function extend(target, source) {
|
|
||||||
var index, length, key, sourceKeys;
|
|
||||||
|
|
||||||
if (source) {
|
|
||||||
sourceKeys = Object.keys(source);
|
|
||||||
|
|
||||||
for (index = 0, length = sourceKeys.length; index < length; index += 1) {
|
|
||||||
key = sourceKeys[index];
|
|
||||||
target[key] = source[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function repeat(string, count) {
|
|
||||||
var result = '',
|
|
||||||
cycle;
|
|
||||||
|
|
||||||
for (cycle = 0; cycle < count; cycle += 1) {
|
|
||||||
result += string;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isNegativeZero(number) {
|
|
||||||
return 0 === number && Number.NEGATIVE_INFINITY === 1 / number;
|
|
||||||
}
|
|
||||||
|
|
@ -1,898 +0,0 @@
|
||||||
'use strict';
|
|
||||||
declare function require(n: string): any;
|
|
||||||
/*eslint-disable no-use-before-define*/
|
|
||||||
|
|
||||||
var common = require('./common');
|
|
||||||
var YAMLException = require('./exception');
|
|
||||||
var DEFAULT_FULL_SCHEMA = require('./schema/default_full');
|
|
||||||
var DEFAULT_SAFE_SCHEMA = require('./schema/default_safe');
|
|
||||||
|
|
||||||
var _toString = Object.prototype.toString;
|
|
||||||
var _hasOwnProperty = Object.prototype.hasOwnProperty;
|
|
||||||
|
|
||||||
var CHAR_TAB = 0x09; /* Tab */
|
|
||||||
var CHAR_LINE_FEED = 0x0a; /* LF */
|
|
||||||
var CHAR_CARRIAGE_RETURN = 0x0d; /* CR */
|
|
||||||
var CHAR_SPACE = 0x20; /* Space */
|
|
||||||
var CHAR_EXCLAMATION = 0x21; /* ! */
|
|
||||||
var CHAR_DOUBLE_QUOTE = 0x22; /* " */
|
|
||||||
var CHAR_SHARP = 0x23; /* # */
|
|
||||||
var CHAR_PERCENT = 0x25; /* % */
|
|
||||||
var CHAR_AMPERSAND = 0x26; /* & */
|
|
||||||
var CHAR_SINGLE_QUOTE = 0x27; /* ' */
|
|
||||||
var CHAR_ASTERISK = 0x2a; /* * */
|
|
||||||
var CHAR_COMMA = 0x2c; /* , */
|
|
||||||
var CHAR_MINUS = 0x2d; /* - */
|
|
||||||
var CHAR_COLON = 0x3a; /* : */
|
|
||||||
var CHAR_GREATER_THAN = 0x3e; /* > */
|
|
||||||
var CHAR_QUESTION = 0x3f; /* ? */
|
|
||||||
var CHAR_COMMERCIAL_AT = 0x40; /* @ */
|
|
||||||
var CHAR_LEFT_SQUARE_BRACKET = 0x5b; /* [ */
|
|
||||||
var CHAR_RIGHT_SQUARE_BRACKET = 0x5d; /* ] */
|
|
||||||
var CHAR_GRAVE_ACCENT = 0x60; /* ` */
|
|
||||||
var CHAR_LEFT_CURLY_BRACKET = 0x7b; /* { */
|
|
||||||
var CHAR_VERTICAL_LINE = 0x7c; /* | */
|
|
||||||
var CHAR_RIGHT_CURLY_BRACKET = 0x7d; /* } */
|
|
||||||
|
|
||||||
var ESCAPE_SEQUENCES = {};
|
|
||||||
|
|
||||||
ESCAPE_SEQUENCES[0x00] = '\\0';
|
|
||||||
ESCAPE_SEQUENCES[0x07] = '\\a';
|
|
||||||
ESCAPE_SEQUENCES[0x08] = '\\b';
|
|
||||||
ESCAPE_SEQUENCES[0x09] = '\\t';
|
|
||||||
ESCAPE_SEQUENCES[0x0a] = '\\n';
|
|
||||||
ESCAPE_SEQUENCES[0x0b] = '\\v';
|
|
||||||
ESCAPE_SEQUENCES[0x0c] = '\\f';
|
|
||||||
ESCAPE_SEQUENCES[0x0d] = '\\r';
|
|
||||||
ESCAPE_SEQUENCES[0x1b] = '\\e';
|
|
||||||
ESCAPE_SEQUENCES[0x22] = '\\"';
|
|
||||||
ESCAPE_SEQUENCES[0x5c] = '\\\\';
|
|
||||||
ESCAPE_SEQUENCES[0x85] = '\\N';
|
|
||||||
ESCAPE_SEQUENCES[0xa0] = '\\_';
|
|
||||||
ESCAPE_SEQUENCES[0x2028] = '\\L';
|
|
||||||
ESCAPE_SEQUENCES[0x2029] = '\\P';
|
|
||||||
|
|
||||||
var DEPRECATED_BOOLEANS_SYNTAX = [
|
|
||||||
'y',
|
|
||||||
'Y',
|
|
||||||
'yes',
|
|
||||||
'Yes',
|
|
||||||
'YES',
|
|
||||||
'on',
|
|
||||||
'On',
|
|
||||||
'ON',
|
|
||||||
'n',
|
|
||||||
'N',
|
|
||||||
'no',
|
|
||||||
'No',
|
|
||||||
'NO',
|
|
||||||
'off',
|
|
||||||
'Off',
|
|
||||||
'OFF',
|
|
||||||
];
|
|
||||||
|
|
||||||
function compileStyleMap(schema, map) {
|
|
||||||
var result, keys, index, length, tag, style, type;
|
|
||||||
|
|
||||||
if (null === map) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
result = {};
|
|
||||||
keys = Object.keys(map);
|
|
||||||
|
|
||||||
for (index = 0, length = keys.length; index < length; index += 1) {
|
|
||||||
tag = keys[index];
|
|
||||||
style = String(map[tag]);
|
|
||||||
|
|
||||||
if ('!!' === tag.slice(0, 2)) {
|
|
||||||
tag = 'tag:yaml.org,2002:' + tag.slice(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
type = schema.compiledTypeMap[tag];
|
|
||||||
|
|
||||||
if (type && _hasOwnProperty.call(type.styleAliases, style)) {
|
|
||||||
style = type.styleAliases[style];
|
|
||||||
}
|
|
||||||
|
|
||||||
result[tag] = style;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function encodeHex(character) {
|
|
||||||
var string, handle, length;
|
|
||||||
|
|
||||||
string = character.toString(16).toUpperCase();
|
|
||||||
|
|
||||||
if (character <= 0xff) {
|
|
||||||
handle = 'x';
|
|
||||||
length = 2;
|
|
||||||
} else if (character <= 0xffff) {
|
|
||||||
handle = 'u';
|
|
||||||
length = 4;
|
|
||||||
} else if (character <= 0xffffffff) {
|
|
||||||
handle = 'U';
|
|
||||||
length = 8;
|
|
||||||
} else {
|
|
||||||
throw new YAMLException(
|
|
||||||
'code point within a string may not be greater than 0xFFFFFFFF'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return '\\' + handle + common.repeat('0', length - string.length) + string;
|
|
||||||
}
|
|
||||||
|
|
||||||
function State(options) {
|
|
||||||
this.schema = options['schema'] || DEFAULT_FULL_SCHEMA;
|
|
||||||
this.indent = Math.max(1, options['indent'] || 2);
|
|
||||||
this.skipInvalid = options['skipInvalid'] || false;
|
|
||||||
this.flowLevel = common.isNothing(options['flowLevel'])
|
|
||||||
? -1
|
|
||||||
: options['flowLevel'];
|
|
||||||
this.styleMap = compileStyleMap(this.schema, options['styles'] || null);
|
|
||||||
|
|
||||||
this.implicitTypes = this.schema.compiledImplicit;
|
|
||||||
this.explicitTypes = this.schema.compiledExplicit;
|
|
||||||
|
|
||||||
this.tag = null;
|
|
||||||
this.result = '';
|
|
||||||
|
|
||||||
this.duplicates = [];
|
|
||||||
this.usedDuplicates = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function indentString(string: string, spaces) {
|
|
||||||
var ind = common.repeat(' ', spaces),
|
|
||||||
position = 0,
|
|
||||||
next = -1,
|
|
||||||
result = '',
|
|
||||||
line,
|
|
||||||
length = string.length;
|
|
||||||
|
|
||||||
while (position < length) {
|
|
||||||
next = string.indexOf('\n', position);
|
|
||||||
if (next === -1) {
|
|
||||||
line = string.slice(position);
|
|
||||||
position = length;
|
|
||||||
} else {
|
|
||||||
line = string.slice(position, next + 1);
|
|
||||||
position = next + 1;
|
|
||||||
}
|
|
||||||
if (line.length && line !== '\n') {
|
|
||||||
result += ind;
|
|
||||||
}
|
|
||||||
result += line;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateNextLine(state, level) {
|
|
||||||
return '\n' + common.repeat(' ', state.indent * level);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testImplicitResolving(state, str) {
|
|
||||||
var index, length, type;
|
|
||||||
|
|
||||||
for (
|
|
||||||
index = 0, length = state.implicitTypes.length;
|
|
||||||
index < length;
|
|
||||||
index += 1
|
|
||||||
) {
|
|
||||||
type = state.implicitTypes[index];
|
|
||||||
|
|
||||||
if (type.resolve(str)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function StringBuilder(source) {
|
|
||||||
this.source = source;
|
|
||||||
this.result = '';
|
|
||||||
this.checkpoint = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
StringBuilder.prototype.takeUpTo = function(position) {
|
|
||||||
var er;
|
|
||||||
|
|
||||||
if (position < this.checkpoint) {
|
|
||||||
er = new Error('position should be > checkpoint');
|
|
||||||
er.position = position;
|
|
||||||
er.checkpoint = this.checkpoint;
|
|
||||||
throw er;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.result += this.source.slice(this.checkpoint, position);
|
|
||||||
this.checkpoint = position;
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
StringBuilder.prototype.escapeChar = function() {
|
|
||||||
var character, esc;
|
|
||||||
|
|
||||||
character = this.source.charCodeAt(this.checkpoint);
|
|
||||||
esc = ESCAPE_SEQUENCES[character] || encodeHex(character);
|
|
||||||
this.result += esc;
|
|
||||||
this.checkpoint += 1;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
StringBuilder.prototype.finish = function() {
|
|
||||||
if (this.source.length > this.checkpoint) {
|
|
||||||
this.takeUpTo(this.source.length);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function writeScalar(state, object, level) {
|
|
||||||
var simple,
|
|
||||||
first,
|
|
||||||
spaceWrap,
|
|
||||||
folded,
|
|
||||||
literal,
|
|
||||||
single,
|
|
||||||
double,
|
|
||||||
sawLineFeed,
|
|
||||||
linePosition,
|
|
||||||
longestLine,
|
|
||||||
indent,
|
|
||||||
max,
|
|
||||||
character,
|
|
||||||
position,
|
|
||||||
escapeSeq,
|
|
||||||
hexEsc,
|
|
||||||
previous,
|
|
||||||
lineLength,
|
|
||||||
modifier,
|
|
||||||
trailingLineBreaks,
|
|
||||||
result;
|
|
||||||
|
|
||||||
if (0 === object.length) {
|
|
||||||
state.dump = "''";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (object.indexOf('!include') == 0) {
|
|
||||||
state.dump = '' + object; //FIXME
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (object.indexOf('!$$$novalue') == 0) {
|
|
||||||
state.dump = ''; //FIXME
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (-1 !== DEPRECATED_BOOLEANS_SYNTAX.indexOf(object)) {
|
|
||||||
state.dump = "'" + object + "'";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
simple = true;
|
|
||||||
first = object.length ? object.charCodeAt(0) : 0;
|
|
||||||
spaceWrap =
|
|
||||||
CHAR_SPACE === first || CHAR_SPACE === object.charCodeAt(object.length - 1);
|
|
||||||
|
|
||||||
// Simplified check for restricted first characters
|
|
||||||
// http://www.yaml.org/spec/1.2/spec.html#ns-plain-first%28c%29
|
|
||||||
if (
|
|
||||||
CHAR_MINUS === first ||
|
|
||||||
CHAR_QUESTION === first ||
|
|
||||||
CHAR_COMMERCIAL_AT === first ||
|
|
||||||
CHAR_GRAVE_ACCENT === first
|
|
||||||
) {
|
|
||||||
simple = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// can only use > and | if not wrapped in spaces.
|
|
||||||
if (spaceWrap) {
|
|
||||||
simple = false;
|
|
||||||
folded = false;
|
|
||||||
literal = false;
|
|
||||||
} else {
|
|
||||||
folded = true;
|
|
||||||
literal = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
single = true;
|
|
||||||
double = new StringBuilder(object);
|
|
||||||
|
|
||||||
sawLineFeed = false;
|
|
||||||
linePosition = 0;
|
|
||||||
longestLine = 0;
|
|
||||||
|
|
||||||
indent = state.indent * level;
|
|
||||||
max = 80;
|
|
||||||
if (indent < 40) {
|
|
||||||
max -= indent;
|
|
||||||
} else {
|
|
||||||
max = 40;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (position = 0; position < object.length; position++) {
|
|
||||||
character = object.charCodeAt(position);
|
|
||||||
if (simple) {
|
|
||||||
// Characters that can never appear in the simple scalar
|
|
||||||
if (!simpleChar(character)) {
|
|
||||||
simple = false;
|
|
||||||
} else {
|
|
||||||
// Still simple. If we make it all the way through like
|
|
||||||
// this, then we can just dump the string as-is.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (single && character === CHAR_SINGLE_QUOTE) {
|
|
||||||
single = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
escapeSeq = ESCAPE_SEQUENCES[character];
|
|
||||||
hexEsc = needsHexEscape(character);
|
|
||||||
|
|
||||||
if (!escapeSeq && !hexEsc) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
character !== CHAR_LINE_FEED &&
|
|
||||||
character !== CHAR_DOUBLE_QUOTE &&
|
|
||||||
character !== CHAR_SINGLE_QUOTE
|
|
||||||
) {
|
|
||||||
folded = false;
|
|
||||||
literal = false;
|
|
||||||
} else if (character === CHAR_LINE_FEED) {
|
|
||||||
sawLineFeed = true;
|
|
||||||
single = false;
|
|
||||||
if (position > 0) {
|
|
||||||
previous = object.charCodeAt(position - 1);
|
|
||||||
if (previous === CHAR_SPACE) {
|
|
||||||
literal = false;
|
|
||||||
folded = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (folded) {
|
|
||||||
lineLength = position - linePosition;
|
|
||||||
linePosition = position;
|
|
||||||
if (lineLength > longestLine) {
|
|
||||||
longestLine = lineLength;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (character !== CHAR_DOUBLE_QUOTE) {
|
|
||||||
single = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
double.takeUpTo(position);
|
|
||||||
double.escapeChar();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (simple && testImplicitResolving(state, object)) {
|
|
||||||
simple = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
modifier = '';
|
|
||||||
if (folded || literal) {
|
|
||||||
trailingLineBreaks = 0;
|
|
||||||
if (object.charCodeAt(object.length - 1) === CHAR_LINE_FEED) {
|
|
||||||
trailingLineBreaks += 1;
|
|
||||||
if (object.charCodeAt(object.length - 2) === CHAR_LINE_FEED) {
|
|
||||||
trailingLineBreaks += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trailingLineBreaks === 0) {
|
|
||||||
modifier = '-';
|
|
||||||
} else if (trailingLineBreaks === 2) {
|
|
||||||
modifier = '+';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (literal && longestLine < max) {
|
|
||||||
folded = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If it's literally one line, then don't bother with the literal.
|
|
||||||
// We may still want to do a fold, though, if it's a super long line.
|
|
||||||
if (!sawLineFeed) {
|
|
||||||
literal = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (simple) {
|
|
||||||
state.dump = object;
|
|
||||||
} else if (single) {
|
|
||||||
state.dump = "'" + object + "'";
|
|
||||||
} else if (folded) {
|
|
||||||
result = fold(object, max);
|
|
||||||
state.dump = '>' + modifier + '\n' + indentString(result, indent);
|
|
||||||
} else if (literal) {
|
|
||||||
if (!modifier) {
|
|
||||||
object = object.replace(/\n$/, '');
|
|
||||||
}
|
|
||||||
state.dump = '|' + modifier + '\n' + indentString(object, indent);
|
|
||||||
} else if (double) {
|
|
||||||
double.finish();
|
|
||||||
state.dump = '"' + double.result + '"';
|
|
||||||
} else {
|
|
||||||
throw new Error('Failed to dump scalar value');
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The `trailing` var is a regexp match of any trailing `\n` characters.
|
|
||||||
//
|
|
||||||
// There are three cases we care about:
|
|
||||||
//
|
|
||||||
// 1. One trailing `\n` on the string. Just use `|` or `>`.
|
|
||||||
// This is the assumed default. (trailing = null)
|
|
||||||
// 2. No trailing `\n` on the string. Use `|-` or `>-` to "chomp" the end.
|
|
||||||
// 3. More than one trailing `\n` on the string. Use `|+` or `>+`.
|
|
||||||
//
|
|
||||||
// In the case of `>+`, these line breaks are *not* doubled (like the line
|
|
||||||
// breaks within the string), so it's important to only end with the exact
|
|
||||||
// same number as we started.
|
|
||||||
function fold(object, max) {
|
|
||||||
var result = '',
|
|
||||||
position = 0,
|
|
||||||
length = object.length,
|
|
||||||
trailing = /\n+$/.exec(object),
|
|
||||||
newLine;
|
|
||||||
|
|
||||||
if (trailing) {
|
|
||||||
length = trailing.index + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (position < length) {
|
|
||||||
newLine = object.indexOf('\n', position);
|
|
||||||
if (newLine > length || newLine === -1) {
|
|
||||||
if (result) {
|
|
||||||
result += '\n\n';
|
|
||||||
}
|
|
||||||
result += foldLine(object.slice(position, length), max);
|
|
||||||
position = length;
|
|
||||||
} else {
|
|
||||||
if (result) {
|
|
||||||
result += '\n\n';
|
|
||||||
}
|
|
||||||
result += foldLine(object.slice(position, newLine), max);
|
|
||||||
position = newLine + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (trailing && trailing[0] !== '\n') {
|
|
||||||
result += trailing[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function foldLine(line, max) {
|
|
||||||
if (line === '') {
|
|
||||||
return line;
|
|
||||||
}
|
|
||||||
|
|
||||||
var foldRe = /[^\s] [^\s]/g,
|
|
||||||
result = '',
|
|
||||||
prevMatch = 0,
|
|
||||||
foldStart = 0,
|
|
||||||
match = foldRe.exec(line),
|
|
||||||
index,
|
|
||||||
foldEnd,
|
|
||||||
folded;
|
|
||||||
|
|
||||||
while (match) {
|
|
||||||
index = match.index;
|
|
||||||
|
|
||||||
// when we cross the max len, if the previous match would've
|
|
||||||
// been ok, use that one, and carry on. If there was no previous
|
|
||||||
// match on this fold section, then just have a long line.
|
|
||||||
if (index - foldStart > max) {
|
|
||||||
if (prevMatch !== foldStart) {
|
|
||||||
foldEnd = prevMatch;
|
|
||||||
} else {
|
|
||||||
foldEnd = index;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result) {
|
|
||||||
result += '\n';
|
|
||||||
}
|
|
||||||
folded = line.slice(foldStart, foldEnd);
|
|
||||||
result += folded;
|
|
||||||
foldStart = foldEnd + 1;
|
|
||||||
}
|
|
||||||
prevMatch = index + 1;
|
|
||||||
match = foldRe.exec(line);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result) {
|
|
||||||
result += '\n';
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we end up with one last word at the end, then the last bit might
|
|
||||||
// be slightly bigger than we wanted, because we exited out of the loop.
|
|
||||||
if (foldStart !== prevMatch && line.length - foldStart > max) {
|
|
||||||
result +=
|
|
||||||
line.slice(foldStart, prevMatch) + '\n' + line.slice(prevMatch + 1);
|
|
||||||
} else {
|
|
||||||
result += line.slice(foldStart);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true if character can be found in a simple scalar
|
|
||||||
function simpleChar(character) {
|
|
||||||
return (
|
|
||||||
CHAR_TAB !== character &&
|
|
||||||
CHAR_LINE_FEED !== character &&
|
|
||||||
CHAR_CARRIAGE_RETURN !== character &&
|
|
||||||
CHAR_COMMA !== character &&
|
|
||||||
CHAR_LEFT_SQUARE_BRACKET !== character &&
|
|
||||||
CHAR_RIGHT_SQUARE_BRACKET !== character &&
|
|
||||||
CHAR_LEFT_CURLY_BRACKET !== character &&
|
|
||||||
CHAR_RIGHT_CURLY_BRACKET !== character &&
|
|
||||||
CHAR_SHARP !== character &&
|
|
||||||
CHAR_AMPERSAND !== character &&
|
|
||||||
CHAR_ASTERISK !== character &&
|
|
||||||
CHAR_EXCLAMATION !== character &&
|
|
||||||
CHAR_VERTICAL_LINE !== character &&
|
|
||||||
CHAR_GREATER_THAN !== character &&
|
|
||||||
CHAR_SINGLE_QUOTE !== character &&
|
|
||||||
CHAR_DOUBLE_QUOTE !== character &&
|
|
||||||
CHAR_PERCENT !== character &&
|
|
||||||
CHAR_COLON !== character &&
|
|
||||||
!ESCAPE_SEQUENCES[character] &&
|
|
||||||
!needsHexEscape(character)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true if the character code needs to be escaped.
|
|
||||||
function needsHexEscape(character) {
|
|
||||||
return !(
|
|
||||||
(0x00020 <= character && character <= 0x00007e) ||
|
|
||||||
0x00085 === character ||
|
|
||||||
(0x000a0 <= character && character <= 0x00d7ff) ||
|
|
||||||
(0x0e000 <= character && character <= 0x00fffd) ||
|
|
||||||
(0x10000 <= character && character <= 0x10ffff)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function writeFlowSequence(state, level, object) {
|
|
||||||
var _result = '',
|
|
||||||
_tag = state.tag,
|
|
||||||
index,
|
|
||||||
length;
|
|
||||||
|
|
||||||
for (index = 0, length = object.length; index < length; index += 1) {
|
|
||||||
// Write only valid elements.
|
|
||||||
if (writeNode(state, level, object[index], false, false)) {
|
|
||||||
if (0 !== index) {
|
|
||||||
_result += ', ';
|
|
||||||
}
|
|
||||||
_result += state.dump;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
state.tag = _tag;
|
|
||||||
state.dump = '[' + _result + ']';
|
|
||||||
}
|
|
||||||
|
|
||||||
function writeBlockSequence(state, level, object, compact) {
|
|
||||||
var _result = '',
|
|
||||||
_tag = state.tag,
|
|
||||||
index,
|
|
||||||
length;
|
|
||||||
|
|
||||||
for (index = 0, length = object.length; index < length; index += 1) {
|
|
||||||
// Write only valid elements.
|
|
||||||
if (writeNode(state, level + 1, object[index], true, true)) {
|
|
||||||
if (!compact || 0 !== index) {
|
|
||||||
_result += generateNextLine(state, level);
|
|
||||||
}
|
|
||||||
_result += '- ' + state.dump;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
state.tag = _tag;
|
|
||||||
state.dump = _result || '[]'; // Empty sequence if no valid values.
|
|
||||||
}
|
|
||||||
|
|
||||||
function writeFlowMapping(state, level, object) {
|
|
||||||
var _result = '',
|
|
||||||
_tag = state.tag,
|
|
||||||
objectKeyList = Object.keys(object),
|
|
||||||
index,
|
|
||||||
length,
|
|
||||||
objectKey,
|
|
||||||
objectValue,
|
|
||||||
pairBuffer;
|
|
||||||
|
|
||||||
for (index = 0, length = objectKeyList.length; index < length; index += 1) {
|
|
||||||
pairBuffer = '';
|
|
||||||
|
|
||||||
if (0 !== index) {
|
|
||||||
pairBuffer += ', ';
|
|
||||||
}
|
|
||||||
|
|
||||||
objectKey = objectKeyList[index];
|
|
||||||
objectValue = object[objectKey];
|
|
||||||
|
|
||||||
if (!writeNode(state, level, objectKey, false, false)) {
|
|
||||||
continue; // Skip this pair because of invalid key;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.dump.length > 1024) {
|
|
||||||
pairBuffer += '? ';
|
|
||||||
}
|
|
||||||
|
|
||||||
pairBuffer += state.dump + ': ';
|
|
||||||
|
|
||||||
if (!writeNode(state, level, objectValue, false, false)) {
|
|
||||||
continue; // Skip this pair because of invalid value.
|
|
||||||
}
|
|
||||||
|
|
||||||
pairBuffer += state.dump;
|
|
||||||
|
|
||||||
// Both key and value are valid.
|
|
||||||
_result += pairBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
state.tag = _tag;
|
|
||||||
state.dump = '{' + _result + '}';
|
|
||||||
}
|
|
||||||
|
|
||||||
function writeBlockMapping(state, level, object, compact) {
|
|
||||||
var _result = '',
|
|
||||||
_tag = state.tag,
|
|
||||||
objectKeyList = Object.keys(object),
|
|
||||||
index,
|
|
||||||
length,
|
|
||||||
objectKey,
|
|
||||||
objectValue,
|
|
||||||
explicitPair,
|
|
||||||
pairBuffer;
|
|
||||||
|
|
||||||
for (index = 0, length = objectKeyList.length; index < length; index += 1) {
|
|
||||||
pairBuffer = '';
|
|
||||||
|
|
||||||
if (!compact || 0 !== index) {
|
|
||||||
pairBuffer += generateNextLine(state, level);
|
|
||||||
}
|
|
||||||
|
|
||||||
objectKey = objectKeyList[index];
|
|
||||||
objectValue = object[objectKey];
|
|
||||||
|
|
||||||
if (!writeNode(state, level + 1, objectKey, true, true)) {
|
|
||||||
continue; // Skip this pair because of invalid key.
|
|
||||||
}
|
|
||||||
|
|
||||||
explicitPair =
|
|
||||||
(null !== state.tag && '?' !== state.tag) ||
|
|
||||||
(state.dump && state.dump.length > 1024);
|
|
||||||
|
|
||||||
if (explicitPair) {
|
|
||||||
if (state.dump && CHAR_LINE_FEED === state.dump.charCodeAt(0)) {
|
|
||||||
pairBuffer += '?';
|
|
||||||
} else {
|
|
||||||
pairBuffer += '? ';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pairBuffer += state.dump;
|
|
||||||
|
|
||||||
if (explicitPair) {
|
|
||||||
pairBuffer += generateNextLine(state, level);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!writeNode(state, level + 1, objectValue, true, explicitPair)) {
|
|
||||||
continue; // Skip this pair because of invalid value.
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.dump && CHAR_LINE_FEED === state.dump.charCodeAt(0)) {
|
|
||||||
pairBuffer += ':';
|
|
||||||
} else {
|
|
||||||
pairBuffer += ': ';
|
|
||||||
}
|
|
||||||
|
|
||||||
pairBuffer += state.dump;
|
|
||||||
|
|
||||||
// Both key and value are valid.
|
|
||||||
_result += pairBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
state.tag = _tag;
|
|
||||||
state.dump = _result || '{}'; // Empty mapping if no valid pairs.
|
|
||||||
}
|
|
||||||
|
|
||||||
function detectType(state, object, explicit) {
|
|
||||||
var _result, typeList, index, length, type, style;
|
|
||||||
|
|
||||||
typeList = explicit ? state.explicitTypes : state.implicitTypes;
|
|
||||||
|
|
||||||
for (index = 0, length = typeList.length; index < length; index += 1) {
|
|
||||||
type = typeList[index];
|
|
||||||
|
|
||||||
if (
|
|
||||||
(type.instanceOf || type.predicate) &&
|
|
||||||
(!type.instanceOf ||
|
|
||||||
('object' === typeof object && object instanceof type.instanceOf)) &&
|
|
||||||
(!type.predicate || type.predicate(object))
|
|
||||||
) {
|
|
||||||
state.tag = explicit ? type.tag : '?';
|
|
||||||
|
|
||||||
if (type.represent) {
|
|
||||||
style = state.styleMap[type.tag] || type.defaultStyle;
|
|
||||||
|
|
||||||
if ('[object Function]' === _toString.call(type.represent)) {
|
|
||||||
_result = type.represent(object, style);
|
|
||||||
} else if (_hasOwnProperty.call(type.represent, style)) {
|
|
||||||
_result = type.represent[style](object, style);
|
|
||||||
} else {
|
|
||||||
throw new YAMLException(
|
|
||||||
'!<' + type.tag + '> tag resolver accepts not "' + style + '" style'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
state.dump = _result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serializes `object` and writes it to global `result`.
|
|
||||||
// Returns true on success, or false on invalid object.
|
|
||||||
//
|
|
||||||
function writeNode(state, level, object, block, compact) {
|
|
||||||
state.tag = null;
|
|
||||||
state.dump = object;
|
|
||||||
|
|
||||||
if (!detectType(state, object, false)) {
|
|
||||||
detectType(state, object, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
var type = _toString.call(state.dump);
|
|
||||||
|
|
||||||
if (block) {
|
|
||||||
block = 0 > state.flowLevel || state.flowLevel > level;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
(null !== state.tag && '?' !== state.tag) ||
|
|
||||||
(2 !== state.indent && level > 0)
|
|
||||||
) {
|
|
||||||
compact = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var objectOrArray = '[object Object]' === type || '[object Array]' === type,
|
|
||||||
duplicateIndex,
|
|
||||||
duplicate;
|
|
||||||
|
|
||||||
if (objectOrArray) {
|
|
||||||
duplicateIndex = state.duplicates.indexOf(object);
|
|
||||||
duplicate = duplicateIndex !== -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (duplicate && state.usedDuplicates[duplicateIndex]) {
|
|
||||||
state.dump = '*ref_' + duplicateIndex;
|
|
||||||
} else {
|
|
||||||
if (objectOrArray && duplicate && !state.usedDuplicates[duplicateIndex]) {
|
|
||||||
state.usedDuplicates[duplicateIndex] = true;
|
|
||||||
}
|
|
||||||
if ('[object Object]' === type) {
|
|
||||||
if (block && 0 !== Object.keys(state.dump).length) {
|
|
||||||
writeBlockMapping(state, level, state.dump, compact);
|
|
||||||
if (duplicate) {
|
|
||||||
state.dump =
|
|
||||||
'&ref_' + duplicateIndex + (0 === level ? '\n' : '') + state.dump;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
writeFlowMapping(state, level, state.dump);
|
|
||||||
if (duplicate) {
|
|
||||||
state.dump = '&ref_' + duplicateIndex + ' ' + state.dump;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if ('[object Array]' === type) {
|
|
||||||
if (block && 0 !== state.dump.length) {
|
|
||||||
writeBlockSequence(state, level, state.dump, compact);
|
|
||||||
if (duplicate) {
|
|
||||||
state.dump =
|
|
||||||
'&ref_' + duplicateIndex + (0 === level ? '\n' : '') + state.dump;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
writeFlowSequence(state, level, state.dump);
|
|
||||||
if (duplicate) {
|
|
||||||
state.dump = '&ref_' + duplicateIndex + ' ' + state.dump;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if ('[object String]' === type) {
|
|
||||||
if ('?' !== state.tag) {
|
|
||||||
writeScalar(state, state.dump, level);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (state.skipInvalid) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
throw new YAMLException('unacceptable kind of an object to dump ' + type);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (null !== state.tag && '?' !== state.tag) {
|
|
||||||
state.dump = '!<' + state.tag + '> ' + state.dump;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDuplicateReferences(object, state) {
|
|
||||||
var objects = [],
|
|
||||||
duplicatesIndexes = [],
|
|
||||||
index,
|
|
||||||
length;
|
|
||||||
|
|
||||||
inspectNode(object, objects, duplicatesIndexes);
|
|
||||||
|
|
||||||
for (
|
|
||||||
index = 0, length = duplicatesIndexes.length;
|
|
||||||
index < length;
|
|
||||||
index += 1
|
|
||||||
) {
|
|
||||||
state.duplicates.push(objects[duplicatesIndexes[index]]);
|
|
||||||
}
|
|
||||||
state.usedDuplicates = new Array(length);
|
|
||||||
}
|
|
||||||
|
|
||||||
function inspectNode(object, objects, duplicatesIndexes) {
|
|
||||||
var type = _toString.call(object),
|
|
||||||
objectKeyList,
|
|
||||||
index,
|
|
||||||
length;
|
|
||||||
|
|
||||||
if (null !== object && 'object' === typeof object) {
|
|
||||||
index = objects.indexOf(object);
|
|
||||||
if (-1 !== index) {
|
|
||||||
if (-1 === duplicatesIndexes.indexOf(index)) {
|
|
||||||
duplicatesIndexes.push(index);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
objects.push(object);
|
|
||||||
|
|
||||||
if (Array.isArray(object)) {
|
|
||||||
for (index = 0, length = object.length; index < length; index += 1) {
|
|
||||||
inspectNode(object[index], objects, duplicatesIndexes);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
objectKeyList = Object.keys(object);
|
|
||||||
|
|
||||||
for (
|
|
||||||
index = 0, length = objectKeyList.length;
|
|
||||||
index < length;
|
|
||||||
index += 1
|
|
||||||
) {
|
|
||||||
inspectNode(object[objectKeyList[index]], objects, duplicatesIndexes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function dump(input, options) {
|
|
||||||
options = options || {};
|
|
||||||
|
|
||||||
var state = new State(options);
|
|
||||||
|
|
||||||
getDuplicateReferences(input, state);
|
|
||||||
|
|
||||||
if (writeNode(state, 0, input, true, true)) {
|
|
||||||
return state.dump + '\n';
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
export function safeDump(input, options) {
|
|
||||||
return dump(input, common.extend({ schema: DEFAULT_SAFE_SCHEMA }, options));
|
|
||||||
}
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
import Mark from './mark';
|
|
||||||
'use strict';
|
|
||||||
class YAMLException {
|
|
||||||
message: string;
|
|
||||||
reason: string;
|
|
||||||
name: string;
|
|
||||||
mark: Mark;
|
|
||||||
isWarning: boolean;
|
|
||||||
|
|
||||||
private static CLASS_IDENTIFIER = 'yaml-ast-parser.YAMLException';
|
|
||||||
|
|
||||||
public static isInstance(instance: any): instance is YAMLException {
|
|
||||||
if (
|
|
||||||
instance != null &&
|
|
||||||
instance.getClassIdentifier &&
|
|
||||||
typeof instance.getClassIdentifier == 'function'
|
|
||||||
) {
|
|
||||||
for (let currentIdentifier of instance.getClassIdentifier()) {
|
|
||||||
if (currentIdentifier == YAMLException.CLASS_IDENTIFIER) return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getClassIdentifier(): string[] {
|
|
||||||
var superIdentifiers = [];
|
|
||||||
|
|
||||||
return superIdentifiers.concat(YAMLException.CLASS_IDENTIFIER);
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(reason: string, mark: Mark = null, isWarning = false) {
|
|
||||||
this.name = 'YAMLException';
|
|
||||||
this.reason = reason;
|
|
||||||
this.mark = mark;
|
|
||||||
this.message = this.toString(false);
|
|
||||||
this.isWarning = isWarning;
|
|
||||||
}
|
|
||||||
|
|
||||||
toString(compact: boolean = false) {
|
|
||||||
var result;
|
|
||||||
|
|
||||||
result = 'JS-YAML: ' + (this.reason || '(unknown reason)');
|
|
||||||
|
|
||||||
if (!compact && this.mark) {
|
|
||||||
result += ' ' + this.mark.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export default YAMLException;
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
/**
|
|
||||||
* Created by kor on 06/05/15.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export { load, loadAll, safeLoad, safeLoadAll, LoadOptions } from './loader';
|
|
||||||
export { dump, safeDump } from './dumper';
|
|
||||||
|
|
||||||
import Mark from './mark';
|
|
||||||
import YAMLException from './exception';
|
|
||||||
|
|
||||||
export * from './yamlAST';
|
|
||||||
|
|
||||||
export type Error = YAMLException;
|
|
||||||
|
|
||||||
function deprecated(name) {
|
|
||||||
return function() {
|
|
||||||
throw new Error('Function ' + name + ' is deprecated and cannot be used.');
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export * from './scalarInference';
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,92 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import * as common from './common';
|
|
||||||
|
|
||||||
class Mark {
|
|
||||||
constructor(
|
|
||||||
public name: string,
|
|
||||||
public buffer: string,
|
|
||||||
public position: number,
|
|
||||||
public line: number,
|
|
||||||
public column: number
|
|
||||||
) {}
|
|
||||||
|
|
||||||
filePath: string;
|
|
||||||
|
|
||||||
toLineEnd: boolean;
|
|
||||||
|
|
||||||
getSnippet(indent: number = 0, maxLength: number = 75) {
|
|
||||||
var head, start, tail, end, snippet;
|
|
||||||
|
|
||||||
if (!this.buffer) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
indent = indent || 4;
|
|
||||||
maxLength = maxLength || 75;
|
|
||||||
|
|
||||||
head = '';
|
|
||||||
start = this.position;
|
|
||||||
|
|
||||||
while (
|
|
||||||
start > 0 &&
|
|
||||||
-1 === '\x00\r\n\x85\u2028\u2029'.indexOf(this.buffer.charAt(start - 1))
|
|
||||||
) {
|
|
||||||
start -= 1;
|
|
||||||
if (this.position - start > maxLength / 2 - 1) {
|
|
||||||
head = ' ... ';
|
|
||||||
start += 5;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tail = '';
|
|
||||||
end = this.position;
|
|
||||||
|
|
||||||
while (
|
|
||||||
end < this.buffer.length &&
|
|
||||||
-1 === '\x00\r\n\x85\u2028\u2029'.indexOf(this.buffer.charAt(end))
|
|
||||||
) {
|
|
||||||
end += 1;
|
|
||||||
if (end - this.position > maxLength / 2 - 1) {
|
|
||||||
tail = ' ... ';
|
|
||||||
end -= 5;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
snippet = this.buffer.slice(start, end);
|
|
||||||
|
|
||||||
return (
|
|
||||||
common.repeat(' ', indent) +
|
|
||||||
head +
|
|
||||||
snippet +
|
|
||||||
tail +
|
|
||||||
'\n' +
|
|
||||||
common.repeat(' ', indent + this.position - start + head.length) +
|
|
||||||
'^'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
toString(compact: boolean = true) {
|
|
||||||
var snippet,
|
|
||||||
where = '';
|
|
||||||
|
|
||||||
if (this.name) {
|
|
||||||
where += 'in "' + this.name + '" ';
|
|
||||||
}
|
|
||||||
|
|
||||||
where += 'at line ' + (this.line + 1) + ', column ' + (this.column + 1);
|
|
||||||
|
|
||||||
if (!compact) {
|
|
||||||
snippet = this.getSnippet();
|
|
||||||
|
|
||||||
if (snippet) {
|
|
||||||
where += ':\n' + snippet;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return where;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export default Mark;
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue