├── .editorconfig ├── .eslintrc.js ├── .github ├── ISSUE_TEMPLATE │ ├── report_bug.md │ └── report_bug.zh-Hans.md ├── dependabot.yml └── workflows │ ├── ci.yaml │ └── vsce-publish.yaml ├── .gitignore ├── .nmimrc ├── .npmignore ├── .prettierrc ├── .swcrc ├── .vscode └── launch.json ├── .vscodeignore ├── LICENSE ├── README.md ├── assets ├── details │ ├── lua_nginx_module.json │ ├── ngx_core_module.json │ ├── ngx_fastcgi.json │ ├── ngx_google_perftools_module.json │ ├── ngx_http_access_module.json │ ├── ngx_http_addition_module.json │ ├── ngx_http_api_module.json │ ├── ngx_http_auth_basic_module.json │ ├── ngx_http_auth_jwt_module.json │ ├── ngx_http_auth_request_module.json │ ├── ngx_http_autoindex_module.json │ ├── ngx_http_browser_module.json │ ├── ngx_http_charset_module.json │ ├── ngx_http_core_module.json │ ├── ngx_http_dav_module.json │ ├── ngx_http_empty_gif_module.json │ ├── ngx_http_f4f_module.json │ ├── ngx_http_fastcgi_module.json │ ├── ngx_http_flv_module.json │ ├── ngx_http_geo_module.json │ ├── ngx_http_geoip_module.json │ ├── ngx_http_grpc_module.json │ ├── ngx_http_gunzip_module.json │ ├── ngx_http_gzip_module.json │ ├── ngx_http_gzip_static_module.json │ ├── ngx_http_headers_module.json │ ├── ngx_http_hls_module.json │ ├── ngx_http_image_filter_module.json │ ├── ngx_http_index_module.json │ ├── ngx_http_internal_redirect_module.json │ ├── ngx_http_js_module.json │ ├── ngx_http_keyval_module.json │ ├── ngx_http_limit_conn_module.json │ ├── ngx_http_limit_req_module.json │ ├── ngx_http_log_module.json │ ├── ngx_http_map_module.json │ ├── ngx_http_memcached_module.json │ ├── ngx_http_mirror_module.json │ ├── ngx_http_mp4_module.json │ ├── ngx_http_perl_module.json │ ├── ngx_http_proxy_module.json │ ├── ngx_http_proxy_protocol_vendor_module.json │ ├── ngx_http_random_index_module.json │ ├── ngx_http_realip_module.json │ ├── ngx_http_referer_module.json │ ├── ngx_http_rewrite_module.json │ ├── ngx_http_scgi_module.json │ ├── ngx_http_secure_link_module.json │ ├── ngx_http_session_log_module.json │ ├── ngx_http_slice_module.json │ ├── ngx_http_spdy_module.json │ ├── ngx_http_split_clients_module.json │ ├── ngx_http_ssi_module.json │ ├── ngx_http_ssl_module.json │ ├── ngx_http_status_module.json │ ├── ngx_http_stub_status_module.json │ ├── ngx_http_sub_module.json │ ├── ngx_http_upstream_conf_module.json │ ├── ngx_http_upstream_hc_module.json │ ├── ngx_http_upstream_module.json │ ├── ngx_http_userid_module.json │ ├── ngx_http_uwsgi_module.json │ ├── ngx_http_v2_module.json │ ├── ngx_http_v3_module.json │ ├── ngx_http_xslt_module.json │ ├── ngx_mail_auth_http_module.json │ ├── ngx_mail_core_module.json │ ├── ngx_mail_imap_module.json │ ├── ngx_mail_pop3_module.json │ ├── ngx_mail_proxy_module.json │ ├── ngx_mail_realip_module.json │ ├── ngx_mail_smtp_module.json │ ├── ngx_mail_ssl_module.json │ ├── ngx_memc.json │ ├── ngx_otel_module.json │ ├── ngx_postgres.json │ ├── ngx_proxy.json │ ├── ngx_redis.json │ ├── ngx_redis2.json │ ├── ngx_stream_access_module.json │ ├── ngx_stream_core_module.json │ ├── ngx_stream_geo_module.json │ ├── ngx_stream_geoip_module.json │ ├── ngx_stream_js_module.json │ ├── ngx_stream_keyval_module.json │ ├── ngx_stream_limit_conn_module.json │ ├── ngx_stream_log_module.json │ ├── ngx_stream_map_module.json │ ├── ngx_stream_mqtt_filter_module.json │ ├── ngx_stream_mqtt_preread_module.json │ ├── ngx_stream_proxy_module.json │ ├── ngx_stream_proxy_protocol_vendor_module.json │ ├── ngx_stream_realip_module.json │ ├── ngx_stream_return_module.json │ ├── ngx_stream_set_module.json │ ├── ngx_stream_split_clients_module.json │ ├── ngx_stream_ssl_module.json │ ├── ngx_stream_ssl_preread_module.json │ ├── ngx_stream_upstream_hc_module.json │ ├── ngx_stream_upstream_module.json │ └── ngx_stream_zone_sync_module.json ├── manifest │ ├── README.md │ ├── core.json │ ├── http_headers.de.json │ ├── http_headers.en.json │ ├── http_headers.es.json │ ├── http_headers.zh-Hans.json │ ├── http_headers.zh-Hant-HK.json │ ├── http_headers.zh-Hant-TW.json │ ├── js.json │ └── lua.json ├── mediatypes │ ├── README.md │ ├── application.json │ ├── audio.json │ ├── font.json │ ├── image.json │ ├── message.json │ ├── model.json │ ├── multipart.json │ ├── text.json │ └── video.json └── snippets │ ├── lua.json │ └── nginx.json ├── docs ├── CHANGELOG.md ├── CHANGELOG.zh-Hans.md ├── CONTRIBUTING.md ├── NGINX-CONF.md ├── RELEASE-NEW-VERSION.md ├── TODO.md └── images │ └── debug-config.jpg ├── images ├── icon.png └── screenshots.gif ├── nginx.configuration.json ├── package.json ├── package.nls.json ├── package.nls.zh-cn.json ├── package.nls.zh-tw.json ├── scripts ├── clean.js ├── fix-eslint-strip-ansi.sh ├── install-git-hooks.sh ├── run.js └── vsce.sh ├── src ├── additonal_snippets │ └── nginx.js ├── downloader │ ├── config_url.ts │ ├── http_headers.ts │ ├── lua_openresty.ts │ ├── mimetype.ts │ ├── nginx_directives.ts │ └── nginx_syntax.ts ├── extension │ ├── .gitignore │ ├── documents │ │ ├── html_view.ts │ │ ├── open_nginx_document.ts │ │ ├── templates │ │ │ ├── directive_container.html │ │ │ ├── directive_item.html │ │ │ ├── index.ts │ │ │ └── variables.html │ │ └── utils.ts │ ├── hint-data │ │ ├── async-loader.ts │ │ ├── details.ts │ │ ├── enum.ts │ │ ├── i18n.ts │ │ ├── link.ts │ │ ├── manifest.ts │ │ ├── media-types.ts │ │ └── types.ts │ ├── logger.ts │ ├── main.desktop.ts │ ├── main.web.ts │ ├── parser │ │ └── index.ts │ ├── providers │ │ ├── command.ts │ │ ├── completion │ │ │ ├── directive-location.ts │ │ │ ├── directives.ts │ │ │ ├── http-header.ts │ │ │ ├── index.ts │ │ │ ├── item.ts │ │ │ ├── named-arguments.ts │ │ │ ├── named-location.ts │ │ │ └── path.ts │ │ ├── config.ts │ │ ├── definition.ts │ │ ├── formatter.ts │ │ ├── hover.ts │ │ ├── link.ts │ │ ├── signature.ts │ │ └── utils.ts │ ├── types.d.ts │ └── utils.ts ├── libs │ └── nginxbeautifier.js ├── syntax │ ├── generate-tmLanguage.ts │ ├── match-names.ts │ ├── nginx.tmLanguage │ ├── patterns.ts │ ├── references │ │ ├── shanoor.nginx.tmLanguage │ │ └── sublime.nginx.tmLanguage │ ├── repository.ts │ ├── syntax.ts │ └── types.d.ts ├── types-manifest.ts └── utils │ ├── build_document_templates.ts │ ├── config.ts │ ├── crawler-utils.ts │ └── extra-headers │ ├── cloudflare.json │ └── mozilla-docs.json ├── test ├── markdownFromElement.ts ├── playground │ ├── file1.conf │ ├── file2.conf │ └── getCursorContext.ts ├── unit-testing │ ├── .editorconfig │ ├── syntax1.conf │ └── syntax1.ts └── workspace │ ├── .vscode │ └── settings.json │ ├── README.md │ ├── example1.conf │ ├── example1.conf.disabled │ ├── example2.conf │ ├── example3.conf │ ├── expires.conf │ ├── lua-nginx-module │ ├── synopsis.conf │ └── synopsis2.conf │ ├── named-location.conf │ ├── named-location2.conf │ ├── nginx.com │ ├── README.md │ ├── active_sync_gateway.conf │ ├── dynamic_ssi.conf │ ├── fastcgi.conf │ ├── fullexample2.conf │ ├── loadbalanceexample.conf │ ├── mime.types │ ├── nginx.conf │ └── proxy.conf │ ├── offical-nginx.conf │ ├── ssl-example.conf │ ├── sublime-example.conf │ ├── test-in-docker.sh │ └── weird │ ├── full-example.conf │ └── part-for-debug.conf ├── tsconfig.json ├── webpack.config.js └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | charset = utf-8 3 | end_of_line = lf 4 | indent_size = 4 5 | indent_style = tab 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | 9 | [*.{md,markdown}] 10 | trim_trailing_whitespace = false 11 | 12 | [*.{yaml,yml}] 13 | indent_size = 2 14 | indent_style = space 15 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | ignorePatterns: ["artifacts", "src/**/*.js", ".eslintrc.js"], 3 | root: true, 4 | parser: "@typescript-eslint/parser", 5 | plugins: ["@typescript-eslint"], 6 | extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"], 7 | rules: { 8 | "no-useless-escape": "off", 9 | "prefer-const": "warn", 10 | "@typescript-eslint/no-var-requires": "off", 11 | // https://github.com/typescript-eslint/typescript-eslint/issues/2621 12 | "no-unused-vars": "off", 13 | "@typescript-eslint/no-unused-vars": ["warn", { args: "none" }], 14 | }, 15 | env: { 16 | browser: true, 17 | node: true, 18 | }, 19 | }; 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/report_bug.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Report bugs 3 | about: Report bugs of this extension 4 | --- 5 | 6 | Required information for this issue: 7 | 8 | - Your VS Code Version: 9 | - Your OS Version: 10 | - Is this extension running on VS Code Remote or : 11 | 12 | Steps to Reproduce: 13 | 14 | 1. 15 | 2. 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/report_bug.zh-Hans.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 上报Bug 3 | about: 上报关于这个插件的Bug 4 | --- 5 | 6 | 请提供以下信息: 7 | 8 | - 你的 VS Code 的版本: 9 | - 你系统的版本: 10 | - 是不是在 VS Code Remote 或 中使用的本插件: 11 | 12 | 重现这个bug的方法: 13 | 14 | 1. 15 | 2. 16 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - directory: / 4 | package-ecosystem: npm 5 | commit-message: 6 | prefix: deps # the git commit message will look like: "deps: ..." 7 | schedule: 8 | interval: daily 9 | ignore: 10 | - dependency-name: '@types/vscode' 11 | - dependency-name: '@types/node' 12 | - directory: / 13 | package-ecosystem: github-actions 14 | commit-message: 15 | prefix: deps 16 | schedule: 17 | interval: weekly 18 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: build-artifact 2 | on: 3 | push: 4 | branches: [main] 5 | pull_request: 6 | branches: [main] 7 | # Allows you to run this workflow manually from the Actions tab 8 | workflow_dispatch: 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 14 | - uses: actions/checkout@v4 15 | - uses: actions/setup-node@v4 16 | with: 17 | node-version: latest 18 | cache: yarn 19 | - name: Install 20 | run: yarn install 21 | - name: Pacth 22 | run: ./scripts/fix-eslint-strip-ansi.sh 23 | - name: Lint 24 | run: yarn run lint:eslint 25 | - name: Build 26 | run: yarn run build 27 | - name: Build npm package and Visual Studio Code package 28 | run: bash ./scripts/vsce.sh build-vsix-and-list 29 | - uses: actions/upload-artifact@v4 30 | with: 31 | name: vscode-extension 32 | path: | 33 | artifacts/vscode/*.vsix 34 | artifacts/vscode/*.list 35 | - uses: actions/upload-artifact@v4 36 | with: 37 | name: npm-package 38 | path: | 39 | artifacts/npm/*.tgz 40 | artifacts/npm/*.list 41 | -------------------------------------------------------------------------------- /.github/workflows/vsce-publish.yaml: -------------------------------------------------------------------------------- 1 | name: publish-vscode-extension 2 | on: 3 | # Only publish vscode extension manually 4 | workflow_dispatch: 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | - uses: actions/setup-node@v4 11 | with: 12 | node-version: latest 13 | cache: yarn 14 | - name: Install 15 | run: yarn install 16 | - name: Pacth 17 | run: ./scripts/fix-eslint-strip-ansi.sh 18 | - name: Build 19 | run: yarn run build 20 | - name: Publish VS Code extension 21 | env: 22 | VSCE_PAT: ${{ secrets.VSCE_PAT }} 23 | run: bash scripts/vsce.sh publish 24 | 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # outdated generated js files 2 | src/utils/**/*.js 3 | src/utils/**/*.js.map 4 | 5 | # vscode test web 6 | .vscode-test-web 7 | /out 8 | 9 | *.tsbuildinfo 10 | 11 | /artifacts 12 | 13 | # macos files 14 | .DS_Store 15 | ._* 16 | .nmimrc 17 | 18 | # ignore archive files 19 | *.tar 20 | *.tgz 21 | *.7z 22 | *.zip 23 | *.vsix 24 | 25 | # ignore mark file about new syntax tip 26 | extension/new_syntax_tip_has_been_shown 27 | 28 | # ignore personal vscode configurations 29 | .vscode/settings.json 30 | .vscode/cSpell.json 31 | 32 | # ignore node_modules.tar (node-modules-in-memory) 33 | node_modules.tar 34 | 35 | # ignore nginx document page html cache 36 | cache 37 | cache-* 38 | 39 | # ignore vscode.d.ts and vscode.proposed.d.ts 40 | vscode.d.ts 41 | vscode.proposed.d.ts 42 | 43 | # Logs 44 | logs 45 | *.log 46 | npm-debug.log* 47 | yarn-debug.log* 48 | yarn-error.log* 49 | 50 | # Runtime data 51 | pids 52 | *.pid 53 | *.seed 54 | *.pid.lock 55 | 56 | # Directory for instrumented libs generated by jscoverage/JSCover 57 | lib-cov 58 | 59 | # Coverage directory used by tools like istanbul 60 | coverage 61 | 62 | # nyc test coverage 63 | .nyc_output 64 | 65 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 66 | .grunt 67 | 68 | # Bower dependency directory (https://bower.io/) 69 | bower_components 70 | 71 | # node-waf configuration 72 | .lock-wscript 73 | 74 | # Compiled binary addons (http://nodejs.org/api/addons.html) 75 | build/Release 76 | 77 | # Dependency directories 78 | node_modules/ 79 | jspm_packages/ 80 | 81 | # Typescript v1 declaration files 82 | typings/ 83 | 84 | # Optional npm cache directory 85 | .npm 86 | 87 | # Optional eslint cache 88 | .eslintcache 89 | 90 | # Optional REPL history 91 | .node_repl_history 92 | 93 | # Output of 'npm pack' 94 | *.tgz 95 | 96 | # Yarn Integrity file 97 | .yarn-integrity 98 | 99 | # dotenv environment variables file 100 | .env 101 | 102 | -------------------------------------------------------------------------------- /.nmimrc: -------------------------------------------------------------------------------- 1 | #!/usr/env/bin bash 2 | 3 | NMIM_PREFERRED_SIZE="128m"; 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # vscode test web 2 | .vscode 3 | .vscode-test-web 4 | 5 | *.tsbuildinfo 6 | 7 | # github files 8 | .github 9 | 10 | # artifacts 11 | artifacts 12 | 13 | # macos files 14 | .DS_Store 15 | ._* 16 | 17 | # ignore archive files 18 | *.tar 19 | *.tgz 20 | *.7z 21 | *.zip 22 | *.vsix 23 | *.log 24 | 25 | # ignore screenshots gif (big file) 26 | images/screenshots.gif 27 | 28 | # cache files 29 | cache 30 | cache-* 31 | 32 | # vscode typing files 33 | vscode.d.ts 34 | vscode.proposed.d.ts 35 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "endOfLine": "lf", 3 | "semi": true, 4 | "singleQuote": false, 5 | "tabWidth": 4, 6 | "trailingComma": "es5", 7 | "printWidth": 120, 8 | "overrides": [ 9 | { 10 | "files": [ 11 | "*.yml", 12 | "*.yaml" 13 | ], 14 | "options": { 15 | "tabWidth": 2 16 | } 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /.swcrc: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/swcrc", 3 | "sourceMaps": true, 4 | "minify": false, 5 | "module": { 6 | "type": "commonjs", 7 | "noInterop": true 8 | }, 9 | "jsc": { 10 | "target": "es2022", 11 | "parser": { 12 | "syntax": "typescript", 13 | "decorators": true 14 | }, 15 | "preserveAllComments": true, 16 | "keepClassNames": true 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible Node.js debug attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Launch Extension", 9 | "type": "extensionHost", 10 | "request": "launch", 11 | "args": [ 12 | "--extensionDevelopmentPath=${workspaceFolder}", 13 | "${workspaceFolder}/test/workspace" 14 | ], 15 | "sourceMaps": true, 16 | "outFiles": [ 17 | "${workspaceFolder}/out/**/*.(m|c|)js", 18 | "!**/node_modules/**" 19 | ], 20 | "preLaunchTask": "npm: build:dev" 21 | }, 22 | { 23 | "name": "Launch Web Extension", 24 | "type": "extensionHost", 25 | "request": "launch", 26 | "args": [ 27 | "--extensionDevelopmentPath=${workspaceFolder}", 28 | "--extensionDevelopmentKind=web", 29 | "${workspaceFolder}/test/workspace" 30 | ], 31 | "debugWebWorkerHost": true, 32 | "sourceMaps": true, 33 | "outFiles": [ 34 | "${workspaceFolder}/out/**/*.(m|c|)js", 35 | "!**/node_modules/**" 36 | ], 37 | "preLaunchTask": "npm: build:web" 38 | }, 39 | { 40 | "name": "Attach", 41 | "port": 9229, 42 | "request": "attach", 43 | "skipFiles": [ 44 | "/**" 45 | ], 46 | "type": "node" 47 | } 48 | ] 49 | } 50 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | # 2 | #region main 3 | # template: .vscodeignore 4 | # author: hangxingliu 5 | # license: MIT 6 | # version: 2024-03-16 7 | # 8 | 9 | # config for building/bundling 10 | tsconfig*.json 11 | *.tsbuildinfo 12 | .swcrc 13 | 14 | # spellchecker 15 | cspell.yml 16 | 17 | # output files 18 | artifacts 19 | .tsc 20 | 21 | # cache directory 22 | cache 23 | cache* 24 | 25 | # scripts 26 | scripts 27 | 28 | 29 | # documents 30 | docs 31 | 32 | # source files 33 | src 34 | *.d.ts 35 | 36 | # test files 37 | *.spec.* 38 | test 39 | 40 | # github files 41 | .github 42 | 43 | # built vscode package 44 | .vsix 45 | 46 | # vscode configuartion and test files 47 | .vscode 48 | .vscode-test 49 | .vscode-test-web 50 | 51 | # ignore gitignore 52 | .gitignore 53 | 54 | # example files 55 | examples 56 | 57 | # archived files 58 | *.tgz 59 | *.vsix 60 | *.zip 61 | *.7z 62 | *.tar 63 | 64 | # macos files 65 | .DS_Store 66 | ._* 67 | 68 | # logs 69 | logs 70 | *.log 71 | #endregion main 72 | -------------------------------------------------------------------------------- /assets/details/ngx_fastcgi.json: -------------------------------------------------------------------------------- 1 | [ 2 | ] -------------------------------------------------------------------------------- /assets/details/ngx_google_perftools_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"google_perftools_profiles","Sets a file name that keeps profiling information of nginx worker process. The ID of the worker process is always a part of the file name and is appended to the end of the file name, after a dot.","

Sets a file name that keeps profiling information of nginx worker process. The ID of the worker process is always a part of the file name and is appended to the end of the file name, after a dot.

",[],"
Syntax:google_perftools_profiles file;
Default:
Context:main
"] 3 | ] -------------------------------------------------------------------------------- /assets/details/ngx_http_access_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"allow","Allows access for the specified network or address. If the special value `unix:` is specified (1.5.1), allows access for all UNIX-domain sockets.","

Allows access for the specified network or address. If the special value unix: is specified (1.5.1), allows access for all UNIX-domain sockets.

",[],"
Syntax:allow address | CIDR | unix: | all;
Default:
Context:http, server, location, limit_except
"], 3 | [4,"deny","Denies access for the specified network or address. If the special value `unix:` is specified (1.5.1), denies access for all UNIX-domain sockets.","

Denies access for the specified network or address. If the special value unix: is specified (1.5.1), denies access for all UNIX-domain sockets.

",[],"
Syntax:deny address | CIDR | unix: | all;
Default:
Context:http, server, location, limit_except
"] 4 | ] -------------------------------------------------------------------------------- /assets/details/ngx_http_addition_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"add_before_body","Adds the text returned as a result of processing a given subrequest before the response body. An empty string (`\"\"`) as a parameter cancels addition inherited from the previous configuration level.","

Adds the text returned as a result of processing a given subrequest before the response body. An empty string (\"\") as a parameter cancels addition inherited from the previous configuration level.

",[],"
Syntax:add_before_body uri;
Default:
Context:http, server, location
"], 3 | [4,"add_after_body","Adds the text returned as a result of processing a given subrequest after the response body. An empty string (`\"\"`) as a parameter cancels addition inherited from the previous configuration level.","

Adds the text returned as a result of processing a given subrequest after the response body. An empty string (\"\") as a parameter cancels addition inherited from the previous configuration level.

",[],"
Syntax:add_after_body uri;
Default:
Context:http, server, location
"], 4 | [4,"addition_types","Allows adding text in responses with the specified MIME types, in addition to “`text/html`”. The special value “`*`” matches any MIME type (0.8.29).","

Allows adding text in responses with the specified MIME types, in addition to “text/html”. The special value “*” matches any MIME type (0.8.29).

",[],"
Syntax:addition_types mime-type ...;
Default:
addition_types text/html;
Context:http, server, location

This directive appeared in version 0.7.9.

"] 5 | ] -------------------------------------------------------------------------------- /assets/details/ngx_http_api_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"api","Turns on the REST API interface in the surrounding location. Access to this location should be [limited](https://nginx.org/en/docs/http/ngx_http_core_module.html#satisfy).","

Turns on the REST API interface in the surrounding location. Access to this location should be limited.

The write parameter determines whether the API is read-only or read-write. By default, the API is read-only.

",[],"
Syntax:api [write=on|off];
Default:
Context:location
"], 3 | [4,"status_zone","Enables collection of virtual [http](https://nginx.org/en/docs/http/ngx_http_core_module.html#server) or [stream](https://nginx.org/en/docs/stream/ngx_stream_core_module.html#server) server status information in the specified `zone`. Several servers may share the same zone.","

Enables collection of virtual http or stream server status information in the specified zone. Several servers may share the same zone.

",[],"
Syntax:status_zone zone;
Default:
Context:server, location, if in location

This directive appeared in version 1.13.12.

"] 4 | ] -------------------------------------------------------------------------------- /assets/details/ngx_http_auth_basic_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"auth_basic","Enables validation of user name and password using the “HTTP Basic Authentication” protocol. The specified parameter is used as a `realm`. Parameter value can contain variables (1.3.10, 1.2.7). The special value `off` cancels the effect of the `auth_basic` directive inherited from the previous configuration level.","

Enables validation of user name and password using the “HTTP Basic Authentication” protocol. The specified parameter is used as a realm. Parameter value can contain variables (1.3.10, 1.2.7). The special value off cancels the effect of the auth_basic directive inherited from the previous configuration level.

",[],"
Syntax:auth_basic string | off;
Default:
auth_basic off;
Context:http, server, location, limit_except
"], 3 | [4,"auth_basic_user_file","Specifies a file that keeps user names and passwords, in the following format:","

Specifies a file that keeps user names and passwords, in the following format:

# comment\nname1:password1\nname2:password2:comment\nname3:password3\n

The file name can contain variables.

The following password types are supported:

",[],"
Syntax:auth_basic_user_file file;
Default:
Context:http, server, location, limit_except
"] 4 | ] -------------------------------------------------------------------------------- /assets/details/ngx_http_auth_request_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"auth_request","Enables authorization based on the result of a subrequest and sets the URI to which the subrequest will be sent.","

Enables authorization based on the result of a subrequest and sets the URI to which the subrequest will be sent.

",[],"
Syntax:auth_request uri | off;
Default:
auth_request off;
Context:http, server, location
"], 3 | [4,"auth_request_set","Sets the request `variable` to the given `value` after the authorization request completes. The value may contain variables from the authorization request, such as `$upstream_http_*`.","

Sets the request variable to the given value after the authorization request completes. The value may contain variables from the authorization request, such as $upstream_http_*.

",[],"
Syntax:auth_request_set $variable value;
Default:
Context:http, server, location
"] 4 | ] -------------------------------------------------------------------------------- /assets/details/ngx_http_autoindex_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"autoindex","Enables or disables the directory listing output.","

Enables or disables the directory listing output.

",[],"
Syntax:autoindex on | off;
Default:
autoindex off;
Context:http, server, location
"], 3 | [4,"autoindex_exact_size","For the HTML [format](https://nginx.org/en/docs/http/ngx_http_autoindex_module.html#autoindex_format), specifies whether exact file sizes should be output in the directory listing, or rather rounded to kilobytes, megabytes, and gigabytes.","

For the HTML format, specifies whether exact file sizes should be output in the directory listing, or rather rounded to kilobytes, megabytes, and gigabytes.

",[],"
Syntax:autoindex_exact_size on | off;
Default:
autoindex_exact_size on;
Context:http, server, location
"], 4 | [4,"autoindex_format","Sets the format of a directory listing.","

Sets the format of a directory listing.

When the JSONP format is used, the name of a callback function is set with the callback request argument. If the argument is missing or has an empty value, then the JSON format is used.

The XML output can be transformed using the ngx_http_xslt_module module.

",[],"
Syntax:autoindex_format html | xml | json | jsonp;
Default:
autoindex_format html;
Context:http, server, location

This directive appeared in version 1.7.9.

"], 5 | [4,"autoindex_localtime","For the HTML [format](https://nginx.org/en/docs/http/ngx_http_autoindex_module.html#autoindex_format), specifies whether times in the directory listing should be output in the local time zone or UTC.","

For the HTML format, specifies whether times in the directory listing should be output in the local time zone or UTC.

",[],"
Syntax:autoindex_localtime on | off;
Default:
autoindex_localtime off;
Context:http, server, location
"] 6 | ] -------------------------------------------------------------------------------- /assets/details/ngx_http_browser_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"ancient_browser","If any of the specified substrings is found in the “User-Agent” request header field, the browser will be considered ancient. The special string “`netscape4`” corresponds to the regular expression “`^Mozilla/[1-4]`”.","

If any of the specified substrings is found in the “User-Agent” request header field, the browser will be considered ancient. The special string “netscape4” corresponds to the regular expression “^Mozilla/[1-4]”.

",[],"
Syntax:ancient_browser string ...;
Default:
Context:http, server, location
"], 3 | [4,"ancient_browser_value","Sets a value for the `$ancient_browser` variables.","

Sets a value for the $ancient_browser variables.

",[],"
Syntax:ancient_browser_value string;
Default:
ancient_browser_value 1;
Context:http, server, location
"], 4 | [4,"modern_browser","Specifies a version starting from which a browser is considered modern. A browser can be any one of the following: `msie`, `gecko` (browsers based on Mozilla), `opera`, `safari`, or `konqueror`.","

Specifies a version starting from which a browser is considered modern. A browser can be any one of the following: msie, gecko (browsers based on Mozilla), opera, safari, or konqueror.

Versions can be specified in the following formats: X, X.X, X.X.X, or X.X.X.X. The maximum values for each of the format are 4000, 4000.99, 4000.99.99, and 4000.99.99.99, respectively.

The special value unlisted specifies to consider a browser as modern if it was not listed by the modern_browser and ancient_browser directives. Otherwise such a browser is considered ancient. If a request does not provide the “User-Agent” field in the header, the browser is treated as not being listed.

",[],"
Syntax:modern_browser browser version;
modern_browser unlisted;
Default:
Context:http, server, location
"], 5 | [4,"modern_browser_value","Sets a value for the `$modern_browser` variables.","

Sets a value for the $modern_browser variables.

",[],"
Syntax:modern_browser_value string;
Default:
modern_browser_value 1;
Context:http, server, location
"] 6 | ] -------------------------------------------------------------------------------- /assets/details/ngx_http_empty_gif_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"empty_gif","Turns on module processing in a surrounding location.","

Turns on module processing in a surrounding location.

",[],"
Syntax:empty_gif;
Default:
Context:location
"] 3 | ] -------------------------------------------------------------------------------- /assets/details/ngx_http_f4f_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"f4f","Turns on module processing in the surrounding location.","

Turns on module processing in the surrounding location.

",[],"
Syntax:f4f;
Default:
Context:location
"], 3 | [4,"f4f_buffer_size","Sets the `size` of the buffer used for reading the `.f4x` index file.","

Sets the size of the buffer used for reading the .f4x index file.

",[],"
Syntax:f4f_buffer_size size;
Default:
f4f_buffer_size 512k;
Context:http, server, location
"] 4 | ] -------------------------------------------------------------------------------- /assets/details/ngx_http_flv_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"flv","Turns on module processing in a surrounding location.","

Turns on module processing in a surrounding location.

",[],"
Syntax:flv;
Default:
Context:location
"] 3 | ] -------------------------------------------------------------------------------- /assets/details/ngx_http_gunzip_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"gunzip","Enables or disables decompression of gzipped responses for clients that lack gzip support. If enabled, the following directives are also taken into account when determining if clients support gzip: [gzip\\_http\\_version](https://nginx.org/en/docs/http/ngx_http_gzip_module.html#gzip_http_version), [gzip\\_proxied](https://nginx.org/en/docs/http/ngx_http_gzip_module.html#gzip_proxied), and [gzip\\_disable](https://nginx.org/en/docs/http/ngx_http_gzip_module.html#gzip_disable). See also the [gzip\\_vary](https://nginx.org/en/docs/http/ngx_http_gzip_module.html#gzip_vary) directive.","

Enables or disables decompression of gzipped responses for clients that lack gzip support. If enabled, the following directives are also taken into account when determining if clients support gzip: gzip_http_version, gzip_proxied, and gzip_disable. See also the gzip_vary directive.

",[],"
Syntax:gunzip on | off;
Default:
gunzip off;
Context:http, server, location
"], 3 | [4,"gunzip_buffers","Sets the `number` and `size` of buffers used to decompress a response. By default, the buffer size is equal to one memory page. This is either 4K or 8K, depending on a platform.","

Sets the number and size of buffers used to decompress a response. By default, the buffer size is equal to one memory page. This is either 4K or 8K, depending on a platform.

",[],"
Syntax:gunzip_buffers number size;
Default:
gunzip_buffers 32 4k|16 8k;
Context:http, server, location
"] 4 | ] -------------------------------------------------------------------------------- /assets/details/ngx_http_gzip_static_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"gzip_static","Enables (“`on`”) or disables (“`off`”) checking the existence of precompressed files. The following directives are also taken into account: [gzip\\_http\\_version](https://nginx.org/en/docs/http/ngx_http_gzip_module.html#gzip_http_version), [gzip\\_proxied](https://nginx.org/en/docs/http/ngx_http_gzip_module.html#gzip_proxied), [gzip\\_disable](https://nginx.org/en/docs/http/ngx_http_gzip_module.html#gzip_disable), and [gzip\\_vary](https://nginx.org/en/docs/http/ngx_http_gzip_module.html#gzip_vary).","

Enables (“on”) or disables (“off”) checking the existence of precompressed files. The following directives are also taken into account: gzip_http_version, gzip_proxied, gzip_disable, and gzip_vary.

With the “always” value (1.3.6), gzipped file is used in all cases, without checking if the client supports it. It is useful if there are no uncompressed files on the disk anyway or the ngx_http_gunzip_module is used.

The files can be compressed using the gzip command, or any other compatible one. It is recommended that the modification date and time of original and compressed files be the same.

",[],"
Syntax:gzip_static on | off | always;
Default:
gzip_static off;
Context:http, server, location
"] 3 | ] -------------------------------------------------------------------------------- /assets/details/ngx_http_index_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"index","Defines files that will be used as an index. The `file` name can contain variables. Files are checked in the specified order. The last element of the list can be a file with an absolute path. Example:","

Defines files that will be used as an index. The file name can contain variables. Files are checked in the specified order. The last element of the list can be a file with an absolute path. Example:

index index.$geo.html index.0.html /index.html;\n

It should be noted that using an index file causes an internal redirect, and the request can be processed in a different location. For example, with the following configuration:

location = / {\n    index index.html;\n}\n\nlocation / {\n    ...\n}\n

a “/” request will actually be processed in the second location as “/index.html”.

",[],"
Syntax:index file ...;
Default:
index index.html;
Context:http, server, location
"] 3 | ] -------------------------------------------------------------------------------- /assets/details/ngx_http_internal_redirect_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"internal_redirect","Sets the URI for internal redirection of the request. It is also possible to use a [named location](https://nginx.org/en/docs/http/ngx_http_core_module.html#location_named) instead of the URI. The `uri` value can contain variables. If the `uri` value is empty, then the redirect will not be made.","

Sets the URI for internal redirection of the request. It is also possible to use a named location instead of the URI. The uri value can contain variables. If the uri value is empty, then the redirect will not be made.

",[],"
Syntax:internal_redirect uri;
Default:
Context:server, location
"] 3 | ] -------------------------------------------------------------------------------- /assets/details/ngx_http_keyval_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"keyval","Creates a new `$variable` whose value is looked up by the `key` in the key-value database. Matching rules are defined by the [`type`](https://nginx.org/en/docs/http/ngx_http_keyval_module.html#keyval_type) parameter of the [`keyval_zone`](https://nginx.org/en/docs/http/ngx_http_keyval_module.html#keyval_zone) directive. The database is stored in a shared memory zone specified by the `zone` parameter.","

Creates a new $variable whose value is looked up by the key in the key-value database. Matching rules are defined by the type parameter of the keyval_zone directive. The database is stored in a shared memory zone specified by the zone parameter.

",[],"
Syntax:keyval key $variable zone=name;
Default:
Context:http
"], 3 | [4,"keyval_zone","Sets the `name` and `size` of the shared memory zone that keeps the key-value database. Key-value pairs are managed by the [API](https://nginx.org/en/docs/http/ngx_http_api_module.html#http_keyvals_).","

Sets the name and size of the shared memory zone that keeps the key-value database. Key-value pairs are managed by the API.

",[],"
Syntax:keyval_zone zone=name:size [state=file] [timeout=time] [type=string|ip|prefix] [sync];
Default:
Context:http
"] 4 | ] -------------------------------------------------------------------------------- /assets/details/ngx_http_mirror_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"mirror","Sets the URI to which an original request will be mirrored. Several mirrors can be specified on the same configuration level.","

Sets the URI to which an original request will be mirrored. Several mirrors can be specified on the same configuration level.

",[],"
Syntax:mirror uri | off;
Default:
mirror off;
Context:http, server, location
"], 3 | [4,"mirror_request_body","Indicates whether the client request body is mirrored. When enabled, the client request body will be read prior to creating mirror subrequests. In this case, unbuffered client request body proxying set by the [proxy\\_request\\_buffering](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_request_buffering), [fastcgi\\_request\\_buffering](https://nginx.org/en/docs/http/ngx_http_fastcgi_module.html#fastcgi_request_buffering), [scgi\\_request\\_buffering](https://nginx.org/en/docs/http/ngx_http_scgi_module.html#scgi_request_buffering), and [uwsgi\\_request\\_buffering](https://nginx.org/en/docs/http/ngx_http_uwsgi_module.html#uwsgi_request_buffering) directives will be disabled.","

Indicates whether the client request body is mirrored. When enabled, the client request body will be read prior to creating mirror subrequests. In this case, unbuffered client request body proxying set by the proxy_request_buffering, fastcgi_request_buffering, scgi_request_buffering, and uwsgi_request_buffering directives will be disabled.

location / {\n    mirror /mirror;\n    mirror_request_body off;\n    proxy_pass http://backend;\n}\n\nlocation = /mirror {\n    internal;\n    proxy_pass http://log_backend;\n    proxy_pass_request_body off;\n    proxy_set_header Content-Length \"\";\n    proxy_set_header X-Original-URI $request_uri;\n}\n
",[],"
Syntax:mirror_request_body on | off;
Default:
mirror_request_body on;
Context:http, server, location
"] 4 | ] -------------------------------------------------------------------------------- /assets/details/ngx_http_perl_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"perl","Sets a Perl handler for the given location.","

Sets a Perl handler for the given location.

",[],"
Syntax:perl module::function|'sub { ... }';
Default:
Context:location, limit_except
"], 3 | [4,"perl_modules","Sets an additional path for Perl modules.","

Sets an additional path for Perl modules.

",[],"
Syntax:perl_modules path;
Default:
Context:http
"], 4 | [4,"perl_require","Defines the name of a module that will be loaded during each reconfiguration. Several `perl_require` directives can be present.","

Defines the name of a module that will be loaded during each reconfiguration. Several perl_require directives can be present.

",[],"
Syntax:perl_require module;
Default:
Context:http
"], 5 | [4,"perl_set","Installs a Perl handler for the specified variable.","

Installs a Perl handler for the specified variable.

",[],"
Syntax:perl_set $variable module::function|'sub { ... }';
Default:
Context:http
"] 6 | ] -------------------------------------------------------------------------------- /assets/details/ngx_http_proxy_protocol_vendor_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [5,"

$proxy_protocol_tlv_aws_vpce_id
TLV value from the PROXY Protocol header representing the ID of AWS VPC endpoint
$proxy_protocol_tlv_azure_pel_id
TLV value from the PROXY Protocol header representing the LinkID of Azure private endpoint
$proxy_protocol_tlv_gcp_conn_id
TLV value from the PROXY Protocol header representing Google Cloud PSC connection ID
"] 3 | ] -------------------------------------------------------------------------------- /assets/details/ngx_http_random_index_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"random_index","Enables or disables module processing in a surrounding location.","

Enables or disables module processing in a surrounding location.

",[],"
Syntax:random_index on | off;
Default:
random_index off;
Context:location
"] 3 | ] -------------------------------------------------------------------------------- /assets/details/ngx_http_referer_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"referer_hash_bucket_size","Sets the bucket size for the valid referers hash tables. The details of setting up hash tables are provided in a separate [document](https://nginx.org/en/docs/hash.html).","

Sets the bucket size for the valid referers hash tables. The details of setting up hash tables are provided in a separate document.

",[],"
Syntax:referer_hash_bucket_size size;
Default:
referer_hash_bucket_size 64;
Context:server, location

This directive appeared in version 1.0.5.

"], 3 | [4,"referer_hash_max_size","Sets the maximum `size` of the valid referers hash tables. The details of setting up hash tables are provided in a separate [document](https://nginx.org/en/docs/hash.html).","

Sets the maximum size of the valid referers hash tables. The details of setting up hash tables are provided in a separate document.

",[],"
Syntax:referer_hash_max_size size;
Default:
referer_hash_max_size 2048;
Context:server, location

This directive appeared in version 1.0.5.

"], 4 | [4,"valid_referers","Specifies the “Referer” request header field values that will cause the embedded `$invalid_referer` variable to be set to an empty string. Otherwise, the variable will be set to “`1`”. Search for a match is case-insensitive.","

Specifies the “Referer” request header field values that will cause the embedded $invalid_referer variable to be set to an empty string. Otherwise, the variable will be set to “1”. Search for a match is case-insensitive.

Parameters can be as follows:

none
the “Referer” field is missing in the request header;
blocked
the “Referer” field is present in the request header, but its value has been deleted by a firewall or proxy server; such values are strings that do not start with “http://” or “https://”;
server_names
the “Referer” request header field contains one of the server names;
arbitrary string
defines a server name and an optional URI prefix. A server name can have an “*” at the beginning or end. During the checking, the server’s port in the “Referer” field is ignored;
regular expression
the first symbol should be a “~”. It should be noted that an expression will be matched against the text starting after the “http://” or “https://”.

Example:

valid_referers none blocked server_names\n               *.example.com example.* www.example.org/galleries/\n               ~\\.google\\.;\n
",[],"
Syntax:valid_referers none | blocked | server_names | string ...;
Default:
Context:server, location
"], 5 | [5,"

$invalid_referer
Empty string, if the “Referer” request header field value is considered valid, otherwise “1”.
"] 6 | ] -------------------------------------------------------------------------------- /assets/details/ngx_http_session_log_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"session_log","Enables the use of the specified session log. The special value `off` cancels the effect of the `session_log` directives inherited from the previous configuration level.","

Enables the use of the specified session log. The special value off cancels the effect of the session_log directives inherited from the previous configuration level.

",[],"
Syntax:session_log name | off;
Default:
session_log off;
Context:http, server, location
"], 3 | [4,"session_log_format","Specifies the output format of a log. The value of the `$body_bytes_sent` variable is aggregated across all requests in a session. The values of all other variables available for logging correspond to the first request in a session.","

Specifies the output format of a log. The value of the $body_bytes_sent variable is aggregated across all requests in a session. The values of all other variables available for logging correspond to the first request in a session.

",[],"
Syntax:session_log_format name string ...;
Default:
session_log_format combined \"...\";
Context:http
"], 4 | [4,"session_log_zone","Sets the path to a log file and configures the shared memory zone that is used to store currently active sessions.","

Sets the path to a log file and configures the shared memory zone that is used to store currently active sessions.

A session is considered active for as long as the time elapsed since the last request in the session does not exceed the specified timeout (by default, 30 seconds). Once a session is no longer active, it is written to the log.

The id parameter identifies the session to which a request is mapped. The id parameter is set to the hexadecimal representation of an MD5 hash (for example, obtained from a cookie using variables). If this parameter is not specified or does not represent the valid MD5 hash, nginx computes the MD5 hash from the value of the md5 parameter and creates a new session using this hash. Both the id and md5 parameters can contain variables.

The format parameter sets the custom session log format configured by the session_log_format directive. If format is not specified, the predefined “combined” format is used.

",[],"
Syntax:session_log_zone path zone=name:size [format=format] [timeout=time] [id=id] [md5=md5] ;
Default:
Context:http
"], 5 | [5,"

The ngx_http_session_log_module module supports two embedded variables:

$session_log_id
current session ID;
$session_log_binary_id
current session ID in binary form (16 bytes).
"] 6 | ] -------------------------------------------------------------------------------- /assets/details/ngx_http_slice_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"slice","Sets the `size` of the slice. The zero value disables splitting responses into slices. Note that a too low value may result in excessive memory usage and opening a large number of files.","

Sets the size of the slice. The zero value disables splitting responses into slices. Note that a too low value may result in excessive memory usage and opening a large number of files.

In order for a subrequest to return the required range, the $slice_range variable should be passed to the proxied server as the Range request header field. If caching is enabled, $slice_range should be added to the cache key and caching of responses with 206 status code should be enabled.

",[],"
Syntax:slice size;
Default:
slice 0;
Context:http, server, location
"], 3 | [5,"

The ngx_http_slice_module module supports the following embedded variables:

$slice_range
the current slice range in HTTP byte range format, for example, bytes=0-1048575.
"] 4 | ] -------------------------------------------------------------------------------- /assets/details/ngx_http_spdy_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"spdy_chunk_size","Sets the maximum size of chunks into which the response body is [sliced](http://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft2#TOC-Data-frames). A too low value results in higher overhead. A too high value impairs prioritization due to [HOL blocking](http://en.wikipedia.org/wiki/Head-of-line_blocking).","

Sets the maximum size of chunks into which the response body is sliced. A too low value results in higher overhead. A too high value impairs prioritization due to HOL blocking.

",[],"
Syntax:spdy_chunk_size size;
Default:
spdy_chunk_size 8k;
Context:http, server, location

This directive appeared in version 1.5.9.

"], 3 | [4,"spdy_headers_comp","Sets the header compression `_level_` of a response in a range from 1 (fastest, less compression) to 9 (slowest, best compression). The special value 0 turns off the header compression.","

Sets the header compression level of a response in a range from 1 (fastest, less compression) to 9 (slowest, best compression). The special value 0 turns off the header compression.

",[],"
Syntax:spdy_headers_comp level;
Default:
spdy_headers_comp 0;
Context:http, server
"], 4 | [5,"

The ngx_http_spdy_module module supports the following embedded variables:

$spdy
SPDY protocol version for SPDY connections, or an empty string otherwise;
$spdy_request_priority
request priority for SPDY connections, or an empty string otherwise.
"] 5 | ] -------------------------------------------------------------------------------- /assets/details/ngx_http_split_clients_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"split_clients","Creates a variable for A/B testing, for example:","

Creates a variable for A/B testing, for example:

split_clients \"${remote_addr}AAA\" $variant {\n               0.5%               .one;\n               2.0%               .two;\n               *                  \"\";\n}\n

The value of the original string is hashed using MurmurHash2. In the example given, hash values from 0 to 21474835 (0.5%) correspond to the value \".one\" of the $variant variable, hash values from 21474836 to 107374180 (2%) correspond to the value \".two\", and hash values from 107374181 to 4294967295 correspond to the value \"\" (an empty string).

",[],"
Syntax:split_clients string $variable { ... }
Default:
Context:http
"] 3 | ] -------------------------------------------------------------------------------- /assets/details/ngx_http_status_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"status","The status information will be accessible from the surrounding location. Access to this location should be [limited](https://nginx.org/en/docs/http/ngx_http_core_module.html#satisfy).","

The status information will be accessible from the surrounding location. Access to this location should be limited.

",[],"
Syntax:status;
Default:
Context:location
"], 3 | [4,"status_format","By default, status information is output in the JSON format.","

By default, status information is output in the JSON format.

Alternatively, data may be output as JSONP. The callback parameter specifies the name of a callback function. Parameter value can contain variables. If parameter is omitted, or the computed value is an empty string, then “ngx_status_jsonp_callback” is used.

",[],"
Syntax:status_format json;
status_format jsonp [callback];
Default:
status_format json;
Context:http, server, location
"], 4 | [4,"status_zone","Enables collection of virtual [http](https://nginx.org/en/docs/http/ngx_http_core_module.html#server) or [stream](https://nginx.org/en/docs/stream/ngx_stream_core_module.html#server) (1.7.11) server status information in the specified `zone`. Several servers may share the same zone.","

Enables collection of virtual http or stream (1.7.11) server status information in the specified zone. Several servers may share the same zone.

",[],"
Syntax:status_zone zone;
Default:
Context:server
"] 5 | ] -------------------------------------------------------------------------------- /assets/details/ngx_http_stub_status_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"stub_status","The basic status information will be accessible from the surrounding location.","

The basic status information will be accessible from the surrounding location.

In versions prior to 1.7.5, the directive syntax required an arbitrary argument, for example, “stub_status on”.
",["In versions prior to 1.7.5, the directive syntax required an arbitrary argument, for example, “`stub_status on`”."],"
Syntax:stub_status;
Default:
Context:server, location
"], 3 | [5,"

The ngx_http_stub_status_module module supports the following embedded variables (1.3.14):

$connections_active
same as the Active connections value;
$connections_reading
same as the Reading value;
$connections_writing
same as the Writing value;
$connections_waiting
same as the Waiting value.
"] 4 | ] -------------------------------------------------------------------------------- /assets/details/ngx_http_sub_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"sub_filter","Sets a string to replace and a replacement string. The string to replace is matched ignoring the case. The string to replace (1.9.4) and replacement string can contain variables. Several `sub_filter` directives can be specified on the same configuration level (1.9.4). These directives are inherited from the previous configuration level if and only if there are no `sub_filter` directives defined on the current level.","

Sets a string to replace and a replacement string. The string to replace is matched ignoring the case. The string to replace (1.9.4) and replacement string can contain variables. Several sub_filter directives can be specified on the same configuration level (1.9.4). These directives are inherited from the previous configuration level if and only if there are no sub_filter directives defined on the current level.

",[],"
Syntax:sub_filter string replacement;
Default:
Context:http, server, location
"], 3 | [4,"sub_filter_last_modified","Allows preserving the “Last-Modified” header field from the original response during replacement to facilitate response caching.","

Allows preserving the “Last-Modified” header field from the original response during replacement to facilitate response caching.

By default, the header field is removed as contents of the response are modified during processing.

",[],"
Syntax:sub_filter_last_modified on | off;
Default:
sub_filter_last_modified off;
Context:http, server, location

This directive appeared in version 1.5.1.

"], 4 | [4,"sub_filter_once","Indicates whether to look for each string to replace once or repeatedly.","

Indicates whether to look for each string to replace once or repeatedly.

",[],"
Syntax:sub_filter_once on | off;
Default:
sub_filter_once on;
Context:http, server, location
"], 5 | [4,"sub_filter_types","Enables string replacement in responses with the specified MIME types in addition to “`text/html`”. The special value “`*`” matches any MIME type (0.8.29).","

Enables string replacement in responses with the specified MIME types in addition to “text/html”. The special value “*” matches any MIME type (0.8.29).

",[],"
Syntax:sub_filter_types mime-type ...;
Default:
sub_filter_types text/html;
Context:http, server, location
"] 6 | ] -------------------------------------------------------------------------------- /assets/details/ngx_http_upstream_conf_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"upstream_conf","Turns on the HTTP interface of upstream configuration in the surrounding location. Access to this location should be [limited](https://nginx.org/en/docs/http/ngx_http_core_module.html#satisfy).","

Turns on the HTTP interface of upstream configuration in the surrounding location. Access to this location should be limited.

Configuration commands can be used to:

",[],"
Syntax:upstream_conf;
Default:
Context:location
"] 3 | ] -------------------------------------------------------------------------------- /assets/details/ngx_mail_auth_http_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"auth_http","Sets the URL of the HTTP authentication server. The protocol is described [below](https://nginx.org/en/docs/mail/ngx_mail_auth_http_module.html#protocol).","

Sets the URL of the HTTP authentication server. The protocol is described below.

",[],"
Syntax:auth_http URL;
Default:
Context:mail, server
"], 3 | [4,"auth_http_header","Appends the specified header to requests sent to the authentication server. This header can be used as the shared secret to verify that the request comes from nginx. For example:","

Appends the specified header to requests sent to the authentication server. This header can be used as the shared secret to verify that the request comes from nginx. For example:

auth_http_header X-Auth-Key \"secret_string\";\n
",[],"
Syntax:auth_http_header header value;
Default:
Context:mail, server
"], 4 | [4,"auth_http_pass_client_cert","Appends the “Auth-SSL-Cert” header with the [client](https://nginx.org/en/docs/mail/ngx_mail_ssl_module.html#ssl_verify_client) certificate in the PEM format (urlencoded) to requests sent to the authentication server.","

Appends the “Auth-SSL-Cert” header with the client certificate in the PEM format (urlencoded) to requests sent to the authentication server.

",[],"
Syntax:auth_http_pass_client_cert on | off;
Default:
auth_http_pass_client_cert off;
Context:mail, server

This directive appeared in version 1.7.11.

"], 5 | [4,"auth_http_timeout","Sets the timeout for communication with the authentication server.","

Sets the timeout for communication with the authentication server.

",[],"
Syntax:auth_http_timeout time;
Default:
auth_http_timeout 60s;
Context:mail, server
"] 6 | ] -------------------------------------------------------------------------------- /assets/details/ngx_mail_pop3_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"pop3_auth","Sets permitted methods of authentication for POP3 clients. Supported methods are:","

Sets permitted methods of authentication for POP3 clients. Supported methods are:

plain
USER/PASS, AUTH PLAIN, AUTH LOGIN
apop
APOP. In order for this method to work, the password must be stored unencrypted.
cram-md5
AUTH CRAM-MD5. In order for this method to work, the password must be stored unencrypted.
external
AUTH EXTERNAL (1.11.6).

Plain text authentication methods (USER/PASS, AUTH PLAIN, and AUTH LOGIN) are always enabled, though if the plain method is not specified, AUTH PLAIN and AUTH LOGIN will not be automatically included in pop3_capabilities.

",[],"
Syntax:pop3_auth method ...;
Default:
pop3_auth plain;
Context:mail, server
"], 3 | [4,"pop3_capabilities","Sets the [POP3 protocol](https://datatracker.ietf.org/doc/html/rfc2449) extensions list that is passed to the client in response to the `CAPA` command. The authentication methods specified in the [pop3\\_auth](https://nginx.org/en/docs/mail/ngx_mail_pop3_module.html#pop3_auth) directive ([SASL](https://datatracker.ietf.org/doc/html/rfc2449) extension) and [STLS](https://datatracker.ietf.org/doc/html/rfc2595) are automatically added to this list depending on the [starttls](https://nginx.org/en/docs/mail/ngx_mail_ssl_module.html#starttls) directive value.","

Sets the POP3 protocol extensions list that is passed to the client in response to the CAPA command. The authentication methods specified in the pop3_auth directive (SASL extension) and STLS are automatically added to this list depending on the starttls directive value.

It makes sense to specify the extensions supported by the POP3 backends to which the clients are proxied (if these extensions are related to commands used after the authentication, when nginx transparently proxies the client connection to the backend).

The current list of standardized extensions is published at www.iana.org.

",[],"
Syntax:pop3_capabilities extension ...;
Default:
pop3_capabilities TOP USER UIDL;
Context:mail, server
"] 4 | ] -------------------------------------------------------------------------------- /assets/details/ngx_mail_realip_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"set_real_ip_from","Defines trusted addresses that are known to send correct replacement addresses. If the special value `unix:` is specified, all UNIX-domain sockets will be trusted.","

Defines trusted addresses that are known to send correct replacement addresses. If the special value unix: is specified, all UNIX-domain sockets will be trusted.

",[],"
Syntax:set_real_ip_from address | CIDR | unix:;
Default:
Context:mail, server
"] 3 | ] -------------------------------------------------------------------------------- /assets/details/ngx_memc.json: -------------------------------------------------------------------------------- 1 | [ 2 | ] -------------------------------------------------------------------------------- /assets/details/ngx_postgres.json: -------------------------------------------------------------------------------- 1 | [ 2 | ] -------------------------------------------------------------------------------- /assets/details/ngx_proxy.json: -------------------------------------------------------------------------------- 1 | [ 2 | ] -------------------------------------------------------------------------------- /assets/details/ngx_redis.json: -------------------------------------------------------------------------------- 1 | [ 2 | ] -------------------------------------------------------------------------------- /assets/details/ngx_redis2.json: -------------------------------------------------------------------------------- 1 | [ 2 | ] -------------------------------------------------------------------------------- /assets/details/ngx_stream_access_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"allow","Allows access for the specified network or address. If the special value `unix:` is specified, allows access for all UNIX-domain sockets.","

Allows access for the specified network or address. If the special value unix: is specified, allows access for all UNIX-domain sockets.

",[],"
Syntax:allow address | CIDR | unix: | all;
Default:
Context:stream, server
"], 3 | [4,"deny","Denies access for the specified network or address. If the special value `unix:` is specified, denies access for all UNIX-domain sockets.","

Denies access for the specified network or address. If the special value unix: is specified, denies access for all UNIX-domain sockets.

",[],"
Syntax:deny address | CIDR | unix: | all;
Default:
Context:stream, server
"] 4 | ] -------------------------------------------------------------------------------- /assets/details/ngx_stream_geo_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"geo","Describes the dependency of values of the specified variable on the client IP address. By default, the address is taken from the `$remote_addr` variable, but it can also be taken from another variable, for example:","

Describes the dependency of values of the specified variable on the client IP address. By default, the address is taken from the $remote_addr variable, but it can also be taken from another variable, for example:

geo $arg_remote_addr $geo {\n    ...;\n}\n
Since variables are evaluated only when used, the mere existence of even a large number of declared “geo” variables does not cause any extra costs for connection processing.

If the value of a variable does not represent a valid IP address then the “255.255.255.255” address is used.

Addresses are specified either as prefixes in CIDR notation (including individual addresses) or as ranges.

The following special parameters are also supported:

delete
deletes the specified network.
default
a value set to the variable if the client address does not match any of the specified addresses. When addresses are specified in CIDR notation, “0.0.0.0/0” and “::/0” can be used instead of default. When default is not specified, the default value will be an empty string.
include
includes a file with addresses and values. There can be several inclusions.
ranges
indicates that addresses are specified as ranges. This parameter should be the first. To speed up loading of a geo base, addresses should be put in ascending order.

Example:

geo $country {\n    default        ZZ;\n    include        conf/geo.conf;\n    delete         127.0.0.0/16;\n\n    127.0.0.0/24   US;\n    127.0.0.1/32   RU;\n    10.1.0.0/16    RU;\n    192.168.1.0/24 UK;\n}\n

The conf/geo.conf file could contain the following lines:

10.2.0.0/16    RU;\n192.168.2.0/24 RU;\n

A value of the most specific match is used. For example, for the 127.0.0.1 address the value “RU” will be chosen, not “US”.

Example with ranges:

geo $country {\n    ranges;\n    default                   ZZ;\n    127.0.0.0-127.0.0.0       US;\n    127.0.0.1-127.0.0.1       RU;\n    127.0.0.1-127.0.0.255     US;\n    10.1.0.0-10.1.255.255     RU;\n    192.168.1.0-192.168.1.255 UK;\n}\n
",["Since variables are evaluated only when used, the mere existence of even a large number of declared “`geo`” variables does not cause any extra costs for connection processing."],"
Syntax:geo [$address] $variable { ... }
Default:
Context:stream
"] 3 | ] -------------------------------------------------------------------------------- /assets/details/ngx_stream_keyval_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"keyval","Creates a new `$variable` whose value is looked up by the `key` in the key-value database. Matching rules are defined by the [`type`](https://nginx.org/en/docs/stream/ngx_stream_keyval_module.html#keyval_type) parameter of the [`keyval_zone`](https://nginx.org/en/docs/stream/ngx_stream_keyval_module.html#keyval_zone) directive. The database is stored in a shared memory zone specified by the `zone` parameter.","

Creates a new $variable whose value is looked up by the key in the key-value database. Matching rules are defined by the type parameter of the keyval_zone directive. The database is stored in a shared memory zone specified by the zone parameter.

",[],"
Syntax:keyval key $variable zone=name;
Default:
Context:stream
"], 3 | [4,"keyval_zone","Sets the `name` and `size` of the shared memory zone that keeps the key-value database. Key-value pairs are managed by the [API](https://nginx.org/en/docs/http/ngx_http_api_module.html#stream_keyvals_).","

Sets the name and size of the shared memory zone that keeps the key-value database. Key-value pairs are managed by the API.

",[],"
Syntax:keyval_zone zone=name:size [state=file] [timeout=time] [type=string|ip|prefix] [sync];
Default:
Context:stream
"] 4 | ] -------------------------------------------------------------------------------- /assets/details/ngx_stream_mqtt_filter_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"mqtt","Enables the MQTT protocol for the given virtual server.","

Enables the MQTT protocol for the given virtual server.

",[],"
Syntax:mqtt on | off;
Default:
mqtt off;
Context:stream, server
"], 3 | [4,"mqtt_buffers","Sets the `number` and `size` of the buffers used for handling MQTT messages, for a single connection.","

Sets the number and size of the buffers used for handling MQTT messages, for a single connection.

",[],"
Syntax:mqtt_buffers number size;
Default:
mqtt_buffers 100 1k;
Context:stream, server

This directive appeared in version 1.25.1.

"], 4 | [4,"mqtt_rewrite_buffer_size","Sets the `size` of the buffer used for writing a modified message. By default, the buffer size is equal to one memory page. This is either 4K or 8K, depending on a platform. It can be made smaller, however.","
This directive is obsolete since version 1.25.1. The mqtt_buffers directive should be used instead.

Sets the size of the buffer used for writing a modified message. By default, the buffer size is equal to one memory page. This is either 4K or 8K, depending on a platform. It can be made smaller, however.

",["This directive is obsolete since version 1.25.1. The [mqtt\\_buffers](https://nginx.org/en/docs/stream/ngx_stream_mqtt_filter_module.html#mqtt_buffers) directive should be used instead."],"
Syntax:mqtt_rewrite_buffer_size size;
Default:
mqtt_rewrite_buffer_size 4k|8k;
Context:server
"], 5 | [4,"mqtt_set_connect","Sets the message `field` to the given `value` for CONNECT message. The following fields are supported: `clientid`, `username`, and `password`. The value can contain text, variables, and their combination.","

Sets the message field to the given value for CONNECT message. The following fields are supported: clientid, username, and password. The value can contain text, variables, and their combination.

Several mqtt_set_connect directives can be specified on the same level:

mqtt_set_connect clientid \"$client\";\nmqtt_set_connect username \"$name\";\n
",[],"
Syntax:mqtt_set_connect field value;
Default:
Context:server
"] 6 | ] -------------------------------------------------------------------------------- /assets/details/ngx_stream_mqtt_preread_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"mqtt_preread","Enables extracting information from the MQTT CONNECT message at the [preread](https://nginx.org/en/docs/stream/stream_processing.html#preread_phase) phase.","

Enables extracting information from the MQTT CONNECT message at the preread phase.

",[],"
Syntax:mqtt_preread on | off;
Default:
mqtt_preread off;
Context:stream, server
"], 3 | [5,"

$mqtt_preread_clientid
the clientid value from the CONNECT message
$mqtt_preread_username
the username value from the CONNECT message
"] 4 | ] -------------------------------------------------------------------------------- /assets/details/ngx_stream_proxy_protocol_vendor_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [5,"

$proxy_protocol_tlv_aws_vpce_id
TLV value from the PROXY Protocol header representing the ID of AWS VPC endpoint
$proxy_protocol_tlv_azure_pel_id
TLV value from the PROXY Protocol header representing the LinkID of Azure private endpoint
$proxy_protocol_tlv_gcp_conn_id
TLV value from the PROXY Protocol header representing Google Cloud PSC connection ID
"] 3 | ] -------------------------------------------------------------------------------- /assets/details/ngx_stream_realip_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"set_real_ip_from","Defines trusted addresses that are known to send correct replacement addresses. If the special value `unix:` is specified, all UNIX-domain sockets will be trusted.","

Defines trusted addresses that are known to send correct replacement addresses. If the special value unix: is specified, all UNIX-domain sockets will be trusted.

",[],"
Syntax:set_real_ip_from address | CIDR | unix:;
Default:
Context:stream, server
"], 3 | [5,"

$realip_remote_addr
keeps the original client address
$realip_remote_port
keeps the original client port
"] 4 | ] -------------------------------------------------------------------------------- /assets/details/ngx_stream_return_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"return","Specifies a `value` to send to the client. The value can contain text, variables, and their combination.","

Specifies a value to send to the client. The value can contain text, variables, and their combination.

",[],"
Syntax:return value;
Default:
Context:server
"] 3 | ] -------------------------------------------------------------------------------- /assets/details/ngx_stream_set_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"set","Sets a `value` for the specified `variable`. The `value` can contain text, variables, and their combination.","

Sets a value for the specified variable. The value can contain text, variables, and their combination.

",[],"
Syntax:set $variable value;
Default:
Context:server
"] 3 | ] -------------------------------------------------------------------------------- /assets/details/ngx_stream_split_clients_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"split_clients","Creates a variable for A/B testing, for example:","

Creates a variable for A/B testing, for example:

split_clients \"${remote_addr}AAA\" $variant {\n               0.5%               .one;\n               2.0%               .two;\n               *                  \"\";\n}\n

The value of the original string is hashed using MurmurHash2. In the example given, hash values from 0 to 21474835 (0.5%) correspond to the value \".one\" of the $variant variable, hash values from 21474836 to 107374180 (2%) correspond to the value \".two\", and hash values from 107374181 to 4294967295 correspond to the value \"\" (an empty string).

",[],"
Syntax:split_clients string $variable { ... }
Default:
Context:stream
"] 3 | ] -------------------------------------------------------------------------------- /assets/details/ngx_stream_ssl_preread_module.json: -------------------------------------------------------------------------------- 1 | [ 2 | [4,"ssl_preread","Enables extracting information from the ClientHello message at the [preread](https://nginx.org/en/docs/stream/stream_processing.html#preread_phase) phase.","

Enables extracting information from the ClientHello message at the preread phase.

",[],"
Syntax:ssl_preread on | off;
Default:
ssl_preread off;
Context:stream, server
"], 3 | [5,"

$ssl_preread_protocol
the highest SSL protocol version supported by the client (1.15.2)
$ssl_preread_server_name
server name requested through SNI
$ssl_preread_alpn_protocols
list of protocols advertised by the client through ALPN (1.13.10). The values are separated by commas.
"] 4 | ] -------------------------------------------------------------------------------- /assets/manifest/README.md: -------------------------------------------------------------------------------- 1 | # Manifest format 2 | 3 | ``` javascript 4 | line = [1, moduleName1, moduleName2, ...] 5 | 6 | 7 | line = [2, name, syntax, defaultValue, contexts, moduleNameIndex, since, link, completionItem]; 8 | // Eg: 9 | line = [2,"accept_mutex",["accept_mutex on | off;"],"accept_mutex off;",["events"],"ngx_core_module",null,"ngx_core_module.html#accept_mutex",null] 10 | ``` 11 | -------------------------------------------------------------------------------- /assets/manifest/http_headers.es.json: -------------------------------------------------------------------------------- 1 | [ 2 | [6,"Accept","Content-Types (tipos de contenido) que se aceptan."], 3 | [6,"Accept-Charset","Conjunto de caracteres que se aceptan."], 4 | [6,"Accept-Encoding","Lista de codificaciones que se aceptan."], 5 | [6,"Accept-Language","Idiomas que se aceptan."], 6 | [6,"Accept-Datetime","Versión de la hora y fecha que se aceptan."], 7 | [6,"Authorization","Credenciales de autorización."], 8 | [6,"Caché-Control","Se controla las políticas de caché."], 9 | [6,"Connection","Se controla el tipo de conexión."], 10 | [6,"Cookie","Una cookie enviada previamente por el servidor usando Set-Cookie"], 11 | [6,"Content-Length","El tamaño del contenido de la petición en bytes"], 12 | [6,"Content-MD5","Un checksum en MD5 sobre el contenido"], 13 | [6,"Content-Type","El tipo de contenido de la petición en POST o PUT"], 14 | [6,"Date","La fecha y la hora de la petición"], 15 | [6,"Forwarded","Indica la información original del cliente en caso de conexión por proxy."], 16 | [6,"From","La dirección de correo electrónico de la petición."], 17 | [6,"Host","El nombre de dominio o dirección IP (puede incluir número de puerto). El uso de la cabecera es obligatorio a partir de HTTP 1.1"], 18 | [6,"Max-Forwards","Limita el número de veces que un mensaje viaja a través de los proxies."], 19 | [6,"Origin","Inicia una petición para servidores con respuesta a Access-Control-Allow-Origin."], 20 | [6,"Pragma","Implementa cabeceras en donde múltiples efectos se aplica a todo."], 21 | [6,"Proxy-Authorization","Credenciales de autorización para conectarse a un proxy."], 22 | [6,"Range","Pide sólo una parte del contenido"], 23 | [6,"Referer","Indica la dirección URL de donde proviene, en otras palabras, es la dirección web del botón Atrás."], 24 | [6,"User-Agent","Contiene la información de la petición, como el navegador, el sistema operativo, etc."], 25 | [6,"Upgrade","Pide al servidor que se actualice la versión de HTTP para funcionar."], 26 | [6,"Warning","Una advertencia general sobre problemas de la entidad."] 27 | ] -------------------------------------------------------------------------------- /assets/mediatypes/README.md: -------------------------------------------------------------------------------- 1 | # Media Types 2 | 3 | Last Updated: 2021-11-15 4 | 5 | 6 | -------------------------------------------------------------------------------- /assets/mediatypes/audio.json: -------------------------------------------------------------------------------- 1 | [ 2 | ["1d-interleaved-parityfec"], 3 | ["32kadpcm"], 4 | ["3gpp"], 5 | ["3gpp2"], 6 | ["aac"], 7 | ["ac3"], 8 | ["AMR"], 9 | ["AMR-WB"], 10 | ["amr-wb+"], 11 | ["aptx"], 12 | ["asc"], 13 | ["ATRAC-ADVANCED-LOSSLESS"], 14 | ["ATRAC-X"], 15 | ["ATRAC3"], 16 | ["basic"], 17 | ["BV16"], 18 | ["BV32"], 19 | ["clearmode"], 20 | ["CN"], 21 | ["DAT12"], 22 | ["dls"], 23 | ["dsr-es201108"], 24 | ["dsr-es202050"], 25 | ["dsr-es202211"], 26 | ["dsr-es202212"], 27 | ["DV"], 28 | ["DVI4"], 29 | ["eac3"], 30 | ["encaprtp"], 31 | ["EVRC"], 32 | ["EVRC-QCP"], 33 | ["EVRC0"], 34 | ["EVRC1"], 35 | ["EVRCB"], 36 | ["EVRCB0"], 37 | ["EVRCB1"], 38 | ["EVRCNW"], 39 | ["EVRCNW0"], 40 | ["EVRCNW1"], 41 | ["EVRCWB"], 42 | ["EVRCWB0"], 43 | ["EVRCWB1"], 44 | ["EVS"], 45 | ["example"], 46 | ["flexfec"], 47 | ["fwdred"], 48 | ["G711-0"], 49 | ["G719"], 50 | ["G7221"], 51 | ["G722"], 52 | ["G723"], 53 | ["G726-16"], 54 | ["G726-24"], 55 | ["G726-32"], 56 | ["G726-40"], 57 | ["G728"], 58 | ["G729"], 59 | ["G7291"], 60 | ["G729D"], 61 | ["G729E"], 62 | ["GSM"], 63 | ["GSM-EFR"], 64 | ["GSM-HR-08"], 65 | ["iLBC"], 66 | ["ip-mr_v2.5"], 67 | ["L8"], 68 | ["L16"], 69 | ["L20"], 70 | ["L24"], 71 | ["LPC"], 72 | ["matroska"], 73 | ["MELP"], 74 | ["MELP600"], 75 | ["MELP1200"], 76 | ["MELP2400"], 77 | ["mhas"], 78 | ["mobile-xmf"], 79 | ["MPA"], 80 | ["mp4"], 81 | ["MP4A-LATM"], 82 | ["mpa-robust"], 83 | ["mpeg"], 84 | ["mpeg4-generic"], 85 | ["ogg"], 86 | ["opus"], 87 | ["parityfec"], 88 | ["PCMA"], 89 | ["PCMA-WB"], 90 | ["PCMU"], 91 | ["PCMU-WB"], 92 | ["prs.sid"], 93 | ["QCELP"], 94 | ["raptorfec"], 95 | ["RED"], 96 | ["rtp-enc-aescm128"], 97 | ["rtploopback"], 98 | ["rtp-midi"], 99 | ["rtx"], 100 | ["scip"], 101 | ["SMV"], 102 | ["SMV0"], 103 | ["SMV-QCP"], 104 | ["sofa"], 105 | ["sp-midi"], 106 | ["speex"], 107 | ["t140c"], 108 | ["t38"], 109 | ["telephone-event"], 110 | ["TETRA_ACELP"], 111 | ["TETRA_ACELP_BB"], 112 | ["tone"], 113 | ["TSVCIS"], 114 | ["UEMCLIP"], 115 | ["ulpfec"], 116 | ["usac"], 117 | ["VDVI"], 118 | ["VMR-WB"], 119 | ["vnd.3gpp.iufp"], 120 | ["vnd.4SB"], 121 | ["vnd.audiokoz"], 122 | ["vnd.CELP"], 123 | ["vnd.cisco.nse"], 124 | ["vnd.cmles.radio-events"], 125 | ["vnd.cns.anp1"], 126 | ["vnd.cns.inf1"], 127 | ["vnd.dece.audio"], 128 | ["vnd.digital-winds"], 129 | ["vnd.dlna.adts"], 130 | ["vnd.dolby.heaac.1"], 131 | ["vnd.dolby.heaac.2"], 132 | ["vnd.dolby.mlp"], 133 | ["vnd.dolby.mps"], 134 | ["vnd.dolby.pl2"], 135 | ["vnd.dolby.pl2x"], 136 | ["vnd.dolby.pl2z"], 137 | ["vnd.dolby.pulse.1"], 138 | ["vnd.dra"], 139 | ["vnd.dts"], 140 | ["vnd.dts.hd"], 141 | ["vnd.dts.uhd"], 142 | ["vnd.dvb.file"], 143 | ["vnd.everad.plj"], 144 | ["vnd.hns.audio"], 145 | ["vnd.lucent.voice"], 146 | ["vnd.ms-playready.media.pya"], 147 | ["vnd.nokia.mobile-xmf"], 148 | ["vnd.nortel.vbk"], 149 | ["vnd.nuera.ecelp4800"], 150 | ["vnd.nuera.ecelp7470"], 151 | ["vnd.nuera.ecelp9600"], 152 | ["vnd.octel.sbc"], 153 | ["vnd.presonus.multitrack"], 154 | ["vnd.qcelp","vnd.qcelp - DEPRECATED in favor of audio/qcelp"], 155 | ["vnd.rhetorex.32kadpcm"], 156 | ["vnd.rip"], 157 | ["vnd.sealedmedia.softseal.mpeg"], 158 | ["vnd.vmx.cvsd"], 159 | ["vorbis"], 160 | ["vorbis-config"] 161 | ] -------------------------------------------------------------------------------- /assets/mediatypes/font.json: -------------------------------------------------------------------------------- 1 | [ 2 | ["collection"], 3 | ["otf"], 4 | ["sfnt"], 5 | ["ttf"], 6 | ["woff"], 7 | ["woff2"] 8 | ] -------------------------------------------------------------------------------- /assets/mediatypes/image.json: -------------------------------------------------------------------------------- 1 | [ 2 | ["aces"], 3 | ["apng"], 4 | ["avci"], 5 | ["avcs"], 6 | ["avif"], 7 | ["bmp"], 8 | ["cgm"], 9 | ["dicom-rle"], 10 | ["dpx"], 11 | ["emf"], 12 | ["example"], 13 | ["fits"], 14 | ["g3fax"], 15 | ["heic"], 16 | ["heic-sequence"], 17 | ["heif"], 18 | ["heif-sequence"], 19 | ["hej2k"], 20 | ["hsj2"], 21 | ["j2c"], 22 | ["jls"], 23 | ["jp2"], 24 | ["jph"], 25 | ["jphc"], 26 | ["jpm"], 27 | ["jpx"], 28 | ["jxr"], 29 | ["jxrA"], 30 | ["jxrS"], 31 | ["jxs"], 32 | ["jxsc"], 33 | ["jxsi"], 34 | ["jxss"], 35 | ["ktx"], 36 | ["ktx2"], 37 | ["naplps"], 38 | ["png"], 39 | ["prs.btif"], 40 | ["prs.pti"], 41 | ["pwg-raster"], 42 | ["svg+xml"], 43 | ["t38"], 44 | ["tiff"], 45 | ["tiff-fx"], 46 | ["vnd.adobe.photoshop"], 47 | ["vnd.airzip.accelerator.azv"], 48 | ["vnd.cns.inf2"], 49 | ["vnd.dece.graphic"], 50 | ["vnd.djvu"], 51 | ["vnd.dwg"], 52 | ["vnd.dxf"], 53 | ["vnd.dvb.subtitle"], 54 | ["vnd.fastbidsheet"], 55 | ["vnd.fpx"], 56 | ["vnd.fst"], 57 | ["vnd.fujixerox.edmics-mmr"], 58 | ["vnd.fujixerox.edmics-rlc"], 59 | ["vnd.globalgraphics.pgb"], 60 | ["vnd.microsoft.icon"], 61 | ["vnd.mix"], 62 | ["vnd.ms-modi"], 63 | ["vnd.mozilla.apng"], 64 | ["vnd.net-fpx"], 65 | ["vnd.pco.b16"], 66 | ["vnd.radiance"], 67 | ["vnd.sealed.png"], 68 | ["vnd.sealedmedia.softseal.gif"], 69 | ["vnd.sealedmedia.softseal.jpg"], 70 | ["vnd.svf"], 71 | ["vnd.tencent.tap"], 72 | ["vnd.valve.source.texture"], 73 | ["vnd.wap.wbmp"], 74 | ["vnd.xiff"], 75 | ["vnd.zbrush.pcx"], 76 | ["webp"], 77 | ["wmf"], 78 | ["emf","x-emf - DEPRECATED in favor of image/emf"], 79 | ["wmf","x-wmf - DEPRECATED in favor of image/wmf"] 80 | ] -------------------------------------------------------------------------------- /assets/mediatypes/message.json: -------------------------------------------------------------------------------- 1 | [ 2 | ["bhttp"], 3 | ["CPIM"], 4 | ["delivery-status"], 5 | ["disposition-notification"], 6 | ["example"], 7 | ["feedback-report"], 8 | ["global"], 9 | ["global-delivery-status"], 10 | ["global-disposition-notification"], 11 | ["global-headers"], 12 | ["http"], 13 | ["imdn+xml"], 14 | ["mls"], 15 | ["news","news (OBSOLETED by [RFC5537])"], 16 | ["ohttp-req"], 17 | ["ohttp-res"], 18 | ["s-http","s-http (OBSOLETE)"], 19 | ["sip"], 20 | ["sipfrag"], 21 | ["tracking-status"], 22 | ["vnd.si.simp","vnd.si.simp (OBSOLETED by request)"], 23 | ["vnd.wfa.wsc"] 24 | ] -------------------------------------------------------------------------------- /assets/mediatypes/model.json: -------------------------------------------------------------------------------- 1 | [ 2 | ["3mf"], 3 | ["e57"], 4 | ["example"], 5 | ["gltf-binary"], 6 | ["gltf+json"], 7 | ["JT"], 8 | ["iges"], 9 | ["mtl"], 10 | ["obj"], 11 | ["prc"], 12 | ["step"], 13 | ["step+xml"], 14 | ["step+zip"], 15 | ["step-xml+zip"], 16 | ["stl"], 17 | ["u3d"], 18 | ["vnd.bary"], 19 | ["vnd.cld"], 20 | ["vnd.collada+xml"], 21 | ["vnd.dwf"], 22 | ["vnd.flatland.3dml"], 23 | ["vnd.gdl"], 24 | ["vnd.gs-gdl"], 25 | ["vnd.gtw"], 26 | ["vnd.moml+xml"], 27 | ["vnd.mts"], 28 | ["vnd.opengex"], 29 | ["vnd.parasolid.transmit.binary"], 30 | ["vnd.parasolid.transmit.text"], 31 | ["vnd.pytha.pyox"], 32 | ["vnd.rosette.annotated-data-model"], 33 | ["vnd.sap.vds"], 34 | ["vnd.usda"], 35 | ["vnd.usdz+zip"], 36 | ["vnd.valve.source.compiled-map"], 37 | ["vnd.vtu"], 38 | ["x3d-vrml"], 39 | ["x3d+fastinfoset"], 40 | ["x3d+xml"] 41 | ] -------------------------------------------------------------------------------- /assets/mediatypes/multipart.json: -------------------------------------------------------------------------------- 1 | [ 2 | ["appledouble"], 3 | ["byteranges"], 4 | ["encrypted"], 5 | ["example"], 6 | ["form-data"], 7 | ["header-set"], 8 | ["multilingual"], 9 | ["related"], 10 | ["report"], 11 | ["signed"], 12 | ["vnd.bint.med-plus"], 13 | ["voice-message"], 14 | ["x-mixed-replace"] 15 | ] -------------------------------------------------------------------------------- /assets/mediatypes/text.json: -------------------------------------------------------------------------------- 1 | [ 2 | ["1d-interleaved-parityfec"], 3 | ["cache-manifest"], 4 | ["calendar"], 5 | ["cql"], 6 | ["cql-expression"], 7 | ["cql-identifier"], 8 | ["css"], 9 | ["csv"], 10 | ["csv-schema"], 11 | ["directory","directory - DEPRECATED by RFC6350"], 12 | ["dns"], 13 | ["ecmascript","ecmascript (OBSOLETED in favor of text/javascript)"], 14 | ["encaprtp"], 15 | ["example"], 16 | ["fhirpath"], 17 | ["flexfec"], 18 | ["fwdred"], 19 | ["gff3"], 20 | ["grammar-ref-list"], 21 | ["hl7v2"], 22 | ["html"], 23 | ["javascript"], 24 | ["jcr-cnd"], 25 | ["markdown"], 26 | ["mizar"], 27 | ["n3"], 28 | ["parameters"], 29 | ["parityfec"], 30 | ["provenance-notation"], 31 | ["prs.fallenstein.rst"], 32 | ["prs.lines.tag"], 33 | ["prs.prop.logic"], 34 | ["prs.texi"], 35 | ["raptorfec"], 36 | ["RED"], 37 | ["rfc822-headers"], 38 | ["rtf"], 39 | ["rtp-enc-aescm128"], 40 | ["rtploopback"], 41 | ["rtx"], 42 | ["SGML"], 43 | ["shaclc"], 44 | ["shex"], 45 | ["spdx"], 46 | ["strings"], 47 | ["t140"], 48 | ["tab-separated-values"], 49 | ["troff"], 50 | ["turtle"], 51 | ["ulpfec"], 52 | ["uri-list"], 53 | ["vcard"], 54 | ["vnd.a"], 55 | ["vnd.abc"], 56 | ["vnd.ascii-art"], 57 | ["vnd.curl"], 58 | ["vnd.debian.copyright"], 59 | ["vnd.DMClientScript"], 60 | ["vnd.dvb.subtitle"], 61 | ["vnd.esmertec.theme-descriptor"], 62 | ["vnd.exchangeable"], 63 | ["vnd.familysearch.gedcom"], 64 | ["vnd.ficlab.flt"], 65 | ["vnd.fly"], 66 | ["vnd.fmi.flexstor"], 67 | ["vnd.gml"], 68 | ["vnd.graphviz"], 69 | ["vnd.hans"], 70 | ["vnd.hgl"], 71 | ["vnd.in3d.3dml"], 72 | ["vnd.in3d.spot"], 73 | ["vnd.IPTC.NewsML"], 74 | ["vnd.IPTC.NITF"], 75 | ["vnd.latex-z"], 76 | ["vnd.motorola.reflex"], 77 | ["vnd.ms-mediapackage"], 78 | ["vnd.net2phone.commcenter.command"], 79 | ["vnd.radisys.msml-basic-layout"], 80 | ["vnd.senx.warpscript"], 81 | ["vnd.si.uricatalogue","vnd.si.uricatalogue (OBSOLETED by request)"], 82 | ["vnd.sun.j2me.app-descriptor"], 83 | ["vnd.sosi"], 84 | ["vnd.trolltech.linguist"], 85 | ["vnd.wap.si"], 86 | ["vnd.wap.sl"], 87 | ["vnd.wap.wml"], 88 | ["vnd.wap.wmlscript"], 89 | ["vtt"], 90 | ["wgsl"], 91 | ["xml"], 92 | ["xml-external-parsed-entity"] 93 | ] -------------------------------------------------------------------------------- /assets/mediatypes/video.json: -------------------------------------------------------------------------------- 1 | [ 2 | ["1d-interleaved-parityfec"], 3 | ["3gpp"], 4 | ["3gpp2"], 5 | ["3gpp-tt"], 6 | ["AV1"], 7 | ["BMPEG"], 8 | ["BT656"], 9 | ["CelB"], 10 | ["DV"], 11 | ["encaprtp"], 12 | ["example"], 13 | ["FFV1"], 14 | ["flexfec"], 15 | ["H261"], 16 | ["H263"], 17 | ["H263-1998"], 18 | ["H263-2000"], 19 | ["H264"], 20 | ["H264-RCDO"], 21 | ["H264-SVC"], 22 | ["H265"], 23 | ["H266"], 24 | ["iso.segment"], 25 | ["JPEG"], 26 | ["jpeg2000"], 27 | ["jxsv"], 28 | ["matroska"], 29 | ["matroska-3d"], 30 | ["mj2"], 31 | ["MP1S"], 32 | ["MP2P"], 33 | ["MP2T"], 34 | ["mp4"], 35 | ["MP4V-ES"], 36 | ["MPV"], 37 | ["mpeg4-generic"], 38 | ["nv"], 39 | ["ogg"], 40 | ["parityfec"], 41 | ["pointer"], 42 | ["quicktime"], 43 | ["raptorfec"], 44 | ["raw"], 45 | ["rtp-enc-aescm128"], 46 | ["rtploopback"], 47 | ["rtx"], 48 | ["scip"], 49 | ["smpte291"], 50 | ["SMPTE292M"], 51 | ["ulpfec"], 52 | ["vc1"], 53 | ["vc2"], 54 | ["vnd.CCTV"], 55 | ["vnd.dece.hd"], 56 | ["vnd.dece.mobile"], 57 | ["vnd.dece.mp4"], 58 | ["vnd.dece.pd"], 59 | ["vnd.dece.sd"], 60 | ["vnd.dece.video"], 61 | ["vnd.directv.mpeg"], 62 | ["vnd.directv.mpeg-tts"], 63 | ["vnd.dlna.mpeg-tts"], 64 | ["vnd.dvb.file"], 65 | ["vnd.fvt"], 66 | ["vnd.hns.video"], 67 | ["vnd.iptvforum.1dparityfec-1010"], 68 | ["vnd.iptvforum.1dparityfec-2005"], 69 | ["vnd.iptvforum.2dparityfec-1010"], 70 | ["vnd.iptvforum.2dparityfec-2005"], 71 | ["vnd.iptvforum.ttsavc"], 72 | ["vnd.iptvforum.ttsmpeg2"], 73 | ["vnd.motorola.video"], 74 | ["vnd.motorola.videop"], 75 | ["vnd.mpegurl"], 76 | ["vnd.ms-playready.media.pyv"], 77 | ["vnd.nokia.interleaved-multimedia"], 78 | ["vnd.nokia.mp4vr"], 79 | ["vnd.nokia.videovoip"], 80 | ["vnd.objectvideo"], 81 | ["vnd.radgamettools.bink"], 82 | ["vnd.radgamettools.smacker"], 83 | ["vnd.sealed.mpeg1"], 84 | ["vnd.sealed.mpeg4"], 85 | ["vnd.sealed.swf"], 86 | ["vnd.sealedmedia.softseal.mov"], 87 | ["vnd.uvvu.mp4"], 88 | ["vnd.youtube.yt"], 89 | ["vnd.vivo"], 90 | ["VP8"], 91 | ["VP9"] 92 | ] -------------------------------------------------------------------------------- /assets/snippets/nginx.json: -------------------------------------------------------------------------------- 1 | { 2 | "Block events": { 3 | "prefix": "events", 4 | "body": "events {\n\t$0\n}" 5 | }, 6 | "Block http": { 7 | "prefix": "http", 8 | "body": "http {\n\t$0\n}" 9 | }, 10 | "Block mail": { 11 | "prefix": "mail", 12 | "body": "mail {\n\t$0\n}" 13 | }, 14 | "Block stream": { 15 | "prefix": "stream", 16 | "body": "stream {\n\t$0\n}" 17 | }, 18 | "Block server": { 19 | "prefix": "server", 20 | "body": "server {\n\t$0\n}" 21 | }, 22 | "Block if": { 23 | "prefix": "if", 24 | "body": "if {\n\t$0\n}" 25 | }, 26 | "Block limit_except": { 27 | "prefix": "limit_except", 28 | "body": "limit_except {\n\t$0\n}" 29 | }, 30 | "Block upstream": { 31 | "prefix": "upstream", 32 | "body": "upstream ${1} {\n\t${0}\n}" 33 | }, 34 | "Block server with directives": { 35 | "prefix": "server", 36 | "body": [ 37 | "server {", 38 | "\tlisten ${1:80};", 39 | "\tserver_name ${2};", 40 | "\taccess_log ${3:logs/server.access.log} main;", 41 | "\t${0}", 42 | "}" 43 | ] 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /docs/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ### 0.3.1 (2024-02-29) 4 | 5 | 1. Update hint data to Dec 5th, 2023. @AdrianDsg 6 | - Nginx directives 7 | - Lua Resty directives 8 | - HTTP headers 9 | - MIME types 10 | 2. Add completion for HTTP headers 11 | 3. Add two file extension names `.conf.default` and `.conf.template`. @halilim 12 | 4. Fix syntax for constant values, language keywords 13 | - 14 | 5. Update Nginx beautifier 15 | 16 | ### 0.3.0 (2021-11-20) 17 | 18 | :mega: There are a lot of updates in this version: 19 | 20 | 1. The syntax of the Nginx configuration file is updated completely 21 | - Because the syntax from sublime extension and shanoor's repo are long time no updates. So I write a syntax generator for better highlight 22 | - New syntax is generated from codes. You can find them in [generate-tmLanguage.ts](../src/syntax/generate-tmLanguage.ts) 23 | - New syntax supports embedded Lua block 24 | - New syntax supports the directive statement in multiple lines 25 | - And extension configuration `nginx-conf-hint.syntax` is removed 26 | 2. Optimize hint data loader and document loader. They use less memory now and load data on demand 27 | 3. This extension can run as a web extension. So you can use this extension on now 28 | 4. The coverage of auto-completion is more, and auto-completion is smarter 29 | 1. Auto completion is based on the context and grammar of the configuration now 30 | 2. Fix some wrong auto-completion items 31 | 3. Autocomplete named location 32 | 4. Autocomplete directive's named argument 33 | 5. Autocomplete media types 34 | 5. Add editor definition support for `location` directive 35 | 6. Add new extension configuration `nginx-conf-hint.externalModules` for controlling enabled of external modules hint data 36 | 7. The source code is rewritten by using Typescript 37 | 38 | 39 | ### 0.2.0 (2021-11-14) 40 | 41 | 1. update syntax and hint data. 42 | - Contributor: [@latipun7](https://github.com/latipun7) 43 | 2. add support for lua module and conf formatter 44 | - Contributor: [@tiansin](https://github.com/tiansin) 45 | 3. add `goto nginx document` into tooltip hover (issue#9) 46 | 47 | ### 0.1.0 (2018-07-09) 48 | 49 | 1. syntax of nginx.conf is provided inside. 50 | - `original` syntax is from [shanoor/vscode-nginx][shanoor-syntax] (**by default**) 51 | - `sublime` syntax is from [sublime-nginx][sublime-syntax] 52 | - you can switch it by configuration: `nginx-conf-hint.syntax` 53 | 2. remove dependent extension `shanoor.vscode-nginx`. 54 | 3. update Nginx hint data(directives and variables) to latest. 55 | 56 | ### 0.0.5 (2018-03-29) 57 | 58 | 1. fix invalid links in nginx documents. 59 | 2. add a configuration to enable/disable strict auto-completion. 60 | 3. update Nginx directives and variables to latest. 61 | 62 | ### 0.0.4 (2017-07-24) 63 | 64 | 1. update Nginx directives and variables to latest. 65 | 2. add access_log into block server snippet 66 | 3. modify snippet project structure 67 | 68 | ### 0.0.3 (2017-04-15) 69 | 70 | 1. add "Goto Nginx Document" context menu to watching directives and variables document 71 | 2. filter directive completion items according to context(block) 72 | 3. add more useful snippet 73 | 4. fixed directives and variables completion item order 74 | 5. fixed auto completion insert wrong text 75 | 76 | ### 0.0.2 (2017-04-11) 77 | 78 | 1. fixed wrong description in `README.md` 79 | 2. add devDependencies into `package.json` 80 | 81 | [shanoor-syntax]: https://github.com/shanoor/vscode-nginx/blob/master/syntaxes/nginx.tmLanguage 82 | [sublime-syntax]: https://github.com/brandonwamboldt/sublime-nginx/blob/master/Syntaxes/nginx.tmLanguage 83 | -------------------------------------------------------------------------------- /docs/CHANGELOG.zh-Hans.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ### 0.3.1 (2024-02-29) 4 | 5 | 1. 更新了补全数据到 2023年12月5日. @AdrianDsg 6 | - Nginx 配置命令 7 | - Lua Resty 配置命令 8 | - HTTP headers 9 | - MIME types 10 | 2. 新增了关于 HTTP headers 的补全 11 | 3. 新增了两个文件扩展名: `.conf.default` 和 `.conf.template`. @halilim 12 | 4. 修正了关于常量值和内置关键字的语法高亮 13 | - 14 | 5. 更新了 Nginx beautifier 15 | 16 | 17 | ### 0.3.0 (2021-11-20) 18 | 19 | :mega: 这个版本更新了很多东西: 20 | 21 | 1. 从头到尾更新了 Nginx 配置文件的语法高亮(tmLanguage) 22 | - 因为之前使用来自 Sublime 插件和 Shanoor 仓库的语法已经很久没人更新了, 所以为了更好的代码高亮我写了一个新的语法 23 | - 新的语法是通过代码来生成的. 相关代码在这儿: [generate-tmLanguage.ts](../src/syntax/generate-tmLanguage.ts) 24 | - 新的语法支持嵌入在 Nginx 配置文件中的 Lua 脚本块 25 | - 新的语法支持一个 Nginx 配置项写在多行 26 | - 旧的语法配置 `nginx-conf-hint.syntax` 被移除了 27 | 2. 优化了提示数据和文档数据的加载. 现在按需加载他们, 减少了内存使用 28 | 3. 这个插件兼容 Web 插件, 所以你可以在 上使用这个插件 29 | 4. 能自动补全的地方更多了, 并且新版的自动补全更加智能了 30 | 1. 现在的自动补全是基于 Nginx 配置文件的语法和上下文来给出的 31 | 2. 修复了之前许多错误的补全内容 32 | 3. 可以自动补全命名了的 `location` 33 | 4. 可以自动补全配置的参数 34 | 5. 可以自动补全媒体类型 35 | 5. 支持了 `location` 的定义与使用间跳转 36 | 6. 添加了新的配置 `nginx-conf-hint.externalModules`, 可以用来控制是否开启 Nginx 外置的模块的补全 37 | 7. 这个插件现在是使用 Typescript 实现的了 38 | -------------------------------------------------------------------------------- /docs/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2023-11-20 3 | --- 4 | # Contributing 5 | 6 | [Pull Request][pr] & [Issues][issues] 7 | 8 | ## Project Structure 9 | 10 | ``` 11 | 12 | + assets Downloaded hints/references data 13 | + docs Documentations for this project 14 | + scripts Some bash scripts for building, publishing, and fixing 15 | + src 16 | + additonal_snippets Extra Visual Studio Code snippets beyond the downloaded snippets 17 | + extension Source code about Visual Studio Code extension 18 | + main.desktop.ts The entrypoint for the desktop extension 19 | + main.web.ts The entrypoint for the web extension 20 | + libs Third-party library/code 21 | + syntax Nginx configuration syntax files and generator 22 | + utils Utilities for downloading and generating hints/references data 23 | + test 24 | ``` 25 | 26 | 27 | ## Prerequisites 28 | 29 | - [Git](https://git-scm.com/) 30 | - [Node.js](https://nodejs.org/en/) 31 | - [Yarn Classic](https://classic.yarnpkg.com/en/) 32 | - [Visual Studio Code](https://code.visualstudio.com/) 33 | 34 | ## References 35 | 36 | - 37 | - The difference between web extension and desktop extension: 38 | 39 | ## Pull and Initialize 40 | 41 | ``` bash 42 | git clone https://github.com/hangxingliu/vscode-nginx-conf-hint.git 43 | cd vscode-nginx-conf-hint 44 | 45 | # Install full dependencies: 46 | yarn install 47 | 48 | # OR You can install without optional dependencies: 49 | # These optional dependencies will not break the building of this project, 50 | # but they are used for linting, testing, and release 51 | yarn install --ignore-optional 52 | ``` 53 | 54 | ## Build 55 | 56 | ``` bash 57 | # Build this project as desktop extension 58 | yarn build:desktop-ext 59 | 60 | # Build this project as a web extension 61 | yarn build:web-ext 62 | 63 | # Build utils in this project 64 | yarn build:utils 65 | ``` 66 | 67 | ## Update Hint Data 68 | 69 | ``` bash 70 | yarn build:utils 71 | node src/utils/download_hint_data 72 | node src/utils/download_http_headers 73 | node src/utils/download_lua_hint_data 74 | node src/utils/download_mimetypes 75 | ``` 76 | 77 | ## Debug 78 | 79 | ### Desktop Extension 80 | 81 | Select "Launch Extension" in your Visual Studio Code like the following screenshot: 82 | 83 | ![debug-config](./images/debug-config.jpg) 84 | 85 | Then press F5 for debugging. 86 | 87 | ### Web Extension 88 | 89 | ``` bash 90 | yarn run test:web-ext 91 | ``` 92 | 93 | 94 | [issues]: https://github.com/ahmadalli/vscode-nginx-conf/issues 95 | [pr]: https://github.com/ahmadalli/vscode-nginx-conf/pulls 96 | -------------------------------------------------------------------------------- /docs/NGINX-CONF.md: -------------------------------------------------------------------------------- 1 | # Nginx Configuration 2 | 3 | > Notes from 4 | 5 | > nginx consists of modules which are controlled by directives specified in the configuration file. 6 | > **Directives** are divided into **simple directives** and **block directives**. 7 | > A **simple directive** consists of the name and parameters separated by spaces and ends with a semicolon (;). 8 | > A **block directive** has the same structure as a simple directive, 9 | > but instead of the semicolon it ends with a set of additional instructions surrounded by braces ({ and }). 10 | > If a block directive can have other directives inside braces, 11 | > it is called a **context** (examples: `events`, `http`, `server`, and `location`). 12 | 13 | > Directives placed in the configuration file outside of any contexts are considered to be in the main context. 14 | > The `events` and `http` directives reside in the main context, `server` in `http`, and `location` in `server`. 15 | -------------------------------------------------------------------------------- /docs/RELEASE-NEW-VERSION.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2024-03-17 3 | --- 4 | # Release New Version SOP 5 | 6 | 1. Modify the version field (and `preview` field) in [package.json](../package.json) 7 | 2. Add new section in [CHANGELOG.md](./CHANGELOG.md) and [CHANGELOG.zh-Hans.md](./CHANGELOG.zh-Hans.md) 8 | 3. Update changelog section in [README.md](../README.md) 9 | 4. Waiting for Github CI build to be done 10 | 5. Download the built extension `vsix` file and test it on the following platform/app: (*Install from VSIX...*) 11 | 1. Visual Studio Code 12 | 2. Visual Studio Code Web - (See the subsequent section to get details) 13 | 3. VSCodium - 14 | 6. Trigger `publish-vscode-extension` workflow on Github 15 | 7. Check it on after 10~15 minutes 16 | 8. Create and push Git tag. e.g., `git tag 2.0.0-preview`, `git push --tags` 17 | 9. Create a new release at [github-release-new] 18 | - Title: `..[-pre.] (--)` 19 | - Content: copying from the [CHANGELOG.md](./CHANGELOG.md) 20 | - Assets: `*.vsix` 21 | 10. Release vsix file to Open VSX 22 | - URL: 23 | - **ONLY** Publish vsix file built through CI from: [github-ci] 24 | 25 | ## Test extension in Visual Studio Code Web 26 | 27 | ``` bash 28 | mkdir testdir 29 | cd testdir 30 | # copy the built vsix file into this directory 31 | unzip -o -d . *.vsix 32 | yarn add @vscode/test-web 33 | ./node_modules/.bin/vscode-test-web none --extensionDevelopmentPath extension 34 | 35 | # OR copying the built vsix file into `vscode-test-web` project 36 | ./unzip-vsix.sh 37 | yarn start 38 | ``` 39 | 40 | 41 | ## Create a New Access Token 42 | 43 | 44 | 45 | - Name: `balabalabala...` 46 | - Organization: `hangxingliu` 47 | - Expiration (UTC): `30days` 48 | - Scopes: (Click "Show all scopes") 49 | - [x] Marketplace > Manage 50 | 51 | 52 | [github-ci]: https://github.com/hangxingliu/vscode-nginx-conf-hint/actions/workflows/ci.yaml 53 | [github-release-new]: https://github.com/hangxingliu/vscode-nginx-conf-hint/releases/new 54 | -------------------------------------------------------------------------------- /docs/TODO.md: -------------------------------------------------------------------------------- 1 | # TODO 2 | 3 | - [ ] improve snippets 4 | - [ ] improve formatter 5 | - [ ] add completion for `location`, `if` 6 | - [x] autocomplete HTTP header name 7 | - 8 | - 9 | - [x] cache the block name cursor located in 10 | - [x] make hint senmantic 11 | - reference: 12 | - [x] support 13 | - [x] Update syntax: 14 | - 15 | - 16 | 17 | ## Finished 18 | 19 | - [x] automatic install vscode.d.ts 20 | - [x] filter autocompletion items by checking context block 21 | - [x] add location block param "location" and "upstream" placeholder 22 | - [x] auto show parameters hint after directive completion 23 | - VSCode don't support automatic show parameters hint after auto-complete 24 | - [x] nginx directive detail document 25 | - [x] directive hint order 26 | - [x] variable documents 27 | - [x] path completion after directive `include` 28 | - [x] make links in nginx configuration documents valid (2018-03-28) 29 | - [x] add config to enable/disable complete directives in contextual block (strict complete). (2018-03-29) 30 | - [x] add syntaxes switch configuration (2018-07-07) 31 | - switch syntaxes tmLanguage file by configuration `"nginx-conf-hint.syntax` (values: `original`, `sublime`) 32 | -------------------------------------------------------------------------------- /docs/images/debug-config.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahmadalli/vscode-nginx-conf/10414d948cc5e52fbf2a8207629427d4ae3bc8c4/docs/images/debug-config.jpg -------------------------------------------------------------------------------- /images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahmadalli/vscode-nginx-conf/10414d948cc5e52fbf2a8207629427d4ae3bc8c4/images/icon.png -------------------------------------------------------------------------------- /images/screenshots.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahmadalli/vscode-nginx-conf/10414d948cc5e52fbf2a8207629427d4ae3bc8c4/images/screenshots.gif -------------------------------------------------------------------------------- /nginx.configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": { 3 | // symbol used for single line comment. Remove this entry if your language does not support line comments 4 | "lineComment": "#" 5 | }, 6 | // symbols used as brackets 7 | "brackets": [ 8 | ["{", "}"], 9 | ["[", "]"], 10 | ["(", ")"] 11 | ], 12 | // symbols that are auto closed when typing 13 | "autoClosingPairs": [ 14 | ["{", "}"], 15 | ["[", "]"], 16 | ["(", ")"], 17 | ["\"", "\""], 18 | ["'", "'"] 19 | ], 20 | // symbols that that can be used to surround a selection 21 | "surroundingPairs": [ 22 | ["{", "}"], 23 | ["[", "]"], 24 | ["(", ")"], 25 | ["\"", "\""], 26 | ["'", "'"] 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /package.nls.json: -------------------------------------------------------------------------------- 1 | { 2 | "ext.command.goto-nginx-document": "Goto Nginx Document", 3 | "ext.command.goto-nginx-online-document": "Goto Online Nginx Document", 4 | "ext.config.title": "Nginx Configuration", 5 | "ext.config.enable-strict-completion.title": "Strict Completion", 6 | "ext.config.enable-strict-completion.description": "If `true`, only auto-complete directives that can be used in current context", 7 | "ext.config.align.title": "Alignment", 8 | "ext.config.align.description": "Set whether to perform alignment formatting", 9 | "ext.config.extenal-modules.title": "Nginx External Modules", 10 | "ext.config.extenal-modules.description": "Enabled hint data for external modules (lua,js)" 11 | } 12 | -------------------------------------------------------------------------------- /package.nls.zh-cn.json: -------------------------------------------------------------------------------- 1 | { 2 | "ext.command.goto-nginx-document": "查看 Nginx 配置文档", 3 | "ext.command.goto-nginx-online-document": "查看 Nginx 在线配置文档", 4 | "ext.config.title": "Nginx 配置文件", 5 | "ext.config.enable-strict-completion.title": "严格补全模式", 6 | "ext.config.enable-strict-completion.description": "如果为 `true`, 则仅基于当前的上下文进行配置指令的补全", 7 | "ext.config.align.title": "格式化:对齐", 8 | "ext.config.align.description": "设置在格式化时是否进行对齐操作", 9 | "ext.config.extenal-modules.title": "Nginx 外部模块", 10 | "ext.config.extenal-modules.description": "是否启用相关外部模块的代码补全 (lua,js)" 11 | } 12 | -------------------------------------------------------------------------------- /package.nls.zh-tw.json: -------------------------------------------------------------------------------- 1 | { 2 | "ext.command.goto-nginx-document": "查看 Nginx 設定參考", 3 | "ext.command.goto-nginx-online-document": "查看 Nginx 設定的線上參考", 4 | "ext.config.title": "Nginx 設定檔", 5 | "ext.config.enable-strict-completion.title": "嚴格補全模式", 6 | "ext.config.enable-strict-completion.description": "如果為 `true`, 則僅基於當前的上下文進行指令的補全", 7 | "ext.config.align.title": "格式器:對齊", 8 | "ext.config.align.description": "設定格式器是否啟用對齊功能", 9 | "ext.config.extenal-modules.title": "Nginx 外部模組", 10 | "ext.config.extenal-modules.description": "是否啟用相關外部模組的程式碼的建議 (lua,js)" 11 | } 12 | -------------------------------------------------------------------------------- /scripts/clean.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /// 3 | 4 | const { resolve } = require("path"); 5 | const rimraf = require("rimraf"); 6 | const patterns = [ 7 | ".tsc", 8 | "out", 9 | // old generated files 10 | "src/extension/**/*.{js,map}", 11 | "src/utils/**/*.{js,map}", 12 | ]; 13 | 14 | process.chdir(resolve(__dirname, "..")); 15 | console.log(`$ chdir '${process.cwd()}'`); 16 | 17 | for (const pattern of patterns) { 18 | console.log(`$ rimraf '${pattern}'`); 19 | rimraf.sync(pattern, { glob: true }); 20 | } 21 | -------------------------------------------------------------------------------- /scripts/fix-eslint-strip-ansi.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # References: 5 | # 6 | # - https://github.com/eslint/eslint/issues/17561 7 | # - https://github.com/eslint/eslint/discussions/17215 8 | # - https://github.com/chalk/strip-ansi/commit/7cda68dcadde18b19bfa31b6223e9f0e60b3e319 9 | # 10 | 11 | 12 | throw() { echo -e "fatal: $1" >&2; exit 1; } 13 | execute() { echo "$ $*"; "$@" || throw "Failed to execute '$1'"; } 14 | 15 | # change the current directory to the project directory 16 | pushd "$( dirname -- "${BASH_SOURCE[0]}" )/.." >/dev/null || exit 1; 17 | 18 | CJS_VERSION=node_modules/strip-ansi-cjs 19 | APPLY_TO=node_modules/eslint/node_modules/strip-ansi 20 | 21 | # test $CJS_VERSION is a directory 22 | test -d "$CJS_VERSION" && 23 | execute cp -r "${CJS_VERSION}/." "$APPLY_TO"; 24 | true; 25 | -------------------------------------------------------------------------------- /scripts/install-git-hooks.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # shellcheck disable=SC2181 3 | 4 | # change the current directory to the script directory 5 | pushd "$( dirname -- "${BASH_SOURCE[0]}" )" >/dev/null || exit 1; 6 | 7 | FROM="git_commit_hook"; 8 | TO="../.git/hooks/pre-commit"; 9 | 10 | cp "$FROM" "$TO" && chmod +x "$TO"; 11 | if [[ "$?" != "0" ]]; then 12 | echo "[fatal] install git commit hook failed!"; 13 | exit 1; 14 | else 15 | echo "[success] hook script install to ${TO}"; 16 | fi 17 | 18 | -------------------------------------------------------------------------------- /src/additonal_snippets/nginx.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "Block location": { 3 | prefix: "location", 4 | body: "location ${1:/} {\n\t${0}\n}", 5 | }, 6 | "Block upstream": { 7 | prefix: "upstream", 8 | body: "upstream ${1} {\n\t${0}\n}", 9 | }, 10 | "Block server with directives": { 11 | prefix: "server", 12 | body: [ 13 | "server {", 14 | "\tlisten ${1:80};", 15 | "\tserver_name ${2};", 16 | "\taccess_log ${3:logs/server.access.log} main;", 17 | "\t${0}", 18 | "}", 19 | ], 20 | }, 21 | }; 22 | -------------------------------------------------------------------------------- /src/downloader/config_url.ts: -------------------------------------------------------------------------------- 1 | export const nginxDocsBaseURL = 'https://nginx.org/en/docs/'; 2 | export const nginxLuaDocsBaseURL = 'https://github.com/openresty/lua-nginx-module/'; 3 | 4 | export const luaRestyDocsURLs = [ 5 | { prefix: 'memcached', url: 'https://github.com/openresty/lua-resty-memcached' }, 6 | { prefix: 'mysql', url: 'https://github.com/openresty/lua-resty-mysql' }, 7 | { prefix: 'redis', url: 'https://github.com/openresty/lua-resty-redis' }, 8 | { prefix: 'dns', url: 'https://github.com/openresty/lua-resty-dns' }, 9 | { prefix: 'upload', url: 'https://github.com/openresty/lua-resty-upload' }, 10 | { prefix: 'websocket', url: 'https://github.com/openresty/lua-resty-websocket' }, 11 | { prefix: 'lock', url: 'https://github.com/openresty/lua-resty-lock' }, 12 | { prefix: 'lrucache', url: 'https://github.com/openresty/lua-resty-lrucache' }, 13 | { prefix: 'healthcheck', url: 'https://github.com/openresty/lua-resty-upstream-healthcheck' }, 14 | { prefix: 'balancer', url: 'https://github.com/openresty/lua-resty-balancer' }, 15 | ] 16 | export const nginxLuaModuleURLs: Array<{ name: string, url: string }> = [ 17 | // { name: 'ngx_memc', url: 'http://github.com/openresty/memc-nginx-module' }, 18 | // { name: 'ngx_postgres', url: 'https://github.com/FRiCKLE/ngx_postgres' }, 19 | // { name: 'ngx_redis2', url: 'http://github.com/openresty/redis2-nginx-module' }, 20 | // { name: 'ngx_redis', url: 'http://wiki.nginx.org/HttpRedisModule' }, 21 | // { name: 'ngx_proxy', url: 'http://nginx.org/en/docs/http/ngx_http_proxy_module.html' }, 22 | // { name: 'ngx_fastcgi', url: 'http://nginx.org/en/docs/http/ngx_http_fastcgi_module.html' }, 23 | ] 24 | 25 | export const mediaTypesURL = 'https://www.iana.org/assignments/media-types/media-types.xhtml' 26 | 27 | export const httpHeadersWikiURLs = { 28 | de: 'https://de.wikipedia.org/wiki/Liste_der_HTTP-Headerfelder', 29 | en: 'https://en.wikipedia.org/wiki/List_of_HTTP_header_fields', 30 | es: 'https://es.wikipedia.org/wiki/Anexo:Cabeceras_HTTP', 31 | 'zh-Hans': 'https://zh.wikipedia.org/zh-cn/HTTP%E5%A4%B4%E5%AD%97%E6%AE%B5', 32 | 'zh-Hant-HK': 'https://zh.wikipedia.org/zh-hk/HTTP%E5%A4%B4%E5%AD%97%E6%AE%B5', 33 | 'zh-Hant-TW': 'https://zh.wikipedia.org/zh-tw/HTTP%E5%A4%B4%E5%AD%97%E6%AE%B5', 34 | } 35 | 36 | export const syntaxURLs = { 37 | original: 'https://raw.githubusercontent.com/shanoor/vscode-nginx/master/syntaxes/nginx.tmLanguage', 38 | sublime: 'https://raw.githubusercontent.com/brandonwamboldt/sublime-nginx/master/Syntaxes/nginx.tmLanguage', 39 | } 40 | -------------------------------------------------------------------------------- /src/downloader/mimetype.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import { cacheDir, mediaTypeFile } from "../utils/config"; 4 | import { JsonFileWriter, getText, SimpleHttpCache, loadHtml } from "../utils/crawler-utils"; 5 | import { mediaTypesURL } from "./config_url"; 6 | 7 | main().catch((e) => console.error(e)); 8 | async function main() { 9 | SimpleHttpCache.init(cacheDir); 10 | const html = await getText("Media Types", mediaTypesURL); 11 | const $ = loadHtml(html); 12 | 13 | const streams: { [x: string]: JsonFileWriter } = {}; 14 | const $links = $("table tr td:nth-child(2) a"); 15 | console.log(`found ${$links.length} media types`); 16 | 17 | for (let i = 0, i2 = $links.length; i < i2; i++) { 18 | const $link = $links.eq(i); 19 | const mediaType = $link.text().trim(); 20 | const mtx = mediaType.match(/^(\w+)\/([\w\.\-+]+)$/); 21 | if (!mtx) throw new Error(`$links[${i}] is invalid media type "${$link.text()}"`); 22 | 23 | const $name = $link.parents("tr").children("td:nth-child(1)"); 24 | const mediaTypeName = $name.text().trim(); 25 | if (!mediaTypeName) throw new Error(`$links[${i}] is invalid media type "${$name.text()}"`); 26 | 27 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 28 | const [_, prefix, suffix] = mtx; 29 | let stream = streams[prefix]; 30 | if (!stream) { 31 | stream = new JsonFileWriter(mediaTypeFile(prefix)); 32 | streams[prefix] = stream; 33 | console.log(`new mime type prefix: "${prefix}"`); 34 | } 35 | if (suffix === mediaTypeName) stream.writeItem([suffix]); 36 | else stream.writeItem([suffix, mediaTypeName]); 37 | } 38 | 39 | Object.keys(streams).forEach((it) => streams[it].close()); 40 | } 41 | -------------------------------------------------------------------------------- /src/downloader/nginx_syntax.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import { getText, print } from "../utils/crawler-utils"; 4 | import { syntaxFiles } from "../utils/config"; 5 | import { syntaxURLs } from "./config_url"; 6 | import { writeFileSync } from "fs"; 7 | 8 | main(); 9 | async function main() { 10 | await Promise.all([ 11 | download('original'), 12 | download('sublime') 13 | ]).then(() => { 14 | print.info('downloaded syntax') 15 | }).catch(error => { 16 | print._error(error.message); 17 | }) 18 | } 19 | 20 | async function download(syntaxName: keyof typeof syntaxURLs) { 21 | const url = syntaxURLs[syntaxName]; 22 | const targetFiles = [syntaxFiles[syntaxName]]; 23 | let text = await getText(`${syntaxName} syntax`, url); 24 | text = text.replace('?>', `?>\n`) 25 | for (const element of targetFiles) 26 | writeFileSync(element, text); 27 | } 28 | -------------------------------------------------------------------------------- /src/extension/.gitignore: -------------------------------------------------------------------------------- 1 | *.js 2 | *.map 3 | -------------------------------------------------------------------------------- /src/extension/documents/html_view.ts: -------------------------------------------------------------------------------- 1 | import { window, Disposable, TextDocumentContentProvider, WebviewPanel, ViewColumn, Uri, Webview } from "vscode"; 2 | 3 | /** 4 | * This module is used for newer version VSCode: 5 | * @see https://code.visualstudio.com/api/extension-guides/webview 6 | */ 7 | export class HTMLView { 8 | 9 | private readonly disposable: Disposable[] = []; 10 | private readonly urlToWebviewPanel = new Map(); 11 | readonly supported: boolean; 12 | 13 | constructor(private readonly contentProvider: TextDocumentContentProvider) { 14 | this.supported = typeof window.createWebviewPanel === 'function'; 15 | } 16 | 17 | dispose() { 18 | let disposable = this.disposable.pop(); 19 | while (disposable) { 20 | disposable.dispose(); 21 | disposable = this.disposable.pop(); 22 | } 23 | } 24 | 25 | loadHTML(uri: string, column: ViewColumn, title: string) { 26 | return this.getWebview(Uri.parse(uri), column, title); 27 | } 28 | 29 | private getWebview(uri: Uri, column: ViewColumn, title: string): Webview { 30 | if (!this.supported) 31 | return null; 32 | 33 | const oldWebviewPanel = this.urlToWebviewPanel.get(uri.toString()); 34 | if (oldWebviewPanel) 35 | return oldWebviewPanel.webview; 36 | 37 | const webviewPanel = window.createWebviewPanel( 38 | 'nginx-conf-hint-html-view', 39 | title, 40 | column, { enableScripts: true, retainContextWhenHidden: true }); 41 | 42 | this.urlToWebviewPanel.set(uri.toString(), webviewPanel); 43 | webviewPanel.onDidDispose(() => { 44 | if (this.urlToWebviewPanel.has(uri.toString())) { 45 | this.urlToWebviewPanel.delete(uri.toString()); 46 | } 47 | }); 48 | 49 | Promise.resolve(true) 50 | .then(() => this.contentProvider.provideTextDocumentContent(uri, undefined)) 51 | .then(html => webviewPanel.webview.html = html); 52 | return webviewPanel.webview; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/extension/documents/templates/directive_container.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | nginx directive: ${name} 6 | 40 | 41 | 42 | 43 |

${name} ${length} (Nginx Directive)

44 |
45 | ${items} 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/extension/documents/templates/directive_item.html: -------------------------------------------------------------------------------- 1 |

(${index}) ${module}

2 | Open in browser 3 |
4 | ${table} 5 |
6 | ${html} 7 |
8 |
9 | -------------------------------------------------------------------------------- /src/extension/documents/templates/index.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /// 3 | /// DON'T edit this file manually, it is generated from util scripts 4 | /// 5 | 6 | export type TemplateDirectiveContainerInput = { 7 | name: any; 8 | length: any; 9 | items: any; 10 | }; 11 | export function templateDirectiveContainer(input: TemplateDirectiveContainerInput) { 12 | return "\n\n\t\n\t\t\n\t\tnginx directive: " + input.name + 13 | "\n\t\t\n\t\n\n\t\n\t\t

" + input.name + 14 | " " + input.length + 15 | " (Nginx Directive)

\n\t\t
\n\t\t" + input.items + 16 | "\n\t\n\n" 17 | } 18 | 19 | export type TemplateDirectiveItemInput = { 20 | index: any; 21 | module: any; 22 | link: any; 23 | table: any; 24 | html: any; 25 | }; 26 | export function templateDirectiveItem(input: TemplateDirectiveItemInput) { 27 | return "

(" + input.index + 28 | ") " + input.module + 29 | "

\nOpen in browser\n
\n\t" + input.table + 31 | "\n
\n" + input.html + 32 | "\n
\n
\n" 33 | } 34 | 35 | export type TemplateVariablesInput = { 36 | module: any; 37 | doc: any; 38 | }; 39 | export function templateVariables(input: TemplateVariablesInput) { 40 | return "\n\n\t\n\t\t\n\t\tnginx module: " + input.module + 41 | "\n\t\t\n\t\n\n\t\n\t\t

" + input.module + 42 | " Embedded Variables

\n\t\t" + input.doc + 43 | "\n\t\n\n" 44 | } 45 | 46 | -------------------------------------------------------------------------------- /src/extension/documents/templates/variables.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | nginx module: ${module} 6 | 16 | 17 | 18 | 19 |

${module} Embedded Variables

20 | ${doc} 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/extension/documents/utils.ts: -------------------------------------------------------------------------------- 1 | export const nginxDocsScheme = 'nginx-doc'; 2 | export const nginxDocsAuthority = 'ahmadalli.nginx-conf'; 3 | 4 | export const enum NginxDocsType { 5 | directive = 'directive', 6 | variable = 'variable', 7 | } 8 | 9 | export function getNginxDocsUri( 10 | type: NginxDocsType, 11 | name: string, 12 | ): string { 13 | return `${nginxDocsScheme}://${nginxDocsAuthority}/${type}/${name}.html`; 14 | } 15 | 16 | type _VSCodeUri = { 17 | scheme: string 18 | authority: string 19 | path: string 20 | } 21 | export function parseNginxDocsUri(uri: _VSCodeUri): { type: NginxDocsType, name: string } { 22 | if (!uri) return null; 23 | if (uri.scheme !== nginxDocsScheme || uri.authority !== nginxDocsAuthority) 24 | return null; 25 | const mtx = uri.path.match(/^\/(\w+)\/([\w-]+)/); 26 | if (!mtx) return null; 27 | const type = mtx[1]; 28 | if (type !== NginxDocsType.directive && type !== NginxDocsType.variable) 29 | return null; 30 | 31 | return { 32 | type, 33 | name: mtx[2], 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /src/extension/hint-data/async-loader.ts: -------------------------------------------------------------------------------- 1 | import { Uri, workspace } from "vscode"; 2 | import { logger } from "../logger"; 3 | 4 | export async function loadHintDataAsync(uri: Uri): Promise { 5 | let data: unknown[][]; 6 | try { 7 | const fileData = await workspace.fs.readFile(uri); 8 | const json = new TextDecoder().decode(fileData); 9 | data = JSON.parse(json); 10 | if (!Array.isArray(data)) 11 | throw new Error(`Invalid hint data: ${json.slice(0, 128)}...`); 12 | } catch (error) { 13 | logger.error(`load hint data "${uri.path}" failed: ${error.message}`, error); 14 | return null; 15 | } 16 | return data; 17 | } 18 | -------------------------------------------------------------------------------- /src/extension/hint-data/details.ts: -------------------------------------------------------------------------------- 1 | import { env, ExtensionContext, UIKind, Uri } from "vscode"; 2 | import { logger } from "../logger"; 3 | import { loadHintDataAsync } from "./async-loader"; 4 | import { ManifestItemType } from "./enum"; 5 | import { NginxDirectiveDetails } from "./types"; 6 | 7 | let detailsUri: Uri; 8 | // let inBrowser = false; 9 | const preloadModules = [ 10 | 'ngx_core_module', 11 | 'ngx_http_core_module', 12 | ] 13 | 14 | class ModuleDetails { 15 | diretives = new Map(); 16 | varDocs = ''; 17 | } 18 | /** value is module name */ 19 | const loadings = new Set(); 20 | /** key is module name and value is ModuleDetails */ 21 | const modulesMap = new Map(); 22 | 23 | export async function loadModuleDetails(moduleName: string) { 24 | if (!detailsUri) { 25 | logger.error(`load module "${moduleName}" details failed: detailsUri is not initialized`); 26 | return null; 27 | } 28 | const moduleUri = Uri.joinPath(detailsUri, `${moduleName}.json`); 29 | const items = await loadHintDataAsync(moduleUri); 30 | if (!items) { 31 | loadings.delete(moduleName); 32 | return null; 33 | } 34 | 35 | const details = new ModuleDetails(); 36 | for (const element of items) { 37 | const cols = element; 38 | switch (cols[0]) { 39 | case ManifestItemType.DirectiveDetails: { 40 | const item: NginxDirectiveDetails = { 41 | name: cols[1] as string, 42 | markdown: cols[2] as string, 43 | html: cols[3] as string, 44 | notes: cols[4] as string[], 45 | table: cols[5] as string, 46 | }; 47 | details.diretives.set(item.name, item); 48 | break; 49 | } 50 | case ManifestItemType.VariableDetails: { 51 | details.varDocs += cols[1]; 52 | break; 53 | } 54 | } 55 | } 56 | modulesMap.set(moduleName, details); 57 | loadings.delete(moduleName); 58 | logger.verbose(`loaded details of module "${moduleName}"`); 59 | return details; 60 | } 61 | 62 | export async function initHintDataDetails(context: ExtensionContext) { 63 | detailsUri = Uri.joinPath(context.extensionUri, 'assets', 'details'); 64 | const logs: string[] = [`detailsUri=${detailsUri.toString()}`]; 65 | if (env.uiKind === UIKind.Web) { 66 | // inBrowser = true; 67 | logs.push(`uiKind=Web`); 68 | logs.push(`indexedDB=${typeof indexedDB}`); 69 | } 70 | logs.push(`TextDecoder=${typeof TextDecoder}`); 71 | logger.log(logs.join(' ')); 72 | 73 | if (preloadModules.length > 0) { 74 | logger.log(`start preloading ${preloadModules.length} modules: ${preloadModules}`); 75 | getMultiModuleDetails(preloadModules); 76 | } 77 | } 78 | 79 | export async function getModuleDetails(moduleName: string) { 80 | const details = modulesMap.get(moduleName); 81 | if (!details) 82 | return loadModuleDetails(moduleName); 83 | return details; 84 | } 85 | 86 | export async function getMultiModuleDetails(moduleNames: string[]) { 87 | return Promise.all(moduleNames.map(getModuleDetails)); 88 | } 89 | 90 | export function getModuleDetailsQuick(moduleName: string) { 91 | const details = modulesMap.get(moduleName); 92 | if (!details && !loadings.has(moduleName)) { 93 | loadings.add(moduleName); 94 | loadModuleDetails(moduleName); 95 | } 96 | return details; 97 | } 98 | 99 | -------------------------------------------------------------------------------- /src/extension/hint-data/enum.ts: -------------------------------------------------------------------------------- 1 | export const enum ManifestItemType { 2 | ModuleNames = 1, 3 | Directive = 2, 4 | Variable = 3, 5 | DirectiveDetails = 4, 6 | VariableDetails = 5, 7 | HttpReqHeader = 6, 8 | HttpResHeader = 7, 9 | } 10 | -------------------------------------------------------------------------------- /src/extension/hint-data/i18n.ts: -------------------------------------------------------------------------------- 1 | import { env } from "vscode"; 2 | 3 | export function getLangNameForManifest() { 4 | const lang = env.language.toLowerCase(); 5 | if (lang.startsWith('de')) return 'de'; 6 | if (lang.startsWith('es')) return 'es'; 7 | if (lang.startsWith('zh-cn')) return 'zh-Hans'; 8 | if (lang.startsWith('zh')) return 'zh-Hant-TW'; 9 | return 'en'; 10 | } 11 | -------------------------------------------------------------------------------- /src/extension/hint-data/link.ts: -------------------------------------------------------------------------------- 1 | export function getNginxExternalDocsLink(item: { 2 | module: string; 3 | link: string; 4 | }): string { 5 | if (!item || typeof item !== "object") return; 6 | 7 | const { link } = item; 8 | if (typeof link === "string" && link) { 9 | if (!/^https?:\/\//i.test(link)) { 10 | if (link.startsWith("/")) return `https://nginx.org${link}`; 11 | else return `https://nginx.org/en/docs/${link}`; 12 | } 13 | return link; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/extension/hint-data/media-types.ts: -------------------------------------------------------------------------------- 1 | export type MediaTypeTuple = [mediaType: string, desc?: string]; 2 | export const mediaTypes: { [x: string]: Array } = { 3 | application: require("../../../assets/mediatypes/application.json"), 4 | audio: require("../../../assets/mediatypes/audio.json"), 5 | font: require("../../../assets/mediatypes/font.json"), 6 | image: require("../../../assets/mediatypes/image.json"), 7 | multipart: require("../../../assets/mediatypes/multipart.json"), 8 | text: require("../../../assets/mediatypes/text.json"), 9 | video: require("../../../assets/mediatypes/video.json"), 10 | }; 11 | export const mediaTypePrefixes = Object.keys(mediaTypes); 12 | export const mediaTypePrefixSet = new Set(mediaTypePrefixes); 13 | -------------------------------------------------------------------------------- /src/extension/hint-data/types.ts: -------------------------------------------------------------------------------- 1 | export type ExternalModuleName = 'js' | 'lua'; 2 | 3 | export const enum HttpHeaderType { 4 | none = 0, 5 | request = 1, // 01 6 | response = 2, // 10 7 | both = 3, // 11 8 | } 9 | 10 | export type HttpHeaderInfo = { 11 | name: string; 12 | lowercase: string; 13 | type: HttpHeaderType; 14 | example: string[]; 15 | markdown: string; 16 | standard?: string; 17 | } 18 | 19 | export type NginxDirective = { 20 | name: string; 21 | filters: string[]; 22 | syntax: string[], 23 | def: string; 24 | contexts: string[], 25 | since: string; 26 | module: string; 27 | link: string; 28 | ci: { insert?: string; args?: string[] } 29 | exmod?: ExternalModuleName; 30 | } 31 | export type NginxDirectiveDetails = { 32 | name: string; 33 | notes: string[]; 34 | markdown: string; 35 | html: string; 36 | table: string; 37 | } 38 | 39 | export type NginxVariable = { 40 | name: string; 41 | desc: string; 42 | module: string; 43 | link: string; 44 | since: string; 45 | ci?: unknown; 46 | } 47 | -------------------------------------------------------------------------------- /src/extension/logger.ts: -------------------------------------------------------------------------------- 1 | import { ExtensionContext, ExtensionMode, window } from "vscode"; 2 | 3 | export const logPrefix = "nginx-conf-hint:"; 4 | 5 | class _Logger { 6 | inProduction = true; 7 | 8 | init(context: ExtensionContext) { 9 | if (context.extensionMode === ExtensionMode.Development || context.extensionMode === ExtensionMode.Test) 10 | this.inProduction = false; 11 | } 12 | 13 | verbose(...args: unknown[]) { 14 | if (this.inProduction) return; 15 | console.log(logPrefix, ...args); 16 | } 17 | log(...args: unknown[]) { 18 | console.log(logPrefix, ...args); 19 | } 20 | error(...args: unknown[]) { 21 | if (!this.inProduction) return this.fatal(...args); 22 | console.error(logPrefix, ...args); 23 | } 24 | fatal(...args: unknown[]) { 25 | console.error(logPrefix, ...args); 26 | const [message, detailObj] = args; 27 | if (detailObj) { 28 | let detail = String(detailObj); 29 | if (detailObj instanceof Error) detail = detailObj.stack; 30 | window.showErrorMessage(`${logPrefix} ${message}`, { detail }); 31 | } else { 32 | window.showErrorMessage(`${logPrefix} ${message}`); 33 | } 34 | } 35 | } 36 | export type Logger = _Logger; 37 | export const logger = new _Logger(); 38 | -------------------------------------------------------------------------------- /src/extension/main.desktop.ts: -------------------------------------------------------------------------------- 1 | import { workspace, Disposable, ExtensionContext } from "vscode"; 2 | import { extensionConfig } from "./providers/config"; 3 | import { NginxCompletionItemsProvider } from "./providers/completion"; 4 | import { NginxDocumentLinkProvider } from "./providers/link"; 5 | import { NginxDefinitionProvider } from "./providers/definition"; 6 | import { NginxDocumentFormatProvider } from "./providers/formatter"; 7 | import { NginxHoverProvider } from "./providers/hover"; 8 | import { NginxSignatureProvider } from "./providers/signature"; 9 | import { NginxCommandProvider } from "./providers/command"; 10 | import { logger } from "./logger"; 11 | import { initHintDataManifest, initI18nManifest } from "./hint-data/manifest"; 12 | import { initHintDataDetails } from "./hint-data/details"; 13 | 14 | export function activate(context: ExtensionContext) { 15 | const startedAt = Date.now(); 16 | console.log('nginx-conf-hint activating...'); 17 | logger.init(context); 18 | 19 | initHintDataManifest(); 20 | initI18nManifest(context); 21 | initHintDataDetails(context); 22 | 23 | const subscriptions = context.subscriptions; 24 | const disposable: Disposable[] = []; 25 | 26 | extensionConfig.reload(); 27 | new NginxDocumentLinkProvider(disposable); 28 | new NginxDefinitionProvider(disposable); 29 | new NginxCompletionItemsProvider(disposable); 30 | new NginxHoverProvider(disposable); 31 | new NginxSignatureProvider(disposable); 32 | new NginxCommandProvider(disposable); 33 | new NginxDocumentFormatProvider(disposable); 34 | 35 | subscriptions.push(workspace.onDidChangeConfiguration(() => { 36 | extensionConfig.reload(); 37 | })); 38 | subscriptions.push(...disposable); 39 | 40 | const elapsed = Date.now() - startedAt; 41 | console.log(`nginx-conf-hint activated (+${elapsed}ms)`); 42 | } 43 | export function deactivate() { 44 | // noop 45 | } 46 | -------------------------------------------------------------------------------- /src/extension/main.web.ts: -------------------------------------------------------------------------------- 1 | import { workspace, Disposable, ExtensionContext } from "vscode"; 2 | import { extensionConfig } from "./providers/config"; 3 | import { NginxCompletionItemsProvider } from "./providers/completion"; 4 | import { NginxDocumentLinkProvider } from "./providers/link"; 5 | import { NginxDefinitionProvider } from "./providers/definition"; 6 | import { NginxDocumentFormatProvider } from "./providers/formatter"; 7 | import { NginxHoverProvider } from "./providers/hover"; 8 | import { NginxSignatureProvider } from "./providers/signature"; 9 | import { NginxCommandProvider } from "./providers/command"; 10 | import { logger } from "./logger"; 11 | import { initHintDataManifest, initI18nManifest } from "./hint-data/manifest"; 12 | import { initHintDataDetails } from "./hint-data/details"; 13 | 14 | export function activate(context: ExtensionContext) { 15 | const startedAt = Date.now(); 16 | console.log('[web] nginx-conf-hint activating...'); 17 | logger.init(context); 18 | 19 | initHintDataManifest(); 20 | initI18nManifest(context); 21 | initHintDataDetails(context); 22 | 23 | const subscriptions = context.subscriptions; 24 | const disposable: Disposable[] = []; 25 | 26 | extensionConfig.reload(); 27 | new NginxDocumentLinkProvider(disposable); 28 | new NginxDefinitionProvider(disposable); 29 | new NginxCompletionItemsProvider(disposable); 30 | new NginxHoverProvider(disposable); 31 | new NginxSignatureProvider(disposable); 32 | new NginxCommandProvider(disposable); 33 | new NginxDocumentFormatProvider(disposable); 34 | 35 | subscriptions.push(workspace.onDidChangeConfiguration(() => { 36 | extensionConfig.reload(); 37 | })); 38 | subscriptions.push(...disposable); 39 | 40 | const elapsed = Date.now() - startedAt; 41 | console.log(`nginx-conf-hint activated (+${elapsed}ms)`); 42 | } 43 | export function deactivate() { 44 | // noop 45 | } 46 | -------------------------------------------------------------------------------- /src/extension/providers/command.ts: -------------------------------------------------------------------------------- 1 | import { window, Disposable, commands } from "vscode"; 2 | import { NGINX_LANGUAGE_ID } from "./utils"; 3 | import {initializeNginxDocument, openDirectiveDoc, openVariableDoc} from "../documents/open_nginx_document"; 4 | import { findManifestByName } from "../hint-data/manifest"; 5 | import { logger } from "../logger"; 6 | 7 | export class NginxCommandProvider { 8 | 9 | constructor(disposables: Disposable[]) { 10 | initializeNginxDocument(disposables); 11 | 12 | disposables.push(commands.registerCommand('nginx-conf-hint.showDocument', () => { 13 | 14 | const editor = window.activeTextEditor; 15 | if (!editor || editor.document.languageId != NGINX_LANGUAGE_ID) 16 | return logger.fatal('Have not opened a nginx configuration document!'); 17 | 18 | const { document, selection } = editor; 19 | let text = (selection.isEmpty 20 | ? document.getText(document.getWordRangeAtPosition(selection.start)) 21 | : document.getText(selection)).trim(); 22 | 23 | console.log("request open nginx document: " + text); 24 | 25 | if (!text) 26 | return logger.fatal(`There not any word around cursor or selected!`); 27 | 28 | text = text.trim(); 29 | if (findManifestByName(text)?.length > 0) 30 | return openDirectiveDoc(text); 31 | 32 | if (findManifestByName(text.replace(/^\$?/, '$'))?.length > 0) 33 | return openVariableDoc(text); 34 | 35 | return logger.fatal(`"${text}" is not a nginx directive or nginx embedded variables!`); 36 | })); 37 | 38 | } 39 | } 40 | 41 | -------------------------------------------------------------------------------- /src/extension/providers/completion/directive-location.ts: -------------------------------------------------------------------------------- 1 | import { SnippetString } from "vscode"; 2 | import { NginxDirective } from "../../hint-data/types"; 3 | import { getDirectiveCompletionItemBase } from "./item"; 4 | 5 | /** 6 | * https://nginx.org/en/docs/http/ngx_http_core_module.html#location 7 | * https://code.visualstudio.com/docs/editor/userdefinedsnippets 8 | */ 9 | const locationSyntax: Array<[string, SnippetString]> = [ 10 | ["location", new SnippetString("location ${1:/} {\n\t$0\n}")], 11 | ["location (named)", new SnippetString("location @${1:name} {\n\t$0\n}")], 12 | ["location (exact match)", new SnippetString("location = ${1:/uri} {\n\t$0\n}")], 13 | ["location (regexp)", new SnippetString("location ~* ${1:/uri} {\n\t$0\n}")], 14 | ["location (regexp, case-sensitive)", new SnippetString("location ~ ${1:/uri} {\n\t$0\n}")], 15 | ["location (^~)", new SnippetString("location ^~ ${1:/uri} {\n\t$0\n}")], 16 | ]; 17 | 18 | export function getDirectiveLocationCItem(directive: NginxDirective) { 19 | return locationSyntax.map((it) => { 20 | const citem = getDirectiveCompletionItemBase(directive); 21 | citem.label = it[0]; 22 | citem.insertText = it[1]; 23 | return citem; 24 | }); 25 | } 26 | -------------------------------------------------------------------------------- /src/extension/providers/completion/directives.ts: -------------------------------------------------------------------------------- 1 | import { CompletionItem, CompletionList } from "vscode"; 2 | import { getDirectivesManifest } from "../../hint-data/manifest"; 3 | import { NginxDirective } from "../../hint-data/types"; 4 | import { NginxConfCursorContext } from "../../parser"; 5 | import { extensionConfig } from "../config"; 6 | import { getDirectiveLocationCItem } from "./directive-location"; 7 | import { getDirectiveCompletionItemBase } from "./item"; 8 | 9 | export function _completeDirectives(cursorContext: NginxConfCursorContext) { 10 | const { list, context: nginxContext } = cursorContext; 11 | if (nginxContext === "types") return this.completeMediaType(list[0] || ""); 12 | if (nginxContext === "map") return; 13 | 14 | // Empty preifx 15 | const inputPrefix = list[0]; 16 | if (!inputPrefix) { 17 | return new CompletionList(getDirectivesManifest().onEmpty.map(getDirectiveCompletionItemBase), true); 18 | } 19 | 20 | const lcPrefix = inputPrefix.toLowerCase(); 21 | const matchedDirectives: NginxDirective[] = []; 22 | const addDirectives = (directives: NginxDirective[]) => { 23 | if (extensionConfig.enableStrictCompletion && nginxContext) { 24 | directives.forEach((it) => { 25 | if (it.contexts.indexOf(nginxContext) == -1 && it.contexts[0] != "any") return; 26 | if (testPrefix(it, lcPrefix)) matchedDirectives.push(it); 27 | }); 28 | } else { 29 | directives.forEach((it) => { 30 | if (testPrefix(it, lcPrefix)) matchedDirectives.push(it); 31 | }); 32 | } 33 | }; // end of addDirectives 34 | 35 | const manifest = getDirectivesManifest(); 36 | addDirectives(manifest.core); 37 | if (extensionConfig.hasJsModule) addDirectives(manifest.js); 38 | if (extensionConfig.hasLuaModule) addDirectives(manifest.lua); 39 | 40 | const result: CompletionItem[] = []; 41 | for (let i = 0; i < matchedDirectives.length; i++) { 42 | const directive = matchedDirectives[i]; 43 | switch (directive.name) { 44 | case "location": 45 | result.push(...getDirectiveLocationCItem(directive)); 46 | break; 47 | default: 48 | result.push(getDirectiveCompletionItemBase(directive)); 49 | break; 50 | } 51 | } 52 | return result; 53 | } 54 | 55 | function testPrefix(item: NginxDirective, lcPrefix: string) { 56 | if (item.name.startsWith(lcPrefix)) return true; 57 | if (item.filters.findIndex((it) => it.startsWith(lcPrefix)) >= 0) return true; 58 | return false; 59 | } 60 | -------------------------------------------------------------------------------- /src/extension/providers/completion/http-header.ts: -------------------------------------------------------------------------------- 1 | import { CompletionItem, CompletionItemKind, Position, TextDocument, Range, MarkdownString } from "vscode"; 2 | import { getHttpHeaders } from "../../hint-data/manifest"; 3 | 4 | const _directives = new Set([ 5 | "add_header", 6 | "auth_http_header", 7 | "fastcgi_pass_header", 8 | "fastcgi_hide_header", 9 | "grpc_hide_header", 10 | "grpc_set_header", 11 | "grpc_pass_header", 12 | "proxy_hide_header", 13 | "proxy_ignore_headers", 14 | "proxy_pass_header", 15 | "proxy_set_header", 16 | "real_ip_header", 17 | "scgi_hide_header", 18 | "scgi_ignore_headers", 19 | "scgi_pass_header", 20 | "uwsgi_hide_header", 21 | "uwsgi_ignore_headers", 22 | "uwsgi_pass_header", 23 | ]); 24 | export function _doesDirectiveNeedHttpHeader(directive: string): boolean { 25 | if (!_directives.has(directive)) return false; 26 | return true; 27 | } 28 | 29 | export function _completeHttpHeader(document: TextDocument, position: Position, currentInput: string) { 30 | let headers = getHttpHeaders(); 31 | let prefix: string; 32 | const lastDash = currentInput.lastIndexOf("-"); 33 | if (lastDash >= 0) { 34 | prefix = currentInput.slice(0, lastDash + 1); 35 | const lcPrefix = prefix.toLowerCase(); 36 | headers = headers.filter((it) => it.lowercase.startsWith(lcPrefix)); 37 | } 38 | const range = prefix ? new Range(position.translate(0, -prefix.length), position) : null; 39 | return headers.map((it) => { 40 | const ci = new CompletionItem(it.name, CompletionItemKind.Constant); 41 | ci.detail = 'HTTP Header'; 42 | if(it.markdown) 43 | ci.documentation = new MarkdownString(it.markdown); 44 | if (range) { 45 | // ci.insertText = it.name.slice(prefix.length); 46 | ci.range = range; 47 | } 48 | return ci; 49 | }); 50 | } 51 | -------------------------------------------------------------------------------- /src/extension/providers/completion/item.ts: -------------------------------------------------------------------------------- 1 | import { CompletionItem, CompletionItemKind, MarkdownString, SnippetString } from "vscode"; 2 | import { getModuleDetails } from "../../hint-data/details"; 3 | import { NginxDirective, NginxVariable } from "../../hint-data/types"; 4 | 5 | export type DirectiveCompletionItemBase = CompletionItem & { 6 | resolved?: boolean; 7 | module?: string; 8 | directive?: string; 9 | filter?: string[]; 10 | contexts?: string[]; 11 | }; 12 | 13 | export function getDirectiveCompletionItemBase(directive: NginxDirective) { 14 | const moduleName = directive.module; 15 | const isCoreFunc = moduleName == "ngx_core_module"; 16 | 17 | const item: DirectiveCompletionItemBase = new CompletionItem(directive.name, CompletionItemKind.Property); 18 | 19 | const documentation = ["``` NGINX", directive.syntax.join("\n"), "```\n"].join("\n"); 20 | item.documentation = new MarkdownString(documentation); 21 | item.detail = isCoreFunc ? "" : moduleName; 22 | item.module = directive.module; 23 | item.directive = directive.name; 24 | 25 | //#region insertText 26 | if (directive.ci?.insert) { 27 | item.insertText = new SnippetString(directive.ci.insert); 28 | } else { 29 | item.insertText = new SnippetString( 30 | directive.def // has default value 31 | ? directive.def.replace(/^(\w+)(\s+)(.+);$/, (_, a, b, c) => `${a}${b}\${1:${c}};`) 32 | : `${directive.name}\$\{0\};` 33 | ); 34 | } 35 | //#endregion insertText 36 | 37 | //for fuzzy matching 38 | item.filter = directive.name.split("_"); 39 | //for checking parent block name 40 | item.contexts = directive.contexts || []; 41 | return item; 42 | } 43 | 44 | export async function resolveDirectiveCompletionItem(item: DirectiveCompletionItemBase) { 45 | if (!item || !item.module || !item.directive || item.resolved) return item; 46 | const moduleDetails = await getModuleDetails(item.module); 47 | if (moduleDetails) { 48 | const d = moduleDetails.diretives.get(item.directive); 49 | if (item.documentation instanceof MarkdownString) { 50 | item.documentation = item.documentation.appendMarkdown(d.markdown); 51 | } 52 | item.resolved = true; 53 | } 54 | return item; 55 | } 56 | 57 | function removeUglyCharactersInCompletionItem(text = "") { 58 | return ( 59 | text 60 | .replace(/&#x(\w{4});/g, (_, hex) => String.fromCharCode(parseInt(hex, 16))) 61 | //remove mark 62 | .replace(/“`(.+?)`”/g, "“$1”") 63 | ); 64 | } 65 | export function getVariableCompletionItemBase(variable: NginxVariable) { 66 | const item = new CompletionItem(variable.name, CompletionItemKind.Variable); 67 | item.documentation = removeUglyCharactersInCompletionItem(variable.desc); 68 | item.detail = variable.module; 69 | item.insertText = variable.name.replace(/^\$/, ""); 70 | return item; 71 | } 72 | -------------------------------------------------------------------------------- /src/extension/providers/completion/named-arguments.ts: -------------------------------------------------------------------------------- 1 | import { CompletionItem, CompletionItemKind, SnippetString } from "vscode"; 2 | import { findManifestByName } from "../../hint-data/manifest"; 3 | import { NginxDirective } from "../../hint-data/types"; 4 | 5 | export function _completeNameArgs(directiveName: string) { 6 | const directives = findManifestByName(directiveName) as NginxDirective[]; 7 | /** key is args, value is module name */ 8 | const result = new Map(); 9 | for (let i = 0; i < directives.length; i++) { 10 | const directive = directives[i]; 11 | const args = directive.ci?.args; 12 | if (!args) continue; 13 | for (let j = 0; j < args.length; j++) { 14 | const moduleName = result.get(args[j]); 15 | if (moduleName) result.set(args[j], moduleName + "," + directive.module); 16 | else result.set(args[j], moduleName); 17 | } 18 | } 19 | 20 | if (result.size > 0) { 21 | const items: CompletionItem[] = []; 22 | const args = Array.from(result.entries()); 23 | for (let j = 0; j < args.length; j++) { 24 | const [arg, moduleName] = args[j]; 25 | const part = arg.match(/^(\??)(\w+)=(.+)$/); 26 | if (!part) continue; 27 | const item = new CompletionItem(`${part[2]}=`, CompletionItemKind.Field); 28 | item.detail = moduleName; 29 | item.insertText = new SnippetString( 30 | part[1] === "?" ? `${part[2]}\${1:=${part[3]}}` : `${part[2]}=\${1:${part[3]}}` 31 | ); 32 | items.push(item); 33 | } 34 | if (items.length > 0) return items; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/extension/providers/completion/named-location.ts: -------------------------------------------------------------------------------- 1 | import { CompletionItem, CompletionItemKind, TextDocument } from "vscode"; 2 | import { getNginxConfDefinitionInfo } from "../../parser"; 3 | 4 | const _directives = new Set(["error_page", "try_files"]); 5 | export function _doesDirectiveCanUseNamedLocation(directive: string) { 6 | return _directives.has(directive); 7 | } 8 | 9 | export function _completeNameLocation(document: TextDocument, input: string) { 10 | const { location } = getNginxConfDefinitionInfo(document.getText()); 11 | const locationNames = Array.from(new Set(location.map((it) => it.name))); 12 | if (locationNames.length < 1) return; 13 | 14 | input = input.toLowerCase(); 15 | return locationNames 16 | .map((it) => { 17 | if (input && !it.toLowerCase().startsWith(input)) return null; 18 | const ci = new CompletionItem(it, CompletionItemKind.Reference); 19 | ci.insertText = it.slice(1); 20 | return ci; 21 | }) 22 | .filter((it) => it); 23 | } 24 | -------------------------------------------------------------------------------- /src/extension/providers/completion/path.ts: -------------------------------------------------------------------------------- 1 | import { CompletionItem, CompletionItemKind, FileType, Uri, workspace } from "vscode"; 2 | 3 | const _directives = new Set([ 4 | "include", 5 | "pid", 6 | "lock_file", 7 | "ssl_certificate", 8 | "ssl_certificate_key", 9 | "ssl_crl", 10 | "ssl_dhparam", 11 | "js_include", 12 | ]); 13 | export function _doesDirectiveNeedCompletePath(directive: string) { 14 | if (_directives.has(directive)) return true; 15 | if (directive.endsWith('_lua_file')) return true; 16 | return false; 17 | } 18 | 19 | export async function _completePath(baseUri: Uri, input = "") { 20 | if (/[\/\\]$/.test(input)) input = input.slice(0, input.length - 1) + "/"; 21 | else input += "/.."; 22 | 23 | const inputUri = input ? Uri.joinPath(baseUri, input) : baseUri; 24 | 25 | const result: CompletionItem[] = []; 26 | try { 27 | const files = await workspace.fs.readDirectory(inputUri); 28 | files 29 | .filter((it) => it[1] === FileType.Directory) 30 | .forEach((it) => result.push(new CompletionItem(it[0], CompletionItemKind.Folder))); 31 | files 32 | .filter((it) => it[1] === FileType.File) 33 | .forEach((it) => result.push(new CompletionItem(it[0], CompletionItemKind.File))); 34 | } catch (error) { 35 | // noop 36 | } 37 | 38 | return result; 39 | } 40 | -------------------------------------------------------------------------------- /src/extension/providers/config.ts: -------------------------------------------------------------------------------- 1 | import { workspace } from "vscode"; 2 | import { ExternalModuleName } from "../types"; 3 | 4 | export class ExtensionConfiguration { 5 | 6 | //#region vscode configurations 7 | enableStrictCompletion = true; 8 | enableFormatAlign = false; 9 | externalModules: ExternalModuleName[] = []; 10 | tabSize = 4; 11 | //#endregion vscode configurations 12 | 13 | hasJsModule = false; 14 | hasLuaModule = false; 15 | 16 | reload = () => { 17 | const config = workspace.getConfiguration('nginx-conf-hint'); 18 | 19 | this.enableStrictCompletion = config.get('enableStrictCompletion', true); 20 | this.enableFormatAlign = !!config.get('format', { align: false }).align; 21 | this.externalModules = config.get('externalModules', []); 22 | this.tabSize = workspace.getConfiguration("editor").get("tabSize", 4); 23 | 24 | this.hasJsModule = this.externalModules.indexOf('js') >= 0; 25 | this.hasLuaModule = this.externalModules.indexOf('lua') >= 0; 26 | } 27 | } 28 | 29 | export const extensionConfig = new ExtensionConfiguration(); 30 | -------------------------------------------------------------------------------- /src/extension/providers/definition.ts: -------------------------------------------------------------------------------- 1 | import { Disposable, Range, languages, TextDocument, DefinitionProvider, Location, Position } from "vscode"; 2 | import { getNginxConfDefinitionInfo } from "../parser"; 3 | import { MapList } from "../utils"; 4 | import { DOCUMENT_SELECTOR } from "./utils"; 5 | 6 | export class NginxDefinitionProvider implements DefinitionProvider { 7 | constructor(disposables: Disposable[]) { 8 | disposables.push(languages.registerDefinitionProvider(DOCUMENT_SELECTOR, this)); 9 | } 10 | 11 | async provideDefinition(document: TextDocument, position: Position) { 12 | const { location } = getNginxConfDefinitionInfo(document.getText()); 13 | if (location.length < 1) return null; 14 | 15 | const rangeMap = new MapList<{ range: Range; isDef?: boolean }>(); 16 | let posName: string; 17 | let posDef: boolean; 18 | location.forEach((it) => { 19 | const range = new Range(document.positionAt(it.begin), document.positionAt(it.end)); 20 | if (range.contains(position)) { 21 | posName = it.name; 22 | posDef = it.isDef; 23 | } 24 | rangeMap.push(it.name, { range, isDef: it.isDef }); 25 | }); 26 | const range = rangeMap.getList(posName); 27 | if (range.length > 1) { 28 | const r = range.filter((it) => it.isDef !== posDef).map((it) => new Location(document.uri, it.range)); 29 | return r.length > 1 ? r : r[0]; 30 | } 31 | return null; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/extension/providers/formatter.ts: -------------------------------------------------------------------------------- 1 | import { CancellationToken, Disposable, Range, languages, TextDocument, DocumentFormattingEditProvider, DocumentRangeFormattingEditProvider, FormattingOptions, ProviderResult, TextEdit, EndOfLine } from "vscode"; 2 | import { DOCUMENT_SELECTOR } from "./utils"; 3 | import * as nginxBeautifier from "../../libs/nginxbeautifier"; 4 | import { extensionConfig } from "./config"; 5 | 6 | 7 | export class NginxDocumentFormatProvider implements DocumentFormattingEditProvider, DocumentRangeFormattingEditProvider { 8 | 9 | constructor(disposables: Disposable[]) { 10 | disposables.push(languages.registerDocumentFormattingEditProvider(DOCUMENT_SELECTOR, this)); 11 | disposables.push(languages.registerDocumentRangeFormattingEditProvider(DOCUMENT_SELECTOR, this)); 12 | } 13 | 14 | provideDocumentFormattingEdits(document: TextDocument, options: FormattingOptions, token: CancellationToken): ProviderResult { 15 | return this.formatDocument(document, null, options); 16 | } 17 | provideDocumentRangeFormattingEdits(document: TextDocument, range: Range, options: FormattingOptions, token: CancellationToken): ProviderResult { 18 | return this.formatDocument(document, range, options); 19 | } 20 | 21 | private formatDocument(document: TextDocument, range: Range, options: FormattingOptions) { 22 | const body = document.getText(); 23 | 24 | if (options.insertSpaces) { 25 | const tabSize = options.tabSize || extensionConfig.tabSize; 26 | const INDENTATION = new Array(tabSize).fill(' ').join(''); 27 | nginxBeautifier.modifyOptions({ INDENTATION }); 28 | } else { 29 | nginxBeautifier.modifyOptions({ INDENTATION: '\t' }); 30 | } 31 | 32 | let cleanLines = nginxBeautifier.clean_lines(body); 33 | cleanLines = nginxBeautifier.join_opening_bracket(cleanLines); 34 | cleanLines = nginxBeautifier.perform_indentation(cleanLines); 35 | if (extensionConfig.enableFormatAlign) { 36 | cleanLines = nginxBeautifier.perform_alignment(cleanLines); 37 | } 38 | 39 | const firstLine = document.lineAt(0); 40 | const lastLine = document.lineAt(document.lineCount - 1); 41 | range = range || new Range( 42 | firstLine.range.start.line, 43 | firstLine.range.start.character, 44 | lastLine.range.end.line, 45 | lastLine.range.end.character 46 | ); 47 | 48 | const newBody = cleanLines 49 | .slice(range.start.line, range.end.line + 1) 50 | .join(document.eol === EndOfLine.LF ? '\n' : '\r\n'); 51 | return [TextEdit.replace(range, newBody)]; 52 | } 53 | 54 | } 55 | 56 | -------------------------------------------------------------------------------- /src/extension/providers/link.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Disposable, 3 | Range, 4 | languages, 5 | TextDocument, 6 | Uri, 7 | workspace, 8 | FileType, 9 | DocumentLinkProvider, 10 | DocumentLink, 11 | CancellationToken, 12 | } from "vscode"; 13 | import { DOCUMENT_SELECTOR } from "./utils"; 14 | 15 | export class NginxDocumentLinkProvider implements DocumentLinkProvider { 16 | constructor(disposables: Disposable[]) { 17 | disposables.push(languages.registerDocumentLinkProvider(DOCUMENT_SELECTOR, this)); 18 | } 19 | 20 | async provideDocumentLinks(document: TextDocument, token: CancellationToken) { 21 | const INCLUDE_REGEXP = /(include\s+['"]?)(\S+)(?:$|['";])/g; 22 | const code: string = document.getText(); 23 | const result = []; 24 | let matched: RegExpMatchArray = null; 25 | 26 | try { 27 | while ((matched = INCLUDE_REGEXP.exec(code)) != null) { 28 | const p = matched[2]; 29 | const uri = Uri.joinPath(document.uri, "..", p); 30 | try { 31 | const stat = await workspace.fs.stat(uri); 32 | if (stat.type !== FileType.File && stat.type !== FileType.SymbolicLink) continue; 33 | } catch (error) { 34 | continue; 35 | } 36 | 37 | const offset1 = matched.index + matched[1].length, 38 | offset2 = offset1 + matched[2].length; 39 | result.push( 40 | new DocumentLink(new Range(document.positionAt(offset1), document.positionAt(offset2)), uri) 41 | ); 42 | } 43 | } catch (ex) { 44 | console.error(ex); 45 | } 46 | return result; 47 | } 48 | 49 | resolveDocumentLink(link: DocumentLink) { 50 | return link; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/extension/providers/signature.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CancellationToken, 3 | Disposable, 4 | languages, 5 | TextDocument, 6 | Position, 7 | SignatureHelpProvider, 8 | SignatureHelpContext, 9 | Range, 10 | SignatureInformation, 11 | SignatureHelp, 12 | MarkdownString, 13 | } from "vscode"; 14 | import { DOCUMENT_SELECTOR } from "./utils"; 15 | import { getNginxConfCursorContext } from "../parser"; 16 | import { findManifestByName } from "../hint-data/manifest"; 17 | import { getModuleDetailsQuick } from "../hint-data/details"; 18 | import { NginxDirective } from "../hint-data/types"; 19 | 20 | const zeroPos = new Position(0, 0); 21 | 22 | export class NginxSignatureProvider implements SignatureHelpProvider { 23 | constructor(disposables: Disposable[]) { 24 | disposables.push(languages.registerSignatureHelpProvider(DOCUMENT_SELECTOR, this, " ")); 25 | } 26 | 27 | provideSignatureHelp( 28 | document: TextDocument, 29 | position: Position, 30 | token: CancellationToken, 31 | context: SignatureHelpContext 32 | ) { 33 | const beforeText = document.getText(new Range(zeroPos, position)); 34 | const { c, list, context: nginxContext } = getNginxConfCursorContext(beforeText); 35 | if (c) return null; 36 | 37 | if (list.length > 0) { 38 | const directiveName = list[0]; 39 | let manifest = findManifestByName(directiveName) as NginxDirective[]; 40 | if (manifest.length === 0) return null; 41 | if (manifest.length > 1 && context) { 42 | const _manifest = manifest.filter((it) => it.contexts.indexOf(nginxContext) >= 0); 43 | if (_manifest.length > 0) manifest = _manifest; 44 | } 45 | const syntax: string[] = []; 46 | const description: MarkdownString[] = []; 47 | manifest.forEach((it) => { 48 | it.syntax.forEach((s) => { 49 | const ms = new MarkdownString(it.module); 50 | const moduleDetails = getModuleDetailsQuick(it.module); 51 | if (moduleDetails) { 52 | const details = moduleDetails.diretives.get(it.name); 53 | if (details) ms.appendMarkdown('\n\n' + details.markdown); 54 | } 55 | syntax.push(s); 56 | description.push(ms); 57 | }); 58 | }); 59 | 60 | const help = new SignatureHelp(); 61 | help.activeSignature = 0; 62 | help.signatures = syntax.map((s, index) => new SignatureInformation(s, description[index])); 63 | return help; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/extension/providers/utils.ts: -------------------------------------------------------------------------------- 1 | export const NGINX_LANGUAGE_ID = 'nginx'; 2 | export const DOCUMENT_SELECTOR = [NGINX_LANGUAGE_ID]; 3 | -------------------------------------------------------------------------------- /src/extension/types.d.ts: -------------------------------------------------------------------------------- 1 | export type ExternalModuleName = 'js' | 'lua'; 2 | 3 | export type SnippetItem = { 4 | description: string; 5 | prefix: string; 6 | body: string; 7 | } 8 | -------------------------------------------------------------------------------- /src/extension/utils.ts: -------------------------------------------------------------------------------- 1 | export class MapList extends Map { 2 | push(key: string, ...items: T[]) { 3 | const value = this.get(key); 4 | if (!value) this.set(key, items); 5 | else value.push(...items); 6 | } 7 | getList(key: string): T[] { 8 | return this.get(key) || []; 9 | } 10 | getKeys() { 11 | return Array.from(this.keys()); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/syntax/match-names.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * 4 | */ 5 | export const names = { 6 | variable: { 7 | other: 'variable.other.nginx', 8 | parameter: 'variable.parameter.nginx', 9 | }, 10 | 11 | entityName: { 12 | context: 'entity.name.context.location.nginx' 13 | }, 14 | 15 | comment: 'comment.line.number-sign', 16 | 17 | ipaddr: 'string.other.ipaddress.nginx', 18 | 19 | cidr: 'constant.numeric.cidr.nginx', 20 | numeric: 'constant.numeric.nginx', 21 | 22 | languageConstant: 'constant.language.nginx', 23 | 24 | string: { 25 | regexp: 'string.regexp.nginx', 26 | doubleQuoted: 'string.quoted.double.nginx', 27 | singleQuoted: 'string.quoted.single.nginx', 28 | escaped: 'constant.character.escape.nginx', 29 | mediaType: 'constant.other.mediatype.nginx' 30 | }, 31 | 32 | meta: { 33 | context: (name: string) => `meta.context.${name}.nginx`, 34 | block: 'meta.block.nginx', 35 | }, 36 | embedded: { 37 | lua: 'meta.embedded.block.lua' 38 | }, 39 | 40 | controlKeyword: 'keyword.control.nginx', 41 | operator: 'keyword.operator.nginx', 42 | otherKeyword: 'keyword.other.nginx', 43 | directiveKeyword: 'keyword.directive.nginx', 44 | unknownDirective: 'keyword.directive.unknown.nginx', 45 | contextDirective: 'storage.type.directive.context.nginx', 46 | 47 | /** 48 | * Symbols that are part of the variable name, 49 | * should additionally be applied the following scope. 50 | * For example, the `$` in PHP and Shell. 51 | */ 52 | $: 'punctuation.definition.variable.nginx', 53 | 54 | /** 55 | * Semicolons or other statement terminators should use: 56 | */ 57 | terminator: 'punctuation.terminator.nginx', 58 | 59 | invalid: 'invalid.illegal.nginx', 60 | } 61 | -------------------------------------------------------------------------------- /src/syntax/syntax.ts: -------------------------------------------------------------------------------- 1 | import { syntaxRepository } from "./repository"; 2 | import { syntaxPatterns } from "./patterns"; 3 | 4 | /** 5 | * @see https://macromates.com/manual/en/language_grammars 6 | */ 7 | export const syntax = { 8 | 9 | name: 'nginx', 10 | 11 | /** 12 | * this should be a unique name for the grammar, following the convention of being a dot-separated name where each new (left-most) part specializes the name. Normally it would be a two-part name where the first is either text or source and the second is the name of the language or document type. But if you are specializing an existing type, you probably want to derive the name from the type you are specializing. For example Markdown is text.html.markdown and Ruby on Rails (rhtml files) is text.html.rails. The advantage of deriving it from (in this case) text.html is that everything which works in the text.html scope will also work in the text.html.«something» scope (but with a lower precedence than something specifically targeting text.html.«something»). 13 | */ 14 | scopeName: 'source.nginx', 15 | 16 | uuid: '0C04066A-12D2-43CA-8238-00A12CE4C12D', 17 | 18 | /** 19 | * this is an array of file type extensions that the grammar should (by default) be used with. This is referenced when TextMate does not know what grammar to use for a file the user opens. If however the user selects a grammar from the language pop-up in the status bar, TextMate will remember that choice 20 | */ 21 | fileTypes: [ 22 | 'conf.erb', 23 | 'conf', 24 | 'ngx', 25 | 'nginx.conf', 26 | 'mime.types', 27 | 'fastcgi_params', 28 | 'scgi_params', 29 | 'uwsgi_params' 30 | ], 31 | 32 | /** 33 | * these are regular expressions that lines (in the document) are matched against. If a line matches one of the patterns (but not both), it becomes a folding marker (see the foldings section for more info). 34 | */ 35 | foldingStartMarker: /\{\s*$/, 36 | 37 | /** 38 | * these are regular expressions that lines (in the document) are matched against. If a line matches one of the patterns (but not both), it becomes a folding marker (see the foldings section for more info). 39 | */ 40 | foldingStopMarker: /^\s*\}/, 41 | 42 | /** Unknown */ 43 | keyEquivalent: /^~N/, 44 | 45 | patterns: syntaxPatterns, 46 | 47 | repository: syntaxRepository, 48 | } 49 | 50 | -------------------------------------------------------------------------------- /src/syntax/types.d.ts: -------------------------------------------------------------------------------- 1 | export type SyntaxPattern = { 2 | comment?: string; 3 | match?: string | RegExp; 4 | name?: string; 5 | contentName?: string; 6 | beginCaptures?: { [x in string]: string | { name: string } }; 7 | captures?: { [x in string]: string | { name: string } }; 8 | endCaptures?: { [x in string]: string | { name: string } }; 9 | include?: string; 10 | begin?: string | RegExp; 11 | end?: string | RegExp; 12 | patterns?: SyntaxPattern[]; 13 | } 14 | -------------------------------------------------------------------------------- /src/types-manifest.ts: -------------------------------------------------------------------------------- 1 | export const enum ManifestItemType { 2 | ModuleNames = 1, 3 | Directive = 2, 4 | Variable = 3, 5 | DirectiveDetails = 4, 6 | VariableDetails = 5, 7 | HttpReqHeader = 6, 8 | HttpResHeader = 7, 9 | } 10 | 11 | export type ManifestItemForModuleNames = [ 12 | type: ManifestItemType.ModuleNames, 13 | /** @example "ngx_http_access_module" */ 14 | ...moduleNames: string[] 15 | ]; 16 | 17 | export type ManifestItemForDirective = [ 18 | type: ManifestItemType.Directive, 19 | /** @example "accept_mutex" */ 20 | directiveName: string, 21 | /** @example ["on | off"] */ 22 | signature: string[], 23 | /** @example "accept_mutex off;" */ 24 | def: string, 25 | /** @example ["events"] */ 26 | contexts: string[], 27 | /** The index of its module name */ 28 | moduleIndex: number, 29 | /** @example "1.9.11" */ 30 | since: null | string, 31 | /** A uri to this directive's docs */ 32 | link: string, 33 | completionItemPatch: null | { insert: string } 34 | ]; 35 | 36 | export type ManifestItemForVariable = [ 37 | type: ManifestItemType.Variable, 38 | varName: `$${string}`, 39 | desc: string, 40 | /** The index of its module name */ 41 | moduleIndex: number, 42 | /** @example "1.9.11" */ 43 | since: null | string, 44 | /** A uri to this directive's docs */ 45 | link: string, 46 | completionItemPatch: null | { insert: string } 47 | ]; 48 | 49 | export type ManifestItemForDirectiveDetails = [ 50 | type: ManifestItemType.DirectiveDetails, 51 | name: string, 52 | markdown: string, 53 | html: string, 54 | nites: string, 55 | table: string 56 | ]; 57 | export type ManifestItemForVariableDetails = [ 58 | // 59 | type: ManifestItemType.VariableDetails, 60 | docs: string 61 | ]; 62 | 63 | export type ManifestItemForHttpReqHeader = [ 64 | // 65 | type: ManifestItemType.HttpReqHeader, 66 | name: string, 67 | description: string, 68 | example: string, 69 | standard: string | null, 70 | ]; 71 | export type ManifestItemForHttpResHeader = [ 72 | // 73 | type: ManifestItemType.HttpResHeader, 74 | name: string, 75 | description: string, 76 | example: string, 77 | standard: string | null, 78 | ]; 79 | 80 | export function isManifestItemForModuleNames(row: unknown[]): row is ManifestItemForModuleNames { 81 | return row && row[0] === ManifestItemType.ModuleNames; 82 | } 83 | export function isManifestItemForDirective(row: unknown[]): row is ManifestItemForDirective { 84 | return row && row[0] === ManifestItemType.Directive; 85 | } 86 | export function isManifestItemForVariable(row: unknown[]): row is ManifestItemForVariable { 87 | return row && row[0] === ManifestItemType.Variable; 88 | } 89 | export function isManifestItemForDirectiveDetails(row: unknown[]): row is ManifestItemForDirectiveDetails { 90 | return row && row[0] === ManifestItemType.DirectiveDetails; 91 | } 92 | export function isManifestItemForVariableDetails(row: unknown[]): row is ManifestItemForVariableDetails { 93 | return row && row[0] === ManifestItemType.VariableDetails; 94 | } 95 | export function isManifestItemForHttpReqHeader(row: unknown[]): row is ManifestItemForHttpReqHeader { 96 | return row && row[0] === ManifestItemType.HttpReqHeader; 97 | } 98 | export function isManifestItemForHttpResHeader(row: unknown[]): row is ManifestItemForHttpResHeader { 99 | return row && row[0] === ManifestItemType.HttpResHeader; 100 | } 101 | -------------------------------------------------------------------------------- /src/utils/build_document_templates.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import { resolve as resolvePath } from "path"; 4 | import { readFileSync, readdirSync, createWriteStream } from "fs"; 5 | import { docsTemplatesDir, docsTemplateTarget } from "./config"; 6 | 7 | const tab = '\t'; 8 | 9 | main(); 10 | function main() { 11 | const files = readdirSync(docsTemplatesDir, { withFileTypes: true }).filter(it => !it.name.startsWith('.') && /\.html$/.test(it.name) && it.isFile()); 12 | 13 | const targetFile = createWriteStream(docsTemplateTarget); 14 | targetFile.write([ 15 | '/* eslint-disable */', 16 | '///', 17 | "/// DO NOT edit this file manually, it is generated from utility scripts", 18 | '///', 19 | '', 20 | '', 21 | ].join('\n')); 22 | for (const element of files) { 23 | const fileName = element.name; 24 | const absPath = resolvePath(docsTemplatesDir, fileName); 25 | 26 | const html = readFileSync(absPath, 'utf8'); 27 | targetFile.write(resolveHTML(fileName.replace(/\..+$/, ''), html)); 28 | } 29 | targetFile.close(); 30 | console.log(`created '${docsTemplateTarget}' from ${files.length} HTML files`); 31 | } 32 | 33 | function resolveHTML(_name: string, html: string) { 34 | html = JSON.stringify(html) 35 | 36 | const templName = _name[0].toUpperCase() + _name.slice(1).replace(/_(\w)/g, (_, ch: string) => ch.toUpperCase()); 37 | const types = [`export type Template${templName}Input = {\n`]; 38 | const func = [ 39 | `export function template${templName}(input: Template${templName}Input) {\n`, 40 | `${tab}return ` 41 | ]; 42 | 43 | const inputNames = new Set(); 44 | func.push( 45 | html.replace(/\$\{(\w+?)\}/g, (_, name) => { 46 | if (!inputNames.has(name)) { 47 | types.push(`${tab}${name}: any;\n`); 48 | inputNames.add(name); 49 | } 50 | return `" + input.${name} +\n${tab}${tab}"`; 51 | }) 52 | ); 53 | types.push('};\n'); 54 | func.push('\n}\n\n'); 55 | return types.join('') + func.join(''); 56 | } 57 | -------------------------------------------------------------------------------- /src/utils/config.ts: -------------------------------------------------------------------------------- 1 | import { resolve } from "path"; 2 | 3 | export const projectDir = resolve(__dirname, '../..'); 4 | export const cacheDir = resolve(projectDir, 'cache') 5 | export const syntaxesDir = resolve(projectDir, 'src/syntax/references'); 6 | export const hintDataDir = resolve(projectDir, 'hint_data'); 7 | 8 | export const manifestDir = resolve(projectDir, 'assets/manifest'); 9 | export const detailsDir = resolve(projectDir, 'assets/details'); 10 | export const snippetDir = resolve(projectDir, 'assets/snippets'); 11 | export const mediaTypeDir = resolve(projectDir, 'assets/mediatypes'); 12 | 13 | export const docsTemplatesDir = resolve(__dirname, '../extension/documents/templates'); 14 | export const docsTemplateTarget = resolve(docsTemplatesDir, 'index.ts'); 15 | 16 | export const syntaxFiles = { 17 | original: resolve(syntaxesDir, 'shanoor.nginx.tmLanguage'), 18 | sublime: resolve(syntaxesDir, 'sublime.nginx.tmLanguage'), 19 | } 20 | 21 | 22 | 23 | export const manifestFiles = { 24 | core: resolve(manifestDir, 'core.json'), 25 | js: resolve(manifestDir, 'js.json'), 26 | lua: resolve(manifestDir, 'lua.json'), 27 | httpHeaders: (language: string) => 28 | resolve(manifestDir, `http_headers.${language}.json`), 29 | } 30 | export const detailsFile = (moduleName: string) => 31 | resolve(detailsDir, `${moduleName}.json`); 32 | export const mediaTypeFile = (prefix: string) => 33 | resolve(mediaTypeDir, `${prefix}.json`); 34 | 35 | export const luaSnippetFile = resolve(snippetDir, 'lua.json'); 36 | -------------------------------------------------------------------------------- /src/utils/extra-headers/cloudflare.json: -------------------------------------------------------------------------------- 1 | { 2 | "url": "https://developers.cloudflare.com/fundamentals/get-started/http-request-headers", 3 | "headers": [ 4 | { 5 | "name": "CF-Connecting-IP", 6 | "markdown": [ 7 | "`CF-Connecting-IP` provides the client IP address, connecting to Cloudflare, to the origin web server. This header will only be sent on the traffic from Cloudflare's edge to your origin webserver." 8 | ] 9 | }, 10 | { 11 | "name": "True-Client-IP", 12 | "markdown": [ 13 | "(Enterprise plan only)", 14 | "`True-Client-IP` provides the original client IP address to the origin web server. `True-Client-IP` is only available on our Enterprise plan. In the example below, `203.0.113.1` is the original visitor IP address. For example: `True-Client-IP`: `203.0.113.1`", 15 | "There is no difference between the `True-Client-IP` and `CF-Connecting-IP` headers besides the name of the header. Some Enterprise customers with legacy devices need `True-Client-IP` to avoid updating firewalls or load-balancers to read a custom header name.", 16 | "> If you are using Cloudflare in a stacked CDN and authenticating HTTP requests based on the IP address value in the `True-Client-IP` header, you must [enable True-Client-IP](https://support.cloudflare.com/hc/articles/206776727#h_4bf7CC7xR9dZJjR4y6wwcG). If you do not enable this feature, the `True-Client-IP` header can be spoofed to any value. Alternatively, if you do not want to receive the `True-Client-IP` header, use a [Transform Rule](https://developers.cloudflare.com/rules/transform) to remove this HTTP request header." 17 | ] 18 | }, 19 | { 20 | "name": "CF-RAY", 21 | "markdown": [ 22 | "The `CF-ray` header is a hashed value that encodes information about the data center and the visitor’s request. For example: `CF-RAY: 230b030023ae2822-SJC`.", 23 | "Add the [CF-Ray](https://support.cloudflare.com/hc/articles/203118044#h_f7a7396f-ec41-4c52-abf5-a110cadaca7c) header to your origin web server logs to match requests proxied to Cloudflare to requests in your server logs. Enterprise customers can also see all requests via [Cloudflare Logs](https://support.cloudflare.com/hc/en-us/articles/216672448-Enterprise-Log-Share-REST-API)." 24 | ] 25 | }, 26 | { 27 | "name": "CF-IPCountry", 28 | "markdown": [ 29 | "`CF-IPCountry` contains a two character country code of the originating visitor’s country. XX is used for unknown country information. This header is added to requests by enabling [Cloudflare IP Geolocation](https://support.cloudflare.com/hc/en-us/articles/200168236-What-does-Cloudflare-IP-Geolocation-do-) in the dashboard. For example: `CF-IPCountry: US`." 30 | ] 31 | }, 32 | { 33 | "name": "CF-Visitor", 34 | "markdown": [ 35 | "Currently, this header is a JSON object, containing only one key called “scheme”. The header will be either HTTP or HTTPS, and it is only relevant if you need to enable Flexible SSL in your Cloudflare settings. For example: `CF-Visitor: { \"scheme\":\"https\"}`." 36 | ] 37 | }, 38 | { 39 | "name": "CDN-Loop", 40 | "markdown": "`CDN-Loop` allows Cloudflare to specify how many times a request can enter Cloudflare's network before it is blocked as a looping request. For example: `CDN-Loop: cloudflare`" 41 | } 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /src/utils/extra-headers/mozilla-docs.json: -------------------------------------------------------------------------------- 1 | { 2 | "url": "https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers", 3 | "headers": [ 4 | { 5 | "name": "WWW-Authenticate", 6 | "en": [ 7 | "The HTTP `WWW-Authenticate` response header defines the [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication) methods (\"challenges\") that might be used to gain access to a specific resource." 8 | ], 9 | "zh-Hans": [ 10 | "HTTP `WWW-Authenticate` 响应头定义了使用何种验证方式去获取对资源的连接。", 11 | "`WWW-Authenticate` header通常会和一个 [`401`](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status/401) `Unauthorized` 的响应一同被发送。" 12 | ], 13 | "es": [ 14 | "La cabezera de la respuesta HTTP `WWW-Authenticate` define el método de autentificación que debe ser utilizado para acceder al recurso solicitado.", 15 | "La cabezera `WWW-Authenticate` es enviada junto al estado [`401`](https://developer.mozilla.org/es/docs/Web/HTTP/Status/401) `Unauthorized` en la respuesta." 16 | ], 17 | "url": "https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/WWW-Authenticate" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /test/markdownFromElement.ts: -------------------------------------------------------------------------------- 1 | import { toMarkdown } from "../src/utils/crawler-utils"; 2 | 3 | const html = "

If accept_mutex is enabled, worker processes will accept new connections by turn. Otherwise, all worker processes will be notified about new connections, and if volume of new connections is low, some of the worker processes may just waste system resources.

There is no need to enable accept_mutex on systems that support the EPOLLEXCLUSIVE flag (1.11.3) or when using reuseport.
Prior to version 1.11.3, the default value was on.
"; 4 | console.log(toMarkdown(html)); 5 | -------------------------------------------------------------------------------- /test/playground/file1.conf: -------------------------------------------------------------------------------- 1 | user nginx; 2 | worker_processes 2; 3 | pid /var/run/nginx.pid; 4 | 5 | events { 6 | worker_connections 2000; 7 | } 8 | 9 | http { 10 | server { 11 | set $var1 "a"; 12 | set $var1 "${var1}b"; 13 | set $var1 "${var1}\\nc"; 14 | 15 | add_header X-Test " # hey hey 16 | three lines string 17 | "; 18 | add_header 19 | X-Test2 20 | $var1; 21 | 22 | "add_header" X-Test3 ???; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /test/playground/file2.conf: -------------------------------------------------------------------------------- 1 | server { 2 | set ${http_name} ; 3 | } 4 | -------------------------------------------------------------------------------- /test/playground/getCursorContext.ts: -------------------------------------------------------------------------------- 1 | import { readFileSync } from "fs"; 2 | import { getNginxConfCursorContext } from "../../src/extension/parser"; 3 | 4 | const conf = readFileSync(__dirname + '/file1.conf', 'utf8'); 5 | const conf2 = readFileSync(__dirname + '/file2.conf', 'utf8'); 6 | 7 | console.log(0, getNginxConfCursorContext(conf.slice(0, 0))) 8 | 9 | console.log(100, getNginxConfCursorContext(conf.slice(0, 100))) 10 | 11 | console.log(124, getNginxConfCursorContext(conf.slice(0, 124))) 12 | 13 | console.log(124, getNginxConfCursorContext(conf.slice(0, 124))) 14 | console.log(126, getNginxConfCursorContext(conf.slice(0, 126))) 15 | console.log(130, getNginxConfCursorContext(conf.slice(0, 130))) 16 | 17 | 18 | console.log(233, getNginxConfCursorContext(conf.slice(0, 233))) 19 | 20 | console.log(267, getNginxConfCursorContext(conf.slice(0, 267))) 21 | 22 | console.log(304, getNginxConfCursorContext(conf.slice(0, 304))) 23 | 24 | console.log(27, getNginxConfCursorContext(conf2.slice(0, 27))) 25 | 26 | 27 | const conf3 = readFileSync(__dirname + '/../workspace/example3.conf', 'utf8'); 28 | 29 | // 129 { n: false, context: 'map', s: true, list: [ '~*text/html', '1;' ] } 30 | console.log(129, getNginxConfCursorContext(conf3.slice(0, 129))) 31 | -------------------------------------------------------------------------------- /test/unit-testing/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | [*.conf] 3 | indent_style = space 4 | indent_size = 4 5 | tab_width = 4 6 | trim_trailing_whitespace = true 7 | -------------------------------------------------------------------------------- /test/unit-testing/syntax1.conf: -------------------------------------------------------------------------------- 1 | # this is a comment 2 | 3 | server { 4 | location /hello { 5 | default_type text/html; 6 | content_by_lua_block { 7 | ngx.say("HelloWorld!") 8 | -- lua comment 9 | } 10 | client_max_body_size 1m; 11 | } 12 | location / { 13 | content_by_lua_block { 14 | --[[} 15 | } 16 | --]]} 17 | 18 | } 19 | -------------------------------------------------------------------------------- /test/unit-testing/syntax1.ts: -------------------------------------------------------------------------------- 1 | import { readFileSync } from "fs"; 2 | import { NginxConfCursorContext, getNginxConfCursorContext } from "../../src/extension/parser"; 3 | 4 | const conf = readFileSync(__dirname + "/syntax1.conf", "utf8"); 5 | 6 | type UnitTest = [position: string, context: Partial]; 7 | 8 | const tests: UnitTest[] = [ 9 | ["1,2", { c: true }], 10 | ["3,6", { list: ["serve"] }], 11 | ["3,7", { list: ["server"] }], 12 | ["3$", { context: "server" }], 13 | ["6$", { context: "content_by_lua_block" }], 14 | ["11^", { context: "location" }], 15 | ["11$", { context: "server" }], 16 | ["14,5", { lua: true, c: true }], 17 | ["14,6", { lua: true, c: true }], 18 | ["15$", { lua: true, c: true }], 19 | ["16,9", { lua: true }], 20 | ["16,10", { context: 'location' }], 21 | ]; 22 | 23 | let error = false; 24 | for (let i = 0; i < tests.length; i++) { 25 | const [position, context] = tests[i]; 26 | const pos = getPositionNumber(conf, position); 27 | const actual = getNginxConfCursorContext(conf.slice(0, pos)); 28 | const keys = Object.keys(context); 29 | let matched = true; 30 | for (let j = 0; j < keys.length; j++) { 31 | const key = keys[j]; 32 | if (Array.isArray(context[key])) matched = JSON.stringify(context[key]) === JSON.stringify(actual[key]); 33 | else matched = actual[key] === context[key]; 34 | 35 | if (!matched) { 36 | console.error('============================='); 37 | console.error(`position ${position}(${pos}) test failed:`); 38 | console.error(`expected:`, context); 39 | console.error(` actual:`, actual); 40 | console.error('============================='); 41 | error = true; 42 | break; 43 | } 44 | } 45 | if(matched) 46 | console.log(` passed:`, position, JSON.stringify(actual)); 47 | } 48 | if (error) process.exit(1); 49 | 50 | function getPositionNumber(text: string, expression: string) { 51 | let lineNo = parseInt(expression.match(/^\d+/)[0], 10); 52 | let index = 0; 53 | while (--lineNo > 0) { 54 | const nextIndex = text.indexOf("\n", index); 55 | if (nextIndex < 0) break; 56 | index = nextIndex + 1; 57 | } 58 | if (expression.endsWith("$")) { 59 | let nextIndex = text.indexOf("\n", index); 60 | if (nextIndex < 0) nextIndex = text.length - 1; 61 | index = nextIndex; 62 | } else if (expression.endsWith("^")) { 63 | // noop 64 | } else { 65 | const incr = parseInt(expression.match(/[,;:](\d+)$/)[1], 10); 66 | index += incr - 1; 67 | } 68 | return index; 69 | } 70 | -------------------------------------------------------------------------------- /test/workspace/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "nginx-conf-hint.enableStrictCompletion": false, 3 | "files.associations": { 4 | "*.conf.disabled": "NGINX" 5 | }, 6 | "nginx-conf-hint.externalModules": ["lua","js"], 7 | // "workbench.colorTheme": "Visual Studio Light", 8 | "editor.tokenColorCustomizations": { 9 | "[Solarized Light]": { 10 | "textMateRules": [ 11 | { 12 | "scope": [ 13 | "keyword.directive" 14 | ], 15 | "settings": { 16 | "foreground": "#0f5e72" 17 | } 18 | } 19 | ] 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test/workspace/README.md: -------------------------------------------------------------------------------- 1 | # Workspace for testing this extension 2 | -------------------------------------------------------------------------------- /test/workspace/example1.conf: -------------------------------------------------------------------------------- 1 | # This is a comment 2 | server { 3 | listen 80; 4 | server_name example.org; 5 | return 301 $scheme://www.example.org$request_uri; 6 | } 7 | 8 | # This is another comment 9 | server { 10 | listen 80; 11 | server_name www.example.org; 12 | 13 | include ./example2.conf; 14 | } 15 | -------------------------------------------------------------------------------- /test/workspace/example1.conf.disabled: -------------------------------------------------------------------------------- 1 | # This is a comment 2 | server { 3 | listen 80; 4 | server_name example.org; 5 | return 301 $scheme://www.example.org$request_uri; 6 | } 7 | 8 | # This is another comment 9 | server { 10 | listen 80; 11 | server_name www.example.org; 12 | 13 | include ./example2.conf; 14 | } 15 | -------------------------------------------------------------------------------- /test/workspace/example2.conf: -------------------------------------------------------------------------------- 1 | location ~ \.php$ { 2 | fastcgi_pass 127.0.0.1:1025; 3 | proxy_responses " # hello";#ssss # jjj 4 | content_by_lua_block { 5 | local ok, err = memc:connect(128, 20); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /test/workspace/example3.conf: -------------------------------------------------------------------------------- 1 | http { 2 | map $sample_variable $second_sample_variable { 3 | # (1) (2) comment block inside map 4 | ~*text/html "1; mode=block"; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/workspace/expires.conf: -------------------------------------------------------------------------------- 1 | # =========================================================================================== 2 | # Reference from: 3 | # https://github.com/h5bp/server-configs-nginx/blob/master/h5bp/location/expires.conf 4 | # =========================================================================================== 5 | 6 | # Expire rules for static content 7 | 8 | # No default expire rule. This config mirrors that of apache as outlined in the 9 | # html5-boilerplate .htaccess file. However, nginx applies rules by location, 10 | # the apache rules are defined by type. A consequence of this difference is that 11 | # if you use no file extension in the url and serve html, with apache you get an 12 | # expire time of 0s, with nginx you'd get an expire header of one month in the 13 | # future (if the default expire rule is 1 month). Therefore, do not use a 14 | # default expire rule with nginx unless your site is completely static 15 | 16 | # cache.appcache, your document html and data 17 | location ~* \.(?:manifest|appcache|html?|xml|json)$ { 18 | add_header Cache-Control "max-age=0"; 19 | } 20 | 21 | # Feed 22 | location ~* \.(?:rss|atom)$ { 23 | add_header Cache-Control "max-age=3600"; 24 | } 25 | 26 | # Media: images, icons, video, audio, HTC 27 | location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|mp4|ogg|ogv|webm|htc)$ { 28 | access_log off; 29 | add_header Cache-Control "max-age=2592000"; 30 | } 31 | 32 | # Media: svgz files are already compressed. 33 | location ~* \.svgz$ { 34 | access_log off; 35 | gzip off; 36 | add_header Cache-Control "max-age=2592000"; 37 | } 38 | 39 | # CSS and Javascript 40 | location ~* \.(?:css|js)$ { 41 | add_header Cache-Control "max-age=31536000"; 42 | access_log off; 43 | } 44 | 45 | # WebFonts 46 | # If you are NOT using cross-domain-fonts.conf, uncomment the following directive 47 | # location ~* \.(?:ttf|ttc|otf|eot|woff|woff2)$ { 48 | # add_header Cache-Control "max-age=2592000"; 49 | # access_log off; 50 | # } 51 | -------------------------------------------------------------------------------- /test/workspace/named-location.conf: -------------------------------------------------------------------------------- 1 | location / { 2 | error_page 404 = @fallback; 3 | } 4 | 5 | location @fallback { 6 | proxy_pass http://backend; 7 | } 8 | -------------------------------------------------------------------------------- /test/workspace/named-location2.conf: -------------------------------------------------------------------------------- 1 | location / { 2 | error_page 404 = @fallback; 3 | } 4 | -------------------------------------------------------------------------------- /test/workspace/nginx.com/README.md: -------------------------------------------------------------------------------- 1 | # Examples form 2 | 3 | ## Links 4 | 5 | - 6 | - 7 | 8 | ## License 9 | 10 | - [Attribution-NonCommercial-ShareAlike 4.0 International](https://github.com/nginxinc/nginx-wiki/blob/master/LICENSE) 11 | -------------------------------------------------------------------------------- /test/workspace/nginx.com/active_sync_gateway.conf: -------------------------------------------------------------------------------- 1 | # https://www.nginx.com/resources/wiki/start/topics/examples/SSL-Offloader/#active-sync-gateway 2 | location /Microsoft-Server-ActiveSync { 3 | access_log /var/log/nginx/activesync.log; 4 | resolver your.dns.server.ip; 5 | # deny anonymous; deny other http methods 6 | if ( $remote_user = "" ) { return 444; break; } 7 | if ( $request_method !~* ^(POST|OPTIONS)$ ) { return 444; break; } 8 | # extract domain and user-id 9 | if ( $remote_user ~* ^(.+)\x5C(.+)$ ) { set $domain $1; set $userid $2; } 10 | if ( $remote_user !~* ^(.+)\x5C(.+)$ ) { return 444; break; } 11 | # replace underscores in username 12 | if ( $userid ~* ^(.+)_(.+)$ ) { set $userdn $1x$2; } 13 | if ( $userid !~* ^(.+)_(.+)$ ) { set $userdn $userid; } 14 | # extract device-type and version 15 | if ( $http_user_agent ~* ^MSFT-(.+)/(.+)\.(.+)\.(.+)$ ) { set $device MSFT$1; set $versio $2x$3x$4; } 16 | if ( $http_user_agent ~* ^Apple-iPhone(.*)/(.+)\.(.+)$ ) { set $device iPhone; set $versio $1x$2x$3; } 17 | if ( $http_user_agent ~* ^Apple-iPad(.+)/(.+)\.(.+)$ ) { set $device iPad; set $versio $1x$2x$3; } 18 | if ( $http_user_agent ~* ^Apple-iPod(.+)/(.+)\.(.+)$ ) { set $device iPod; set $versio $1x$2x$3; } 19 | if ( $http_user_agent ~* ^Android-(.+)/(.+)\.(.+)$ ) { set $device Android; set $versio $1x$2x$3; } 20 | # always allow initial requests without arguments 21 | set $initia $request_method:$args; 22 | if ( $initia ~* ^OPTIONS:$ ) { set $target $domain-exchange; set $versio ok; } 23 | if ( $versio = "" ) { return 444; break; } 24 | # set target, if usernames match 25 | if ( $userid = $arg_User ) { set $target $domain-$userdn-$arg_DeviceId-$device-$versio; } 26 | # forward request 27 | proxy_pass http://$target.your.internal.sync.domain; 28 | } 29 | -------------------------------------------------------------------------------- /test/workspace/nginx.com/dynamic_ssi.conf: -------------------------------------------------------------------------------- 1 | user nginx; 2 | worker_processes 1; 3 | 4 | events { 5 | worker_connections 1024; 6 | } 7 | 8 | http { 9 | include mime.types; 10 | default_type application/octet-stream; 11 | 12 | sendfile on; 13 | tcp_nopush on; 14 | keepalive_timeout 10; 15 | gzip on; 16 | 17 | server { 18 | server_name localhost; 19 | charset utf-8; 20 | access_log /var/log/nginx/access.log; 21 | 22 | root /var/www; 23 | 24 | location = / { 25 | rewrite ^ /home redirect; 26 | } 27 | 28 | location / { 29 | ssi on; 30 | set $inc $request_uri; 31 | if (!-f $request_filename) { 32 | rewrite ^ /index.html last; 33 | } 34 | if (!-f $document_root$inc.html) { 35 | return 404; 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /test/workspace/nginx.com/fastcgi.conf: -------------------------------------------------------------------------------- 1 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 2 | fastcgi_param QUERY_STRING $query_string; 3 | fastcgi_param REQUEST_METHOD $request_method; 4 | fastcgi_param CONTENT_TYPE $content_type; 5 | fastcgi_param CONTENT_LENGTH $content_length; 6 | fastcgi_param SCRIPT_NAME $fastcgi_script_name; 7 | fastcgi_param REQUEST_URI $request_uri; 8 | fastcgi_param DOCUMENT_URI $document_uri; 9 | fastcgi_param DOCUMENT_ROOT $document_root; 10 | fastcgi_param SERVER_PROTOCOL $server_protocol; 11 | fastcgi_param GATEWAY_INTERFACE CGI/1.1; 12 | fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; 13 | fastcgi_param REMOTE_ADDR $remote_addr; 14 | fastcgi_param REMOTE_PORT $remote_port; 15 | fastcgi_param SERVER_ADDR $server_addr; 16 | fastcgi_param SERVER_PORT $server_port; 17 | fastcgi_param SERVER_NAME $server_name; 18 | 19 | fastcgi_index index.php; 20 | 21 | fastcgi_param REDIRECT_STATUS 200; 22 | -------------------------------------------------------------------------------- /test/workspace/nginx.com/fullexample2.conf: -------------------------------------------------------------------------------- 1 | user www www; 2 | worker_processes 2; 3 | pid /var/run/nginx.pid; 4 | 5 | # [ debug | info | notice | warn | error | crit ] 6 | error_log /var/log/nginx.error_log info; 7 | 8 | events { 9 | worker_connections 2000; 10 | # use [ kqueue | rtsig | epoll | /dev/poll | select | poll ] ; 11 | use kqueue; 12 | } 13 | 14 | http { 15 | include conf/mime.types; 16 | default_type application/octet-stream; 17 | 18 | log_format main '$remote_addr - $remote_user [$time_local] ' 19 | '"$request" $status $bytes_sent ' 20 | '"$http_referer" "$http_user_agent" ' 21 | '"$gzip_ratio"'; 22 | 23 | log_format download '$remote_addr - $remote_user [$time_local] ' 24 | '"$request" $status $bytes_sent ' 25 | '"$http_referer" "$http_user_agent" ' 26 | '"$http_range" "$sent_http_content_range"'; 27 | 28 | client_header_timeout 3m; 29 | client_body_timeout 3m; 30 | send_timeout 3m; 31 | 32 | client_header_buffer_size 1k; 33 | large_client_header_buffers 4 4k; 34 | 35 | gzip on; 36 | gzip_min_length 1100; 37 | gzip_buffers 4 8k; 38 | gzip_types text/plain; 39 | 40 | output_buffers 1 32k; 41 | postpone_output 1460; 42 | 43 | sendfile on; 44 | tcp_nopush on; 45 | 46 | tcp_nodelay on; 47 | send_lowat 12000; 48 | 49 | keepalive_timeout 75 20; 50 | 51 | # lingering_time 30; 52 | # lingering_timeout 10; 53 | # reset_timedout_connection on; 54 | 55 | 56 | server { 57 | listen one.example.com; 58 | server_name one.example.com www.one.example.com; 59 | 60 | access_log /var/log/nginx.access_log main; 61 | 62 | location / { 63 | proxy_pass http://127.0.0.1/; 64 | proxy_redirect off; 65 | 66 | proxy_set_header Host $host; 67 | proxy_set_header X-Real-IP $remote_addr; 68 | # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 69 | 70 | client_max_body_size 10m; 71 | client_body_buffer_size 128k; 72 | 73 | client_body_temp_path /var/nginx/client_body_temp; 74 | 75 | proxy_connect_timeout 90; 76 | proxy_send_timeout 90; 77 | proxy_read_timeout 90; 78 | proxy_send_lowat 12000; 79 | 80 | proxy_buffer_size 4k; 81 | proxy_buffers 4 32k; 82 | proxy_busy_buffers_size 64k; 83 | proxy_temp_file_write_size 64k; 84 | 85 | proxy_temp_path /var/nginx/proxy_temp; 86 | 87 | charset koi8-r; 88 | } 89 | 90 | error_page 404 /404.html; 91 | 92 | location /404.html { 93 | root /spool/www; 94 | 95 | charset on; 96 | source_charset koi8-r; 97 | } 98 | 99 | location /old_stuff/ { 100 | rewrite ^/old_stuff/(.*)$ /new_stuff/$1 permanent; 101 | } 102 | 103 | location /download/ { 104 | valid_referers none blocked server_names *.example.com; 105 | 106 | if ($invalid_referer) { 107 | #rewrite ^/ http://www.example.com/; 108 | return 403; 109 | } 110 | 111 | # rewrite_log on; 112 | # rewrite /download/*/mp3/*.any_ext to /download/*/mp3/*.mp3 113 | rewrite ^/(download/.*)/mp3/(.*)\..*$ /$1/mp3/$2.mp3 break; 114 | 115 | root /spool/www; 116 | # autoindex on; 117 | access_log /var/log/nginx-download.access_log download; 118 | } 119 | 120 | location ~* ^.+\.(jpg|jpeg|gif)$ { 121 | root /spool/www; 122 | access_log off; 123 | expires 30d; 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /test/workspace/nginx.com/loadbalanceexample.conf: -------------------------------------------------------------------------------- 1 | http { 2 | upstream myproject { 3 | server 127.0.0.1:8000 weight=3; 4 | server 127.0.0.1:8001; 5 | server 127.0.0.1:8002; 6 | server 127.0.0.1:8003; 7 | } 8 | 9 | server { 10 | listen 80; 11 | server_name www.domain.com; 12 | location / { 13 | proxy_pass http://myproject; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/workspace/nginx.com/mime.types: -------------------------------------------------------------------------------- 1 | types { 2 | text/html html htm shtml; 3 | text/css css; 4 | text/xml xml rss; 5 | image/gif gif; 6 | image/jpeg jpeg jpg; 7 | application/x-javascript js; 8 | text/plain txt; 9 | text/x-component htc; 10 | text/mathml mml; 11 | image/png png; 12 | image/x-icon ico; 13 | image/x-jng jng; 14 | image/vnd.wap.wbmp wbmp; 15 | application/java-archive jar war ear; 16 | application/mac-binhex40 hqx; 17 | application/pdf pdf; 18 | application/x-cocoa cco; 19 | application/x-java-archive-diff jardiff; 20 | application/x-java-jnlp-file jnlp; 21 | application/x-makeself run; 22 | application/x-perl pl pm; 23 | application/x-pilot prc pdb; 24 | application/x-rar-compressed rar; 25 | application/x-redhat-package-manager rpm; 26 | application/x-sea sea; 27 | application/x-shockwave-flash swf; 28 | application/x-stuffit sit; 29 | application/x-tcl tcl tk; 30 | application/x-x509-ca-cert der pem crt; 31 | application/x-xpinstall xpi; 32 | application/zip zip; 33 | application/octet-stream deb; 34 | application/octet-stream bin exe dll; 35 | application/octet-stream dmg; 36 | application/octet-stream eot; 37 | application/octet-stream iso img; 38 | application/octet-stream msi msp msm; 39 | audio/mpeg mp3; 40 | audio/x-realaudio ra; 41 | video/mpeg mpeg mpg; 42 | video/quicktime mov; 43 | video/x-flv flv; 44 | video/x-msvideo avi; 45 | video/x-ms-wmv wmv; 46 | video/x-ms-asf asx asf; 47 | video/x-mng mng; 48 | } 49 | -------------------------------------------------------------------------------- /test/workspace/nginx.com/nginx.conf: -------------------------------------------------------------------------------- 1 | user www www; ## Default: nobody 2 | worker_processes 5; ## Default: 1 3 | error_log logs/error.log; 4 | pid logs/nginx.pid; 5 | worker_rlimit_nofile 8192; 6 | 7 | events { 8 | worker_connections 4096; ## Default: 1024 9 | } 10 | 11 | http { 12 | include conf/mime.types; 13 | include /etc/nginx/proxy.conf; 14 | include /etc/nginx/fastcgi.conf; 15 | index index.html index.htm index.php; 16 | 17 | default_type application/octet-stream; 18 | log_format main '$remote_addr - $remote_user [$time_local] $status ' 19 | '"$request" $body_bytes_sent "$http_referer" ' 20 | '"$http_user_agent" "$http_x_forwarded_for"'; 21 | access_log logs/access.log main; 22 | sendfile on; 23 | tcp_nopush on; 24 | server_names_hash_bucket_size 128; # this seems to be required for some vhosts 25 | 26 | server { # php/fastcgi 27 | listen 80; 28 | server_name domain1.com www.domain1.com; 29 | access_log logs/domain1.access.log main; 30 | root html; 31 | 32 | location ~ \.php$ { 33 | fastcgi_pass 127.0.0.1:1025; 34 | } 35 | } 36 | 37 | server { # simple reverse-proxy 38 | listen 80; 39 | server_name domain2.com www.domain2.com; 40 | access_log logs/domain2.access.log main; 41 | 42 | # serve static files 43 | location ~ ^/(images|javascript|js|css|flash|media|static)/ { 44 | root /var/www/virtual/big.server.com/htdocs; 45 | expires 30d; 46 | } 47 | 48 | # pass requests for dynamic content to rails/turbogears/zope, et al 49 | location / { 50 | proxy_pass http://127.0.0.1:8080; 51 | } 52 | } 53 | 54 | upstream big_server_com { 55 | server 127.0.0.3:8000 weight=5; 56 | server 127.0.0.3:8001 weight=5; 57 | server 192.168.0.1:8000; 58 | server 192.168.0.1:8001; 59 | } 60 | 61 | server { # simple load balancing 62 | listen 80; 63 | server_name big.server.com; 64 | access_log logs/big.server.access.log main; 65 | 66 | location / { 67 | proxy_pass http://big_server_com; 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /test/workspace/nginx.com/proxy.conf: -------------------------------------------------------------------------------- 1 | proxy_redirect off; 2 | proxy_set_header Host $host; 3 | proxy_set_header X-Real-IP $remote_addr; 4 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 5 | client_max_body_size 10m; 6 | client_body_buffer_size 128k; 7 | proxy_connect_timeout 90; 8 | proxy_send_timeout 90; 9 | proxy_read_timeout 90; 10 | proxy_buffers 32 4k; 11 | -------------------------------------------------------------------------------- /test/workspace/ssl-example.conf: -------------------------------------------------------------------------------- 1 | # =========================================================================================== 2 | # Reference from: 3 | # https://github.com/h5bp/server-configs-nginx/blob/master/sites-available/ssl.example.com 4 | # =========================================================================================== 5 | 6 | # Choose between www and non-www, listen on the *wrong* one and redirect to 7 | # the right one -- http://wiki.nginx.org/Pitfalls#Server_Name 8 | # 9 | server { 10 | listen [::]:80; 11 | listen 80; 12 | 13 | # listen on both hosts 14 | server_name example.com www.example.com; 15 | 16 | # and redirect to the https host (declared below) 17 | # avoiding http://www -> https://www -> https:// chain. 18 | return 301 https://example.com$request_uri; 19 | } 20 | 21 | server { 22 | listen [::]:443 ssl http2; 23 | listen 443 ssl http2; 24 | 25 | # listen on the wrong host 26 | server_name www.example.com; 27 | 28 | include h5bp/directive-only/ssl.conf; 29 | 30 | # and redirect to the non-www host (declared below) 31 | return 301 https://example.com$request_uri; 32 | } 33 | 34 | server { 35 | 36 | # listen [::]:443 ssl http2 accept_filter=dataready; # for FreeBSD 37 | # listen 443 ssl http2 accept_filter=dataready; # for FreeBSD 38 | # listen [::]:443 ssl http2 deferred; # for Linux 39 | # listen 443 ssl http2 deferred; # for Linux 40 | listen [::]:443 ssl http2; 41 | listen 443 ssl http2; 42 | 43 | # The host name to respond to 44 | server_name example.com; 45 | 46 | include h5bp/directive-only/ssl.conf; 47 | 48 | # Path for static files 49 | root /var/www/example.com/public; 50 | 51 | #Specify a charset 52 | charset utf-8; 53 | 54 | # Custom 404 page 55 | error_page 404 /404.html; 56 | 57 | # Include the basic h5bp config set 58 | include h5bp/basic.conf; 59 | } 60 | -------------------------------------------------------------------------------- /test/workspace/sublime-example.conf: -------------------------------------------------------------------------------- 1 | user www www; ## Default: nobody 2 | worker_processes 5; ## Default: 1 3 | error_log logs/error.log; 4 | pid logs/nginx.pid; 5 | worker_rlimit_nofile 8192; 6 | 7 | events { 8 | worker_connections 4096; ## Default: 1024 9 | } 10 | 11 | http { 12 | include conf/mime.types; 13 | include /etc/nginx/proxy.conf; 14 | include /etc/nginx/fastcgi.conf; 15 | index index.html index.htm index.php; 16 | 17 | default_type application/octet-stream; 18 | log_format main '$remote_addr - $remote_user [$time_local] $status ' 19 | '"$request" $body_bytes_sent "$http_referer" ' 20 | '"$http_user_agent" "$http_x_forwarded_for"'; 21 | access_log logs/access.log main; 22 | sendfile on; 23 | tcp_nopush on; 24 | server_names_hash_bucket_size 128; # this seems to be required for some vhosts 25 | 26 | server { # php/fastcgi 27 | listen 80; 28 | server_name domain1.com www.domain1.com; 29 | access_log logs/domain1.access.log main; 30 | root html; 31 | 32 | location ~ \.php$ { 33 | fastcgi_pass 127.0.0.1:1025; 34 | } 35 | } 36 | 37 | server { # simple reverse-proxy 38 | listen 80; 39 | server_name domain2.com www.domain2.com; 40 | access_log logs/domain2.access.log main; 41 | 42 | # serve static files 43 | location ~ ^/(images|javascript|js|css|flash|media|static)/ { 44 | root /var/www/virtual/big.server.com/htdocs; 45 | expires 30d; 46 | } 47 | 48 | # pass requests for dynamic content to rails/turbogears/zope, et al 49 | location / { 50 | proxy_pass http://127.0.0.1:8080; 51 | } 52 | } 53 | 54 | upstream big_server_com { 55 | server 127.0.0.3:8000 weight=5; 56 | server 127.0.0.3:8001 weight=5; 57 | server 192.168.0.1:8000; 58 | server 192.168.0.1:8001; 59 | } 60 | 61 | server { # simple load balancing 62 | listen 80; 63 | server_name big.server.com; 64 | access_log logs/big.server.access.log main; 65 | 66 | location / { 67 | proxy_pass http://big_server_com; 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /test/workspace/test-in-docker.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | image="nginx:latest" 4 | 5 | [ -z "$1" ] && exit 1; 6 | 7 | file="$(realpath "$1")"; 8 | [ -z "$file" ] && exit 1; 9 | 10 | dir="$(dirname "$file")"; 11 | file="$(basename "$file")"; 12 | [ -z "$dir" ] && exit 1; 13 | [ -z "$file" ] && exit 1; 14 | 15 | set -x; 16 | docker run --rm -it -v "${dir}:/test:ro" --entrypoint nginx "$image" -t -c "/test/${file}"; 17 | echo "$?" 18 | -------------------------------------------------------------------------------- /test/workspace/weird/full-example.conf: -------------------------------------------------------------------------------- 1 | user nginx; 2 | worker_processes 2; 3 | pid /var/run/nginx.pid; 4 | 5 | events { 6 | worker_connections 2000; 7 | } 8 | 9 | http { 10 | server { 11 | set $var1 "a"; 12 | set $var1 "${var1}b"; 13 | set $var1 "${var1}\\nc"; 14 | 15 | add_header X-Test " 16 | three lines string 17 | "; 18 | add_header 19 | X-Test2 20 | $var1; 21 | 22 | "add_header" X-Test3 ???; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /test/workspace/weird/part-for-debug.conf: -------------------------------------------------------------------------------- 1 | "add_header" X-Test3 ???; 2 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "out", 4 | "module": "CommonJS", 5 | "declaration": false, 6 | "removeComments": false, 7 | "target": "ES2022", 8 | "sourceMap": true, 9 | "incremental": true, 10 | "skipLibCheck": true, 11 | "strict": true, 12 | "strictNullChecks": false 13 | }, 14 | "include": [ 15 | "src" 16 | ], 17 | "exclude": [ 18 | ".tsc", 19 | "out", 20 | "node_modules" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | //@ts-check 2 | "use strict"; 3 | 4 | const path = require("path"); 5 | const webpack = require("webpack"); 6 | 7 | /** @type {import('webpack').RuleSetUse} */ 8 | const tsLoader = { 9 | loader: "swc-loader", 10 | }; 11 | 12 | /** @typedef {import('webpack').Configuration} WebpackConfig **/ 13 | /** @type WebpackConfig */ 14 | const webExtensionConfig = { 15 | mode: "none", // this leaves the source code as close as possible to the original (when packaging we set this to 'production') 16 | target: "webworker", // extensions run in a webworker context 17 | entry: { 18 | "main.web": "./src/extension/main.web.ts", // source of the web extension main file 19 | }, 20 | output: { 21 | filename: "[name].js", 22 | path: path.join(__dirname, "./out/extension"), 23 | libraryTarget: "commonjs", 24 | }, 25 | resolve: { 26 | mainFields: ["browser", "module", "main"], // look for `browser` entry point in imported node modules 27 | extensions: [".ts", ".js"], // support ts-files and js-files 28 | alias: { 29 | // provides alternate implementation for node module and source files 30 | }, 31 | fallback: { 32 | // Webpack 5 no longer polyfills Node.js core modules automatically. 33 | // see https://webpack.js.org/configuration/resolve/#resolvefallback 34 | // for the list of Node.js core module polyfills. 35 | assert: require.resolve("assert"), 36 | }, 37 | }, 38 | module: { 39 | rules: [ 40 | { 41 | test: /\.ts$/, 42 | exclude: /node_modules/, 43 | use: [tsLoader], 44 | }, 45 | ], 46 | }, 47 | plugins: [ 48 | new webpack.ProvidePlugin({ 49 | process: "process/browser", // provide a shim for the global `process` variable 50 | }), 51 | ], 52 | externals: { 53 | vscode: "commonjs vscode", // ignored because it doesn't exist 54 | }, 55 | performance: { 56 | hints: false, 57 | }, 58 | devtool: "nosources-source-map", // create a source map that points to the original source file 59 | }; 60 | module.exports = [webExtensionConfig]; 61 | --------------------------------------------------------------------------------