mirror of
https://github.com/Microsoft/vscode
synced 2024-08-27 13:00:58 +00:00
Merge branch 'master' into list-extensions
This commit is contained in:
commit
7b16f15d00
1981
.eslintrc.json
1981
.eslintrc.json
File diff suppressed because it is too large
Load diff
574
.github/workflows/ci.yml
vendored
574
.github/workflows/ci.yml
vendored
|
@ -11,25 +11,33 @@ on:
|
|||
- release/*
|
||||
|
||||
jobs:
|
||||
hygiene:
|
||||
name: Hygiene and Layering
|
||||
runs-on: ubuntu-latest
|
||||
windows:
|
||||
name: Windows
|
||||
runs-on: windows-latest
|
||||
env:
|
||||
CHILD_CONCURRENCY: "1"
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/setup-node@v2-beta
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 12
|
||||
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: "2.x"
|
||||
|
||||
- name: Compute node modules cache key
|
||||
id: nodeModulesCacheKey
|
||||
run: echo "::set-output name=value::$(node build/azure-pipelines/common/computeNodeModulesCacheKey.js)"
|
||||
- name: Cache node modules
|
||||
id: cacheNodeModules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: "**/node_modules"
|
||||
key: ${{ runner.os }}-cacheNodeModules6-${{ hashFiles('.yarnrc', 'remote/.yarnrc', '**/yarn.lock', '!**/node_modules/**/yarn.lock', '!**/.*/**/yarn.lock') }}
|
||||
restore-keys: ${{ runner.os }}-cacheNodeModules6-
|
||||
key: ${{ runner.os }}-cacheNodeModules8-${{ steps.nodeModulesCacheKey.outputs.value }}
|
||||
restore-keys: ${{ runner.os }}-cacheNodeModules8-
|
||||
- name: Get yarn cache directory path
|
||||
id: yarnCacheDirPath
|
||||
if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }}
|
||||
|
@ -39,7 +47,7 @@ jobs:
|
|||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ steps.yarnCacheDirPath.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarnCacheDir-${{ hashFiles('.yarnrc', 'remote/.yarnrc', '**/yarn.lock', '!**/node_modules/**/yarn.lock', '!**/.*/**/yarn.lock') }}
|
||||
key: ${{ runner.os }}-yarnCacheDir-${{ steps.nodeModulesCacheKey.outputs.value }}
|
||||
restore-keys: ${{ runner.os }}-yarnCacheDir-
|
||||
- name: Execute yarn
|
||||
if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }}
|
||||
|
@ -48,414 +56,17 @@ jobs:
|
|||
ELECTRON_SKIP_BINARY_DOWNLOAD: 1
|
||||
run: yarn --frozen-lockfile --network-timeout 180000
|
||||
|
||||
- name: Run Hygiene Checks
|
||||
run: yarn gulp hygiene
|
||||
- name: Compile and Download
|
||||
run: yarn npm-run-all --max_old_space_size=4095 -lp compile "electron x64" playwright-install download-builtin-extensions
|
||||
|
||||
- name: Run Valid Layers Checks
|
||||
run: yarn valid-layers-check
|
||||
- name: Run Unit Tests (Electron)
|
||||
run: .\scripts\test.bat
|
||||
|
||||
# build-compile-core:
|
||||
# name: "Build: Compile Core"
|
||||
# runs-on: ubuntu-latest
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# steps:
|
||||
# - uses: actions/checkout@v2
|
||||
- name: Run Unit Tests (Browser)
|
||||
run: yarn test-browser --browser chromium
|
||||
|
||||
# - uses: actions/setup-node@v2-beta
|
||||
# with:
|
||||
# node-version: 12
|
||||
|
||||
# - name: Cache node modules
|
||||
# id: cacheNodeModules
|
||||
# uses: actions/cache@v2
|
||||
# with:
|
||||
# path: '**/node_modules'
|
||||
# key: ${{ runner.os }}-cacheNodeModules6-${{ hashFiles('.yarnrc', 'remote/.yarnrc', '**/yarn.lock', '!**/node_modules/**/yarn.lock', '!**/.*/**/yarn.lock') }}
|
||||
# restore-keys: ${{ runner.os }}-cacheNodeModules6-
|
||||
# - name: Get yarn cache directory path
|
||||
# id: yarnCacheDirPath
|
||||
# if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }}
|
||||
# run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
# - name: Cache yarn directory
|
||||
# if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }}
|
||||
# uses: actions/cache@v2
|
||||
# with:
|
||||
# path: ${{ steps.yarnCacheDirPath.outputs.dir }}
|
||||
# key: ${{ runner.os }}-yarnCacheDir-${{ hashFiles('.yarnrc', 'remote/.yarnrc', '**/yarn.lock', '!**/node_modules/**/yarn.lock', '!**/.*/**/yarn.lock') }}
|
||||
# restore-keys: ${{ runner.os }}-yarnCacheDir-
|
||||
# - name: Execute yarn
|
||||
# if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }}
|
||||
# env:
|
||||
# PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
|
||||
# ELECTRON_SKIP_BINARY_DOWNLOAD: 1
|
||||
# run: yarn --frozen-lockfile --network-timeout 180000
|
||||
|
||||
# - name: Cache compiled core code
|
||||
# id: cacheCompiledCoreCode
|
||||
# uses: actions/cache@v2
|
||||
# with:
|
||||
# path: |
|
||||
# out-build
|
||||
# out-vscode-min
|
||||
# key: cacheCompiledCoreCode-${{ github.sha }}
|
||||
|
||||
# - name: Compile Core
|
||||
# if: ${{ steps.cacheCompiledCoreCode.outputs.cache-hit != 'true' }}
|
||||
# run: yarn gulp compile-build
|
||||
|
||||
# - name: Minify VS Code
|
||||
# if: ${{ steps.cacheCompiledCoreCode.outputs.cache-hit != 'true' }}
|
||||
# run: yarn gulp minify-vscode
|
||||
|
||||
# build-compile-extensions:
|
||||
# name: "Build: Compile Extensions"
|
||||
# runs-on: ubuntu-latest
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# steps:
|
||||
# - uses: actions/checkout@v2
|
||||
|
||||
# - uses: actions/setup-node@v2-beta
|
||||
# with:
|
||||
# node-version: 12
|
||||
|
||||
# - name: Cache node modules
|
||||
# id: cacheNodeModules
|
||||
# uses: actions/cache@v2
|
||||
# with:
|
||||
# path: '**/node_modules'
|
||||
# key: ${{ runner.os }}-cacheNodeModules6-${{ hashFiles('.yarnrc', 'remote/.yarnrc', '**/yarn.lock', '!**/node_modules/**/yarn.lock', '!**/.*/**/yarn.lock') }}
|
||||
# restore-keys: ${{ runner.os }}-cacheNodeModules6-
|
||||
# - name: Get yarn cache directory path
|
||||
# id: yarnCacheDirPath
|
||||
# if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }}
|
||||
# run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
# - name: Cache yarn directory
|
||||
# if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }}
|
||||
# uses: actions/cache@v2
|
||||
# with:
|
||||
# path: ${{ steps.yarnCacheDirPath.outputs.dir }}
|
||||
# key: ${{ runner.os }}-yarnCacheDir-${{ hashFiles('.yarnrc', 'remote/.yarnrc', '**/yarn.lock', '!**/node_modules/**/yarn.lock', '!**/.*/**/yarn.lock') }}
|
||||
# restore-keys: ${{ runner.os }}-yarnCacheDir-
|
||||
# - name: Execute yarn
|
||||
# if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }}
|
||||
# env:
|
||||
# PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
|
||||
# ELECTRON_SKIP_BINARY_DOWNLOAD: 1
|
||||
# run: yarn --frozen-lockfile --network-timeout 180000
|
||||
|
||||
# - name: Cache compiled extensions code
|
||||
# id: cacheCompiledExtensionsCode
|
||||
# uses: actions/cache@v2
|
||||
# with:
|
||||
# path: .build
|
||||
# key: cacheCompiledExtensionsCode-${{ github.sha }}
|
||||
|
||||
# - name: Compile Extensions
|
||||
# if: ${{ steps.cacheCompiledExtensionsCode.outputs.cache-hit != 'true' }}
|
||||
# run: yarn gulp compile-extensions-build
|
||||
|
||||
# build-linux-unit-tests:
|
||||
# name: "Build: Linux Unit Tests"
|
||||
# needs: [build-compile-core, build-compile-extensions]
|
||||
# runs-on: ubuntu-latest
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# steps:
|
||||
# - uses: actions/checkout@v2
|
||||
|
||||
# # TODO: rename azure-pipelines/linux/xvfb.init to github-actions
|
||||
# - name: Setup Build Environment
|
||||
# run: |
|
||||
# sudo apt-get update
|
||||
# sudo apt-get install -y libxkbfile-dev pkg-config libsecret-1-dev libxss1 dbus xvfb libgtk-3-0 libgbm1
|
||||
# sudo cp build/azure-pipelines/linux/xvfb.init /etc/init.d/xvfb
|
||||
# sudo chmod +x /etc/init.d/xvfb
|
||||
# sudo update-rc.d xvfb defaults
|
||||
# sudo service xvfb start
|
||||
|
||||
# - uses: actions/setup-node@v2-beta
|
||||
# with:
|
||||
# node-version: 12
|
||||
|
||||
# - name: Restore cached node modules
|
||||
# id: cacheNodeModules
|
||||
# uses: actions/cache@v2
|
||||
# with:
|
||||
# path: '**/node_modules'
|
||||
# key: ${{ runner.os }}-cacheNodeModules6-${{ hashFiles('.yarnrc', 'remote/.yarnrc', '**/yarn.lock', '!**/node_modules/**/yarn.lock', '!**/.*/**/yarn.lock') }}
|
||||
# restore-keys: ${{ runner.os }}-cacheNodeModules6-
|
||||
|
||||
# - name: Restore compiled core code
|
||||
# id: cacheCompiledCoreCode
|
||||
# uses: actions/cache@v2
|
||||
# with:
|
||||
# path: |
|
||||
# out-build
|
||||
# out-vscode-min
|
||||
# key: cacheCompiledCoreCode-${{ github.sha }}
|
||||
|
||||
# - name: Restore compiled extensions code
|
||||
# id: cacheCompiledExtensionsCode
|
||||
# uses: actions/cache@v2
|
||||
# with:
|
||||
# path: .build
|
||||
# key: cacheCompiledExtensionsCode-${{ github.sha }}
|
||||
|
||||
# - name: Build VS Code
|
||||
# run: yarn gulp vscode-linux-x64-min-ci
|
||||
|
||||
# - name: Download Electron
|
||||
# run: yarn electron x64
|
||||
|
||||
# - name: Run Unit Tests (Electron)
|
||||
# run: DISPLAY=:10 ./scripts/test.sh --build
|
||||
|
||||
# - name: Download Playwright
|
||||
# run: node ./node_modules/playwright/install.js
|
||||
|
||||
# - name: Run Unit Tests (Browser)
|
||||
# run: DISPLAY=:10 yarn test-browser --build --browser chromium
|
||||
|
||||
# build-linux-integration-tests:
|
||||
# name: "Build: Linux Integration Tests"
|
||||
# needs: [build-compile-core, build-compile-extensions]
|
||||
# runs-on: ubuntu-latest
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# steps:
|
||||
# - uses: actions/checkout@v2
|
||||
|
||||
# # TODO: rename azure-pipelines/linux/xvfb.init to github-actions
|
||||
# - name: Setup Build Environment
|
||||
# run: |
|
||||
# sudo apt-get update
|
||||
# sudo apt-get install -y libxkbfile-dev pkg-config libsecret-1-dev libxss1 dbus xvfb libgtk-3-0 libgbm1
|
||||
# sudo cp build/azure-pipelines/linux/xvfb.init /etc/init.d/xvfb
|
||||
# sudo chmod +x /etc/init.d/xvfb
|
||||
# sudo update-rc.d xvfb defaults
|
||||
# sudo service xvfb start
|
||||
|
||||
# - uses: actions/setup-node@v2-beta
|
||||
# with:
|
||||
# node-version: 12
|
||||
|
||||
# - name: Restore cached node modules
|
||||
# id: cacheNodeModules
|
||||
# uses: actions/cache@v2
|
||||
# with:
|
||||
# path: '**/node_modules'
|
||||
# key: ${{ runner.os }}-cacheNodeModules6-${{ hashFiles('.yarnrc', 'remote/.yarnrc', '**/yarn.lock', '!**/node_modules/**/yarn.lock', '!**/.*/**/yarn.lock') }}
|
||||
# restore-keys: ${{ runner.os }}-cacheNodeModules6-
|
||||
|
||||
# - name: Restore compiled core code
|
||||
# id: cacheCompiledCoreCode
|
||||
# uses: actions/cache@v2
|
||||
# with:
|
||||
# path: |
|
||||
# out-build
|
||||
# out-vscode-min
|
||||
# key: cacheCompiledCoreCode-${{ github.sha }}
|
||||
|
||||
# - name: Restore compiled extensions code
|
||||
# id: cacheCompiledExtensionsCode
|
||||
# uses: actions/cache@v2
|
||||
# with:
|
||||
# path: .build
|
||||
# key: cacheCompiledExtensionsCode-${{ github.sha }}
|
||||
|
||||
# - name: Build VS Code
|
||||
# run: yarn gulp vscode-linux-x64-min-ci
|
||||
|
||||
# - name: Download Electron
|
||||
# run: yarn electron x64
|
||||
|
||||
# - name: Run Integration Tests (Electron)
|
||||
# run: DISPLAY=:10 ./scripts/test-integration.sh --build
|
||||
|
||||
# build-darwin-node-modules:
|
||||
# name: "Build: macOS Node Modules"
|
||||
# runs-on: macos-latest
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# steps:
|
||||
# - uses: actions/checkout@v2
|
||||
|
||||
# - uses: actions/setup-node@v2-beta
|
||||
# with:
|
||||
# node-version: 12
|
||||
|
||||
# - name: Cache node modules
|
||||
# id: cacheNodeModules
|
||||
# uses: actions/cache@v2
|
||||
# with:
|
||||
# path: '**/node_modules'
|
||||
# key: ${{ runner.os }}-cacheNodeModules6-${{ hashFiles('.yarnrc', 'remote/.yarnrc', '**/yarn.lock', '!**/node_modules/**/yarn.lock', '!**/.*/**/yarn.lock') }}
|
||||
# restore-keys: ${{ runner.os }}-cacheNodeModules6-
|
||||
# - name: Get yarn cache directory path
|
||||
# id: yarnCacheDirPath
|
||||
# if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }}
|
||||
# run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
# - name: Cache yarn directory
|
||||
# if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }}
|
||||
# uses: actions/cache@v2
|
||||
# with:
|
||||
# path: ${{ steps.yarnCacheDirPath.outputs.dir }}
|
||||
# key: ${{ runner.os }}-yarnCacheDir-${{ hashFiles('.yarnrc', 'remote/.yarnrc', '**/yarn.lock', '!**/node_modules/**/yarn.lock', '!**/.*/**/yarn.lock') }}
|
||||
# restore-keys: ${{ runner.os }}-yarnCacheDir-
|
||||
# - name: Execute yarn
|
||||
# if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }}
|
||||
# env:
|
||||
# PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
|
||||
# ELECTRON_SKIP_BINARY_DOWNLOAD: 1
|
||||
# run: yarn --frozen-lockfile --network-timeout 180000
|
||||
|
||||
# build-darwin-unit-tests:
|
||||
# name: "Build: macOS Unit Tests"
|
||||
# needs: [build-compile-core, build-compile-extensions, build-darwin-node-modules]
|
||||
# runs-on: macos-latest
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# steps:
|
||||
# - uses: actions/checkout@v2
|
||||
|
||||
# - uses: actions/setup-node@v2-beta
|
||||
# with:
|
||||
# node-version: 12
|
||||
|
||||
# - name: Restore cached node modules
|
||||
# id: cacheNodeModules
|
||||
# uses: actions/cache@v2
|
||||
# with:
|
||||
# path: '**/node_modules'
|
||||
# key: ${{ runner.os }}-cacheNodeModules6-${{ hashFiles('.yarnrc', 'remote/.yarnrc', '**/yarn.lock', '!**/node_modules/**/yarn.lock', '!**/.*/**/yarn.lock') }}
|
||||
# restore-keys: ${{ runner.os }}-cacheNodeModules6-
|
||||
|
||||
# - name: Restore compiled core code
|
||||
# id: cacheCompiledCoreCode
|
||||
# uses: actions/cache@v2
|
||||
# with:
|
||||
# path: |
|
||||
# out-build
|
||||
# out-vscode-min
|
||||
# key: cacheCompiledCoreCode-${{ github.sha }}
|
||||
|
||||
# - name: Restore compiled extensions code
|
||||
# id: cacheCompiledExtensionsCode
|
||||
# uses: actions/cache@v2
|
||||
# with:
|
||||
# path: .build
|
||||
# key: cacheCompiledExtensionsCode-${{ github.sha }}
|
||||
|
||||
# - name: Build VS Code
|
||||
# run: yarn gulp vscode-darwin-x64-min-ci
|
||||
|
||||
# - name: Download Electron
|
||||
# run: yarn electron x64
|
||||
|
||||
# - name: Run Unit Tests (Electron)
|
||||
# run: ./scripts/test.sh --build
|
||||
|
||||
# - name: Download Playwright
|
||||
# run: node ./node_modules/playwright/install.js
|
||||
|
||||
# - name: Run Unit Tests (Browser)
|
||||
# run: yarn test-browser --build --browser chromium --browser webkit --browser firefox
|
||||
|
||||
# build-darwin-integration-tests:
|
||||
# name: "Build: macOS Integration Tests"
|
||||
# needs: [build-compile-core, build-compile-extensions, build-darwin-node-modules]
|
||||
# runs-on: macos-latest
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# steps:
|
||||
# - uses: actions/checkout@v2
|
||||
|
||||
# - uses: actions/setup-node@v2-beta
|
||||
# with:
|
||||
# node-version: 12
|
||||
|
||||
# - name: Restore cached node modules
|
||||
# id: cacheNodeModules
|
||||
# uses: actions/cache@v2
|
||||
# with:
|
||||
# path: '**/node_modules'
|
||||
# key: ${{ runner.os }}-cacheNodeModules6-${{ hashFiles('.yarnrc', 'remote/.yarnrc', '**/yarn.lock', '!**/node_modules/**/yarn.lock', '!**/.*/**/yarn.lock') }}
|
||||
# restore-keys: ${{ runner.os }}-cacheNodeModules6-
|
||||
|
||||
# - name: Restore compiled core code
|
||||
# id: cacheCompiledCoreCode
|
||||
# uses: actions/cache@v2
|
||||
# with:
|
||||
# path: |
|
||||
# out-build
|
||||
# out-vscode-min
|
||||
# key: cacheCompiledCoreCode-${{ github.sha }}
|
||||
|
||||
# - name: Restore compiled extensions code
|
||||
# id: cacheCompiledExtensionsCode
|
||||
# uses: actions/cache@v2
|
||||
# with:
|
||||
# path: .build
|
||||
# key: cacheCompiledExtensionsCode-${{ github.sha }}
|
||||
|
||||
# - name: Build VS Code
|
||||
# run: yarn gulp vscode-darwin-x64-min-ci
|
||||
|
||||
# - name: Download Electron
|
||||
# run: yarn electron x64
|
||||
|
||||
# - name: Run Integration Tests (Electron)
|
||||
# run: ./scripts/test-integration.sh --build
|
||||
|
||||
# build-darwin-smoke-tests:
|
||||
# name: "Build: macOS Smoke Tests"
|
||||
# needs: [build-compile-core, build-compile-extensions, build-darwin-node-modules]
|
||||
# runs-on: macos-latest
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# steps:
|
||||
# - uses: actions/checkout@v2
|
||||
|
||||
# - uses: actions/setup-node@v2-beta
|
||||
# with:
|
||||
# node-version: 12
|
||||
|
||||
# - name: Restore cached node modules
|
||||
# id: cacheNodeModules
|
||||
# uses: actions/cache@v2
|
||||
# with:
|
||||
# path: '**/node_modules'
|
||||
# key: ${{ runner.os }}-cacheNodeModules6-${{ hashFiles('.yarnrc', 'remote/.yarnrc', '**/yarn.lock', '!**/node_modules/**/yarn.lock', '!**/.*/**/yarn.lock') }}
|
||||
# restore-keys: ${{ runner.os }}-cacheNodeModules6-
|
||||
|
||||
# - name: Restore compiled core code
|
||||
# id: cacheCompiledCoreCode
|
||||
# uses: actions/cache@v2
|
||||
# with:
|
||||
# path: |
|
||||
# out-build
|
||||
# out-vscode-min
|
||||
# key: cacheCompiledCoreCode-${{ github.sha }}
|
||||
|
||||
# - name: Restore compiled extensions code
|
||||
# id: cacheCompiledExtensionsCode
|
||||
# uses: actions/cache@v2
|
||||
# with:
|
||||
# path: .build
|
||||
# key: cacheCompiledExtensionsCode-${{ github.sha }}
|
||||
|
||||
# - name: Build VS Code
|
||||
# run: yarn gulp vscode-darwin-x64-min-ci
|
||||
|
||||
# - name: Run Smoke Tests (Electron)
|
||||
# continue-on-error: true
|
||||
# run: |
|
||||
# set -e
|
||||
# APP_ROOT=$(GITHUB_WORKSPACE)/VSCode-darwin-x64
|
||||
# APP_NAME="`ls $APP_ROOT | head -n 1`"
|
||||
# yarn smoketest --build "$APP_ROOT/$APP_NAME"
|
||||
- name: Run Integration Tests (Electron)
|
||||
run: .\scripts\test-integration.bat
|
||||
|
||||
linux:
|
||||
name: Linux
|
||||
|
@ -475,17 +86,20 @@ jobs:
|
|||
sudo update-rc.d xvfb defaults
|
||||
sudo service xvfb start
|
||||
|
||||
- uses: actions/setup-node@v2-beta
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 12
|
||||
|
||||
- name: Compute node modules cache key
|
||||
id: nodeModulesCacheKey
|
||||
run: echo "::set-output name=value::$(node build/azure-pipelines/common/computeNodeModulesCacheKey.js)"
|
||||
- name: Cache node modules
|
||||
id: cacheNodeModules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: "**/node_modules"
|
||||
key: ${{ runner.os }}-cacheNodeModules6-${{ hashFiles('.yarnrc', 'remote/.yarnrc', '**/yarn.lock', '!**/node_modules/**/yarn.lock', '!**/.*/**/yarn.lock') }}
|
||||
restore-keys: ${{ runner.os }}-cacheNodeModules6-
|
||||
key: ${{ runner.os }}-cacheNodeModules8-${{ steps.nodeModulesCacheKey.outputs.value }}
|
||||
restore-keys: ${{ runner.os }}-cacheNodeModules8-
|
||||
- name: Get yarn cache directory path
|
||||
id: yarnCacheDirPath
|
||||
if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }}
|
||||
|
@ -495,7 +109,7 @@ jobs:
|
|||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ steps.yarnCacheDirPath.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarnCacheDir-${{ hashFiles('.yarnrc', 'remote/.yarnrc', '**/yarn.lock', '!**/node_modules/**/yarn.lock', '!**/.*/**/yarn.lock') }}
|
||||
key: ${{ runner.os }}-yarnCacheDir-${{ steps.nodeModulesCacheKey.outputs.value }}
|
||||
restore-keys: ${{ runner.os }}-yarnCacheDir-
|
||||
- name: Execute yarn
|
||||
if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }}
|
||||
|
@ -519,65 +133,6 @@ jobs:
|
|||
id: electron-integration-tests
|
||||
run: DISPLAY=:10 ./scripts/test-integration.sh
|
||||
|
||||
windows:
|
||||
name: Windows
|
||||
runs-on: windows-latest
|
||||
env:
|
||||
CHILD_CONCURRENCY: "1"
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/setup-node@v2-beta
|
||||
with:
|
||||
node-version: 12
|
||||
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: "2.x"
|
||||
|
||||
- name: Cache node modules
|
||||
id: cacheNodeModules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
!**/Release/**/*.pdb
|
||||
!**/Release/**/*.ilk
|
||||
!**/Release/**/*.obj
|
||||
!**/Release/**/*.tlog
|
||||
key: ${{ runner.os }}-cacheNodeModules6-${{ hashFiles('.yarnrc', 'remote/.yarnrc', '**/yarn.lock', '!**/node_modules/**/yarn.lock', '!**/.*/**/yarn.lock') }}
|
||||
restore-keys: ${{ runner.os }}-cacheNodeModules6-
|
||||
- name: Get yarn cache directory path
|
||||
id: yarnCacheDirPath
|
||||
if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }}
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Cache yarn directory
|
||||
if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }}
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ steps.yarnCacheDirPath.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarnCacheDir-${{ hashFiles('.yarnrc', 'remote/.yarnrc', '**/yarn.lock', '!**/node_modules/**/yarn.lock', '!**/.*/**/yarn.lock') }}
|
||||
restore-keys: ${{ runner.os }}-yarnCacheDir-
|
||||
- name: Execute yarn
|
||||
if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }}
|
||||
env:
|
||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
|
||||
ELECTRON_SKIP_BINARY_DOWNLOAD: 1
|
||||
run: yarn --frozen-lockfile --network-timeout 180000
|
||||
|
||||
- name: Compile and Download
|
||||
run: yarn npm-run-all --max_old_space_size=4095 -lp compile "electron x64" playwright-install download-builtin-extensions
|
||||
|
||||
- name: Run Unit Tests (Electron)
|
||||
run: .\scripts\test.bat
|
||||
|
||||
- name: Run Unit Tests (Browser)
|
||||
run: yarn test-browser --browser chromium
|
||||
|
||||
- name: Run Integration Tests (Electron)
|
||||
run: .\scripts\test-integration.bat
|
||||
|
||||
darwin:
|
||||
name: macOS
|
||||
runs-on: macos-latest
|
||||
|
@ -586,17 +141,20 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/setup-node@v2-beta
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 12
|
||||
|
||||
- name: Compute node modules cache key
|
||||
id: nodeModulesCacheKey
|
||||
run: echo "::set-output name=value::$(node build/azure-pipelines/common/computeNodeModulesCacheKey.js)"
|
||||
- name: Cache node modules
|
||||
id: cacheNodeModules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: "**/node_modules"
|
||||
key: ${{ runner.os }}-cacheNodeModules6-${{ hashFiles('.yarnrc', 'remote/.yarnrc', '**/yarn.lock', '!**/node_modules/**/yarn.lock', '!**/.*/**/yarn.lock') }}
|
||||
restore-keys: ${{ runner.os }}-cacheNodeModules6-
|
||||
key: ${{ runner.os }}-cacheNodeModules8-${{ steps.nodeModulesCacheKey.outputs.value }}
|
||||
restore-keys: ${{ runner.os }}-cacheNodeModules8-
|
||||
- name: Get yarn cache directory path
|
||||
id: yarnCacheDirPath
|
||||
if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }}
|
||||
|
@ -606,7 +164,7 @@ jobs:
|
|||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ steps.yarnCacheDirPath.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarnCacheDir-${{ hashFiles('.yarnrc', 'remote/.yarnrc', '**/yarn.lock', '!**/node_modules/**/yarn.lock', '!**/.*/**/yarn.lock') }}
|
||||
key: ${{ runner.os }}-yarnCacheDir-${{ steps.nodeModulesCacheKey.outputs.value }}
|
||||
restore-keys: ${{ runner.os }}-yarnCacheDir-
|
||||
- name: Execute yarn
|
||||
if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }}
|
||||
|
@ -627,25 +185,39 @@ jobs:
|
|||
- name: Run Integration Tests (Electron)
|
||||
run: DISPLAY=:10 ./scripts/test-integration.sh
|
||||
|
||||
monaco:
|
||||
name: Monaco Editor
|
||||
hygiene:
|
||||
name: Hygiene, Layering and Monaco Editor
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/setup-node@v2-beta
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 12
|
||||
|
||||
- name: Compute node modules cache key
|
||||
id: nodeModulesCacheKey
|
||||
run: echo "::set-output name=value::$(node build/azure-pipelines/common/computeNodeModulesCacheKey.js)"
|
||||
- name: Cache node modules
|
||||
id: cacheNodeModules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: "**/node_modules"
|
||||
key: ${{ runner.os }}-cacheNodeModules6-${{ hashFiles('.yarnrc', 'remote/.yarnrc', '**/yarn.lock', '!**/node_modules/**/yarn.lock', '!**/.*/**/yarn.lock') }}
|
||||
restore-keys: ${{ runner.os }}-cacheNodeModules6-
|
||||
key: ${{ runner.os }}-cacheNodeModules8-${{ steps.nodeModulesCacheKey.outputs.value }}
|
||||
restore-keys: ${{ runner.os }}-cacheNodeModules8-
|
||||
- name: Get yarn cache directory path
|
||||
id: yarnCacheDirPath
|
||||
if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }}
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Cache yarn directory
|
||||
if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }}
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ steps.yarnCacheDirPath.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarnCacheDir-${{ steps.nodeModulesCacheKey.outputs.value }}
|
||||
restore-keys: ${{ runner.os }}-yarnCacheDir-
|
||||
- name: Execute yarn
|
||||
if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }}
|
||||
env:
|
||||
|
@ -653,8 +225,42 @@ jobs:
|
|||
ELECTRON_SKIP_BINARY_DOWNLOAD: 1
|
||||
run: yarn --frozen-lockfile --network-timeout 180000
|
||||
|
||||
- name: Run Hygiene Checks
|
||||
run: yarn gulp hygiene
|
||||
|
||||
- name: Run Valid Layers Checks
|
||||
run: yarn valid-layers-check
|
||||
|
||||
- name: Run Monaco Editor Checks
|
||||
run: yarn monaco-compile-check
|
||||
|
||||
- name: Editor Distro & ESM Bundle
|
||||
run: yarn gulp editor-esm-bundle
|
||||
|
||||
- name: Typings validation prep
|
||||
run: |
|
||||
mkdir typings-test
|
||||
|
||||
- name: Typings validation
|
||||
working-directory: ./typings-test
|
||||
run: |
|
||||
yarn init -yp
|
||||
../node_modules/.bin/tsc --init
|
||||
echo "import '../out-monaco-editor-core';" > a.ts
|
||||
../node_modules/.bin/tsc --noEmit
|
||||
|
||||
- name: Webpack Editor
|
||||
working-directory: ./test/monaco
|
||||
run: yarn run bundle
|
||||
|
||||
- name: Compile Editor Tests
|
||||
working-directory: ./test/monaco
|
||||
run: yarn run compile
|
||||
|
||||
- name: Download Playwright
|
||||
run: yarn playwright-install
|
||||
|
||||
- name: Run Editor Tests
|
||||
timeout-minutes: 5
|
||||
working-directory: ./test/monaco
|
||||
run: yarn test
|
||||
|
|
22
.gitignore
vendored
22
.gitignore
vendored
|
@ -5,26 +5,8 @@ Thumbs.db
|
|||
node_modules/
|
||||
.build/
|
||||
extensions/**/dist/
|
||||
out/
|
||||
out-build/
|
||||
out-editor/
|
||||
out-editor-src/
|
||||
out-editor-build/
|
||||
out-editor-esm/
|
||||
out-editor-esm-bundle/
|
||||
out-editor-min/
|
||||
out-monaco-editor-core/
|
||||
out-vscode/
|
||||
out-vscode-min/
|
||||
out-vscode-reh/
|
||||
out-vscode-reh-min/
|
||||
out-vscode-reh-pkg/
|
||||
out-vscode-reh-web/
|
||||
out-vscode-reh-web-min/
|
||||
out-vscode-reh-web-pkg/
|
||||
out-vscode-web/
|
||||
out-vscode-web-min/
|
||||
out-vscode-web-pkg/
|
||||
/out*/
|
||||
/extensions/**/out/
|
||||
src/vs/server
|
||||
resources/server
|
||||
build/node_modules
|
||||
|
|
3
.vscode/extensions.json
vendored
3
.vscode/extensions.json
vendored
|
@ -3,7 +3,6 @@
|
|||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"EditorConfig.EditorConfig",
|
||||
"msjsdiag.debugger-for-chrome"
|
||||
"EditorConfig.EditorConfig"
|
||||
]
|
||||
}
|
||||
|
|
22
.vscode/launch.json
vendored
22
.vscode/launch.json
vendored
|
@ -203,7 +203,7 @@
|
|||
{
|
||||
"type": "pwa-chrome",
|
||||
"request": "launch",
|
||||
"name": "Launch VS Code",
|
||||
"name": "Launch VS Code Internal",
|
||||
"windows": {
|
||||
"runtimeExecutable": "${workspaceFolder}/scripts/code.bat"
|
||||
},
|
||||
|
@ -235,7 +235,9 @@
|
|||
"${workspaceFolder}/out/**/*.js"
|
||||
],
|
||||
"browserLaunchLocation": "workspace",
|
||||
"preLaunchTask": "Ensure Prelaunch Dependencies",
|
||||
"presentation": {
|
||||
"hidden": true,
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
|
@ -472,7 +474,7 @@
|
|||
"name": "VS Code",
|
||||
"stopAll": true,
|
||||
"configurations": [
|
||||
"Launch VS Code",
|
||||
"Launch VS Code Internal",
|
||||
"Attach to Main Process",
|
||||
"Attach to Extension Host",
|
||||
"Attach to Shared Process",
|
||||
|
@ -486,7 +488,7 @@
|
|||
{
|
||||
"name": "Search and Renderer processes",
|
||||
"configurations": [
|
||||
"Launch VS Code",
|
||||
"Launch VS Code Internal",
|
||||
"Attach to Search Process"
|
||||
],
|
||||
"presentation": {
|
||||
|
@ -497,7 +499,7 @@
|
|||
{
|
||||
"name": "Renderer and Extension Host processes",
|
||||
"configurations": [
|
||||
"Launch VS Code",
|
||||
"Launch VS Code Internal",
|
||||
"Attach to Extension Host"
|
||||
],
|
||||
"presentation": {
|
||||
|
@ -526,6 +528,14 @@
|
|||
"group": "1_vscode",
|
||||
"order": 2
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Launch VS Code",
|
||||
"stopAll": true,
|
||||
"configurations": [
|
||||
"Launch VS Code Internal",
|
||||
],
|
||||
"preLaunchTask": "Ensure Prelaunch Dependencies"
|
||||
},
|
||||
]
|
||||
}
|
||||
|
|
18
.vscode/notebooks/my-work.github-issues
vendored
18
.vscode/notebooks/my-work.github-issues
vendored
|
@ -83,6 +83,24 @@
|
|||
"value": "$repos assignee:@me is:open milestone:\"Backlog Candidates\"",
|
||||
"editable": false
|
||||
},
|
||||
{
|
||||
"kind": 1,
|
||||
"language": "markdown",
|
||||
"value": "### Personal Inbox\n",
|
||||
"editable": true
|
||||
},
|
||||
{
|
||||
"kind": 1,
|
||||
"language": "markdown",
|
||||
"value": "\n#### Missing Type label",
|
||||
"editable": true
|
||||
},
|
||||
{
|
||||
"kind": 2,
|
||||
"language": "github-issues",
|
||||
"value": "$repos assignee:@me is:open type:issue -label:bug -label:\"needs more info\" -label:feature-request -label:under-discussion -label:debt -label:plan-item -label:upstream",
|
||||
"editable": true
|
||||
},
|
||||
{
|
||||
"kind": 1,
|
||||
"language": "markdown",
|
||||
|
|
|
@ -1 +1 @@
|
|||
2020-12-17T07:59:24.060Z
|
||||
2021-01-07T09:53:10.404Z
|
||||
|
|
25
build/azure-pipelines/common/computeNodeModulesCacheKey.js
Normal file
25
build/azure-pipelines/common/computeNodeModulesCacheKey.js
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const crypto = require("crypto");
|
||||
const { dirs } = require('../../npm/dirs');
|
||||
const ROOT = path.join(__dirname, '../../../');
|
||||
const shasum = crypto.createHash('sha1');
|
||||
shasum.update(fs.readFileSync(path.join(ROOT, 'build/.cachesalt')));
|
||||
shasum.update(fs.readFileSync(path.join(ROOT, '.yarnrc')));
|
||||
shasum.update(fs.readFileSync(path.join(ROOT, 'remote/.yarnrc')));
|
||||
// Add `yarn.lock` files
|
||||
for (let dir of dirs) {
|
||||
const yarnLockPath = path.join(ROOT, dir, 'yarn.lock');
|
||||
shasum.update(fs.readFileSync(yarnLockPath));
|
||||
}
|
||||
// Add any other command line arguments
|
||||
for (let i = 2; i < process.argv.length; i++) {
|
||||
shasum.update(process.argv[i]);
|
||||
}
|
||||
process.stdout.write(shasum.digest('hex'));
|
32
build/azure-pipelines/common/computeNodeModulesCacheKey.ts
Normal file
32
build/azure-pipelines/common/computeNodeModulesCacheKey.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as crypto from 'crypto';
|
||||
const { dirs } = require('../../npm/dirs');
|
||||
|
||||
const ROOT = path.join(__dirname, '../../../');
|
||||
|
||||
const shasum = crypto.createHash('sha1');
|
||||
|
||||
shasum.update(fs.readFileSync(path.join(ROOT, 'build/.cachesalt')));
|
||||
shasum.update(fs.readFileSync(path.join(ROOT, '.yarnrc')));
|
||||
shasum.update(fs.readFileSync(path.join(ROOT, 'remote/.yarnrc')));
|
||||
|
||||
// Add `yarn.lock` files
|
||||
for (let dir of dirs) {
|
||||
const yarnLockPath = path.join(ROOT, dir, 'yarn.lock');
|
||||
shasum.update(fs.readFileSync(yarnLockPath));
|
||||
}
|
||||
|
||||
// Add any other command line arguments
|
||||
for (let i = 2; i < process.argv.length; i++) {
|
||||
shasum.update(process.argv[i]);
|
||||
}
|
||||
|
||||
process.stdout.write(shasum.digest('hex'));
|
|
@ -10,8 +10,8 @@ git clone --depth 1 https://github.com/microsoft/vscode-node-debug2.git
|
|||
git clone --depth 1 https://github.com/microsoft/vscode-node-debug.git
|
||||
git clone --depth 1 https://github.com/microsoft/vscode-html-languageservice.git
|
||||
git clone --depth 1 https://github.com/microsoft/vscode-json-languageservice.git
|
||||
node $BUILD_SOURCESDIRECTORY/build/node_modules/.bin/vscode-telemetry-extractor --sourceDir $BUILD_SOURCESDIRECTORY --excludedDir $BUILD_SOURCESDIRECTORY/extensions --outputDir . --applyEndpoints
|
||||
node $BUILD_SOURCESDIRECTORY/build/node_modules/.bin/vscode-telemetry-extractor --config $BUILD_SOURCESDIRECTORY/build/azure-pipelines/common/telemetry-config.json -o .
|
||||
node $BUILD_SOURCESDIRECTORY/node_modules/.bin/vscode-telemetry-extractor --sourceDir $BUILD_SOURCESDIRECTORY --excludedDir $BUILD_SOURCESDIRECTORY/extensions --outputDir . --applyEndpoints
|
||||
node $BUILD_SOURCESDIRECTORY/node_modules/.bin/vscode-telemetry-extractor --config $BUILD_SOURCESDIRECTORY/build/azure-pipelines/common/telemetry-config.json -o .
|
||||
mkdir -p $BUILD_SOURCESDIRECTORY/.build/telemetry
|
||||
mv declarations-resolved.json $BUILD_SOURCESDIRECTORY/.build/telemetry/telemetry-core.json
|
||||
mv config-resolved.json $BUILD_SOURCESDIRECTORY/.build/telemetry/telemetry-extensions.json
|
||||
|
|
40
build/azure-pipelines/common/listNodeModules.js
Normal file
40
build/azure-pipelines/common/listNodeModules.js
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
if (process.argv.length !== 3) {
|
||||
console.error('Usage: node listNodeModules.js OUTPUT_FILE');
|
||||
process.exit(-1);
|
||||
}
|
||||
const ROOT = path.join(__dirname, '../../../');
|
||||
function findNodeModulesFiles(location, inNodeModules, result) {
|
||||
const entries = fs.readdirSync(path.join(ROOT, location));
|
||||
for (const entry of entries) {
|
||||
const entryPath = `${location}/${entry}`;
|
||||
if (/(^\/out)|(^\/src$)|(^\/.git$)|(^\/.build$)/.test(entryPath)) {
|
||||
continue;
|
||||
}
|
||||
let stat;
|
||||
try {
|
||||
stat = fs.statSync(path.join(ROOT, entryPath));
|
||||
}
|
||||
catch (err) {
|
||||
continue;
|
||||
}
|
||||
if (stat.isDirectory()) {
|
||||
findNodeModulesFiles(entryPath, inNodeModules || (entry === 'node_modules'), result);
|
||||
}
|
||||
else {
|
||||
if (inNodeModules) {
|
||||
result.push(entryPath.substr(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const result = [];
|
||||
findNodeModulesFiles('', false, result);
|
||||
fs.writeFileSync(process.argv[2], result.join('\n') + '\n');
|
46
build/azure-pipelines/common/listNodeModules.ts
Normal file
46
build/azure-pipelines/common/listNodeModules.ts
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
if (process.argv.length !== 3) {
|
||||
console.error('Usage: node listNodeModules.js OUTPUT_FILE');
|
||||
process.exit(-1);
|
||||
}
|
||||
|
||||
const ROOT = path.join(__dirname, '../../../');
|
||||
|
||||
function findNodeModulesFiles(location: string, inNodeModules: boolean, result: string[]) {
|
||||
const entries = fs.readdirSync(path.join(ROOT, location));
|
||||
for (const entry of entries) {
|
||||
const entryPath = `${location}/${entry}`;
|
||||
|
||||
if (/(^\/out)|(^\/src$)|(^\/.git$)|(^\/.build$)/.test(entryPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let stat: fs.Stats;
|
||||
try {
|
||||
stat = fs.statSync(path.join(ROOT, entryPath));
|
||||
} catch (err) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
findNodeModulesFiles(entryPath, inNodeModules || (entry === 'node_modules'), result);
|
||||
} else {
|
||||
if (inNodeModules) {
|
||||
result.push(entryPath.substr(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const result: string[] = [];
|
||||
findNodeModulesFiles('', false, result);
|
||||
fs.writeFileSync(process.argv[2], result.join('\n') + '\n');
|
|
@ -7,9 +7,14 @@ steps:
|
|||
inputs:
|
||||
versionSpec: "1.x"
|
||||
|
||||
- script: |
|
||||
mkdir -p .build
|
||||
node build/azure-pipelines/common/computeNodeModulesCacheKey.js > .build/yarnlockhash
|
||||
displayName: Prepare yarn cache flags
|
||||
|
||||
- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1
|
||||
inputs:
|
||||
keyfile: ".yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock"
|
||||
keyfile: ".build/yarnlockhash"
|
||||
targetfolder: "**/node_modules, !**/node_modules/**/node_modules"
|
||||
vstsFeed: "vscode-build-cache"
|
||||
|
||||
|
@ -20,7 +25,7 @@ steps:
|
|||
|
||||
- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1
|
||||
inputs:
|
||||
keyfile: ".yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock"
|
||||
keyfile: ".build/yarnlockhash"
|
||||
targetfolder: "**/node_modules, !**/node_modules/**/node_modules"
|
||||
vstsFeed: "vscode-build-cache"
|
||||
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
|
||||
|
|
|
@ -48,27 +48,35 @@ steps:
|
|||
displayName: Merge distro
|
||||
|
||||
- script: |
|
||||
npx https://aka.ms/enablesecurefeed standAlone
|
||||
displayName: Switch to Terrapin packages
|
||||
timeoutInMinutes: 5
|
||||
condition: and(succeeded(), eq(variables['ENABLE_TERRAPIN'], 'true'))
|
||||
|
||||
- script: |
|
||||
echo -n $(VSCODE_ARCH) > .build/arch
|
||||
echo -n $ENABLE_TERRAPIN > .build/terrapin
|
||||
mkdir -p .build
|
||||
node build/azure-pipelines/common/computeNodeModulesCacheKey.js $VSCODE_ARCH $ENABLE_TERRAPIN > .build/yarnlockhash
|
||||
displayName: Prepare yarn cache flags
|
||||
|
||||
- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1
|
||||
- task: Cache@2
|
||||
inputs:
|
||||
keyfile: ".build/arch, .build/terrapin, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock"
|
||||
targetfolder: "**/node_modules, !**/node_modules/**/node_modules"
|
||||
vstsFeed: "npm-vscode"
|
||||
key: 'nodeModules | $(Agent.OS) | .build/yarnlockhash'
|
||||
path: .build/node_modules_cache
|
||||
cacheHitVar: NODE_MODULES_RESTORED
|
||||
displayName: Restore node_modules cache
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
tar -xzf .build/node_modules_cache/cache.tgz
|
||||
condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true'))
|
||||
displayName: Extract node_modules cache
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
npm install -g node-gyp@latest
|
||||
node-gyp --version
|
||||
displayName: Update node-gyp
|
||||
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
|
||||
|
||||
- script: |
|
||||
npx https://aka.ms/enablesecurefeed standAlone
|
||||
timeoutInMinutes: 5
|
||||
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true'))
|
||||
displayName: Switch to Terrapin packages
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
|
@ -88,14 +96,15 @@ steps:
|
|||
ELECTRON_SKIP_BINARY_DOWNLOAD: 1
|
||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
|
||||
displayName: Install dependencies
|
||||
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
|
||||
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
|
||||
|
||||
- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1
|
||||
inputs:
|
||||
keyfile: ".build/arch, .build/terrapin, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock"
|
||||
targetfolder: "**/node_modules, !**/node_modules/**/node_modules"
|
||||
vstsFeed: "npm-vscode"
|
||||
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
|
||||
- script: |
|
||||
set -e
|
||||
node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt
|
||||
mkdir -p .build/node_modules_cache
|
||||
tar -czf .build/node_modules_cache/cache.tgz --files-from .build/node_modules_list.txt
|
||||
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
|
||||
displayName: Create node_modules archive
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
|
@ -108,7 +117,7 @@ steps:
|
|||
cd ./node_modules/keytar
|
||||
node-gyp rebuild
|
||||
displayName: Rebuild native modules for ARM64
|
||||
condition: eq(variables['VSCODE_ARCH'], 'arm64')
|
||||
condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'arm64'))
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
|
@ -166,7 +175,7 @@ steps:
|
|||
set -e
|
||||
yarn --cwd test/integration/browser compile
|
||||
displayName: Compile integration tests
|
||||
condition: and(succeeded(), eq(variables['CacheRestored'], 'true'), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
||||
condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
||||
|
||||
- script: |
|
||||
# Figure out the full absolute path of the product we just built
|
||||
|
@ -205,7 +214,7 @@ steps:
|
|||
set -e
|
||||
yarn --cwd test/smoke compile
|
||||
displayName: Compile smoke tests
|
||||
condition: and(succeeded(), eq(variables['CacheRestored'], 'true'), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
||||
condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
|
@ -239,7 +248,7 @@ steps:
|
|||
inputs:
|
||||
testResultsFiles: "*-results.xml"
|
||||
searchFolder: "$(Build.ArtifactStagingDirectory)/test-results"
|
||||
condition: succeededOrFailed()
|
||||
condition: and(succeededOrFailed(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
|
|
|
@ -16,9 +16,14 @@ steps:
|
|||
inputs:
|
||||
versionSpec: "1.x"
|
||||
|
||||
- script: |
|
||||
mkdir -p .build
|
||||
node build/azure-pipelines/common/computeNodeModulesCacheKey.js > .build/yarnlockhash
|
||||
displayName: Prepare yarn cache flags
|
||||
|
||||
- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1
|
||||
inputs:
|
||||
keyfile: ".yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock"
|
||||
keyfile: ".build/yarnlockhash"
|
||||
targetfolder: "**/node_modules, !**/node_modules/**/node_modules"
|
||||
vstsFeed: "vscode-build-cache"
|
||||
|
||||
|
@ -29,7 +34,7 @@ steps:
|
|||
|
||||
- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1
|
||||
inputs:
|
||||
keyfile: ".yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock"
|
||||
keyfile: ".build/yarnlockhash"
|
||||
targetfolder: "**/node_modules, !**/node_modules/**/node_modules"
|
||||
vstsFeed: "vscode-build-cache"
|
||||
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
|
||||
|
|
|
@ -51,21 +51,28 @@ steps:
|
|||
displayName: Merge distro
|
||||
|
||||
- script: |
|
||||
npx https://aka.ms/enablesecurefeed standAlone
|
||||
displayName: Switch to Terrapin packages
|
||||
timeoutInMinutes: 5
|
||||
condition: and(succeeded(), eq(variables['ENABLE_TERRAPIN'], 'true'))
|
||||
|
||||
- script: |
|
||||
echo -n "alpine" > .build/arch
|
||||
echo -n $ENABLE_TERRAPIN > .build/terrapin
|
||||
mkdir -p .build
|
||||
node build/azure-pipelines/common/computeNodeModulesCacheKey.js "alpine" $ENABLE_TERRAPIN > .build/yarnlockhash
|
||||
displayName: Prepare yarn cache flags
|
||||
|
||||
- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1
|
||||
- task: Cache@2
|
||||
inputs:
|
||||
keyfile: ".build/arch, .build/terrapin, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock"
|
||||
targetfolder: "**/node_modules, !**/node_modules/**/node_modules"
|
||||
vstsFeed: "npm-vscode"
|
||||
key: 'nodeModules | $(Agent.OS) | .build/yarnlockhash'
|
||||
path: .build/node_modules_cache
|
||||
cacheHitVar: NODE_MODULES_RESTORED
|
||||
displayName: Restore node_modules cache
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
tar -xzf .build/node_modules_cache/cache.tgz
|
||||
condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true'))
|
||||
displayName: Extract node_modules cache
|
||||
|
||||
- script: |
|
||||
npx https://aka.ms/enablesecurefeed standAlone
|
||||
timeoutInMinutes: 5
|
||||
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true'))
|
||||
displayName: Switch to Terrapin packages
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
|
@ -81,14 +88,15 @@ steps:
|
|||
ELECTRON_SKIP_BINARY_DOWNLOAD: 1
|
||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
|
||||
displayName: Install dependencies
|
||||
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
|
||||
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
|
||||
|
||||
- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1
|
||||
inputs:
|
||||
keyfile: ".build/arch, .build/terrapin, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock"
|
||||
targetfolder: "**/node_modules, !**/node_modules/**/node_modules"
|
||||
vstsFeed: "npm-vscode"
|
||||
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
|
||||
- script: |
|
||||
set -e
|
||||
node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt
|
||||
mkdir -p .build/node_modules_cache
|
||||
tar -czf .build/node_modules_cache/cache.tgz --files-from .build/node_modules_list.txt
|
||||
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
|
||||
displayName: Create node_modules archive
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
|
|
|
@ -42,28 +42,35 @@ steps:
|
|||
displayName: Merge distro
|
||||
|
||||
- script: |
|
||||
npx https://aka.ms/enablesecurefeed standAlone
|
||||
displayName: Switch to Terrapin packages
|
||||
timeoutInMinutes: 5
|
||||
condition: and(succeeded(), eq(variables['ENABLE_TERRAPIN'], 'true'))
|
||||
|
||||
- script: |
|
||||
echo -n $(VSCODE_ARCH) > .build/arch
|
||||
echo -n $ENABLE_TERRAPIN > .build/terrapin
|
||||
mkdir -p .build
|
||||
node build/azure-pipelines/common/computeNodeModulesCacheKey.js $VSCODE_ARCH $ENABLE_TERRAPIN > .build/yarnlockhash
|
||||
displayName: Prepare yarn cache flags
|
||||
|
||||
- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1
|
||||
- task: Cache@2
|
||||
inputs:
|
||||
keyfile: ".build/arch, .build/terrapin, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock"
|
||||
targetfolder: "**/node_modules, !**/node_modules/**/node_modules"
|
||||
vstsFeed: "npm-vscode"
|
||||
key: 'nodeModules | $(Agent.OS) | .build/yarnlockhash'
|
||||
path: .build/node_modules_cache
|
||||
cacheHitVar: NODE_MODULES_RESTORED
|
||||
displayName: Restore node_modules cache
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
tar -xzf .build/node_modules_cache/cache.tgz
|
||||
condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true'))
|
||||
displayName: Extract node_modules cache
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
npm install -g node-gyp@latest
|
||||
node-gyp --version
|
||||
displayName: Update node-gyp
|
||||
condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'))
|
||||
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['VSCODE_ARCH'], 'x64'))
|
||||
|
||||
- script: |
|
||||
npx https://aka.ms/enablesecurefeed standAlone
|
||||
timeoutInMinutes: 5
|
||||
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true'))
|
||||
displayName: Switch to Terrapin packages
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
|
@ -92,14 +99,15 @@ steps:
|
|||
ELECTRON_SKIP_BINARY_DOWNLOAD: 1
|
||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
|
||||
displayName: Install dependencies
|
||||
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
|
||||
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
|
||||
|
||||
- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1
|
||||
inputs:
|
||||
keyfile: ".build/arch, .build/terrapin, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock"
|
||||
targetfolder: "**/node_modules, !**/node_modules/**/node_modules"
|
||||
vstsFeed: "npm-vscode"
|
||||
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
|
||||
- script: |
|
||||
set -e
|
||||
node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt
|
||||
mkdir -p .build/node_modules_cache
|
||||
tar -czf .build/node_modules_cache/cache.tgz --files-from .build/node_modules_list.txt
|
||||
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
|
||||
displayName: Create node_modules archive
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
|
@ -144,7 +152,7 @@ steps:
|
|||
set -e
|
||||
yarn --cwd test/integration/browser compile
|
||||
displayName: Compile integration tests
|
||||
condition: and(succeeded(), eq(variables['CacheRestored'], 'true'), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
||||
condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
||||
|
||||
- script: |
|
||||
# Figure out the full absolute path of the product we just built
|
||||
|
|
|
@ -7,9 +7,93 @@ schedules:
|
|||
include:
|
||||
- master
|
||||
|
||||
parameters:
|
||||
- name: VSCODE_QUALITY
|
||||
displayName: Quality
|
||||
type: string
|
||||
default: insider
|
||||
values:
|
||||
- exploration
|
||||
- insider
|
||||
- stable
|
||||
- name: ENABLE_TERRAPIN
|
||||
displayName: "Enable Terrapin"
|
||||
type: boolean
|
||||
default: true
|
||||
- name: VSCODE_BUILD_WIN32
|
||||
displayName: "🎯 Windows x64"
|
||||
type: boolean
|
||||
default: true
|
||||
- name: VSCODE_BUILD_WIN32_32BIT
|
||||
displayName: "🎯 Windows ia32"
|
||||
type: boolean
|
||||
default: true
|
||||
- name: VSCODE_BUILD_WIN32_ARM64
|
||||
displayName: "🎯 Windows arm64"
|
||||
type: boolean
|
||||
default: true
|
||||
- name: VSCODE_BUILD_LINUX
|
||||
displayName: "🎯 Linux x64"
|
||||
type: boolean
|
||||
default: true
|
||||
- name: VSCODE_BUILD_LINUX_ARM64
|
||||
displayName: "🎯 Linux arm64"
|
||||
type: boolean
|
||||
default: true
|
||||
- name: VSCODE_BUILD_LINUX_ARMHF
|
||||
displayName: "🎯 Linux armhf"
|
||||
type: boolean
|
||||
default: true
|
||||
- name: VSCODE_BUILD_LINUX_ALPINE
|
||||
displayName: "🎯 Alpine Linux"
|
||||
type: boolean
|
||||
default: true
|
||||
- name: VSCODE_BUILD_MACOS
|
||||
displayName: "🎯 macOS x64"
|
||||
type: boolean
|
||||
default: true
|
||||
- name: VSCODE_BUILD_MACOS_ARM64
|
||||
displayName: "🎯 macOS arm64"
|
||||
type: boolean
|
||||
default: true
|
||||
- name: VSCODE_BUILD_WEB
|
||||
displayName: "🎯 Web"
|
||||
type: boolean
|
||||
default: true
|
||||
- name: VSCODE_PUBLISH
|
||||
displayName: "Publish to builds.code.visualstudio.com"
|
||||
type: boolean
|
||||
default: true
|
||||
- name: VSCODE_RELEASE
|
||||
displayName: "Release build if successful"
|
||||
type: boolean
|
||||
default: false
|
||||
- name: VSCODE_COMPILE_ONLY
|
||||
displayName: "Run Compile stage exclusively"
|
||||
type: boolean
|
||||
default: false
|
||||
- name: VSCODE_STEP_ON_IT
|
||||
displayName: "Skip tests"
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
variables:
|
||||
${{ if and(eq(variables['VSCODE_PUBLISH'], ''), or(eq(variables['Build.Reason'], 'IndividualCI'), eq(variables['Build.Reason'], 'BatchedCI'))) }}:
|
||||
VSCODE_PUBLISH: "false"
|
||||
- name: ENABLE_TERRAPIN
|
||||
value: ${{ eq(parameters.ENABLE_TERRAPIN, true) }}
|
||||
- name: VSCODE_BUILD_STAGE_WINDOWS
|
||||
value: ${{ or(eq(parameters.VSCODE_BUILD_WIN32, true), eq(parameters.VSCODE_BUILD_WIN32_32BIT, true), eq(parameters.VSCODE_BUILD_WIN32_ARM64, true)) }}
|
||||
- name: VSCODE_BUILD_STAGE_LINUX
|
||||
value: ${{ or(eq(parameters.VSCODE_BUILD_LINUX, true), eq(parameters.VSCODE_BUILD_LINUX_ARMHF, true), eq(parameters.VSCODE_BUILD_LINUX_ARM64, true), eq(parameters.VSCODE_BUILD_LINUX_ALPINE, true), eq(parameters.VSCODE_BUILD_WEB, true)) }}
|
||||
- name: VSCODE_BUILD_STAGE_MACOS
|
||||
value: ${{ or(eq(parameters.VSCODE_BUILD_MACOS, true), eq(parameters.VSCODE_BUILD_MACOS_ARM64, true)) }}
|
||||
- name: VSCODE_CIBUILD
|
||||
value: ${{ in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI') }}
|
||||
- name: VSCODE_PUBLISH
|
||||
value: ${{ and(eq(parameters.VSCODE_PUBLISH, true), eq(variables.VSCODE_CIBUILD, false)) }}
|
||||
- name: VSCODE_SCHEDULEDBUILD
|
||||
value: ${{ eq(variables['Build.Reason'], 'Schedule') }}
|
||||
- name: VSCODE_STEP_ON_IT
|
||||
value: ${{ eq(parameters.VSCODE_STEP_ON_IT, true) }}
|
||||
|
||||
resources:
|
||||
containers:
|
||||
|
@ -30,166 +114,165 @@ stages:
|
|||
- stage: Compile
|
||||
jobs:
|
||||
- job: Compile
|
||||
pool:
|
||||
vmImage: "Ubuntu-18.04"
|
||||
container: vscode-x64
|
||||
pool: compile
|
||||
variables:
|
||||
VSCODE_ARCH: x64
|
||||
steps:
|
||||
- template: product-compile.yml
|
||||
- job: Hygiene
|
||||
pool:
|
||||
vmImage: "Ubuntu-18.04"
|
||||
container: vscode-x64
|
||||
variables:
|
||||
VSCODE_ARCH: x64
|
||||
steps:
|
||||
- template: product-hygiene.yml
|
||||
|
||||
- stage: Windows
|
||||
dependsOn:
|
||||
- Compile
|
||||
condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'))
|
||||
pool:
|
||||
vmImage: VS2017-Win2016
|
||||
jobs:
|
||||
- job: Windows
|
||||
condition: and(succeeded(), eq(variables['VSCODE_BUILD_WIN32'], 'true'))
|
||||
timeoutInMinutes: 90
|
||||
variables:
|
||||
VSCODE_ARCH: x64
|
||||
steps:
|
||||
- template: win32/product-build-win32.yml
|
||||
- ${{ if and(eq(parameters.VSCODE_COMPILE_ONLY, false), eq(variables['VSCODE_BUILD_STAGE_WINDOWS'], true)) }}:
|
||||
- stage: Windows
|
||||
dependsOn:
|
||||
- Compile
|
||||
pool:
|
||||
vmImage: VS2017-Win2016
|
||||
jobs:
|
||||
|
||||
- job: Windows32
|
||||
condition: and(succeeded(), eq(variables['VSCODE_BUILD_WIN32_32BIT'], 'true'), ne(variables['VSCODE_PUBLISH'], 'false'))
|
||||
timeoutInMinutes: 90
|
||||
variables:
|
||||
VSCODE_ARCH: ia32
|
||||
steps:
|
||||
- template: win32/product-build-win32.yml
|
||||
- ${{ if eq(parameters.VSCODE_BUILD_WIN32, true) }}:
|
||||
- job: Windows
|
||||
timeoutInMinutes: 90
|
||||
variables:
|
||||
VSCODE_ARCH: x64
|
||||
steps:
|
||||
- template: win32/product-build-win32.yml
|
||||
|
||||
- job: WindowsARM64
|
||||
condition: and(succeeded(), eq(variables['VSCODE_BUILD_WIN32_ARM64'], 'true'), ne(variables['VSCODE_PUBLISH'], 'false'))
|
||||
timeoutInMinutes: 90
|
||||
variables:
|
||||
VSCODE_ARCH: arm64
|
||||
steps:
|
||||
- template: win32/product-build-win32.yml
|
||||
- ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_WIN32_32BIT, true)) }}:
|
||||
- job: Windows32
|
||||
timeoutInMinutes: 90
|
||||
variables:
|
||||
VSCODE_ARCH: ia32
|
||||
steps:
|
||||
- template: win32/product-build-win32.yml
|
||||
|
||||
- stage: Linux
|
||||
dependsOn:
|
||||
- Compile
|
||||
condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'))
|
||||
pool:
|
||||
vmImage: "Ubuntu-18.04"
|
||||
jobs:
|
||||
- job: Linux
|
||||
condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX'], 'true'))
|
||||
container: vscode-x64
|
||||
variables:
|
||||
VSCODE_ARCH: x64
|
||||
NPM_ARCH: x64
|
||||
steps:
|
||||
- template: linux/product-build-linux.yml
|
||||
- ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_WIN32_ARM64, true)) }}:
|
||||
- job: WindowsARM64
|
||||
timeoutInMinutes: 90
|
||||
variables:
|
||||
VSCODE_ARCH: arm64
|
||||
steps:
|
||||
- template: win32/product-build-win32.yml
|
||||
|
||||
- job: LinuxSnap
|
||||
dependsOn:
|
||||
- ${{ if and(eq(parameters.VSCODE_COMPILE_ONLY, false), eq(variables['VSCODE_BUILD_STAGE_LINUX'], true)) }}:
|
||||
- stage: Linux
|
||||
dependsOn:
|
||||
- Compile
|
||||
pool:
|
||||
vmImage: "Ubuntu-18.04"
|
||||
jobs:
|
||||
|
||||
- ${{ if eq(parameters.VSCODE_BUILD_LINUX, true) }}:
|
||||
- job: Linux
|
||||
container: vscode-x64
|
||||
variables:
|
||||
VSCODE_ARCH: x64
|
||||
NPM_ARCH: x64
|
||||
steps:
|
||||
- template: linux/product-build-linux.yml
|
||||
|
||||
- ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_LINUX, true)) }}:
|
||||
- job: LinuxSnap
|
||||
dependsOn:
|
||||
- Linux
|
||||
container: snapcraft
|
||||
variables:
|
||||
VSCODE_ARCH: x64
|
||||
steps:
|
||||
- template: linux/snap-build-linux.yml
|
||||
|
||||
- ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_LINUX_ARMHF, true)) }}:
|
||||
- job: LinuxArmhf
|
||||
container: vscode-armhf
|
||||
variables:
|
||||
VSCODE_ARCH: armhf
|
||||
NPM_ARCH: armv7l
|
||||
steps:
|
||||
- template: linux/product-build-linux.yml
|
||||
|
||||
- ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_LINUX_ARMHF, true)) }}:
|
||||
- job: LinuxSnapArmhf
|
||||
dependsOn:
|
||||
- LinuxArmhf
|
||||
container: snapcraft
|
||||
variables:
|
||||
VSCODE_ARCH: armhf
|
||||
steps:
|
||||
- template: linux/snap-build-linux.yml
|
||||
|
||||
- ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_LINUX_ARM64, true)) }}:
|
||||
- job: LinuxArm64
|
||||
container: vscode-arm64
|
||||
variables:
|
||||
VSCODE_ARCH: arm64
|
||||
NPM_ARCH: arm64
|
||||
steps:
|
||||
- template: linux/product-build-linux.yml
|
||||
|
||||
- ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_LINUX_ARM64, true)) }}:
|
||||
- job: LinuxSnapArm64
|
||||
dependsOn:
|
||||
- LinuxArm64
|
||||
container: snapcraft
|
||||
variables:
|
||||
VSCODE_ARCH: arm64
|
||||
steps:
|
||||
- template: linux/snap-build-linux.yml
|
||||
|
||||
- ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_LINUX_ALPINE, true)) }}:
|
||||
- job: LinuxAlpine
|
||||
steps:
|
||||
- template: linux/product-build-alpine.yml
|
||||
|
||||
- ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_WEB, true)) }}:
|
||||
- job: LinuxWeb
|
||||
variables:
|
||||
VSCODE_ARCH: x64
|
||||
steps:
|
||||
- template: web/product-build-web.yml
|
||||
|
||||
- ${{ if and(eq(parameters.VSCODE_COMPILE_ONLY, false), eq(variables['VSCODE_BUILD_STAGE_MACOS'], true)) }}:
|
||||
- stage: macOS
|
||||
dependsOn:
|
||||
- Compile
|
||||
pool:
|
||||
vmImage: macOS-latest
|
||||
jobs:
|
||||
|
||||
- ${{ if eq(parameters.VSCODE_BUILD_MACOS, true) }}:
|
||||
- job: macOS
|
||||
timeoutInMinutes: 90
|
||||
variables:
|
||||
VSCODE_ARCH: x64
|
||||
steps:
|
||||
- template: darwin/product-build-darwin.yml
|
||||
|
||||
- ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_MACOS_ARM64, true)) }}:
|
||||
- job: macOSARM64
|
||||
timeoutInMinutes: 90
|
||||
variables:
|
||||
VSCODE_ARCH: arm64
|
||||
steps:
|
||||
- template: darwin/product-build-darwin.yml
|
||||
|
||||
- ${{ if and(eq(variables['VSCODE_PUBLISH'], true), eq(parameters.VSCODE_COMPILE_ONLY, false)) }}:
|
||||
- stage: Publish
|
||||
dependsOn:
|
||||
- ${{ if eq(variables['VSCODE_BUILD_STAGE_WINDOWS'], true) }}:
|
||||
- Windows
|
||||
- ${{ if eq(variables['VSCODE_BUILD_STAGE_LINUX'], true) }}:
|
||||
- Linux
|
||||
condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX'], 'true'), ne(variables['VSCODE_PUBLISH'], 'false'))
|
||||
container: snapcraft
|
||||
variables:
|
||||
VSCODE_ARCH: x64
|
||||
steps:
|
||||
- template: linux/snap-build-linux.yml
|
||||
- ${{ if eq(variables['VSCODE_BUILD_STAGE_MACOS'], true) }}:
|
||||
- macOS
|
||||
condition: succeededOrFailed()
|
||||
pool:
|
||||
vmImage: "Ubuntu-18.04"
|
||||
jobs:
|
||||
- job: SyncMooncake
|
||||
displayName: Sync Mooncake
|
||||
steps:
|
||||
- template: sync-mooncake.yml
|
||||
|
||||
- job: LinuxArmhf
|
||||
condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX_ARMHF'], 'true'), ne(variables['VSCODE_PUBLISH'], 'false'))
|
||||
container: vscode-armhf
|
||||
variables:
|
||||
VSCODE_ARCH: armhf
|
||||
NPM_ARCH: armv7l
|
||||
steps:
|
||||
- template: linux/product-build-linux.yml
|
||||
|
||||
- job: LinuxSnapArmhf
|
||||
dependsOn:
|
||||
- LinuxArmhf
|
||||
condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX_ARMHF'], 'true'), ne(variables['VSCODE_PUBLISH'], 'false'))
|
||||
container: snapcraft
|
||||
variables:
|
||||
VSCODE_ARCH: armhf
|
||||
steps:
|
||||
- template: linux/snap-build-linux.yml
|
||||
|
||||
- job: LinuxArm64
|
||||
condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX_ARM64'], 'true'), ne(variables['VSCODE_PUBLISH'], 'false'))
|
||||
container: vscode-arm64
|
||||
variables:
|
||||
VSCODE_ARCH: arm64
|
||||
NPM_ARCH: arm64
|
||||
steps:
|
||||
- template: linux/product-build-linux.yml
|
||||
|
||||
- job: LinuxSnapArm64
|
||||
dependsOn:
|
||||
- LinuxArm64
|
||||
condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX_ARM64'], 'true'), ne(variables['VSCODE_PUBLISH'], 'false'))
|
||||
container: snapcraft
|
||||
variables:
|
||||
VSCODE_ARCH: arm64
|
||||
steps:
|
||||
- template: linux/snap-build-linux.yml
|
||||
|
||||
- job: LinuxAlpine
|
||||
condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX_ALPINE'], 'true'), ne(variables['VSCODE_PUBLISH'], 'false'))
|
||||
steps:
|
||||
- template: linux/product-build-alpine.yml
|
||||
|
||||
- job: LinuxWeb
|
||||
condition: and(succeeded(), eq(variables['VSCODE_BUILD_WEB'], 'true'), ne(variables['VSCODE_PUBLISH'], 'false'))
|
||||
variables:
|
||||
VSCODE_ARCH: x64
|
||||
steps:
|
||||
- template: web/product-build-web.yml
|
||||
|
||||
- stage: macOS
|
||||
dependsOn:
|
||||
- Compile
|
||||
condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'))
|
||||
pool:
|
||||
vmImage: macOS-latest
|
||||
jobs:
|
||||
- job: macOS
|
||||
condition: and(succeeded(), eq(variables['VSCODE_BUILD_MACOS'], 'true'))
|
||||
timeoutInMinutes: 90
|
||||
variables:
|
||||
VSCODE_ARCH: x64
|
||||
steps:
|
||||
- template: darwin/product-build-darwin.yml
|
||||
- job: macOSARM64
|
||||
condition: and(succeeded(), eq(variables['VSCODE_BUILD_MACOS_ARM64'], 'true'), ne(variables['VSCODE_PUBLISH'], 'false'))
|
||||
timeoutInMinutes: 90
|
||||
variables:
|
||||
VSCODE_ARCH: arm64
|
||||
steps:
|
||||
- template: darwin/product-build-darwin.yml
|
||||
|
||||
- stage: Publish
|
||||
dependsOn:
|
||||
- Windows
|
||||
- Linux
|
||||
- macOS
|
||||
condition: and(succeededOrFailed(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), ne(variables['VSCODE_PUBLISH'], 'false'))
|
||||
pool:
|
||||
vmImage: "Ubuntu-18.04"
|
||||
jobs:
|
||||
- job: SyncMooncake
|
||||
displayName: Sync Mooncake
|
||||
steps:
|
||||
- template: sync-mooncake.yml
|
||||
- job: ReleaseBuild
|
||||
condition: and(succeeded(), or(eq(variables['VSCODE_RELEASE'], 'true'), and(or(eq(variables['VSCODE_QUALITY'], 'insider'), eq(variables['VSCODE_QUALITY'], 'exploration')), eq(variables['Build.Reason'], 'Schedule'))))
|
||||
displayName: Release Build
|
||||
steps:
|
||||
- template: release.yml
|
||||
- ${{ if or(eq(parameters.VSCODE_RELEASE, true), and(in(parameters.VSCODE_QUALITY, 'insider', 'exploration'), eq(variables['VSCODE_SCHEDULEDBUILD'], true))) }}:
|
||||
- job: ReleaseBuild
|
||||
displayName: Release Build
|
||||
steps:
|
||||
- template: release.yml
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
steps:
|
||||
- task: NodeTool@0
|
||||
inputs:
|
||||
versionSpec: "12.18.3"
|
||||
versionSpec: "12.x"
|
||||
|
||||
- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2
|
||||
inputs:
|
||||
|
@ -30,39 +30,40 @@ steps:
|
|||
git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro")
|
||||
displayName: Merge distro
|
||||
|
||||
- script: |
|
||||
npx https://aka.ms/enablesecurefeed standAlone
|
||||
displayName: Switch to Terrapin packages
|
||||
timeoutInMinutes: 5
|
||||
condition: and(succeeded(), eq(variables['ENABLE_TERRAPIN'], 'true'))
|
||||
|
||||
- script: |
|
||||
mkdir -p .build
|
||||
echo -n $(VSCODE_ARCH) > .build/arch
|
||||
echo -n $ENABLE_TERRAPIN > .build/terrapin
|
||||
node build/azure-pipelines/common/computeNodeModulesCacheKey.js $VSCODE_ARCH $ENABLE_TERRAPIN > .build/yarnlockhash
|
||||
displayName: Prepare yarn cache flags
|
||||
|
||||
- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1
|
||||
# using `genericNodeModules` instead of `nodeModules` here to avoid sharing the cache with builds running inside containers
|
||||
- task: Cache@2
|
||||
inputs:
|
||||
keyfile: ".build/arch, .build/terrapin, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock"
|
||||
targetfolder: "**/node_modules, !**/node_modules/**/node_modules"
|
||||
vstsFeed: "npm-vscode"
|
||||
key: 'genericNodeModules | $(Agent.OS) | .build/yarnlockhash'
|
||||
path: .build/node_modules_cache
|
||||
cacheHitVar: NODE_MODULES_RESTORED
|
||||
displayName: Restore node_modules cache
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
export npm_config_arch=$(NPM_ARCH)
|
||||
tar -xzf .build/node_modules_cache/cache.tgz
|
||||
condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true'))
|
||||
displayName: Extract node_modules cache
|
||||
|
||||
if [ -z "$CC" ] || [ -z "$CXX" ]; then
|
||||
export CC=$(which gcc-5)
|
||||
export CXX=$(which g++-5)
|
||||
fi
|
||||
- script: |
|
||||
npx https://aka.ms/enablesecurefeed standAlone
|
||||
timeoutInMinutes: 5
|
||||
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true'))
|
||||
displayName: Switch to Terrapin packages
|
||||
|
||||
if [ "$VSCODE_ARCH" == "x64" ]; then
|
||||
export VSCODE_REMOTE_CC=$(which gcc-4.8)
|
||||
export VSCODE_REMOTE_CXX=$(which g++-4.8)
|
||||
export VSCODE_REMOTE_NODE_GYP=$(which node-gyp)
|
||||
fi
|
||||
- script: |
|
||||
set -e
|
||||
sudo apt update -y
|
||||
sudo apt install -y build-essential pkg-config libx11-dev libx11-xcb-dev libxkbfile-dev libsecret-1-dev libnotify-bin
|
||||
displayName: Install build tools
|
||||
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
for i in {1..3}; do # try 3 times, for Terrapin
|
||||
yarn --frozen-lockfile && break
|
||||
if [ $i -eq 3 ]; then
|
||||
|
@ -75,14 +76,15 @@ steps:
|
|||
ELECTRON_SKIP_BINARY_DOWNLOAD: 1
|
||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
|
||||
displayName: Install dependencies
|
||||
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
|
||||
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
|
||||
|
||||
- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1
|
||||
inputs:
|
||||
keyfile: ".build/arch, .build/terrapin, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock"
|
||||
targetfolder: "**/node_modules, !**/node_modules/**/node_modules"
|
||||
vstsFeed: "npm-vscode"
|
||||
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
|
||||
- script: |
|
||||
set -e
|
||||
node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt
|
||||
mkdir -p .build/node_modules_cache
|
||||
tar -czf .build/node_modules_cache/cache.tgz --files-from .build/node_modules_list.txt
|
||||
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
|
||||
displayName: Create node_modules archive
|
||||
|
||||
# Mixin must run before optimize, because the CSS loader will inline small SVGs
|
||||
- script: |
|
||||
|
@ -92,12 +94,8 @@ steps:
|
|||
|
||||
- script: |
|
||||
set -e
|
||||
yarn gulp compile-build
|
||||
yarn gulp compile-extensions-build
|
||||
yarn gulp minify-vscode
|
||||
yarn gulp minify-vscode-reh
|
||||
yarn gulp minify-vscode-reh-web
|
||||
displayName: Compile
|
||||
yarn npm-run-all -lp core-ci extensions-ci hygiene eslint valid-layers-check
|
||||
displayName: Compile & Hygiene
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
|
@ -106,6 +104,19 @@ steps:
|
|||
displayName: Upload sourcemaps
|
||||
condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false'))
|
||||
|
||||
- script: |
|
||||
set -
|
||||
./build/azure-pipelines/common/extract-telemetry.sh
|
||||
displayName: Extract Telemetry
|
||||
condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false'))
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
AZURE_WEBVIEW_STORAGE_ACCESS_KEY="$(vscode-webview-storage-key)" \
|
||||
./build/azure-pipelines/common/publish-webview.sh
|
||||
displayName: Publish Webview
|
||||
condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false'))
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
VERSION=`node -p "require(\"./package.json\").version"`
|
||||
|
|
|
@ -1,92 +0,0 @@
|
|||
steps:
|
||||
- task: NodeTool@0
|
||||
inputs:
|
||||
versionSpec: "12.18.3"
|
||||
|
||||
- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2
|
||||
inputs:
|
||||
versionSpec: "1.x"
|
||||
|
||||
- task: AzureKeyVault@1
|
||||
displayName: "Azure Key Vault: Get Secrets"
|
||||
inputs:
|
||||
azureSubscription: "vscode-builds-subscription"
|
||||
KeyVaultName: vscode
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
cat << EOF > ~/.netrc
|
||||
machine github.com
|
||||
login vscode
|
||||
password $(github-distro-mixin-password)
|
||||
EOF
|
||||
|
||||
git config user.email "vscode@microsoft.com"
|
||||
git config user.name "VSCode"
|
||||
displayName: Prepare tooling
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro")
|
||||
displayName: Merge distro
|
||||
|
||||
- script: |
|
||||
npx https://aka.ms/enablesecurefeed standAlone
|
||||
displayName: Switch to Terrapin packages
|
||||
timeoutInMinutes: 5
|
||||
condition: and(succeeded(), eq(variables['ENABLE_TERRAPIN'], 'true'))
|
||||
|
||||
- script: |
|
||||
mkdir -p .build
|
||||
echo -n $(VSCODE_ARCH) > .build/arch
|
||||
echo -n $ENABLE_TERRAPIN > .build/terrapin
|
||||
displayName: Prepare yarn cache flags
|
||||
|
||||
- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1
|
||||
inputs:
|
||||
keyfile: ".build/arch, .build/terrapin, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock"
|
||||
targetfolder: "**/node_modules, !**/node_modules/**/node_modules"
|
||||
vstsFeed: "npm-vscode"
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
for i in {1..3}; do # try 3 times, for Terrapin
|
||||
yarn --frozen-lockfile && break
|
||||
if [ $i -eq 3 ]; then
|
||||
echo "Yarn failed too many times" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "Yarn failed $i, trying again..."
|
||||
done
|
||||
env:
|
||||
ELECTRON_SKIP_BINARY_DOWNLOAD: 1
|
||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
|
||||
displayName: Install dependencies
|
||||
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
|
||||
|
||||
# Mixin must run before optimize, because the CSS loader will inline small SVGs
|
||||
- script: |
|
||||
set -e
|
||||
node build/azure-pipelines/mixin
|
||||
displayName: Mix in quality
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
yarn gulp hygiene
|
||||
yarn monaco-compile-check
|
||||
yarn valid-layers-check
|
||||
displayName: Run hygiene, monaco compile & valid layers checks
|
||||
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
||||
|
||||
- script: |
|
||||
set -
|
||||
./build/azure-pipelines/common/extract-telemetry.sh
|
||||
displayName: Extract Telemetry
|
||||
condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false'))
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
AZURE_WEBVIEW_STORAGE_ACCESS_KEY="$(vscode-webview-storage-key)" \
|
||||
./build/azure-pipelines/common/publish-webview.sh
|
||||
displayName: Publish Webview
|
||||
condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false'))
|
|
@ -42,21 +42,28 @@ steps:
|
|||
displayName: Merge distro
|
||||
|
||||
- script: |
|
||||
npx https://aka.ms/enablesecurefeed standAlone
|
||||
displayName: Switch to Terrapin packages
|
||||
timeoutInMinutes: 5
|
||||
condition: and(succeeded(), eq(variables['ENABLE_TERRAPIN'], 'true'))
|
||||
mkdir -p .build
|
||||
node build/azure-pipelines/common/computeNodeModulesCacheKey.js "web" $ENABLE_TERRAPIN > .build/yarnlockhash
|
||||
displayName: Prepare yarn cache flags
|
||||
|
||||
- task: Cache@2
|
||||
inputs:
|
||||
key: 'nodeModules | $(Agent.OS) | .build/yarnlockhash'
|
||||
path: .build/node_modules_cache
|
||||
cacheHitVar: NODE_MODULES_RESTORED
|
||||
displayName: Restore node_modules cache
|
||||
|
||||
- script: |
|
||||
echo -n "web" > .build/arch
|
||||
echo -n $ENABLE_TERRAPIN > .build/terrapin
|
||||
displayName: Prepare yarn cache flag
|
||||
set -e
|
||||
tar -xzf .build/node_modules_cache/cache.tgz
|
||||
condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true'))
|
||||
displayName: Extract node_modules cache
|
||||
|
||||
- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1
|
||||
inputs:
|
||||
keyfile: ".build/arch, .build/terrapin, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock"
|
||||
targetfolder: "**/node_modules, !**/node_modules/**/node_modules"
|
||||
vstsFeed: "npm-vscode"
|
||||
- script: |
|
||||
npx https://aka.ms/enablesecurefeed standAlone
|
||||
timeoutInMinutes: 5
|
||||
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true'))
|
||||
displayName: Switch to Terrapin packages
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
|
@ -72,14 +79,15 @@ steps:
|
|||
ELECTRON_SKIP_BINARY_DOWNLOAD: 1
|
||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
|
||||
displayName: Install dependencies
|
||||
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
|
||||
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
|
||||
|
||||
- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1
|
||||
inputs:
|
||||
keyfile: ".build/arch, .build/terrapin, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock"
|
||||
targetfolder: "**/node_modules, !**/node_modules/**/node_modules"
|
||||
vstsFeed: "npm-vscode"
|
||||
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
|
||||
- script: |
|
||||
set -e
|
||||
node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt
|
||||
mkdir -p .build/node_modules_cache
|
||||
tar -czf .build/node_modules_cache/cache.tgz --files-from .build/node_modules_list.txt
|
||||
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
|
||||
displayName: Create node_modules archive
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
|
|
|
@ -12,9 +12,14 @@ steps:
|
|||
versionSpec: "2.x"
|
||||
addToPath: true
|
||||
|
||||
- powershell: |
|
||||
mkdir -Force .build
|
||||
node build/azure-pipelines/common/computeNodeModulesCacheKey.js > .build/yarnlockhash
|
||||
displayName: Prepare yarn cache flags
|
||||
|
||||
- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1
|
||||
inputs:
|
||||
keyfile: ".yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock"
|
||||
keyfile: ".build/yarnlockhash"
|
||||
targetfolder: "**/node_modules, !**/node_modules/**/node_modules"
|
||||
vstsFeed: "vscode-build-cache"
|
||||
|
||||
|
@ -27,7 +32,7 @@ steps:
|
|||
|
||||
- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1
|
||||
inputs:
|
||||
keyfile: ".yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock"
|
||||
keyfile: ".build/yarnlockhash"
|
||||
targetfolder: "**/node_modules, !**/node_modules/**/node_modules"
|
||||
vstsFeed: "vscode-build-cache"
|
||||
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
|
||||
|
|
|
@ -45,22 +45,31 @@ steps:
|
|||
exec { git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro") }
|
||||
displayName: Merge distro
|
||||
|
||||
- script: |
|
||||
npx https://aka.ms/enablesecurefeed standAlone
|
||||
displayName: Switch to Terrapin packages
|
||||
timeoutInMinutes: 7
|
||||
condition: and(succeeded(), eq(variables['ENABLE_TERRAPIN'], 'true'))
|
||||
|
||||
- powershell: |
|
||||
"$(VSCODE_ARCH)" | Out-File -Encoding ascii -NoNewLine .build\arch
|
||||
"$env:ENABLE_TERRAPIN" | Out-File -Encoding ascii -NoNewLine .build\terrapin
|
||||
node build/azure-pipelines/common/computeNodeModulesCacheKey.js > .build/yarnlockhash
|
||||
displayName: Prepare yarn cache flags
|
||||
|
||||
- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1
|
||||
- task: Cache@2
|
||||
inputs:
|
||||
keyfile: ".build/arch, .build/terrapin, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock"
|
||||
targetfolder: "**/node_modules, !**/node_modules/**/node_modules"
|
||||
vstsFeed: "npm-vscode"
|
||||
key: 'nodeModules | $(Agent.OS) | .build/arch, .build/terrapin, .build/yarnlockhash'
|
||||
path: .build/node_modules_cache
|
||||
cacheHitVar: NODE_MODULES_RESTORED
|
||||
displayName: Restore node_modules cache
|
||||
|
||||
- powershell: |
|
||||
. build/azure-pipelines/win32/exec.ps1
|
||||
$ErrorActionPreference = "Stop"
|
||||
exec { 7z.exe x .build/node_modules_cache/cache.7z -aos }
|
||||
condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true'))
|
||||
displayName: Extract node_modules cache
|
||||
|
||||
- script: |
|
||||
npx https://aka.ms/enablesecurefeed standAlone
|
||||
timeoutInMinutes: 5
|
||||
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true'))
|
||||
displayName: Switch to Terrapin packages
|
||||
|
||||
- powershell: |
|
||||
. build/azure-pipelines/win32/exec.ps1
|
||||
|
@ -73,14 +82,16 @@ steps:
|
|||
ELECTRON_SKIP_BINARY_DOWNLOAD: 1
|
||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
|
||||
displayName: Install dependencies
|
||||
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
|
||||
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
|
||||
|
||||
- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1
|
||||
inputs:
|
||||
keyfile: ".build/arch, .build/terrapin, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock"
|
||||
targetfolder: "**/node_modules, !**/node_modules/**/node_modules"
|
||||
vstsFeed: "npm-vscode"
|
||||
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
|
||||
- powershell: |
|
||||
. build/azure-pipelines/win32/exec.ps1
|
||||
$ErrorActionPreference = "Stop"
|
||||
exec { node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt }
|
||||
exec { mkdir -Force .build/node_modules_cache }
|
||||
exec { 7z.exe a .build/node_modules_cache/cache.7z -mx3 `@.build/node_modules_list.txt }
|
||||
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
|
||||
displayName: Create node_modules archive
|
||||
|
||||
- powershell: |
|
||||
. build/azure-pipelines/win32/exec.ps1
|
||||
|
@ -144,7 +155,7 @@ steps:
|
|||
$ErrorActionPreference = "Stop"
|
||||
exec { yarn --cwd test/integration/browser compile }
|
||||
displayName: Compile integration tests
|
||||
condition: and(succeeded(), eq(variables['CacheRestored'], 'true'), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64'))
|
||||
condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64'))
|
||||
|
||||
- powershell: |
|
||||
# Figure out the full absolute path of the product we just built
|
||||
|
|
36
build/eslint.js
Normal file
36
build/eslint.js
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
const es = require('event-stream');
|
||||
const vfs = require('vinyl-fs');
|
||||
const { jsHygieneFilter, tsHygieneFilter } = require('./filters');
|
||||
|
||||
function eslint() {
|
||||
const gulpeslint = require('gulp-eslint');
|
||||
return vfs
|
||||
.src([...jsHygieneFilter, ...tsHygieneFilter], { base: '.', follow: true, allowEmpty: true })
|
||||
.pipe(
|
||||
gulpeslint({
|
||||
configFile: '.eslintrc.json',
|
||||
rulePaths: ['./build/lib/eslint'],
|
||||
})
|
||||
)
|
||||
.pipe(gulpeslint.formatEach('compact'))
|
||||
.pipe(
|
||||
gulpeslint.results((results) => {
|
||||
if (results.warningCount > 0 || results.errorCount > 0) {
|
||||
throw new Error('eslint failed with warnings and/or errors');
|
||||
}
|
||||
})
|
||||
).pipe(es.through(function () { /* noop, important for the stream to end */ }));
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
eslint().on('error', (err) => {
|
||||
console.error();
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
151
build/filters.js
Normal file
151
build/filters.js
Normal file
|
@ -0,0 +1,151 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Hygiene works by creating cascading subsets of all our files and
|
||||
* passing them through a sequence of checks. Here are the current subsets,
|
||||
* named according to the checks performed on them. Each subset contains
|
||||
* the following one, as described in mathematical notation:
|
||||
*
|
||||
* all ⊃ eol ⊇ indentation ⊃ copyright ⊃ typescript
|
||||
*/
|
||||
|
||||
module.exports.all = [
|
||||
'*',
|
||||
'build/**/*',
|
||||
'extensions/**/*',
|
||||
'scripts/**/*',
|
||||
'src/**/*',
|
||||
'test/**/*',
|
||||
'!out*/**',
|
||||
'!test/**/out/**',
|
||||
'!**/node_modules/**',
|
||||
];
|
||||
|
||||
module.exports.indentationFilter = [
|
||||
'**',
|
||||
|
||||
// except specific files
|
||||
'!**/ThirdPartyNotices.txt',
|
||||
'!**/LICENSE.{txt,rtf}',
|
||||
'!LICENSES.chromium.html',
|
||||
'!**/LICENSE',
|
||||
'!src/vs/nls.js',
|
||||
'!src/vs/nls.build.js',
|
||||
'!src/vs/css.js',
|
||||
'!src/vs/css.build.js',
|
||||
'!src/vs/loader.js',
|
||||
'!src/vs/base/common/insane/insane.js',
|
||||
'!src/vs/base/common/marked/marked.js',
|
||||
'!src/vs/base/common/semver/semver.js',
|
||||
'!src/vs/base/node/terminateProcess.sh',
|
||||
'!src/vs/base/node/cpuUsage.sh',
|
||||
'!test/unit/assert.js',
|
||||
'!resources/linux/snap/electron-launch',
|
||||
|
||||
// except specific folders
|
||||
'!test/automation/out/**',
|
||||
'!test/monaco/out/**',
|
||||
'!test/smoke/out/**',
|
||||
'!extensions/typescript-language-features/test-workspace/**',
|
||||
'!extensions/vscode-api-tests/testWorkspace/**',
|
||||
'!extensions/vscode-api-tests/testWorkspace2/**',
|
||||
'!build/monaco/**',
|
||||
'!build/win32/**',
|
||||
|
||||
// except multiple specific files
|
||||
'!**/package.json',
|
||||
'!**/yarn.lock',
|
||||
'!**/yarn-error.log',
|
||||
|
||||
// except multiple specific folders
|
||||
'!**/codicon/**',
|
||||
'!**/fixtures/**',
|
||||
'!**/lib/**',
|
||||
'!extensions/**/dist/**',
|
||||
'!extensions/**/out/**',
|
||||
'!extensions/**/snippets/**',
|
||||
'!extensions/**/syntaxes/**',
|
||||
'!extensions/**/themes/**',
|
||||
'!extensions/**/colorize-fixtures/**',
|
||||
|
||||
// except specific file types
|
||||
'!src/vs/*/**/*.d.ts',
|
||||
'!src/typings/**/*.d.ts',
|
||||
'!extensions/**/*.d.ts',
|
||||
'!**/*.{svg,exe,png,bmp,jpg,scpt,bat,cmd,cur,ttf,woff,eot,md,ps1,template,yaml,yml,d.ts.recipe,ico,icns,plist}',
|
||||
'!build/{lib,download,darwin}/**/*.js',
|
||||
'!build/**/*.sh',
|
||||
'!build/azure-pipelines/**/*.js',
|
||||
'!build/azure-pipelines/**/*.config',
|
||||
'!**/Dockerfile',
|
||||
'!**/Dockerfile.*',
|
||||
'!**/*.Dockerfile',
|
||||
'!**/*.dockerfile',
|
||||
'!extensions/markdown-language-features/media/*.js',
|
||||
'!extensions/simple-browser/media/*.js',
|
||||
];
|
||||
|
||||
module.exports.copyrightFilter = [
|
||||
'**',
|
||||
'!**/*.desktop',
|
||||
'!**/*.json',
|
||||
'!**/*.html',
|
||||
'!**/*.template',
|
||||
'!**/*.md',
|
||||
'!**/*.bat',
|
||||
'!**/*.cmd',
|
||||
'!**/*.ico',
|
||||
'!**/*.icns',
|
||||
'!**/*.xml',
|
||||
'!**/*.sh',
|
||||
'!**/*.txt',
|
||||
'!**/*.xpm',
|
||||
'!**/*.opts',
|
||||
'!**/*.disabled',
|
||||
'!**/*.code-workspace',
|
||||
'!**/*.js.map',
|
||||
'!build/**/*.init',
|
||||
'!resources/linux/snap/snapcraft.yaml',
|
||||
'!resources/win32/bin/code.js',
|
||||
'!resources/web/code-web.js',
|
||||
'!resources/completions/**',
|
||||
'!extensions/configuration-editing/build/inline-allOf.ts',
|
||||
'!extensions/markdown-language-features/media/highlight.css',
|
||||
'!extensions/html-language-features/server/src/modes/typescript/*',
|
||||
'!extensions/*/server/bin/*',
|
||||
'!src/vs/editor/test/node/classification/typescript-test.ts',
|
||||
];
|
||||
|
||||
module.exports.jsHygieneFilter = [
|
||||
'src/**/*.js',
|
||||
'build/gulpfile.*.js',
|
||||
'!src/vs/loader.js',
|
||||
'!src/vs/css.js',
|
||||
'!src/vs/nls.js',
|
||||
'!src/vs/css.build.js',
|
||||
'!src/vs/nls.build.js',
|
||||
'!src/**/insane.js',
|
||||
'!src/**/marked.js',
|
||||
'!src/**/semver.js',
|
||||
'!**/test/**',
|
||||
];
|
||||
|
||||
module.exports.tsHygieneFilter = [
|
||||
'src/**/*.ts',
|
||||
'test/**/*.ts',
|
||||
'extensions/**/*.ts',
|
||||
'!src/vs/*/**/*.d.ts',
|
||||
'!src/typings/**/*.d.ts',
|
||||
'!extensions/**/*.d.ts',
|
||||
'!**/fixtures/**',
|
||||
'!**/typings/**',
|
||||
'!**/node_modules/**',
|
||||
'!extensions/typescript-basics/test/colorize-fixtures/**',
|
||||
'!extensions/vscode-api-tests/testWorkspace/**',
|
||||
'!extensions/vscode-api-tests/testWorkspace2/**',
|
||||
'!extensions/**/*.test.ts',
|
||||
'!extensions/html-language-features/server/lib/jquery.d.ts',
|
||||
];
|
|
@ -11,6 +11,11 @@ const task = require('./lib/task');
|
|||
const compilation = require('./lib/compilation');
|
||||
|
||||
// Full compile, including nls and inline sources in sourcemaps, for build
|
||||
const compileBuildTask = task.define('compile-build', task.series(util.rimraf('out-build'), compilation.compileTask('src', 'out-build', true)));
|
||||
const compileBuildTask = task.define('compile-build',
|
||||
task.series(
|
||||
util.rimraf('out-build'),
|
||||
compilation.compileTask('src', 'out-build', true)
|
||||
)
|
||||
);
|
||||
gulp.task(compileBuildTask);
|
||||
exports.compileBuildTask = compileBuildTask;
|
||||
exports.compileBuildTask = compileBuildTask;
|
||||
|
|
|
@ -16,8 +16,6 @@ const cp = require('child_process');
|
|||
const compilation = require('./lib/compilation');
|
||||
const monacoapi = require('./lib/monaco-api');
|
||||
const fs = require('fs');
|
||||
const webpack = require('webpack');
|
||||
const webpackGulp = require('webpack-stream');
|
||||
|
||||
let root = path.dirname(__dirname);
|
||||
let sha1 = util.getVersion(root);
|
||||
|
@ -369,6 +367,9 @@ gulp.task('editor-distro',
|
|||
);
|
||||
|
||||
const bundleEditorESMTask = task.define('editor-esm-bundle-webpack', () => {
|
||||
const webpack = require('webpack');
|
||||
const webpackGulp = require('webpack-stream');
|
||||
|
||||
const result = es.through();
|
||||
|
||||
const webpackConfigPath = path.join(root, 'build/monaco/monaco.webpack.config.js');
|
||||
|
|
|
@ -9,17 +9,13 @@ require('events').EventEmitter.defaultMaxListeners = 100;
|
|||
const gulp = require('gulp');
|
||||
const path = require('path');
|
||||
const nodeUtil = require('util');
|
||||
const tsb = require('gulp-tsb');
|
||||
const es = require('event-stream');
|
||||
const filter = require('gulp-filter');
|
||||
const webpack = require('webpack');
|
||||
const util = require('./lib/util');
|
||||
const task = require('./lib/task');
|
||||
const watcher = require('./lib/watch');
|
||||
const createReporter = require('./lib/reporter').createReporter;
|
||||
const glob = require('glob');
|
||||
const sourcemaps = require('gulp-sourcemaps');
|
||||
const nlsDev = require('vscode-nls-dev');
|
||||
const root = path.dirname(__dirname);
|
||||
const commit = util.getVersion(root);
|
||||
const plumber = require('gulp-plumber');
|
||||
|
@ -29,10 +25,50 @@ const ext = require('./lib/extensions');
|
|||
|
||||
const extensionsPath = path.join(path.dirname(__dirname), 'extensions');
|
||||
|
||||
const compilations = glob.sync('**/tsconfig.json', {
|
||||
cwd: extensionsPath,
|
||||
ignore: ['**/out/**', '**/node_modules/**']
|
||||
});
|
||||
// To save 250ms for each gulp startup, we are caching the result here
|
||||
// const compilations = glob.sync('**/tsconfig.json', {
|
||||
// cwd: extensionsPath,
|
||||
// ignore: ['**/out/**', '**/node_modules/**']
|
||||
// });
|
||||
const compilations = [
|
||||
'configuration-editing/build/tsconfig.json',
|
||||
'configuration-editing/tsconfig.json',
|
||||
'css-language-features/client/tsconfig.json',
|
||||
'css-language-features/server/tsconfig.json',
|
||||
'debug-auto-launch/tsconfig.json',
|
||||
'debug-server-ready/tsconfig.json',
|
||||
'emmet/tsconfig.json',
|
||||
'extension-editing/tsconfig.json',
|
||||
'git-ui/tsconfig.json',
|
||||
'git/tsconfig.json',
|
||||
'github-authentication/tsconfig.json',
|
||||
'github/tsconfig.json',
|
||||
'grunt/tsconfig.json',
|
||||
'gulp/tsconfig.json',
|
||||
'html-language-features/client/tsconfig.json',
|
||||
'html-language-features/server/tsconfig.json',
|
||||
'image-preview/tsconfig.json',
|
||||
'jake/tsconfig.json',
|
||||
'json-language-features/client/tsconfig.json',
|
||||
'json-language-features/server/tsconfig.json',
|
||||
'markdown-language-features/preview-src/tsconfig.json',
|
||||
'markdown-language-features/tsconfig.json',
|
||||
'merge-conflict/tsconfig.json',
|
||||
'microsoft-authentication/tsconfig.json',
|
||||
'npm/tsconfig.json',
|
||||
'php-language-features/tsconfig.json',
|
||||
'python/tsconfig.json',
|
||||
'search-result/tsconfig.json',
|
||||
'simple-browser/tsconfig.json',
|
||||
'testing-editor-contributions/tsconfig.json',
|
||||
'typescript-language-features/test-workspace/tsconfig.json',
|
||||
'typescript-language-features/tsconfig.json',
|
||||
'vscode-api-tests/tsconfig.json',
|
||||
'vscode-colorize-tests/tsconfig.json',
|
||||
'vscode-custom-editor-tests/tsconfig.json',
|
||||
'vscode-notebook-tests/tsconfig.json',
|
||||
'vscode-test-resolver/tsconfig.json'
|
||||
];
|
||||
|
||||
const getBaseUrl = out => `https://ticino.blob.core.windows.net/sourcemaps/${commit}/${out}`;
|
||||
|
||||
|
@ -64,6 +100,10 @@ const tasks = compilations.map(function (tsconfigFile) {
|
|||
}
|
||||
|
||||
function createPipeline(build, emitError) {
|
||||
const nlsDev = require('vscode-nls-dev');
|
||||
const tsb = require('gulp-tsb');
|
||||
const sourcemaps = require('gulp-sourcemaps');
|
||||
|
||||
const reporter = createReporter('extensions');
|
||||
|
||||
overrideOptions.inlineSources = Boolean(build);
|
||||
|
@ -170,6 +210,8 @@ const compileExtensionsBuildTask = task.define('compile-extensions-build', task.
|
|||
));
|
||||
|
||||
gulp.task(compileExtensionsBuildTask);
|
||||
gulp.task(task.define('extensions-ci', task.series(compileExtensionsBuildTask)));
|
||||
|
||||
exports.compileExtensionsBuildTask = compileExtensionsBuildTask;
|
||||
|
||||
const compileWebExtensionsTask = task.define('compile-web', () => buildWebExtensions(false));
|
||||
|
@ -181,6 +223,7 @@ gulp.task(watchWebExtensionsTask);
|
|||
exports.watchWebExtensionsTask = watchWebExtensionsTask;
|
||||
|
||||
async function buildWebExtensions(isWatch) {
|
||||
const webpack = require('webpack');
|
||||
|
||||
const webpackConfigLocations = await nodeUtil.promisify(glob)(
|
||||
path.join(extensionsPath, '**', 'extension-browser.webpack.config.js'),
|
||||
|
|
|
@ -4,51 +4,28 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
const gulp = require('gulp');
|
||||
const filter = require('gulp-filter');
|
||||
const es = require('event-stream');
|
||||
const gulpeslint = require('gulp-eslint');
|
||||
const vfs = require('vinyl-fs');
|
||||
const path = require('path');
|
||||
const task = require('./lib/task');
|
||||
const { all, jsHygieneFilter, tsHygieneFilter, hygiene } = require('./hygiene');
|
||||
|
||||
gulp.task('eslint', () => {
|
||||
return vfs
|
||||
.src(all, { base: '.', follow: true, allowEmpty: true })
|
||||
.pipe(filter(jsHygieneFilter.concat(tsHygieneFilter)))
|
||||
.pipe(
|
||||
gulpeslint({
|
||||
configFile: '.eslintrc.json',
|
||||
rulePaths: ['./build/lib/eslint'],
|
||||
})
|
||||
)
|
||||
.pipe(gulpeslint.formatEach('compact'))
|
||||
.pipe(
|
||||
gulpeslint.results((results) => {
|
||||
if (results.warningCount > 0 || results.errorCount > 0) {
|
||||
throw new Error('eslint failed with warnings and/or errors');
|
||||
}
|
||||
})
|
||||
);
|
||||
});
|
||||
const { hygiene } = require('./hygiene');
|
||||
|
||||
function checkPackageJSON(actualPath) {
|
||||
const actual = require(path.join(__dirname, '..', actualPath));
|
||||
const rootPackageJSON = require('../package.json');
|
||||
const checkIncluded = (set1, set2) => {
|
||||
for (let depName in set1) {
|
||||
const depVersion = set1[depName];
|
||||
const rootDepVersion = set2[depName];
|
||||
if (!rootDepVersion) {
|
||||
// missing in root is allowed
|
||||
continue;
|
||||
}
|
||||
if (depVersion !== rootDepVersion) {
|
||||
this.emit(
|
||||
'error',
|
||||
`The dependency ${depName} in '${actualPath}' (${depVersion}) is different than in the root package.json (${rootDepVersion})`
|
||||
);
|
||||
}
|
||||
const depVersion = set1[depName];
|
||||
const rootDepVersion = set2[depName];
|
||||
if (!rootDepVersion) {
|
||||
// missing in root is allowed
|
||||
continue;
|
||||
}
|
||||
if (depVersion !== rootDepVersion) {
|
||||
this.emit(
|
||||
'error',
|
||||
`The dependency ${depName} in '${actualPath}' (${depVersion}) is different than in the root package.json (${rootDepVersion})`
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -67,7 +44,5 @@ const checkPackageJSONTask = task.define('check-package-json', () => {
|
|||
});
|
||||
gulp.task(checkPackageJSONTask);
|
||||
|
||||
gulp.task(
|
||||
'hygiene',
|
||||
task.series(checkPackageJSONTask, () => hygiene())
|
||||
);
|
||||
const hygieneTask = task.define('hygiene', task.series(checkPackageJSONTask, () => hygiene(undefined, false)));
|
||||
gulp.task(hygieneTask);
|
||||
|
|
|
@ -14,10 +14,8 @@ const task = require('./lib/task');
|
|||
const vfs = require('vinyl-fs');
|
||||
const flatmap = require('gulp-flatmap');
|
||||
const gunzip = require('gulp-gunzip');
|
||||
const untar = require('gulp-untar');
|
||||
const File = require('vinyl');
|
||||
const fs = require('fs');
|
||||
const remote = require('gulp-remote-retry-src');
|
||||
const rename = require('gulp-rename');
|
||||
const filter = require('gulp-filter');
|
||||
const cp = require('child_process');
|
||||
|
@ -77,6 +75,9 @@ if (defaultNodeTask) {
|
|||
}
|
||||
|
||||
function nodejs(platform, arch) {
|
||||
const remote = require('gulp-remote-retry-src');
|
||||
const untar = require('gulp-untar');
|
||||
|
||||
if (arch === 'ia32') {
|
||||
arch = 'x86';
|
||||
}
|
||||
|
|
|
@ -11,13 +11,10 @@ const os = require('os');
|
|||
const cp = require('child_process');
|
||||
const path = require('path');
|
||||
const es = require('event-stream');
|
||||
const azure = require('gulp-azure-storage');
|
||||
const electron = require('gulp-atom-electron');
|
||||
const vfs = require('vinyl-fs');
|
||||
const rename = require('gulp-rename');
|
||||
const replace = require('gulp-replace');
|
||||
const filter = require('gulp-filter');
|
||||
const json = require('gulp-json-editor');
|
||||
const _ = require('underscore');
|
||||
const util = require('./lib/util');
|
||||
const task = require('./lib/task');
|
||||
|
@ -102,6 +99,16 @@ const minifyVSCodeTask = task.define('minify-vscode', task.series(
|
|||
));
|
||||
gulp.task(minifyVSCodeTask);
|
||||
|
||||
const core = task.define('core-ci', task.series(
|
||||
gulp.task('compile-build'),
|
||||
task.parallel(
|
||||
gulp.task('minify-vscode'),
|
||||
gulp.task('minify-vscode-reh'),
|
||||
gulp.task('minify-vscode-reh-web'),
|
||||
)
|
||||
));
|
||||
gulp.task(core);
|
||||
|
||||
/**
|
||||
* Compute checksums for some files.
|
||||
*
|
||||
|
@ -143,6 +150,9 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op
|
|||
platform = platform || process.platform;
|
||||
|
||||
return () => {
|
||||
const electron = require('gulp-atom-electron');
|
||||
const json = require('gulp-json-editor');
|
||||
|
||||
const out = sourceFolderName;
|
||||
|
||||
const checksums = computeChecksums(out, [
|
||||
|
@ -503,6 +513,8 @@ gulp.task(task.define(
|
|||
task.series(
|
||||
generateVSCodeConfigurationTask,
|
||||
() => {
|
||||
const azure = require('gulp-azure-storage');
|
||||
|
||||
if (!shouldSetupSettingsSearch()) {
|
||||
const branch = process.env.BUILD_SOURCEBRANCH;
|
||||
console.log(`Only runs on master and release branches, not ${branch}`);
|
||||
|
|
190
build/hygiene.js
190
build/hygiene.js
|
@ -5,157 +5,12 @@
|
|||
|
||||
const filter = require('gulp-filter');
|
||||
const es = require('event-stream');
|
||||
const gulpeslint = require('gulp-eslint');
|
||||
const tsfmt = require('typescript-formatter');
|
||||
const VinylFile = require('vinyl');
|
||||
const vfs = require('vinyl-fs');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const pall = require('p-all');
|
||||
|
||||
/**
|
||||
* Hygiene works by creating cascading subsets of all our files and
|
||||
* passing them through a sequence of checks. Here are the current subsets,
|
||||
* named according to the checks performed on them. Each subset contains
|
||||
* the following one, as described in mathematical notation:
|
||||
*
|
||||
* all ⊃ eol ⊇ indentation ⊃ copyright ⊃ typescript
|
||||
*/
|
||||
|
||||
const all = [
|
||||
'*',
|
||||
'build/**/*',
|
||||
'extensions/**/*',
|
||||
'scripts/**/*',
|
||||
'src/**/*',
|
||||
'test/**/*',
|
||||
'!test/**/out/**',
|
||||
'!**/node_modules/**',
|
||||
];
|
||||
module.exports.all = all;
|
||||
|
||||
const indentationFilter = [
|
||||
'**',
|
||||
|
||||
// except specific files
|
||||
'!**/ThirdPartyNotices.txt',
|
||||
'!**/LICENSE.{txt,rtf}',
|
||||
'!LICENSES.chromium.html',
|
||||
'!**/LICENSE',
|
||||
'!src/vs/nls.js',
|
||||
'!src/vs/nls.build.js',
|
||||
'!src/vs/css.js',
|
||||
'!src/vs/css.build.js',
|
||||
'!src/vs/loader.js',
|
||||
'!src/vs/base/common/insane/insane.js',
|
||||
'!src/vs/base/common/marked/marked.js',
|
||||
'!src/vs/base/common/semver/semver.js',
|
||||
'!src/vs/base/node/terminateProcess.sh',
|
||||
'!src/vs/base/node/cpuUsage.sh',
|
||||
'!test/unit/assert.js',
|
||||
'!resources/linux/snap/electron-launch',
|
||||
|
||||
// except specific folders
|
||||
'!test/automation/out/**',
|
||||
'!test/smoke/out/**',
|
||||
'!extensions/typescript-language-features/test-workspace/**',
|
||||
'!extensions/vscode-api-tests/testWorkspace/**',
|
||||
'!extensions/vscode-api-tests/testWorkspace2/**',
|
||||
'!build/monaco/**',
|
||||
'!build/win32/**',
|
||||
|
||||
// except multiple specific files
|
||||
'!**/package.json',
|
||||
'!**/yarn.lock',
|
||||
'!**/yarn-error.log',
|
||||
|
||||
// except multiple specific folders
|
||||
'!**/codicon/**',
|
||||
'!**/fixtures/**',
|
||||
'!**/lib/**',
|
||||
'!extensions/**/dist/**',
|
||||
'!extensions/**/out/**',
|
||||
'!extensions/**/snippets/**',
|
||||
'!extensions/**/syntaxes/**',
|
||||
'!extensions/**/themes/**',
|
||||
'!extensions/**/colorize-fixtures/**',
|
||||
|
||||
// except specific file types
|
||||
'!src/vs/*/**/*.d.ts',
|
||||
'!src/typings/**/*.d.ts',
|
||||
'!extensions/**/*.d.ts',
|
||||
'!**/*.{svg,exe,png,bmp,jpg,scpt,bat,cmd,cur,ttf,woff,eot,md,ps1,template,yaml,yml,d.ts.recipe,ico,icns,plist}',
|
||||
'!build/{lib,download,darwin}/**/*.js',
|
||||
'!build/**/*.sh',
|
||||
'!build/azure-pipelines/**/*.js',
|
||||
'!build/azure-pipelines/**/*.config',
|
||||
'!**/Dockerfile',
|
||||
'!**/Dockerfile.*',
|
||||
'!**/*.Dockerfile',
|
||||
'!**/*.dockerfile',
|
||||
'!extensions/markdown-language-features/media/*.js',
|
||||
];
|
||||
|
||||
const copyrightFilter = [
|
||||
'**',
|
||||
'!**/*.desktop',
|
||||
'!**/*.json',
|
||||
'!**/*.html',
|
||||
'!**/*.template',
|
||||
'!**/*.md',
|
||||
'!**/*.bat',
|
||||
'!**/*.cmd',
|
||||
'!**/*.ico',
|
||||
'!**/*.icns',
|
||||
'!**/*.xml',
|
||||
'!**/*.sh',
|
||||
'!**/*.txt',
|
||||
'!**/*.xpm',
|
||||
'!**/*.opts',
|
||||
'!**/*.disabled',
|
||||
'!**/*.code-workspace',
|
||||
'!**/*.js.map',
|
||||
'!build/**/*.init',
|
||||
'!resources/linux/snap/snapcraft.yaml',
|
||||
'!resources/win32/bin/code.js',
|
||||
'!resources/web/code-web.js',
|
||||
'!resources/completions/**',
|
||||
'!extensions/configuration-editing/build/inline-allOf.ts',
|
||||
'!extensions/markdown-language-features/media/highlight.css',
|
||||
'!extensions/html-language-features/server/src/modes/typescript/*',
|
||||
'!extensions/*/server/bin/*',
|
||||
'!src/vs/editor/test/node/classification/typescript-test.ts',
|
||||
];
|
||||
|
||||
const jsHygieneFilter = [
|
||||
'src/**/*.js',
|
||||
'build/gulpfile.*.js',
|
||||
'!src/vs/loader.js',
|
||||
'!src/vs/css.js',
|
||||
'!src/vs/nls.js',
|
||||
'!src/vs/css.build.js',
|
||||
'!src/vs/nls.build.js',
|
||||
'!src/**/insane.js',
|
||||
'!src/**/marked.js',
|
||||
'!src/**/semver.js',
|
||||
'!**/test/**',
|
||||
];
|
||||
module.exports.jsHygieneFilter = jsHygieneFilter;
|
||||
|
||||
const tsHygieneFilter = [
|
||||
'src/**/*.ts',
|
||||
'test/**/*.ts',
|
||||
'extensions/**/*.ts',
|
||||
'!**/fixtures/**',
|
||||
'!**/typings/**',
|
||||
'!**/node_modules/**',
|
||||
'!extensions/typescript-basics/test/colorize-fixtures/**',
|
||||
'!extensions/vscode-api-tests/testWorkspace/**',
|
||||
'!extensions/vscode-api-tests/testWorkspace2/**',
|
||||
'!extensions/**/*.test.ts',
|
||||
'!extensions/html-language-features/server/lib/jquery.d.ts',
|
||||
];
|
||||
module.exports.tsHygieneFilter = tsHygieneFilter;
|
||||
const { all, copyrightFilter, indentationFilter, jsHygieneFilter, tsHygieneFilter } = require('./filters');
|
||||
|
||||
const copyrightHeaderLines = [
|
||||
'/*---------------------------------------------------------------------------------------------',
|
||||
|
@ -164,7 +19,10 @@ const copyrightHeaderLines = [
|
|||
' *--------------------------------------------------------------------------------------------*/',
|
||||
];
|
||||
|
||||
function hygiene(some) {
|
||||
function hygiene(some, linting = true) {
|
||||
const gulpeslint = require('gulp-eslint');
|
||||
const tsfmt = require('typescript-formatter');
|
||||
|
||||
let errorCount = 0;
|
||||
|
||||
const productJson = es.through(function (file) {
|
||||
|
@ -274,26 +132,32 @@ function hygiene(some) {
|
|||
.pipe(filter(copyrightFilter))
|
||||
.pipe(copyrights);
|
||||
|
||||
const typescript = result.pipe(filter(tsHygieneFilter)).pipe(formatting);
|
||||
const streams = [
|
||||
result.pipe(filter(tsHygieneFilter)).pipe(formatting)
|
||||
];
|
||||
|
||||
const javascript = result
|
||||
.pipe(filter(jsHygieneFilter.concat(tsHygieneFilter)))
|
||||
.pipe(
|
||||
gulpeslint({
|
||||
configFile: '.eslintrc.json',
|
||||
rulePaths: ['./build/lib/eslint'],
|
||||
})
|
||||
)
|
||||
.pipe(gulpeslint.formatEach('compact'))
|
||||
.pipe(
|
||||
gulpeslint.results((results) => {
|
||||
errorCount += results.warningCount;
|
||||
errorCount += results.errorCount;
|
||||
})
|
||||
if (linting) {
|
||||
streams.push(
|
||||
result
|
||||
.pipe(filter([...jsHygieneFilter, ...tsHygieneFilter]))
|
||||
.pipe(
|
||||
gulpeslint({
|
||||
configFile: '.eslintrc.json',
|
||||
rulePaths: ['./build/lib/eslint'],
|
||||
})
|
||||
)
|
||||
.pipe(gulpeslint.formatEach('compact'))
|
||||
.pipe(
|
||||
gulpeslint.results((results) => {
|
||||
errorCount += results.warningCount;
|
||||
errorCount += results.errorCount;
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
let count = 0;
|
||||
return es.merge(typescript, javascript).pipe(
|
||||
return es.merge(...streams).pipe(
|
||||
es.through(
|
||||
function (data) {
|
||||
count++;
|
||||
|
|
|
@ -8,9 +8,6 @@ exports.watchTask = exports.compileTask = void 0;
|
|||
const es = require("event-stream");
|
||||
const fs = require("fs");
|
||||
const gulp = require("gulp");
|
||||
const bom = require("gulp-bom");
|
||||
const sourcemaps = require("gulp-sourcemaps");
|
||||
const tsb = require("gulp-tsb");
|
||||
const path = require("path");
|
||||
const monacodts = require("./monaco-api");
|
||||
const nls = require("./nls");
|
||||
|
@ -36,10 +33,13 @@ function getTypeScriptCompilerOptions(src) {
|
|||
return options;
|
||||
}
|
||||
function createCompile(src, build, emitError) {
|
||||
const tsb = require('gulp-tsb');
|
||||
const sourcemaps = require('gulp-sourcemaps');
|
||||
const projectPath = path.join(__dirname, '../../', src, 'tsconfig.json');
|
||||
const overrideOptions = Object.assign(Object.assign({}, getTypeScriptCompilerOptions(src)), { inlineSources: Boolean(build) });
|
||||
const compilation = tsb.create(projectPath, overrideOptions, false, err => reporter(err));
|
||||
function pipeline(token) {
|
||||
const bom = require('gulp-bom');
|
||||
const utf8Filter = util.filter(data => /(\/|\\)test(\/|\\).*utf8/.test(data.path));
|
||||
const tsFilter = util.filter(data => /\.ts$/.test(data.path));
|
||||
const noDeclarationsFilter = util.filter(data => !(/\.d\.ts$/.test(data.path)));
|
||||
|
@ -52,7 +52,7 @@ function createCompile(src, build, emitError) {
|
|||
.pipe(util.loadSourcemaps())
|
||||
.pipe(compilation(token))
|
||||
.pipe(noDeclarationsFilter)
|
||||
.pipe(build ? nls() : es.through())
|
||||
.pipe(build ? nls.nls() : es.through())
|
||||
.pipe(noDeclarationsFilter.restore)
|
||||
.pipe(sourcemaps.write('.', {
|
||||
addComment: false,
|
||||
|
|
|
@ -8,9 +8,6 @@
|
|||
import * as es from 'event-stream';
|
||||
import * as fs from 'fs';
|
||||
import * as gulp from 'gulp';
|
||||
import * as bom from 'gulp-bom';
|
||||
import * as sourcemaps from 'gulp-sourcemaps';
|
||||
import * as tsb from 'gulp-tsb';
|
||||
import * as path from 'path';
|
||||
import * as monacodts from './monaco-api';
|
||||
import * as nls from './nls';
|
||||
|
@ -41,12 +38,17 @@ function getTypeScriptCompilerOptions(src: string): ts.CompilerOptions {
|
|||
}
|
||||
|
||||
function createCompile(src: string, build: boolean, emitError?: boolean) {
|
||||
const tsb = require('gulp-tsb') as typeof import('gulp-tsb');
|
||||
const sourcemaps = require('gulp-sourcemaps') as typeof import('gulp-sourcemaps');
|
||||
|
||||
|
||||
const projectPath = path.join(__dirname, '../../', src, 'tsconfig.json');
|
||||
const overrideOptions = { ...getTypeScriptCompilerOptions(src), inlineSources: Boolean(build) };
|
||||
|
||||
const compilation = tsb.create(projectPath, overrideOptions, false, err => reporter(err));
|
||||
|
||||
function pipeline(token?: util.ICancellationToken) {
|
||||
const bom = require('gulp-bom') as typeof import('gulp-bom');
|
||||
|
||||
const utf8Filter = util.filter(data => /(\/|\\)test(\/|\\).*utf8/.test(data.path));
|
||||
const tsFilter = util.filter(data => /\.ts$/.test(data.path));
|
||||
|
@ -61,7 +63,7 @@ function createCompile(src: string, build: boolean, emitError?: boolean) {
|
|||
.pipe(util.loadSourcemaps())
|
||||
.pipe(compilation(token))
|
||||
.pipe(noDeclarationsFilter)
|
||||
.pipe(build ? nls() : es.through())
|
||||
.pipe(build ? nls.nls() : es.through())
|
||||
.pipe(noDeclarationsFilter.restore)
|
||||
.pipe(sourcemaps.write('.', {
|
||||
addComment: false,
|
||||
|
|
|
@ -9,10 +9,8 @@ const fs = require("fs");
|
|||
const path = require("path");
|
||||
const vfs = require("vinyl-fs");
|
||||
const filter = require("gulp-filter");
|
||||
const json = require("gulp-json-editor");
|
||||
const _ = require("underscore");
|
||||
const util = require("./util");
|
||||
const electron = require('gulp-atom-electron');
|
||||
const root = path.dirname(path.dirname(__dirname));
|
||||
const product = JSON.parse(fs.readFileSync(path.join(root, 'product.json'), 'utf8'));
|
||||
const commit = util.getVersion(root);
|
||||
|
@ -80,6 +78,8 @@ exports.config = {
|
|||
};
|
||||
function getElectron(arch) {
|
||||
return () => {
|
||||
const electron = require('gulp-atom-electron');
|
||||
const json = require('gulp-json-editor');
|
||||
const electronOpts = _.extend({}, exports.config, {
|
||||
platform: process.platform,
|
||||
arch: arch === 'armhf' ? 'arm' : arch,
|
||||
|
|
|
@ -9,12 +9,9 @@ import * as fs from 'fs';
|
|||
import * as path from 'path';
|
||||
import * as vfs from 'vinyl-fs';
|
||||
import * as filter from 'gulp-filter';
|
||||
import * as json from 'gulp-json-editor';
|
||||
import * as _ from 'underscore';
|
||||
import * as util from './util';
|
||||
|
||||
const electron = require('gulp-atom-electron');
|
||||
|
||||
const root = path.dirname(path.dirname(__dirname));
|
||||
const product = JSON.parse(fs.readFileSync(path.join(root, 'product.json'), 'utf8'));
|
||||
const commit = util.getVersion(root);
|
||||
|
@ -86,6 +83,9 @@ export const config = {
|
|||
|
||||
function getElectron(arch: string): () => NodeJS.ReadWriteStream {
|
||||
return () => {
|
||||
const electron = require('gulp-atom-electron');
|
||||
const json = require('gulp-json-editor') as typeof import('gulp-json-editor');
|
||||
|
||||
const electronOpts = _.extend({}, config, {
|
||||
platform: process.platform,
|
||||
arch: arch === 'armhf' ? 'arm' : arch,
|
||||
|
|
33
build/lib/eslint/vscode-dts-cancellation.js
Normal file
33
build/lib/eslint/vscode-dts-cancellation.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
"use strict";
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils");
|
||||
module.exports = new class ApiProviderNaming {
|
||||
constructor() {
|
||||
this.meta = {
|
||||
messages: {
|
||||
noToken: 'Function lacks a cancellation token, preferable as last argument',
|
||||
}
|
||||
};
|
||||
}
|
||||
create(context) {
|
||||
return {
|
||||
['TSInterfaceDeclaration[id.name=/.+Provider/] TSMethodSignature[key.name=/^(provide|resolve).+/]']: (node) => {
|
||||
let found = false;
|
||||
for (let param of node.params) {
|
||||
if (param.type === experimental_utils_1.AST_NODE_TYPES.Identifier) {
|
||||
found = found || param.name === 'token';
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: 'noToken'
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
38
build/lib/eslint/vscode-dts-cancellation.ts
Normal file
38
build/lib/eslint/vscode-dts-cancellation.ts
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as eslint from 'eslint';
|
||||
import { AST_NODE_TYPES, TSESTree } from '@typescript-eslint/experimental-utils';
|
||||
|
||||
export = new class ApiProviderNaming implements eslint.Rule.RuleModule {
|
||||
|
||||
readonly meta: eslint.Rule.RuleMetaData = {
|
||||
messages: {
|
||||
noToken: 'Function lacks a cancellation token, preferable as last argument',
|
||||
}
|
||||
};
|
||||
|
||||
create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener {
|
||||
|
||||
return {
|
||||
['TSInterfaceDeclaration[id.name=/.+Provider/] TSMethodSignature[key.name=/^(provide|resolve).+/]']: (node: any) => {
|
||||
|
||||
let found = false;
|
||||
for (let param of (<TSESTree.TSMethodSignature>node).params) {
|
||||
if (param.type === AST_NODE_TYPES.Identifier) {
|
||||
found = found || param.name === 'token';
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: 'noToken'
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
38
build/lib/eslint/vscode-dts-provider-naming.js
Normal file
38
build/lib/eslint/vscode-dts-provider-naming.js
Normal file
|
@ -0,0 +1,38 @@
|
|||
"use strict";
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
var _a;
|
||||
module.exports = new (_a = class ApiProviderNaming {
|
||||
constructor() {
|
||||
this.meta = {
|
||||
messages: {
|
||||
naming: 'A provider should only have functions like provideXYZ or resolveXYZ',
|
||||
}
|
||||
};
|
||||
}
|
||||
create(context) {
|
||||
const config = context.options[0];
|
||||
const allowed = new Set(config.allowed);
|
||||
return {
|
||||
['TSInterfaceDeclaration[id.name=/.+Provider/] TSMethodSignature']: (node) => {
|
||||
var _a;
|
||||
const interfaceName = ((_a = node.parent) === null || _a === void 0 ? void 0 : _a.parent).id.name;
|
||||
if (allowed.has(interfaceName)) {
|
||||
// allowed
|
||||
return;
|
||||
}
|
||||
const methodName = node.key.name;
|
||||
if (!ApiProviderNaming._providerFunctionNames.test(methodName)) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: 'naming'
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
_a._providerFunctionNames = /^(provide|resolve|prepare).+/,
|
||||
_a);
|
45
build/lib/eslint/vscode-dts-provider-naming.ts
Normal file
45
build/lib/eslint/vscode-dts-provider-naming.ts
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as eslint from 'eslint';
|
||||
import { TSESTree } from '@typescript-eslint/experimental-utils';
|
||||
|
||||
export = new class ApiProviderNaming implements eslint.Rule.RuleModule {
|
||||
|
||||
readonly meta: eslint.Rule.RuleMetaData = {
|
||||
messages: {
|
||||
naming: 'A provider should only have functions like provideXYZ or resolveXYZ',
|
||||
}
|
||||
};
|
||||
|
||||
private static _providerFunctionNames = /^(provide|resolve|prepare).+/;
|
||||
|
||||
create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener {
|
||||
|
||||
const config = <{ allowed: string[] }>context.options[0];
|
||||
const allowed = new Set(config.allowed);
|
||||
|
||||
return {
|
||||
['TSInterfaceDeclaration[id.name=/.+Provider/] TSMethodSignature']: (node: any) => {
|
||||
|
||||
|
||||
const interfaceName = (<TSESTree.TSInterfaceDeclaration>(<TSESTree.Identifier>node).parent?.parent).id.name;
|
||||
if (allowed.has(interfaceName)) {
|
||||
// allowed
|
||||
return;
|
||||
}
|
||||
|
||||
const methodName = (<any>(<TSESTree.TSMethodSignatureNonComputedName>node).key).name;
|
||||
|
||||
if (!ApiProviderNaming._providerFunctionNames.test(methodName)) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: 'naming'
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
|
@ -11,20 +11,15 @@ const glob = require("glob");
|
|||
const gulp = require("gulp");
|
||||
const path = require("path");
|
||||
const File = require("vinyl");
|
||||
const vsce = require("vsce");
|
||||
const stats_1 = require("./stats");
|
||||
const util2 = require("./util");
|
||||
const remote = require("gulp-remote-retry-src");
|
||||
const vzip = require('gulp-vinyl-zip');
|
||||
const filter = require("gulp-filter");
|
||||
const rename = require("gulp-rename");
|
||||
const fancyLog = require("fancy-log");
|
||||
const ansiColors = require("ansi-colors");
|
||||
const buffer = require('gulp-buffer');
|
||||
const json = require("gulp-json-editor");
|
||||
const jsoncParser = require("jsonc-parser");
|
||||
const webpack = require('webpack');
|
||||
const webpackGulp = require('webpack-stream');
|
||||
const util = require('./util');
|
||||
const root = path.dirname(path.dirname(__dirname));
|
||||
const commit = util.getVersion(root);
|
||||
|
@ -88,6 +83,9 @@ function fromLocalWebpack(extensionPath, webpackConfigFileName) {
|
|||
}
|
||||
}
|
||||
}
|
||||
const vsce = require('vsce');
|
||||
const webpack = require('webpack');
|
||||
const webpackGulp = require('webpack-stream');
|
||||
vsce.listFiles({ cwd: extensionPath, packageManager: vsce.PackageManager.Yarn, packagedDependencies }).then(fileNames => {
|
||||
const files = fileNames
|
||||
.map(fileName => path.join(extensionPath, fileName))
|
||||
|
@ -149,6 +147,7 @@ function fromLocalWebpack(extensionPath, webpackConfigFileName) {
|
|||
}
|
||||
function fromLocalNormal(extensionPath) {
|
||||
const result = es.through();
|
||||
const vsce = require('vsce');
|
||||
vsce.listFiles({ cwd: extensionPath, packageManager: vsce.PackageManager.Yarn })
|
||||
.then(fileNames => {
|
||||
const files = fileNames
|
||||
|
@ -170,6 +169,8 @@ const baseHeaders = {
|
|||
'X-Market-User-Id': '291C1CD0-051A-4123-9B4B-30D60EF52EE2',
|
||||
};
|
||||
function fromMarketplace(extensionName, version, metadata) {
|
||||
const remote = require('gulp-remote-retry-src');
|
||||
const json = require('gulp-json-editor');
|
||||
const [publisher, name] = extensionName.split('.');
|
||||
const url = `https://marketplace.visualstudio.com/_apis/public/gallery/publishers/${publisher}/vsextensions/${name}/${version}/vspackage`;
|
||||
fancyLog('Downloading extension:', ansiColors.yellow(`${extensionName}@${version}`), '...');
|
||||
|
|
|
@ -10,20 +10,15 @@ import * as gulp from 'gulp';
|
|||
import * as path from 'path';
|
||||
import { Stream } from 'stream';
|
||||
import * as File from 'vinyl';
|
||||
import * as vsce from 'vsce';
|
||||
import { createStatsStream } from './stats';
|
||||
import * as util2 from './util';
|
||||
import remote = require('gulp-remote-retry-src');
|
||||
const vzip = require('gulp-vinyl-zip');
|
||||
import filter = require('gulp-filter');
|
||||
import rename = require('gulp-rename');
|
||||
import * as fancyLog from 'fancy-log';
|
||||
import * as ansiColors from 'ansi-colors';
|
||||
const buffer = require('gulp-buffer');
|
||||
import json = require('gulp-json-editor');
|
||||
import * as jsoncParser from 'jsonc-parser';
|
||||
const webpack = require('webpack');
|
||||
const webpackGulp = require('webpack-stream');
|
||||
const util = require('./util');
|
||||
const root = path.dirname(path.dirname(__dirname));
|
||||
const commit = util.getVersion(root);
|
||||
|
@ -97,6 +92,10 @@ function fromLocalWebpack(extensionPath: string, webpackConfigFileName: string):
|
|||
}
|
||||
}
|
||||
|
||||
const vsce = require('vsce') as typeof import('vsce');
|
||||
const webpack = require('webpack');
|
||||
const webpackGulp = require('webpack-stream');
|
||||
|
||||
vsce.listFiles({ cwd: extensionPath, packageManager: vsce.PackageManager.Yarn, packagedDependencies }).then(fileNames => {
|
||||
const files = fileNames
|
||||
.map(fileName => path.join(extensionPath, fileName))
|
||||
|
@ -175,6 +174,8 @@ function fromLocalWebpack(extensionPath: string, webpackConfigFileName: string):
|
|||
function fromLocalNormal(extensionPath: string): Stream {
|
||||
const result = es.through();
|
||||
|
||||
const vsce = require('vsce') as typeof import('vsce');
|
||||
|
||||
vsce.listFiles({ cwd: extensionPath, packageManager: vsce.PackageManager.Yarn })
|
||||
.then(fileNames => {
|
||||
const files = fileNames
|
||||
|
@ -200,6 +201,9 @@ const baseHeaders = {
|
|||
};
|
||||
|
||||
export function fromMarketplace(extensionName: string, version: string, metadata: any): Stream {
|
||||
const remote = require('gulp-remote-retry-src');
|
||||
const json = require('gulp-json-editor') as typeof import('gulp-json-editor');
|
||||
|
||||
const [publisher, name] = extensionName.split('.');
|
||||
const url = `https://marketplace.visualstudio.com/_apis/public/gallery/publishers/${publisher}/vsextensions/${name}/${version}/vspackage`;
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.execute = exports.run3 = exports.DeclarationResolver = exports.FSProvider = exports.RECIPE_PATH = void 0;
|
||||
const fs = require("fs");
|
||||
const ts = require("typescript");
|
||||
const path = require("path");
|
||||
const fancyLog = require("fancy-log");
|
||||
const ansiColors = require("ansi-colors");
|
||||
|
@ -18,7 +17,7 @@ const DECLARATION_PATH = path.join(__dirname, '../../src/vs/monaco.d.ts');
|
|||
function logErr(message, ...rest) {
|
||||
fancyLog(ansiColors.yellow(`[monaco.d.ts]`), message, ...rest);
|
||||
}
|
||||
function isDeclaration(a) {
|
||||
function isDeclaration(ts, a) {
|
||||
return (a.kind === ts.SyntaxKind.InterfaceDeclaration
|
||||
|| a.kind === ts.SyntaxKind.EnumDeclaration
|
||||
|| a.kind === ts.SyntaxKind.ClassDeclaration
|
||||
|
@ -26,7 +25,7 @@ function isDeclaration(a) {
|
|||
|| a.kind === ts.SyntaxKind.FunctionDeclaration
|
||||
|| a.kind === ts.SyntaxKind.ModuleDeclaration);
|
||||
}
|
||||
function visitTopLevelDeclarations(sourceFile, visitor) {
|
||||
function visitTopLevelDeclarations(ts, sourceFile, visitor) {
|
||||
let stop = false;
|
||||
let visit = (node) => {
|
||||
if (stop) {
|
||||
|
@ -49,9 +48,9 @@ function visitTopLevelDeclarations(sourceFile, visitor) {
|
|||
};
|
||||
visit(sourceFile);
|
||||
}
|
||||
function getAllTopLevelDeclarations(sourceFile) {
|
||||
function getAllTopLevelDeclarations(ts, sourceFile) {
|
||||
let all = [];
|
||||
visitTopLevelDeclarations(sourceFile, (node) => {
|
||||
visitTopLevelDeclarations(ts, sourceFile, (node) => {
|
||||
if (node.kind === ts.SyntaxKind.InterfaceDeclaration || node.kind === ts.SyntaxKind.ClassDeclaration || node.kind === ts.SyntaxKind.ModuleDeclaration) {
|
||||
let interfaceDeclaration = node;
|
||||
let triviaStart = interfaceDeclaration.pos;
|
||||
|
@ -71,10 +70,10 @@ function getAllTopLevelDeclarations(sourceFile) {
|
|||
});
|
||||
return all;
|
||||
}
|
||||
function getTopLevelDeclaration(sourceFile, typeName) {
|
||||
function getTopLevelDeclaration(ts, sourceFile, typeName) {
|
||||
let result = null;
|
||||
visitTopLevelDeclarations(sourceFile, (node) => {
|
||||
if (isDeclaration(node) && node.name) {
|
||||
visitTopLevelDeclarations(ts, sourceFile, (node) => {
|
||||
if (isDeclaration(ts, node) && node.name) {
|
||||
if (node.name.text === typeName) {
|
||||
result = node;
|
||||
return true /*stop*/;
|
||||
|
@ -104,18 +103,18 @@ function hasModifier(modifiers, kind) {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
function isStatic(member) {
|
||||
function isStatic(ts, member) {
|
||||
return hasModifier(member.modifiers, ts.SyntaxKind.StaticKeyword);
|
||||
}
|
||||
function isDefaultExport(declaration) {
|
||||
function isDefaultExport(ts, declaration) {
|
||||
return (hasModifier(declaration.modifiers, ts.SyntaxKind.DefaultKeyword)
|
||||
&& hasModifier(declaration.modifiers, ts.SyntaxKind.ExportKeyword));
|
||||
}
|
||||
function getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, usage, enums) {
|
||||
function getMassagedTopLevelDeclarationText(ts, sourceFile, declaration, importName, usage, enums) {
|
||||
let result = getNodeText(sourceFile, declaration);
|
||||
if (declaration.kind === ts.SyntaxKind.InterfaceDeclaration || declaration.kind === ts.SyntaxKind.ClassDeclaration) {
|
||||
let interfaceDeclaration = declaration;
|
||||
const staticTypeName = (isDefaultExport(interfaceDeclaration)
|
||||
const staticTypeName = (isDefaultExport(ts, interfaceDeclaration)
|
||||
? `${importName}.default`
|
||||
: `${importName}.${declaration.name.text}`);
|
||||
let instanceTypeName = staticTypeName;
|
||||
|
@ -137,7 +136,7 @@ function getMassagedTopLevelDeclarationText(sourceFile, declaration, importName,
|
|||
else {
|
||||
const memberName = member.name.text;
|
||||
const memberAccess = (memberName.indexOf('.') >= 0 ? `['${memberName}']` : `.${memberName}`);
|
||||
if (isStatic(member)) {
|
||||
if (isStatic(ts, member)) {
|
||||
usage.push(`a = ${staticTypeName}${memberAccess};`);
|
||||
}
|
||||
else {
|
||||
|
@ -191,7 +190,7 @@ function getMassagedTopLevelDeclarationText(sourceFile, declaration, importName,
|
|||
}
|
||||
return result;
|
||||
}
|
||||
function format(text, endl) {
|
||||
function format(ts, text, endl) {
|
||||
const REALLY_FORMAT = false;
|
||||
text = preformat(text, endl);
|
||||
if (!REALLY_FORMAT) {
|
||||
|
@ -336,7 +335,7 @@ function createReplacer(data) {
|
|||
});
|
||||
return createReplacerFromDirectives(directives);
|
||||
}
|
||||
function generateDeclarationFile(recipe, sourceFileGetter) {
|
||||
function generateDeclarationFile(ts, recipe, sourceFileGetter) {
|
||||
const endl = /\r\n/.test(recipe) ? '\r\n' : '\n';
|
||||
let lines = recipe.split(endl);
|
||||
let result = [];
|
||||
|
@ -379,14 +378,14 @@ function generateDeclarationFile(recipe, sourceFileGetter) {
|
|||
if (typeName.length === 0) {
|
||||
return;
|
||||
}
|
||||
let declaration = getTopLevelDeclaration(sourceFile, typeName);
|
||||
let declaration = getTopLevelDeclaration(ts, sourceFile, typeName);
|
||||
if (!declaration) {
|
||||
logErr(`While handling ${line}`);
|
||||
logErr(`Cannot find ${typeName}`);
|
||||
failed = true;
|
||||
return;
|
||||
}
|
||||
result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, usage, enums)));
|
||||
result.push(replacer(getMassagedTopLevelDeclarationText(ts, sourceFile, declaration, importName, usage, enums)));
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -413,8 +412,8 @@ function generateDeclarationFile(recipe, sourceFileGetter) {
|
|||
typesToExcludeMap[typeName] = true;
|
||||
typesToExcludeArr.push(typeName);
|
||||
});
|
||||
getAllTopLevelDeclarations(sourceFile).forEach((declaration) => {
|
||||
if (isDeclaration(declaration) && declaration.name) {
|
||||
getAllTopLevelDeclarations(ts, sourceFile).forEach((declaration) => {
|
||||
if (isDeclaration(ts, declaration) && declaration.name) {
|
||||
if (typesToExcludeMap[declaration.name.text]) {
|
||||
return;
|
||||
}
|
||||
|
@ -428,7 +427,7 @@ function generateDeclarationFile(recipe, sourceFileGetter) {
|
|||
}
|
||||
}
|
||||
}
|
||||
result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, usage, enums)));
|
||||
result.push(replacer(getMassagedTopLevelDeclarationText(ts, sourceFile, declaration, importName, usage, enums)));
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -450,7 +449,7 @@ function generateDeclarationFile(recipe, sourceFileGetter) {
|
|||
resultTxt = resultTxt.replace(/\bURI\b/g, 'Uri');
|
||||
resultTxt = resultTxt.replace(/\bEvent</g, 'IEvent<');
|
||||
resultTxt = resultTxt.split(/\r\n|\n|\r/).join(endl);
|
||||
resultTxt = format(resultTxt, endl);
|
||||
resultTxt = format(ts, resultTxt, endl);
|
||||
resultTxt = resultTxt.split(/\r\n|\n|\r/).join(endl);
|
||||
enums.sort((e1, e2) => {
|
||||
if (e1.enumName < e2.enumName) {
|
||||
|
@ -471,7 +470,7 @@ function generateDeclarationFile(recipe, sourceFileGetter) {
|
|||
''
|
||||
].concat(enums.map(e => e.text)).join(endl);
|
||||
resultEnums = resultEnums.split(/\r\n|\n|\r/).join(endl);
|
||||
resultEnums = format(resultEnums, endl);
|
||||
resultEnums = format(ts, resultEnums, endl);
|
||||
resultEnums = resultEnums.split(/\r\n|\n|\r/).join(endl);
|
||||
return {
|
||||
result: resultTxt,
|
||||
|
@ -479,9 +478,9 @@ function generateDeclarationFile(recipe, sourceFileGetter) {
|
|||
enums: resultEnums
|
||||
};
|
||||
}
|
||||
function _run(sourceFileGetter) {
|
||||
function _run(ts, sourceFileGetter) {
|
||||
const recipe = fs.readFileSync(exports.RECIPE_PATH).toString();
|
||||
const t = generateDeclarationFile(recipe, sourceFileGetter);
|
||||
const t = generateDeclarationFile(ts, recipe, sourceFileGetter);
|
||||
if (!t) {
|
||||
return null;
|
||||
}
|
||||
|
@ -521,6 +520,7 @@ class CacheEntry {
|
|||
class DeclarationResolver {
|
||||
constructor(_fsProvider) {
|
||||
this._fsProvider = _fsProvider;
|
||||
this.ts = require('typescript');
|
||||
this._sourceFileCache = Object.create(null);
|
||||
}
|
||||
invalidateCache(moduleId) {
|
||||
|
@ -555,25 +555,26 @@ class DeclarationResolver {
|
|||
if (/\.d\.ts$/.test(moduleId)) {
|
||||
// const mtime = this._fsProvider.statFileSync()
|
||||
const fileContents = this._fsProvider.readFileSync(moduleId, fileName).toString();
|
||||
return new CacheEntry(ts.createSourceFile(fileName, fileContents, ts.ScriptTarget.ES5), mtime);
|
||||
return new CacheEntry(this.ts.createSourceFile(fileName, fileContents, this.ts.ScriptTarget.ES5), mtime);
|
||||
}
|
||||
const fileContents = this._fsProvider.readFileSync(moduleId, fileName).toString();
|
||||
const fileMap = {
|
||||
'file.ts': fileContents
|
||||
};
|
||||
const service = ts.createLanguageService(new TypeScriptLanguageServiceHost({}, fileMap, {}));
|
||||
const service = this.ts.createLanguageService(new TypeScriptLanguageServiceHost(this.ts, {}, fileMap, {}));
|
||||
const text = service.getEmitOutput('file.ts', true, true).outputFiles[0].text;
|
||||
return new CacheEntry(ts.createSourceFile(fileName, text, ts.ScriptTarget.ES5), mtime);
|
||||
return new CacheEntry(this.ts.createSourceFile(fileName, text, this.ts.ScriptTarget.ES5), mtime);
|
||||
}
|
||||
}
|
||||
exports.DeclarationResolver = DeclarationResolver;
|
||||
function run3(resolver) {
|
||||
const sourceFileGetter = (moduleId) => resolver.getDeclarationSourceFile(moduleId);
|
||||
return _run(sourceFileGetter);
|
||||
return _run(resolver.ts, sourceFileGetter);
|
||||
}
|
||||
exports.run3 = run3;
|
||||
class TypeScriptLanguageServiceHost {
|
||||
constructor(libs, files, compilerOptions) {
|
||||
constructor(ts, libs, files, compilerOptions) {
|
||||
this._ts = ts;
|
||||
this._libs = libs;
|
||||
this._files = files;
|
||||
this._compilerOptions = compilerOptions;
|
||||
|
@ -595,17 +596,17 @@ class TypeScriptLanguageServiceHost {
|
|||
}
|
||||
getScriptSnapshot(fileName) {
|
||||
if (this._files.hasOwnProperty(fileName)) {
|
||||
return ts.ScriptSnapshot.fromString(this._files[fileName]);
|
||||
return this._ts.ScriptSnapshot.fromString(this._files[fileName]);
|
||||
}
|
||||
else if (this._libs.hasOwnProperty(fileName)) {
|
||||
return ts.ScriptSnapshot.fromString(this._libs[fileName]);
|
||||
return this._ts.ScriptSnapshot.fromString(this._libs[fileName]);
|
||||
}
|
||||
else {
|
||||
return ts.ScriptSnapshot.fromString('');
|
||||
return this._ts.ScriptSnapshot.fromString('');
|
||||
}
|
||||
}
|
||||
getScriptKind(_fileName) {
|
||||
return ts.ScriptKind.TS;
|
||||
return this._ts.ScriptKind.TS;
|
||||
}
|
||||
getCurrentDirectory() {
|
||||
return '';
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as ts from 'typescript';
|
||||
import type * as ts from 'typescript';
|
||||
import * as path from 'path';
|
||||
import * as fancyLog from 'fancy-log';
|
||||
import * as ansiColors from 'ansi-colors';
|
||||
|
@ -26,7 +26,7 @@ type SourceFileGetter = (moduleId: string) => ts.SourceFile | null;
|
|||
type TSTopLevelDeclaration = ts.InterfaceDeclaration | ts.EnumDeclaration | ts.ClassDeclaration | ts.TypeAliasDeclaration | ts.FunctionDeclaration | ts.ModuleDeclaration;
|
||||
type TSTopLevelDeclare = TSTopLevelDeclaration | ts.VariableStatement;
|
||||
|
||||
function isDeclaration(a: TSTopLevelDeclare): a is TSTopLevelDeclaration {
|
||||
function isDeclaration(ts: typeof import('typescript'), a: TSTopLevelDeclare): a is TSTopLevelDeclaration {
|
||||
return (
|
||||
a.kind === ts.SyntaxKind.InterfaceDeclaration
|
||||
|| a.kind === ts.SyntaxKind.EnumDeclaration
|
||||
|
@ -37,7 +37,7 @@ function isDeclaration(a: TSTopLevelDeclare): a is TSTopLevelDeclaration {
|
|||
);
|
||||
}
|
||||
|
||||
function visitTopLevelDeclarations(sourceFile: ts.SourceFile, visitor: (node: TSTopLevelDeclare) => boolean): void {
|
||||
function visitTopLevelDeclarations(ts: typeof import('typescript'), sourceFile: ts.SourceFile, visitor: (node: TSTopLevelDeclare) => boolean): void {
|
||||
let stop = false;
|
||||
|
||||
let visit = (node: ts.Node): void => {
|
||||
|
@ -66,9 +66,9 @@ function visitTopLevelDeclarations(sourceFile: ts.SourceFile, visitor: (node: TS
|
|||
}
|
||||
|
||||
|
||||
function getAllTopLevelDeclarations(sourceFile: ts.SourceFile): TSTopLevelDeclare[] {
|
||||
function getAllTopLevelDeclarations(ts: typeof import('typescript'), sourceFile: ts.SourceFile): TSTopLevelDeclare[] {
|
||||
let all: TSTopLevelDeclare[] = [];
|
||||
visitTopLevelDeclarations(sourceFile, (node) => {
|
||||
visitTopLevelDeclarations(ts, sourceFile, (node) => {
|
||||
if (node.kind === ts.SyntaxKind.InterfaceDeclaration || node.kind === ts.SyntaxKind.ClassDeclaration || node.kind === ts.SyntaxKind.ModuleDeclaration) {
|
||||
let interfaceDeclaration = <ts.InterfaceDeclaration>node;
|
||||
let triviaStart = interfaceDeclaration.pos;
|
||||
|
@ -90,10 +90,10 @@ function getAllTopLevelDeclarations(sourceFile: ts.SourceFile): TSTopLevelDeclar
|
|||
}
|
||||
|
||||
|
||||
function getTopLevelDeclaration(sourceFile: ts.SourceFile, typeName: string): TSTopLevelDeclare | null {
|
||||
function getTopLevelDeclaration(ts: typeof import('typescript'), sourceFile: ts.SourceFile, typeName: string): TSTopLevelDeclare | null {
|
||||
let result: TSTopLevelDeclare | null = null;
|
||||
visitTopLevelDeclarations(sourceFile, (node) => {
|
||||
if (isDeclaration(node) && node.name) {
|
||||
visitTopLevelDeclarations(ts, sourceFile, (node) => {
|
||||
if (isDeclaration(ts, node) && node.name) {
|
||||
if (node.name.text === typeName) {
|
||||
result = node;
|
||||
return true /*stop*/;
|
||||
|
@ -127,24 +127,24 @@ function hasModifier(modifiers: ts.NodeArray<ts.Modifier> | undefined, kind: ts.
|
|||
return false;
|
||||
}
|
||||
|
||||
function isStatic(member: ts.ClassElement | ts.TypeElement): boolean {
|
||||
function isStatic(ts: typeof import('typescript'), member: ts.ClassElement | ts.TypeElement): boolean {
|
||||
return hasModifier(member.modifiers, ts.SyntaxKind.StaticKeyword);
|
||||
}
|
||||
|
||||
function isDefaultExport(declaration: ts.InterfaceDeclaration | ts.ClassDeclaration): boolean {
|
||||
function isDefaultExport(ts: typeof import('typescript'), declaration: ts.InterfaceDeclaration | ts.ClassDeclaration): boolean {
|
||||
return (
|
||||
hasModifier(declaration.modifiers, ts.SyntaxKind.DefaultKeyword)
|
||||
&& hasModifier(declaration.modifiers, ts.SyntaxKind.ExportKeyword)
|
||||
);
|
||||
}
|
||||
|
||||
function getMassagedTopLevelDeclarationText(sourceFile: ts.SourceFile, declaration: TSTopLevelDeclare, importName: string, usage: string[], enums: IEnumEntry[]): string {
|
||||
function getMassagedTopLevelDeclarationText(ts: typeof import('typescript'), sourceFile: ts.SourceFile, declaration: TSTopLevelDeclare, importName: string, usage: string[], enums: IEnumEntry[]): string {
|
||||
let result = getNodeText(sourceFile, declaration);
|
||||
if (declaration.kind === ts.SyntaxKind.InterfaceDeclaration || declaration.kind === ts.SyntaxKind.ClassDeclaration) {
|
||||
let interfaceDeclaration = <ts.InterfaceDeclaration | ts.ClassDeclaration>declaration;
|
||||
|
||||
const staticTypeName = (
|
||||
isDefaultExport(interfaceDeclaration)
|
||||
isDefaultExport(ts, interfaceDeclaration)
|
||||
? `${importName}.default`
|
||||
: `${importName}.${declaration.name!.text}`
|
||||
);
|
||||
|
@ -168,7 +168,7 @@ function getMassagedTopLevelDeclarationText(sourceFile: ts.SourceFile, declarati
|
|||
} else {
|
||||
const memberName = (<ts.Identifier | ts.StringLiteral>member.name).text;
|
||||
const memberAccess = (memberName.indexOf('.') >= 0 ? `['${memberName}']` : `.${memberName}`);
|
||||
if (isStatic(member)) {
|
||||
if (isStatic(ts, member)) {
|
||||
usage.push(`a = ${staticTypeName}${memberAccess};`);
|
||||
} else {
|
||||
usage.push(`a = (<${instanceTypeName}>b)${memberAccess};`);
|
||||
|
@ -222,7 +222,7 @@ function getMassagedTopLevelDeclarationText(sourceFile: ts.SourceFile, declarati
|
|||
return result;
|
||||
}
|
||||
|
||||
function format(text: string, endl: string): string {
|
||||
function format(ts: typeof import('typescript'), text: string, endl: string): string {
|
||||
const REALLY_FORMAT = false;
|
||||
|
||||
text = preformat(text, endl);
|
||||
|
@ -396,7 +396,7 @@ interface IEnumEntry {
|
|||
text: string;
|
||||
}
|
||||
|
||||
function generateDeclarationFile(recipe: string, sourceFileGetter: SourceFileGetter): ITempResult | null {
|
||||
function generateDeclarationFile(ts: typeof import('typescript'), recipe: string, sourceFileGetter: SourceFileGetter): ITempResult | null {
|
||||
const endl = /\r\n/.test(recipe) ? '\r\n' : '\n';
|
||||
|
||||
let lines = recipe.split(endl);
|
||||
|
@ -452,14 +452,14 @@ function generateDeclarationFile(recipe: string, sourceFileGetter: SourceFileGet
|
|||
if (typeName.length === 0) {
|
||||
return;
|
||||
}
|
||||
let declaration = getTopLevelDeclaration(sourceFile, typeName);
|
||||
let declaration = getTopLevelDeclaration(ts, sourceFile, typeName);
|
||||
if (!declaration) {
|
||||
logErr(`While handling ${line}`);
|
||||
logErr(`Cannot find ${typeName}`);
|
||||
failed = true;
|
||||
return;
|
||||
}
|
||||
result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, usage, enums)));
|
||||
result.push(replacer(getMassagedTopLevelDeclarationText(ts, sourceFile, declaration, importName, usage, enums)));
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -491,8 +491,8 @@ function generateDeclarationFile(recipe: string, sourceFileGetter: SourceFileGet
|
|||
typesToExcludeArr.push(typeName);
|
||||
});
|
||||
|
||||
getAllTopLevelDeclarations(sourceFile).forEach((declaration) => {
|
||||
if (isDeclaration(declaration) && declaration.name) {
|
||||
getAllTopLevelDeclarations(ts, sourceFile).forEach((declaration) => {
|
||||
if (isDeclaration(ts, declaration) && declaration.name) {
|
||||
if (typesToExcludeMap[declaration.name.text]) {
|
||||
return;
|
||||
}
|
||||
|
@ -505,7 +505,7 @@ function generateDeclarationFile(recipe: string, sourceFileGetter: SourceFileGet
|
|||
}
|
||||
}
|
||||
}
|
||||
result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, usage, enums)));
|
||||
result.push(replacer(getMassagedTopLevelDeclarationText(ts, sourceFile, declaration, importName, usage, enums)));
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -530,7 +530,7 @@ function generateDeclarationFile(recipe: string, sourceFileGetter: SourceFileGet
|
|||
resultTxt = resultTxt.replace(/\bURI\b/g, 'Uri');
|
||||
resultTxt = resultTxt.replace(/\bEvent</g, 'IEvent<');
|
||||
resultTxt = resultTxt.split(/\r\n|\n|\r/).join(endl);
|
||||
resultTxt = format(resultTxt, endl);
|
||||
resultTxt = format(ts, resultTxt, endl);
|
||||
resultTxt = resultTxt.split(/\r\n|\n|\r/).join(endl);
|
||||
|
||||
enums.sort((e1, e2) => {
|
||||
|
@ -553,7 +553,7 @@ function generateDeclarationFile(recipe: string, sourceFileGetter: SourceFileGet
|
|||
''
|
||||
].concat(enums.map(e => e.text)).join(endl);
|
||||
resultEnums = resultEnums.split(/\r\n|\n|\r/).join(endl);
|
||||
resultEnums = format(resultEnums, endl);
|
||||
resultEnums = format(ts, resultEnums, endl);
|
||||
resultEnums = resultEnums.split(/\r\n|\n|\r/).join(endl);
|
||||
|
||||
return {
|
||||
|
@ -571,9 +571,9 @@ export interface IMonacoDeclarationResult {
|
|||
isTheSame: boolean;
|
||||
}
|
||||
|
||||
function _run(sourceFileGetter: SourceFileGetter): IMonacoDeclarationResult | null {
|
||||
function _run(ts: typeof import('typescript'), sourceFileGetter: SourceFileGetter): IMonacoDeclarationResult | null {
|
||||
const recipe = fs.readFileSync(RECIPE_PATH).toString();
|
||||
const t = generateDeclarationFile(recipe, sourceFileGetter);
|
||||
const t = generateDeclarationFile(ts, recipe, sourceFileGetter);
|
||||
if (!t) {
|
||||
return null;
|
||||
}
|
||||
|
@ -617,9 +617,11 @@ class CacheEntry {
|
|||
|
||||
export class DeclarationResolver {
|
||||
|
||||
public readonly ts: typeof import('typescript');
|
||||
private _sourceFileCache: { [moduleId: string]: CacheEntry | null; };
|
||||
|
||||
constructor(private readonly _fsProvider: FSProvider) {
|
||||
this.ts = require('typescript') as typeof import('typescript');
|
||||
this._sourceFileCache = Object.create(null);
|
||||
}
|
||||
|
||||
|
@ -659,7 +661,7 @@ export class DeclarationResolver {
|
|||
// const mtime = this._fsProvider.statFileSync()
|
||||
const fileContents = this._fsProvider.readFileSync(moduleId, fileName).toString();
|
||||
return new CacheEntry(
|
||||
ts.createSourceFile(fileName, fileContents, ts.ScriptTarget.ES5),
|
||||
this.ts.createSourceFile(fileName, fileContents, this.ts.ScriptTarget.ES5),
|
||||
mtime
|
||||
);
|
||||
}
|
||||
|
@ -667,10 +669,10 @@ export class DeclarationResolver {
|
|||
const fileMap: IFileMap = {
|
||||
'file.ts': fileContents
|
||||
};
|
||||
const service = ts.createLanguageService(new TypeScriptLanguageServiceHost({}, fileMap, {}));
|
||||
const service = this.ts.createLanguageService(new TypeScriptLanguageServiceHost(this.ts, {}, fileMap, {}));
|
||||
const text = service.getEmitOutput('file.ts', true, true).outputFiles[0].text;
|
||||
return new CacheEntry(
|
||||
ts.createSourceFile(fileName, text, ts.ScriptTarget.ES5),
|
||||
this.ts.createSourceFile(fileName, text, this.ts.ScriptTarget.ES5),
|
||||
mtime
|
||||
);
|
||||
}
|
||||
|
@ -678,7 +680,7 @@ export class DeclarationResolver {
|
|||
|
||||
export function run3(resolver: DeclarationResolver): IMonacoDeclarationResult | null {
|
||||
const sourceFileGetter = (moduleId: string) => resolver.getDeclarationSourceFile(moduleId);
|
||||
return _run(sourceFileGetter);
|
||||
return _run(resolver.ts, sourceFileGetter);
|
||||
}
|
||||
|
||||
|
||||
|
@ -689,11 +691,13 @@ interface IFileMap { [fileName: string]: string; }
|
|||
|
||||
class TypeScriptLanguageServiceHost implements ts.LanguageServiceHost {
|
||||
|
||||
private readonly _ts: typeof import('typescript');
|
||||
private readonly _libs: ILibMap;
|
||||
private readonly _files: IFileMap;
|
||||
private readonly _compilerOptions: ts.CompilerOptions;
|
||||
|
||||
constructor(libs: ILibMap, files: IFileMap, compilerOptions: ts.CompilerOptions) {
|
||||
constructor(ts: typeof import('typescript'), libs: ILibMap, files: IFileMap, compilerOptions: ts.CompilerOptions) {
|
||||
this._ts = ts;
|
||||
this._libs = libs;
|
||||
this._files = files;
|
||||
this._compilerOptions = compilerOptions;
|
||||
|
@ -719,15 +723,15 @@ class TypeScriptLanguageServiceHost implements ts.LanguageServiceHost {
|
|||
}
|
||||
getScriptSnapshot(fileName: string): ts.IScriptSnapshot {
|
||||
if (this._files.hasOwnProperty(fileName)) {
|
||||
return ts.ScriptSnapshot.fromString(this._files[fileName]);
|
||||
return this._ts.ScriptSnapshot.fromString(this._files[fileName]);
|
||||
} else if (this._libs.hasOwnProperty(fileName)) {
|
||||
return ts.ScriptSnapshot.fromString(this._libs[fileName]);
|
||||
return this._ts.ScriptSnapshot.fromString(this._libs[fileName]);
|
||||
} else {
|
||||
return ts.ScriptSnapshot.fromString('');
|
||||
return this._ts.ScriptSnapshot.fromString('');
|
||||
}
|
||||
}
|
||||
getScriptKind(_fileName: string): ts.ScriptKind {
|
||||
return ts.ScriptKind.TS;
|
||||
return this._ts.ScriptKind.TS;
|
||||
}
|
||||
getCurrentDirectory(): string {
|
||||
return '';
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
const ts = require("typescript");
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.nls = void 0;
|
||||
const lazy = require("lazy.js");
|
||||
const event_stream_1 = require("event-stream");
|
||||
const File = require("vinyl");
|
||||
|
@ -16,7 +17,7 @@ var CollectStepResult;
|
|||
CollectStepResult[CollectStepResult["No"] = 2] = "No";
|
||||
CollectStepResult[CollectStepResult["NoAndRecurse"] = 3] = "NoAndRecurse";
|
||||
})(CollectStepResult || (CollectStepResult = {}));
|
||||
function collect(node, fn) {
|
||||
function collect(ts, node, fn) {
|
||||
const result = [];
|
||||
function loop(node) {
|
||||
const stepResult = fn(node);
|
||||
|
@ -69,14 +70,16 @@ function nls() {
|
|||
if (!typescript) {
|
||||
return this.emit('error', new Error(`File ${f.relative} does not have the original content in the source map.`));
|
||||
}
|
||||
nls.patchFiles(f, typescript).forEach(f => this.emit('data', f));
|
||||
_nls.patchFiles(f, typescript).forEach(f => this.emit('data', f));
|
||||
}));
|
||||
return event_stream_1.duplex(input, output);
|
||||
}
|
||||
function isImportNode(node) {
|
||||
exports.nls = nls;
|
||||
function isImportNode(ts, node) {
|
||||
return node.kind === ts.SyntaxKind.ImportDeclaration || node.kind === ts.SyntaxKind.ImportEqualsDeclaration;
|
||||
}
|
||||
(function (nls_1) {
|
||||
var _nls;
|
||||
(function (_nls) {
|
||||
function fileFrom(file, contents, path = file.path) {
|
||||
return new File({
|
||||
contents: Buffer.from(contents),
|
||||
|
@ -85,17 +88,14 @@ function isImportNode(node) {
|
|||
path: path
|
||||
});
|
||||
}
|
||||
nls_1.fileFrom = fileFrom;
|
||||
function mappedPositionFrom(source, lc) {
|
||||
return { source, line: lc.line + 1, column: lc.character };
|
||||
}
|
||||
nls_1.mappedPositionFrom = mappedPositionFrom;
|
||||
function lcFrom(position) {
|
||||
return { line: position.line - 1, character: position.column };
|
||||
}
|
||||
nls_1.lcFrom = lcFrom;
|
||||
class SingleFileServiceHost {
|
||||
constructor(options, filename, contents) {
|
||||
constructor(ts, options, filename, contents) {
|
||||
this.options = options;
|
||||
this.filename = filename;
|
||||
this.getCompilationSettings = () => this.options;
|
||||
|
@ -108,20 +108,19 @@ function isImportNode(node) {
|
|||
this.lib = ts.ScriptSnapshot.fromString('');
|
||||
}
|
||||
}
|
||||
nls_1.SingleFileServiceHost = SingleFileServiceHost;
|
||||
function isCallExpressionWithinTextSpanCollectStep(textSpan, node) {
|
||||
function isCallExpressionWithinTextSpanCollectStep(ts, textSpan, node) {
|
||||
if (!ts.textSpanContainsTextSpan({ start: node.pos, length: node.end - node.pos }, textSpan)) {
|
||||
return CollectStepResult.No;
|
||||
}
|
||||
return node.kind === ts.SyntaxKind.CallExpression ? CollectStepResult.YesAndRecurse : CollectStepResult.NoAndRecurse;
|
||||
}
|
||||
function analyze(contents, options = {}) {
|
||||
function analyze(ts, contents, options = {}) {
|
||||
const filename = 'file.ts';
|
||||
const serviceHost = new SingleFileServiceHost(Object.assign(clone(options), { noResolve: true }), filename, contents);
|
||||
const serviceHost = new SingleFileServiceHost(ts, Object.assign(clone(options), { noResolve: true }), filename, contents);
|
||||
const service = ts.createLanguageService(serviceHost);
|
||||
const sourceFile = ts.createSourceFile(filename, contents, ts.ScriptTarget.ES5, true);
|
||||
// all imports
|
||||
const imports = lazy(collect(sourceFile, n => isImportNode(n) ? CollectStepResult.YesAndRecurse : CollectStepResult.NoAndRecurse));
|
||||
const imports = lazy(collect(ts, sourceFile, n => isImportNode(ts, n) ? CollectStepResult.YesAndRecurse : CollectStepResult.NoAndRecurse));
|
||||
// import nls = require('vs/nls');
|
||||
const importEqualsDeclarations = imports
|
||||
.filter(n => n.kind === ts.SyntaxKind.ImportEqualsDeclaration)
|
||||
|
@ -152,7 +151,7 @@ function isImportNode(node) {
|
|||
.flatten()
|
||||
.filter(r => !r.isWriteAccess)
|
||||
// find the deepest call expressions AST nodes that contain those references
|
||||
.map(r => collect(sourceFile, n => isCallExpressionWithinTextSpanCollectStep(r.textSpan, n)))
|
||||
.map(r => collect(ts, sourceFile, n => isCallExpressionWithinTextSpanCollectStep(ts, r.textSpan, n)))
|
||||
.map(a => lazy(a).last())
|
||||
.filter(n => !!n)
|
||||
.map(n => n)
|
||||
|
@ -178,7 +177,7 @@ function isImportNode(node) {
|
|||
// find the deepest call expressions AST nodes that contain those references
|
||||
const localizeCallExpressions = localizeReferences
|
||||
.concat(namedLocalizeReferences)
|
||||
.map(r => collect(sourceFile, n => isCallExpressionWithinTextSpanCollectStep(r.textSpan, n)))
|
||||
.map(r => collect(ts, sourceFile, n => isCallExpressionWithinTextSpanCollectStep(ts, r.textSpan, n)))
|
||||
.map(a => lazy(a).last())
|
||||
.filter(n => !!n)
|
||||
.map(n => n);
|
||||
|
@ -199,7 +198,6 @@ function isImportNode(node) {
|
|||
nlsExpressions: nlsExpressions.toArray()
|
||||
};
|
||||
}
|
||||
nls_1.analyze = analyze;
|
||||
class TextModel {
|
||||
constructor(contents) {
|
||||
const regex = /\r\n|\r|\n/g;
|
||||
|
@ -250,9 +248,8 @@ function isImportNode(node) {
|
|||
.flatten().toArray().join('');
|
||||
}
|
||||
}
|
||||
nls_1.TextModel = TextModel;
|
||||
function patchJavascript(patches, contents, moduleId) {
|
||||
const model = new nls.TextModel(contents);
|
||||
const model = new TextModel(contents);
|
||||
// patch the localize calls
|
||||
lazy(patches).reverse().each(p => model.apply(p));
|
||||
// patch the 'vs/nls' imports
|
||||
|
@ -261,7 +258,6 @@ function isImportNode(node) {
|
|||
model.set(0, patchedFirstLine);
|
||||
return model.toString();
|
||||
}
|
||||
nls_1.patchJavascript = patchJavascript;
|
||||
function patchSourcemap(patches, rsm, smc) {
|
||||
const smg = new sm.SourceMapGenerator({
|
||||
file: rsm.file,
|
||||
|
@ -297,9 +293,8 @@ function isImportNode(node) {
|
|||
}
|
||||
return JSON.parse(smg.toString());
|
||||
}
|
||||
nls_1.patchSourcemap = patchSourcemap;
|
||||
function patch(moduleId, typescript, javascript, sourcemap) {
|
||||
const { localizeCalls, nlsExpressions } = analyze(typescript);
|
||||
function patch(ts, moduleId, typescript, javascript, sourcemap) {
|
||||
const { localizeCalls, nlsExpressions } = analyze(ts, typescript);
|
||||
if (localizeCalls.length === 0) {
|
||||
return { javascript, sourcemap };
|
||||
}
|
||||
|
@ -332,13 +327,13 @@ function isImportNode(node) {
|
|||
sourcemap = patchSourcemap(patches, sourcemap, smc);
|
||||
return { javascript, sourcemap, nlsKeys, nls };
|
||||
}
|
||||
nls_1.patch = patch;
|
||||
function patchFiles(javascriptFile, typescript) {
|
||||
const ts = require('typescript');
|
||||
// hack?
|
||||
const moduleId = javascriptFile.relative
|
||||
.replace(/\.js$/, '')
|
||||
.replace(/\\/g, '/');
|
||||
const { javascript, sourcemap, nlsKeys, nls } = patch(moduleId, typescript, javascriptFile.contents.toString(), javascriptFile.sourceMap);
|
||||
const { javascript, sourcemap, nlsKeys, nls } = patch(ts, moduleId, typescript, javascriptFile.contents.toString(), javascriptFile.sourceMap);
|
||||
const result = [fileFrom(javascriptFile, javascript)];
|
||||
result[0].sourceMap = sourcemap;
|
||||
if (nlsKeys) {
|
||||
|
@ -349,6 +344,5 @@ function isImportNode(node) {
|
|||
}
|
||||
return result;
|
||||
}
|
||||
nls_1.patchFiles = patchFiles;
|
||||
})(nls || (nls = {}));
|
||||
module.exports = nls;
|
||||
_nls.patchFiles = patchFiles;
|
||||
})(_nls || (_nls = {}));
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as ts from 'typescript';
|
||||
import type * as ts from 'typescript';
|
||||
import * as lazy from 'lazy.js';
|
||||
import { duplex, through } from 'event-stream';
|
||||
import * as File from 'vinyl';
|
||||
|
@ -21,7 +21,7 @@ enum CollectStepResult {
|
|||
NoAndRecurse
|
||||
}
|
||||
|
||||
function collect(node: ts.Node, fn: (node: ts.Node) => CollectStepResult): ts.Node[] {
|
||||
function collect(ts: typeof import('typescript'), node: ts.Node, fn: (node: ts.Node) => CollectStepResult): ts.Node[] {
|
||||
const result: ts.Node[] = [];
|
||||
|
||||
function loop(node: ts.Node) {
|
||||
|
@ -65,7 +65,7 @@ define([], [${ wrap + lines.map(l => indent + l).join(',\n') + wrap}]);`;
|
|||
/**
|
||||
* Returns a stream containing the patched JavaScript and source maps.
|
||||
*/
|
||||
function nls(): NodeJS.ReadWriteStream {
|
||||
export function nls(): NodeJS.ReadWriteStream {
|
||||
const input = through();
|
||||
const output = input.pipe(through(function (f: FileSourceMap) {
|
||||
if (!f.sourceMap) {
|
||||
|
@ -87,48 +87,48 @@ function nls(): NodeJS.ReadWriteStream {
|
|||
return this.emit('error', new Error(`File ${f.relative} does not have the original content in the source map.`));
|
||||
}
|
||||
|
||||
nls.patchFiles(f, typescript).forEach(f => this.emit('data', f));
|
||||
_nls.patchFiles(f, typescript).forEach(f => this.emit('data', f));
|
||||
}));
|
||||
|
||||
return duplex(input, output);
|
||||
}
|
||||
|
||||
function isImportNode(node: ts.Node): boolean {
|
||||
function isImportNode(ts: typeof import('typescript'), node: ts.Node): boolean {
|
||||
return node.kind === ts.SyntaxKind.ImportDeclaration || node.kind === ts.SyntaxKind.ImportEqualsDeclaration;
|
||||
}
|
||||
|
||||
module nls {
|
||||
module _nls {
|
||||
|
||||
export interface INlsStringResult {
|
||||
interface INlsStringResult {
|
||||
javascript: string;
|
||||
sourcemap: sm.RawSourceMap;
|
||||
nls?: string;
|
||||
nlsKeys?: string;
|
||||
}
|
||||
|
||||
export interface ISpan {
|
||||
interface ISpan {
|
||||
start: ts.LineAndCharacter;
|
||||
end: ts.LineAndCharacter;
|
||||
}
|
||||
|
||||
export interface ILocalizeCall {
|
||||
interface ILocalizeCall {
|
||||
keySpan: ISpan;
|
||||
key: string;
|
||||
valueSpan: ISpan;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface ILocalizeAnalysisResult {
|
||||
interface ILocalizeAnalysisResult {
|
||||
localizeCalls: ILocalizeCall[];
|
||||
nlsExpressions: ISpan[];
|
||||
}
|
||||
|
||||
export interface IPatch {
|
||||
interface IPatch {
|
||||
span: ISpan;
|
||||
content: string;
|
||||
}
|
||||
|
||||
export function fileFrom(file: File, contents: string, path: string = file.path) {
|
||||
function fileFrom(file: File, contents: string, path: string = file.path) {
|
||||
return new File({
|
||||
contents: Buffer.from(contents),
|
||||
base: file.base,
|
||||
|
@ -137,20 +137,20 @@ module nls {
|
|||
});
|
||||
}
|
||||
|
||||
export function mappedPositionFrom(source: string, lc: ts.LineAndCharacter): sm.MappedPosition {
|
||||
function mappedPositionFrom(source: string, lc: ts.LineAndCharacter): sm.MappedPosition {
|
||||
return { source, line: lc.line + 1, column: lc.character };
|
||||
}
|
||||
|
||||
export function lcFrom(position: sm.Position): ts.LineAndCharacter {
|
||||
function lcFrom(position: sm.Position): ts.LineAndCharacter {
|
||||
return { line: position.line - 1, character: position.column };
|
||||
}
|
||||
|
||||
export class SingleFileServiceHost implements ts.LanguageServiceHost {
|
||||
class SingleFileServiceHost implements ts.LanguageServiceHost {
|
||||
|
||||
private file: ts.IScriptSnapshot;
|
||||
private lib: ts.IScriptSnapshot;
|
||||
|
||||
constructor(private options: ts.CompilerOptions, private filename: string, contents: string) {
|
||||
constructor(ts: typeof import('typescript'), private options: ts.CompilerOptions, private filename: string, contents: string) {
|
||||
this.file = ts.ScriptSnapshot.fromString(contents);
|
||||
this.lib = ts.ScriptSnapshot.fromString('');
|
||||
}
|
||||
|
@ -163,7 +163,7 @@ module nls {
|
|||
getDefaultLibFileName = () => 'lib.d.ts';
|
||||
}
|
||||
|
||||
function isCallExpressionWithinTextSpanCollectStep(textSpan: ts.TextSpan, node: ts.Node): CollectStepResult {
|
||||
function isCallExpressionWithinTextSpanCollectStep(ts: typeof import('typescript'), textSpan: ts.TextSpan, node: ts.Node): CollectStepResult {
|
||||
if (!ts.textSpanContainsTextSpan({ start: node.pos, length: node.end - node.pos }, textSpan)) {
|
||||
return CollectStepResult.No;
|
||||
}
|
||||
|
@ -171,14 +171,14 @@ module nls {
|
|||
return node.kind === ts.SyntaxKind.CallExpression ? CollectStepResult.YesAndRecurse : CollectStepResult.NoAndRecurse;
|
||||
}
|
||||
|
||||
export function analyze(contents: string, options: ts.CompilerOptions = {}): ILocalizeAnalysisResult {
|
||||
function analyze(ts: typeof import('typescript'), contents: string, options: ts.CompilerOptions = {}): ILocalizeAnalysisResult {
|
||||
const filename = 'file.ts';
|
||||
const serviceHost = new SingleFileServiceHost(Object.assign(clone(options), { noResolve: true }), filename, contents);
|
||||
const serviceHost = new SingleFileServiceHost(ts, Object.assign(clone(options), { noResolve: true }), filename, contents);
|
||||
const service = ts.createLanguageService(serviceHost);
|
||||
const sourceFile = ts.createSourceFile(filename, contents, ts.ScriptTarget.ES5, true);
|
||||
|
||||
// all imports
|
||||
const imports = lazy(collect(sourceFile, n => isImportNode(n) ? CollectStepResult.YesAndRecurse : CollectStepResult.NoAndRecurse));
|
||||
const imports = lazy(collect(ts, sourceFile, n => isImportNode(ts, n) ? CollectStepResult.YesAndRecurse : CollectStepResult.NoAndRecurse));
|
||||
|
||||
// import nls = require('vs/nls');
|
||||
const importEqualsDeclarations = imports
|
||||
|
@ -215,7 +215,7 @@ module nls {
|
|||
.filter(r => !r.isWriteAccess)
|
||||
|
||||
// find the deepest call expressions AST nodes that contain those references
|
||||
.map(r => collect(sourceFile, n => isCallExpressionWithinTextSpanCollectStep(r.textSpan, n)))
|
||||
.map(r => collect(ts, sourceFile, n => isCallExpressionWithinTextSpanCollectStep(ts, r.textSpan, n)))
|
||||
.map(a => lazy(a).last())
|
||||
.filter(n => !!n)
|
||||
.map(n => <ts.CallExpression>n)
|
||||
|
@ -246,7 +246,7 @@ module nls {
|
|||
// find the deepest call expressions AST nodes that contain those references
|
||||
const localizeCallExpressions = localizeReferences
|
||||
.concat(namedLocalizeReferences)
|
||||
.map(r => collect(sourceFile, n => isCallExpressionWithinTextSpanCollectStep(r.textSpan, n)))
|
||||
.map(r => collect(ts, sourceFile, n => isCallExpressionWithinTextSpanCollectStep(ts, r.textSpan, n)))
|
||||
.map(a => lazy(a).last())
|
||||
.filter(n => !!n)
|
||||
.map(n => <ts.CallExpression>n);
|
||||
|
@ -270,7 +270,7 @@ module nls {
|
|||
};
|
||||
}
|
||||
|
||||
export class TextModel {
|
||||
class TextModel {
|
||||
|
||||
private lines: string[];
|
||||
private lineEndings: string[];
|
||||
|
@ -336,8 +336,8 @@ module nls {
|
|||
}
|
||||
}
|
||||
|
||||
export function patchJavascript(patches: IPatch[], contents: string, moduleId: string): string {
|
||||
const model = new nls.TextModel(contents);
|
||||
function patchJavascript(patches: IPatch[], contents: string, moduleId: string): string {
|
||||
const model = new TextModel(contents);
|
||||
|
||||
// patch the localize calls
|
||||
lazy(patches).reverse().each(p => model.apply(p));
|
||||
|
@ -350,7 +350,7 @@ module nls {
|
|||
return model.toString();
|
||||
}
|
||||
|
||||
export function patchSourcemap(patches: IPatch[], rsm: sm.RawSourceMap, smc: sm.SourceMapConsumer): sm.RawSourceMap {
|
||||
function patchSourcemap(patches: IPatch[], rsm: sm.RawSourceMap, smc: sm.SourceMapConsumer): sm.RawSourceMap {
|
||||
const smg = new sm.SourceMapGenerator({
|
||||
file: rsm.file,
|
||||
sourceRoot: rsm.sourceRoot
|
||||
|
@ -395,8 +395,8 @@ module nls {
|
|||
return JSON.parse(smg.toString());
|
||||
}
|
||||
|
||||
export function patch(moduleId: string, typescript: string, javascript: string, sourcemap: sm.RawSourceMap): INlsStringResult {
|
||||
const { localizeCalls, nlsExpressions } = analyze(typescript);
|
||||
function patch(ts: typeof import('typescript'), moduleId: string, typescript: string, javascript: string, sourcemap: sm.RawSourceMap): INlsStringResult {
|
||||
const { localizeCalls, nlsExpressions } = analyze(ts, typescript);
|
||||
|
||||
if (localizeCalls.length === 0) {
|
||||
return { javascript, sourcemap };
|
||||
|
@ -438,12 +438,14 @@ module nls {
|
|||
}
|
||||
|
||||
export function patchFiles(javascriptFile: File, typescript: string): File[] {
|
||||
const ts = require('typescript') as typeof import('typescript');
|
||||
// hack?
|
||||
const moduleId = javascriptFile.relative
|
||||
.replace(/\.js$/, '')
|
||||
.replace(/\\/g, '/');
|
||||
|
||||
const { javascript, sourcemap, nlsKeys, nls } = patch(
|
||||
ts,
|
||||
moduleId,
|
||||
typescript,
|
||||
javascriptFile.contents.toString(),
|
||||
|
@ -464,5 +466,3 @@ module nls {
|
|||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export = nls;
|
||||
|
|
|
@ -8,17 +8,11 @@ exports.minifyTask = exports.optimizeTask = exports.loaderConfig = void 0;
|
|||
const es = require("event-stream");
|
||||
const gulp = require("gulp");
|
||||
const concat = require("gulp-concat");
|
||||
const minifyCSS = require("gulp-cssnano");
|
||||
const filter = require("gulp-filter");
|
||||
const flatmap = require("gulp-flatmap");
|
||||
const sourcemaps = require("gulp-sourcemaps");
|
||||
const uglify = require("gulp-uglify");
|
||||
const composer = require("gulp-uglify/composer");
|
||||
const fancyLog = require("fancy-log");
|
||||
const ansiColors = require("ansi-colors");
|
||||
const path = require("path");
|
||||
const pump = require("pump");
|
||||
const terser = require("terser");
|
||||
const VinylFile = require("vinyl");
|
||||
const bundle = require("./bundle");
|
||||
const i18n_1 = require("./i18n");
|
||||
|
@ -124,6 +118,7 @@ function optimizeTask(opts) {
|
|||
const out = opts.out;
|
||||
const fileContentMapper = opts.fileContentMapper || ((contents, _path) => contents);
|
||||
return function () {
|
||||
const sourcemaps = require('gulp-sourcemaps');
|
||||
const bundlesStream = es.through(); // this stream will contain the bundled files
|
||||
const resourcesStream = es.through(); // this stream will contain the resources
|
||||
const bundleInfoStream = es.through(); // this stream will contain bundleInfo.json
|
||||
|
@ -166,55 +161,31 @@ function optimizeTask(opts) {
|
|||
};
|
||||
}
|
||||
exports.optimizeTask = optimizeTask;
|
||||
/**
|
||||
* Wrap around uglify and allow the preserveComments function
|
||||
* to have a file "context" to include our copyright only once per file.
|
||||
*/
|
||||
function uglifyWithCopyrights() {
|
||||
const preserveComments = (f) => {
|
||||
return (_node, comment) => {
|
||||
const text = comment.value;
|
||||
const type = comment.type;
|
||||
if (/@minifier_do_not_preserve/.test(text)) {
|
||||
return false;
|
||||
}
|
||||
const isOurCopyright = IS_OUR_COPYRIGHT_REGEXP.test(text);
|
||||
if (isOurCopyright) {
|
||||
if (f.__hasOurCopyright) {
|
||||
return false;
|
||||
}
|
||||
f.__hasOurCopyright = true;
|
||||
return true;
|
||||
}
|
||||
if ('comment2' === type) {
|
||||
// check for /*!. Note that text doesn't contain leading /*
|
||||
return (text.length > 0 && text[0] === '!') || /@preserve|license|@cc_on|copyright/i.test(text);
|
||||
}
|
||||
else if ('comment1' === type) {
|
||||
return /license|copyright/i.test(text);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
};
|
||||
const minify = composer(terser);
|
||||
const input = es.through();
|
||||
const output = input
|
||||
.pipe(flatmap((stream, f) => {
|
||||
return stream.pipe(minify({
|
||||
output: {
|
||||
comments: preserveComments(f),
|
||||
max_line_len: 1024
|
||||
}
|
||||
}));
|
||||
}));
|
||||
return es.duplex(input, output);
|
||||
}
|
||||
function minifyTask(src, sourceMapBaseUrl) {
|
||||
const esbuild = require('esbuild');
|
||||
const sourceMappingURL = sourceMapBaseUrl ? ((f) => `${sourceMapBaseUrl}/${f.relative}.map`) : undefined;
|
||||
return cb => {
|
||||
const minifyCSS = require('gulp-cssnano');
|
||||
const sourcemaps = require('gulp-sourcemaps');
|
||||
const jsFilter = filter('**/*.js', { restore: true });
|
||||
const cssFilter = filter('**/*.css', { restore: true });
|
||||
pump(gulp.src([src + '/**', '!' + src + '/**/*.map']), jsFilter, sourcemaps.init({ loadMaps: true }), uglifyWithCopyrights(), jsFilter.restore, cssFilter, minifyCSS({ reduceIdents: false }), cssFilter.restore, sourcemaps.mapSources((sourcePath) => {
|
||||
pump(gulp.src([src + '/**', '!' + src + '/**/*.map']), jsFilter, sourcemaps.init({ loadMaps: true }), es.map((f, cb) => {
|
||||
esbuild.build({
|
||||
entryPoints: [f.path],
|
||||
minify: true,
|
||||
sourcemap: 'external',
|
||||
outdir: '.',
|
||||
platform: 'node',
|
||||
target: ['node12.18'],
|
||||
write: false
|
||||
}).then(res => {
|
||||
const jsFile = res.outputFiles.find(f => /\.js$/.test(f.path));
|
||||
const sourceMapFile = res.outputFiles.find(f => /\.js\.map$/.test(f.path));
|
||||
f.contents = Buffer.from(jsFile.contents);
|
||||
f.sourceMap = JSON.parse(sourceMapFile.text);
|
||||
cb(undefined, f);
|
||||
}, cb);
|
||||
}), jsFilter.restore, cssFilter, minifyCSS({ reduceIdents: false }), cssFilter.restore, sourcemaps.mapSources((sourcePath) => {
|
||||
if (sourcePath === 'bootstrap-fork.js') {
|
||||
return 'bootstrap-fork.orig.js';
|
||||
}
|
||||
|
@ -224,12 +195,7 @@ function minifyTask(src, sourceMapBaseUrl) {
|
|||
sourceRoot: undefined,
|
||||
includeContent: true,
|
||||
addComment: true
|
||||
}), gulp.dest(src + '-min'), (err) => {
|
||||
if (err instanceof uglify.GulpUglifyError) {
|
||||
console.error(`Uglify error in '${err.cause && err.cause.filename}'`);
|
||||
}
|
||||
cb(err);
|
||||
});
|
||||
}), gulp.dest(src + '-min'), (err) => cb(err));
|
||||
};
|
||||
}
|
||||
exports.minifyTask = minifyTask;
|
||||
|
|
|
@ -8,17 +8,11 @@
|
|||
import * as es from 'event-stream';
|
||||
import * as gulp from 'gulp';
|
||||
import * as concat from 'gulp-concat';
|
||||
import * as minifyCSS from 'gulp-cssnano';
|
||||
import * as filter from 'gulp-filter';
|
||||
import * as flatmap from 'gulp-flatmap';
|
||||
import * as sourcemaps from 'gulp-sourcemaps';
|
||||
import * as uglify from 'gulp-uglify';
|
||||
import * as composer from 'gulp-uglify/composer';
|
||||
import * as fancyLog from 'fancy-log';
|
||||
import * as ansiColors from 'ansi-colors';
|
||||
import * as path from 'path';
|
||||
import * as pump from 'pump';
|
||||
import * as terser from 'terser';
|
||||
import * as VinylFile from 'vinyl';
|
||||
import * as bundle from './bundle';
|
||||
import { Language, processNlsFiles } from './i18n';
|
||||
|
@ -184,6 +178,8 @@ export function optimizeTask(opts: IOptimizeTaskOpts): () => NodeJS.ReadWriteStr
|
|||
const fileContentMapper = opts.fileContentMapper || ((contents: string, _path: string) => contents);
|
||||
|
||||
return function () {
|
||||
const sourcemaps = require('gulp-sourcemaps') as typeof import('gulp-sourcemaps');
|
||||
|
||||
const bundlesStream = es.through(); // this stream will contain the bundled files
|
||||
const resourcesStream = es.through(); // this stream will contain the resources
|
||||
const bundleInfoStream = es.through(); // this stream will contain bundleInfo.json
|
||||
|
@ -235,62 +231,14 @@ export function optimizeTask(opts: IOptimizeTaskOpts): () => NodeJS.ReadWriteStr
|
|||
};
|
||||
}
|
||||
|
||||
declare class FileWithCopyright extends VinylFile {
|
||||
public __hasOurCopyright: boolean;
|
||||
}
|
||||
/**
|
||||
* Wrap around uglify and allow the preserveComments function
|
||||
* to have a file "context" to include our copyright only once per file.
|
||||
*/
|
||||
function uglifyWithCopyrights(): NodeJS.ReadWriteStream {
|
||||
const preserveComments = (f: FileWithCopyright) => {
|
||||
return (_node: any, comment: { value: string; type: string; }) => {
|
||||
const text = comment.value;
|
||||
const type = comment.type;
|
||||
|
||||
if (/@minifier_do_not_preserve/.test(text)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const isOurCopyright = IS_OUR_COPYRIGHT_REGEXP.test(text);
|
||||
|
||||
if (isOurCopyright) {
|
||||
if (f.__hasOurCopyright) {
|
||||
return false;
|
||||
}
|
||||
f.__hasOurCopyright = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if ('comment2' === type) {
|
||||
// check for /*!. Note that text doesn't contain leading /*
|
||||
return (text.length > 0 && text[0] === '!') || /@preserve|license|@cc_on|copyright/i.test(text);
|
||||
} else if ('comment1' === type) {
|
||||
return /license|copyright/i.test(text);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
};
|
||||
|
||||
const minify = (composer as any)(terser);
|
||||
const input = es.through();
|
||||
const output = input
|
||||
.pipe(flatmap((stream, f) => {
|
||||
return stream.pipe(minify({
|
||||
output: {
|
||||
comments: preserveComments(<FileWithCopyright>f),
|
||||
max_line_len: 1024
|
||||
}
|
||||
}));
|
||||
}));
|
||||
|
||||
return es.duplex(input, output);
|
||||
}
|
||||
|
||||
export function minifyTask(src: string, sourceMapBaseUrl?: string): (cb: any) => void {
|
||||
const esbuild = require('esbuild') as typeof import('esbuild');
|
||||
const sourceMappingURL = sourceMapBaseUrl ? ((f: any) => `${sourceMapBaseUrl}/${f.relative}.map`) : undefined;
|
||||
|
||||
return cb => {
|
||||
const minifyCSS = require('gulp-cssnano') as typeof import('gulp-cssnano');
|
||||
const sourcemaps = require('gulp-sourcemaps') as typeof import('gulp-sourcemaps');
|
||||
|
||||
const jsFilter = filter('**/*.js', { restore: true });
|
||||
const cssFilter = filter('**/*.css', { restore: true });
|
||||
|
||||
|
@ -298,7 +246,25 @@ export function minifyTask(src: string, sourceMapBaseUrl?: string): (cb: any) =>
|
|||
gulp.src([src + '/**', '!' + src + '/**/*.map']),
|
||||
jsFilter,
|
||||
sourcemaps.init({ loadMaps: true }),
|
||||
uglifyWithCopyrights(),
|
||||
es.map((f: any, cb) => {
|
||||
esbuild.build({
|
||||
entryPoints: [f.path],
|
||||
minify: true,
|
||||
sourcemap: 'external',
|
||||
outdir: '.',
|
||||
platform: 'node',
|
||||
target: ['node12.18'],
|
||||
write: false
|
||||
}).then(res => {
|
||||
const jsFile = res.outputFiles.find(f => /\.js$/.test(f.path))!;
|
||||
const sourceMapFile = res.outputFiles.find(f => /\.js\.map$/.test(f.path))!;
|
||||
|
||||
f.contents = Buffer.from(jsFile.contents);
|
||||
f.sourceMap = JSON.parse(sourceMapFile.text);
|
||||
|
||||
cb(undefined, f);
|
||||
}, cb);
|
||||
}),
|
||||
jsFilter.restore,
|
||||
cssFilter,
|
||||
minifyCSS({ reduceIdents: false }),
|
||||
|
@ -316,13 +282,7 @@ export function minifyTask(src: string, sourceMapBaseUrl?: string): (cb: any) =>
|
|||
includeContent: true,
|
||||
addComment: true
|
||||
} as any),
|
||||
gulp.dest(src + '-min')
|
||||
, (err: any) => {
|
||||
if (err instanceof (uglify as any).GulpUglifyError) {
|
||||
console.error(`Uglify error in '${err.cause && err.cause.filename}'`);
|
||||
}
|
||||
|
||||
cb(err);
|
||||
});
|
||||
gulp.dest(src + '-min'),
|
||||
(err: any) => cb(err));
|
||||
};
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createESMSourcesAndResources2 = exports.extractEditor = void 0;
|
||||
const ts = require("typescript");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const tss = require("./treeshaking");
|
||||
|
@ -29,6 +28,7 @@ function writeFile(filePath, contents) {
|
|||
}
|
||||
function extractEditor(options) {
|
||||
var _a;
|
||||
const ts = require('typescript');
|
||||
const tsConfig = JSON.parse(fs.readFileSync(path.join(options.sourcesRoot, 'tsconfig.monaco.json')).toString());
|
||||
let compilerOptions;
|
||||
if (tsConfig.extends) {
|
||||
|
@ -115,6 +115,7 @@ function extractEditor(options) {
|
|||
}
|
||||
exports.extractEditor = extractEditor;
|
||||
function createESMSourcesAndResources2(options) {
|
||||
const ts = require('typescript');
|
||||
const SRC_FOLDER = path.join(REPO_ROOT, options.srcFolder);
|
||||
const OUT_FOLDER = path.join(REPO_ROOT, options.outFolder);
|
||||
const OUT_RESOURCES_FOLDER = path.join(REPO_ROOT, options.outResourcesFolder);
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as ts from 'typescript';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as tss from './treeshaking';
|
||||
|
@ -31,6 +30,8 @@ function writeFile(filePath: string, contents: Buffer | string): void {
|
|||
}
|
||||
|
||||
export function extractEditor(options: tss.ITreeShakingOptions & { destRoot: string }): void {
|
||||
const ts = require('typescript') as typeof import('typescript');
|
||||
|
||||
const tsConfig = JSON.parse(fs.readFileSync(path.join(options.sourcesRoot, 'tsconfig.monaco.json')).toString());
|
||||
let compilerOptions: { [key: string]: any };
|
||||
if (tsConfig.extends) {
|
||||
|
@ -134,6 +135,8 @@ export interface IOptions2 {
|
|||
}
|
||||
|
||||
export function createESMSourcesAndResources2(options: IOptions2): void {
|
||||
const ts = require('typescript') as typeof import('typescript');
|
||||
|
||||
const SRC_FOLDER = path.join(REPO_ROOT, options.srcFolder);
|
||||
const OUT_FOLDER = path.join(REPO_ROOT, options.outFolder);
|
||||
const OUT_RESOURCES_FOLDER = path.join(REPO_ROOT, options.outResourcesFolder);
|
||||
|
|
|
@ -8,7 +8,6 @@ exports.submitAllStats = exports.createStatsStream = void 0;
|
|||
const es = require("event-stream");
|
||||
const fancyLog = require("fancy-log");
|
||||
const ansiColors = require("ansi-colors");
|
||||
const appInsights = require("applicationinsights");
|
||||
class Entry {
|
||||
constructor(name, totalCount, totalSize) {
|
||||
this.name = name;
|
||||
|
@ -73,6 +72,7 @@ function createStatsStream(group, log) {
|
|||
}
|
||||
exports.createStatsStream = createStatsStream;
|
||||
function submitAllStats(productJson, commit) {
|
||||
const appInsights = require('applicationinsights');
|
||||
const sorted = [];
|
||||
// move entries for single files to the front
|
||||
_entries.forEach(value => {
|
||||
|
|
|
@ -9,7 +9,6 @@ import * as es from 'event-stream';
|
|||
import * as fancyLog from 'fancy-log';
|
||||
import * as ansiColors from 'ansi-colors';
|
||||
import * as File from 'vinyl';
|
||||
import * as appInsights from 'applicationinsights';
|
||||
|
||||
class Entry {
|
||||
constructor(readonly name: string, public totalCount: number, public totalSize: number) { }
|
||||
|
@ -75,6 +74,7 @@ export function createStatsStream(group: string, log?: boolean): es.ThroughStrea
|
|||
}
|
||||
|
||||
export function submitAllStats(productJson: any, commit: string): Promise<boolean> {
|
||||
const appInsights = require('applicationinsights') as typeof import('applicationinsights');
|
||||
|
||||
const sorted: Entry[] = [];
|
||||
// move entries for single files to the front
|
||||
|
|
|
@ -7,7 +7,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|||
exports.shake = exports.toStringShakeLevel = exports.ShakeLevel = void 0;
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const ts = require("typescript");
|
||||
const TYPESCRIPT_LIB_FOLDER = path.dirname(require.resolve('typescript/lib/lib.d.ts'));
|
||||
var ShakeLevel;
|
||||
(function (ShakeLevel) {
|
||||
|
@ -41,7 +40,8 @@ function printDiagnostics(options, diagnostics) {
|
|||
}
|
||||
}
|
||||
function shake(options) {
|
||||
const languageService = createTypeScriptLanguageService(options);
|
||||
const ts = require('typescript');
|
||||
const languageService = createTypeScriptLanguageService(ts, options);
|
||||
const program = languageService.getProgram();
|
||||
const globalDiagnostics = program.getGlobalDiagnostics();
|
||||
if (globalDiagnostics.length > 0) {
|
||||
|
@ -58,14 +58,14 @@ function shake(options) {
|
|||
printDiagnostics(options, semanticDiagnostics);
|
||||
throw new Error(`Compilation Errors encountered.`);
|
||||
}
|
||||
markNodes(languageService, options);
|
||||
return generateResult(languageService, options.shakeLevel);
|
||||
markNodes(ts, languageService, options);
|
||||
return generateResult(ts, languageService, options.shakeLevel);
|
||||
}
|
||||
exports.shake = shake;
|
||||
//#region Discovery, LanguageService & Setup
|
||||
function createTypeScriptLanguageService(options) {
|
||||
function createTypeScriptLanguageService(ts, options) {
|
||||
// Discover referenced files
|
||||
const FILES = discoverAndReadFiles(options);
|
||||
const FILES = discoverAndReadFiles(ts, options);
|
||||
// Add fake usage files
|
||||
options.inlineEntryPoints.forEach((inlineEntryPoint, index) => {
|
||||
FILES[`inlineEntryPoint.${index}.ts`] = inlineEntryPoint;
|
||||
|
@ -76,15 +76,15 @@ function createTypeScriptLanguageService(options) {
|
|||
FILES[typing] = fs.readFileSync(filePath).toString();
|
||||
});
|
||||
// Resolve libs
|
||||
const RESOLVED_LIBS = processLibFiles(options);
|
||||
const RESOLVED_LIBS = processLibFiles(ts, options);
|
||||
const compilerOptions = ts.convertCompilerOptionsFromJson(options.compilerOptions, options.sourcesRoot).options;
|
||||
const host = new TypeScriptLanguageServiceHost(RESOLVED_LIBS, FILES, compilerOptions);
|
||||
const host = new TypeScriptLanguageServiceHost(ts, RESOLVED_LIBS, FILES, compilerOptions);
|
||||
return ts.createLanguageService(host);
|
||||
}
|
||||
/**
|
||||
* Read imports and follow them until all files have been handled
|
||||
*/
|
||||
function discoverAndReadFiles(options) {
|
||||
function discoverAndReadFiles(ts, options) {
|
||||
const FILES = {};
|
||||
const in_queue = Object.create(null);
|
||||
const queue = [];
|
||||
|
@ -137,7 +137,7 @@ function discoverAndReadFiles(options) {
|
|||
/**
|
||||
* Read lib files and follow lib references
|
||||
*/
|
||||
function processLibFiles(options) {
|
||||
function processLibFiles(ts, options) {
|
||||
const stack = [...options.compilerOptions.lib];
|
||||
const result = {};
|
||||
while (stack.length > 0) {
|
||||
|
@ -161,7 +161,8 @@ function processLibFiles(options) {
|
|||
* A TypeScript language service host
|
||||
*/
|
||||
class TypeScriptLanguageServiceHost {
|
||||
constructor(libs, files, compilerOptions) {
|
||||
constructor(ts, libs, files, compilerOptions) {
|
||||
this._ts = ts;
|
||||
this._libs = libs;
|
||||
this._files = files;
|
||||
this._compilerOptions = compilerOptions;
|
||||
|
@ -183,17 +184,17 @@ class TypeScriptLanguageServiceHost {
|
|||
}
|
||||
getScriptSnapshot(fileName) {
|
||||
if (this._files.hasOwnProperty(fileName)) {
|
||||
return ts.ScriptSnapshot.fromString(this._files[fileName]);
|
||||
return this._ts.ScriptSnapshot.fromString(this._files[fileName]);
|
||||
}
|
||||
else if (this._libs.hasOwnProperty(fileName)) {
|
||||
return ts.ScriptSnapshot.fromString(this._libs[fileName]);
|
||||
return this._ts.ScriptSnapshot.fromString(this._libs[fileName]);
|
||||
}
|
||||
else {
|
||||
return ts.ScriptSnapshot.fromString('');
|
||||
return this._ts.ScriptSnapshot.fromString('');
|
||||
}
|
||||
}
|
||||
getScriptKind(_fileName) {
|
||||
return ts.ScriptKind.TS;
|
||||
return this._ts.ScriptKind.TS;
|
||||
}
|
||||
getCurrentDirectory() {
|
||||
return '';
|
||||
|
@ -240,7 +241,7 @@ function nodeOrChildIsBlack(node) {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
function markNodes(languageService, options) {
|
||||
function markNodes(ts, languageService, options) {
|
||||
const program = languageService.getProgram();
|
||||
if (!program) {
|
||||
throw new Error('Could not get program from language service');
|
||||
|
@ -342,7 +343,7 @@ function markNodes(languageService, options) {
|
|||
if (!referenceSourceFile) {
|
||||
continue;
|
||||
}
|
||||
const referenceNode = getTokenAtPosition(referenceSourceFile, reference.textSpan.start, false, false);
|
||||
const referenceNode = getTokenAtPosition(ts, referenceSourceFile, reference.textSpan.start, false, false);
|
||||
if (ts.isMethodDeclaration(referenceNode.parent)
|
||||
|| ts.isPropertyDeclaration(referenceNode.parent)
|
||||
|| ts.isGetAccessor(referenceNode.parent)
|
||||
|
@ -408,7 +409,7 @@ function markNodes(languageService, options) {
|
|||
}
|
||||
const nodeSourceFile = node.getSourceFile();
|
||||
const loop = (node) => {
|
||||
const [symbol, symbolImportNode] = getRealNodeSymbol(checker, node);
|
||||
const [symbol, symbolImportNode] = getRealNodeSymbol(ts, checker, node);
|
||||
if (symbolImportNode) {
|
||||
setColor(symbolImportNode, 2 /* Black */);
|
||||
}
|
||||
|
@ -420,7 +421,7 @@ function markNodes(languageService, options) {
|
|||
// (they can be the declaration of a module import)
|
||||
continue;
|
||||
}
|
||||
if (options.shakeLevel === 2 /* ClassMembers */ && (ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration)) && !isLocalCodeExtendingOrInheritingFromDefaultLibSymbol(program, checker, declaration)) {
|
||||
if (options.shakeLevel === 2 /* ClassMembers */ && (ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration)) && !isLocalCodeExtendingOrInheritingFromDefaultLibSymbol(ts, program, checker, declaration)) {
|
||||
enqueue_black(declaration.name);
|
||||
for (let j = 0; j < declaration.members.length; j++) {
|
||||
const member = declaration.members[j];
|
||||
|
@ -484,7 +485,7 @@ function nodeIsInItsOwnDeclaration(nodeSourceFile, node, symbol) {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
function generateResult(languageService, shakeLevel) {
|
||||
function generateResult(ts, languageService, shakeLevel) {
|
||||
const program = languageService.getProgram();
|
||||
if (!program) {
|
||||
throw new Error('Could not get program from language service');
|
||||
|
@ -614,11 +615,11 @@ function generateResult(languageService, shakeLevel) {
|
|||
}
|
||||
//#endregion
|
||||
//#region Utils
|
||||
function isLocalCodeExtendingOrInheritingFromDefaultLibSymbol(program, checker, declaration) {
|
||||
function isLocalCodeExtendingOrInheritingFromDefaultLibSymbol(ts, program, checker, declaration) {
|
||||
if (!program.isSourceFileDefaultLibrary(declaration.getSourceFile()) && declaration.heritageClauses) {
|
||||
for (const heritageClause of declaration.heritageClauses) {
|
||||
for (const type of heritageClause.types) {
|
||||
const symbol = findSymbolFromHeritageType(checker, type);
|
||||
const symbol = findSymbolFromHeritageType(ts, checker, type);
|
||||
if (symbol) {
|
||||
const decl = symbol.valueDeclaration || (symbol.declarations && symbol.declarations[0]);
|
||||
if (decl && program.isSourceFileDefaultLibrary(decl.getSourceFile())) {
|
||||
|
@ -630,22 +631,22 @@ function isLocalCodeExtendingOrInheritingFromDefaultLibSymbol(program, checker,
|
|||
}
|
||||
return false;
|
||||
}
|
||||
function findSymbolFromHeritageType(checker, type) {
|
||||
function findSymbolFromHeritageType(ts, checker, type) {
|
||||
if (ts.isExpressionWithTypeArguments(type)) {
|
||||
return findSymbolFromHeritageType(checker, type.expression);
|
||||
return findSymbolFromHeritageType(ts, checker, type.expression);
|
||||
}
|
||||
if (ts.isIdentifier(type)) {
|
||||
return getRealNodeSymbol(checker, type)[0];
|
||||
return getRealNodeSymbol(ts, checker, type)[0];
|
||||
}
|
||||
if (ts.isPropertyAccessExpression(type)) {
|
||||
return findSymbolFromHeritageType(checker, type.name);
|
||||
return findSymbolFromHeritageType(ts, checker, type.name);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Returns the node's symbol and the `import` node (if the symbol resolved from a different module)
|
||||
*/
|
||||
function getRealNodeSymbol(checker, node) {
|
||||
function getRealNodeSymbol(ts, checker, node) {
|
||||
const getPropertySymbolsFromContextualType = ts.getPropertySymbolsFromContextualType;
|
||||
const getContainingObjectLiteralElement = ts.getContainingObjectLiteralElement;
|
||||
const getNameFromPropertyName = ts.getNameFromPropertyName;
|
||||
|
@ -758,7 +759,7 @@ function getRealNodeSymbol(checker, node) {
|
|||
return [null, null];
|
||||
}
|
||||
/** Get the token whose text contains the position */
|
||||
function getTokenAtPosition(sourceFile, position, allowPositionInLeadingTrivia, includeEndPosition) {
|
||||
function getTokenAtPosition(ts, sourceFile, position, allowPositionInLeadingTrivia, includeEndPosition) {
|
||||
let current = sourceFile;
|
||||
outer: while (true) {
|
||||
// find the child that contains 'position'
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
import type * as ts from 'typescript';
|
||||
|
||||
const TYPESCRIPT_LIB_FOLDER = path.dirname(require.resolve('typescript/lib/lib.d.ts'));
|
||||
|
||||
|
@ -82,7 +82,8 @@ function printDiagnostics(options: ITreeShakingOptions, diagnostics: ReadonlyArr
|
|||
}
|
||||
|
||||
export function shake(options: ITreeShakingOptions): ITreeShakingResult {
|
||||
const languageService = createTypeScriptLanguageService(options);
|
||||
const ts = require('typescript') as typeof import('typescript');
|
||||
const languageService = createTypeScriptLanguageService(ts, options);
|
||||
const program = languageService.getProgram()!;
|
||||
|
||||
const globalDiagnostics = program.getGlobalDiagnostics();
|
||||
|
@ -103,15 +104,15 @@ export function shake(options: ITreeShakingOptions): ITreeShakingResult {
|
|||
throw new Error(`Compilation Errors encountered.`);
|
||||
}
|
||||
|
||||
markNodes(languageService, options);
|
||||
markNodes(ts, languageService, options);
|
||||
|
||||
return generateResult(languageService, options.shakeLevel);
|
||||
return generateResult(ts, languageService, options.shakeLevel);
|
||||
}
|
||||
|
||||
//#region Discovery, LanguageService & Setup
|
||||
function createTypeScriptLanguageService(options: ITreeShakingOptions): ts.LanguageService {
|
||||
function createTypeScriptLanguageService(ts: typeof import('typescript'), options: ITreeShakingOptions): ts.LanguageService {
|
||||
// Discover referenced files
|
||||
const FILES = discoverAndReadFiles(options);
|
||||
const FILES = discoverAndReadFiles(ts, options);
|
||||
|
||||
// Add fake usage files
|
||||
options.inlineEntryPoints.forEach((inlineEntryPoint, index) => {
|
||||
|
@ -125,18 +126,18 @@ function createTypeScriptLanguageService(options: ITreeShakingOptions): ts.Langu
|
|||
});
|
||||
|
||||
// Resolve libs
|
||||
const RESOLVED_LIBS = processLibFiles(options);
|
||||
const RESOLVED_LIBS = processLibFiles(ts, options);
|
||||
|
||||
const compilerOptions = ts.convertCompilerOptionsFromJson(options.compilerOptions, options.sourcesRoot).options;
|
||||
|
||||
const host = new TypeScriptLanguageServiceHost(RESOLVED_LIBS, FILES, compilerOptions);
|
||||
const host = new TypeScriptLanguageServiceHost(ts, RESOLVED_LIBS, FILES, compilerOptions);
|
||||
return ts.createLanguageService(host);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read imports and follow them until all files have been handled
|
||||
*/
|
||||
function discoverAndReadFiles(options: ITreeShakingOptions): IFileMap {
|
||||
function discoverAndReadFiles(ts: typeof import('typescript'), options: ITreeShakingOptions): IFileMap {
|
||||
const FILES: IFileMap = {};
|
||||
|
||||
const in_queue: { [module: string]: boolean; } = Object.create(null);
|
||||
|
@ -199,7 +200,7 @@ function discoverAndReadFiles(options: ITreeShakingOptions): IFileMap {
|
|||
/**
|
||||
* Read lib files and follow lib references
|
||||
*/
|
||||
function processLibFiles(options: ITreeShakingOptions): ILibMap {
|
||||
function processLibFiles(ts: typeof import('typescript'), options: ITreeShakingOptions): ILibMap {
|
||||
|
||||
const stack: string[] = [...options.compilerOptions.lib];
|
||||
const result: ILibMap = {};
|
||||
|
@ -232,11 +233,13 @@ interface IFileMap { [fileName: string]: string; }
|
|||
*/
|
||||
class TypeScriptLanguageServiceHost implements ts.LanguageServiceHost {
|
||||
|
||||
private readonly _ts: typeof import('typescript');
|
||||
private readonly _libs: ILibMap;
|
||||
private readonly _files: IFileMap;
|
||||
private readonly _compilerOptions: ts.CompilerOptions;
|
||||
|
||||
constructor(libs: ILibMap, files: IFileMap, compilerOptions: ts.CompilerOptions) {
|
||||
constructor(ts: typeof import('typescript'), libs: ILibMap, files: IFileMap, compilerOptions: ts.CompilerOptions) {
|
||||
this._ts = ts;
|
||||
this._libs = libs;
|
||||
this._files = files;
|
||||
this._compilerOptions = compilerOptions;
|
||||
|
@ -262,15 +265,15 @@ class TypeScriptLanguageServiceHost implements ts.LanguageServiceHost {
|
|||
}
|
||||
getScriptSnapshot(fileName: string): ts.IScriptSnapshot {
|
||||
if (this._files.hasOwnProperty(fileName)) {
|
||||
return ts.ScriptSnapshot.fromString(this._files[fileName]);
|
||||
return this._ts.ScriptSnapshot.fromString(this._files[fileName]);
|
||||
} else if (this._libs.hasOwnProperty(fileName)) {
|
||||
return ts.ScriptSnapshot.fromString(this._libs[fileName]);
|
||||
return this._ts.ScriptSnapshot.fromString(this._libs[fileName]);
|
||||
} else {
|
||||
return ts.ScriptSnapshot.fromString('');
|
||||
return this._ts.ScriptSnapshot.fromString('');
|
||||
}
|
||||
}
|
||||
getScriptKind(_fileName: string): ts.ScriptKind {
|
||||
return ts.ScriptKind.TS;
|
||||
return this._ts.ScriptKind.TS;
|
||||
}
|
||||
getCurrentDirectory(): string {
|
||||
return '';
|
||||
|
@ -320,7 +323,7 @@ function nodeOrChildIsBlack(node: ts.Node): boolean {
|
|||
return false;
|
||||
}
|
||||
|
||||
function markNodes(languageService: ts.LanguageService, options: ITreeShakingOptions) {
|
||||
function markNodes(ts: typeof import('typescript'), languageService: ts.LanguageService, options: ITreeShakingOptions) {
|
||||
const program = languageService.getProgram();
|
||||
if (!program) {
|
||||
throw new Error('Could not get program from language service');
|
||||
|
@ -446,7 +449,7 @@ function markNodes(languageService: ts.LanguageService, options: ITreeShakingOpt
|
|||
continue;
|
||||
}
|
||||
|
||||
const referenceNode = getTokenAtPosition(referenceSourceFile, reference.textSpan.start, false, false);
|
||||
const referenceNode = getTokenAtPosition(ts, referenceSourceFile, reference.textSpan.start, false, false);
|
||||
if (
|
||||
ts.isMethodDeclaration(referenceNode.parent)
|
||||
|| ts.isPropertyDeclaration(referenceNode.parent)
|
||||
|
@ -522,7 +525,7 @@ function markNodes(languageService: ts.LanguageService, options: ITreeShakingOpt
|
|||
const nodeSourceFile = node.getSourceFile();
|
||||
|
||||
const loop = (node: ts.Node) => {
|
||||
const [symbol, symbolImportNode] = getRealNodeSymbol(checker, node);
|
||||
const [symbol, symbolImportNode] = getRealNodeSymbol(ts, checker, node);
|
||||
if (symbolImportNode) {
|
||||
setColor(symbolImportNode, NodeColor.Black);
|
||||
}
|
||||
|
@ -536,7 +539,7 @@ function markNodes(languageService: ts.LanguageService, options: ITreeShakingOpt
|
|||
continue;
|
||||
}
|
||||
|
||||
if (options.shakeLevel === ShakeLevel.ClassMembers && (ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration)) && !isLocalCodeExtendingOrInheritingFromDefaultLibSymbol(program, checker, declaration)) {
|
||||
if (options.shakeLevel === ShakeLevel.ClassMembers && (ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration)) && !isLocalCodeExtendingOrInheritingFromDefaultLibSymbol(ts, program, checker, declaration)) {
|
||||
enqueue_black(declaration.name!);
|
||||
|
||||
for (let j = 0; j < declaration.members.length; j++) {
|
||||
|
@ -607,7 +610,7 @@ function nodeIsInItsOwnDeclaration(nodeSourceFile: ts.SourceFile, node: ts.Node,
|
|||
return false;
|
||||
}
|
||||
|
||||
function generateResult(languageService: ts.LanguageService, shakeLevel: ShakeLevel): ITreeShakingResult {
|
||||
function generateResult(ts: typeof import('typescript'), languageService: ts.LanguageService, shakeLevel: ShakeLevel): ITreeShakingResult {
|
||||
const program = languageService.getProgram();
|
||||
if (!program) {
|
||||
throw new Error('Could not get program from language service');
|
||||
|
@ -752,11 +755,11 @@ function generateResult(languageService: ts.LanguageService, shakeLevel: ShakeLe
|
|||
|
||||
//#region Utils
|
||||
|
||||
function isLocalCodeExtendingOrInheritingFromDefaultLibSymbol(program: ts.Program, checker: ts.TypeChecker, declaration: ts.ClassDeclaration | ts.InterfaceDeclaration): boolean {
|
||||
function isLocalCodeExtendingOrInheritingFromDefaultLibSymbol(ts: typeof import('typescript'), program: ts.Program, checker: ts.TypeChecker, declaration: ts.ClassDeclaration | ts.InterfaceDeclaration): boolean {
|
||||
if (!program.isSourceFileDefaultLibrary(declaration.getSourceFile()) && declaration.heritageClauses) {
|
||||
for (const heritageClause of declaration.heritageClauses) {
|
||||
for (const type of heritageClause.types) {
|
||||
const symbol = findSymbolFromHeritageType(checker, type);
|
||||
const symbol = findSymbolFromHeritageType(ts, checker, type);
|
||||
if (symbol) {
|
||||
const decl = symbol.valueDeclaration || (symbol.declarations && symbol.declarations[0]);
|
||||
if (decl && program.isSourceFileDefaultLibrary(decl.getSourceFile())) {
|
||||
|
@ -769,15 +772,15 @@ function isLocalCodeExtendingOrInheritingFromDefaultLibSymbol(program: ts.Progra
|
|||
return false;
|
||||
}
|
||||
|
||||
function findSymbolFromHeritageType(checker: ts.TypeChecker, type: ts.ExpressionWithTypeArguments | ts.Expression | ts.PrivateIdentifier): ts.Symbol | null {
|
||||
function findSymbolFromHeritageType(ts: typeof import('typescript'), checker: ts.TypeChecker, type: ts.ExpressionWithTypeArguments | ts.Expression | ts.PrivateIdentifier): ts.Symbol | null {
|
||||
if (ts.isExpressionWithTypeArguments(type)) {
|
||||
return findSymbolFromHeritageType(checker, type.expression);
|
||||
return findSymbolFromHeritageType(ts, checker, type.expression);
|
||||
}
|
||||
if (ts.isIdentifier(type)) {
|
||||
return getRealNodeSymbol(checker, type)[0];
|
||||
return getRealNodeSymbol(ts, checker, type)[0];
|
||||
}
|
||||
if (ts.isPropertyAccessExpression(type)) {
|
||||
return findSymbolFromHeritageType(checker, type.name);
|
||||
return findSymbolFromHeritageType(ts, checker, type.name);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -785,7 +788,7 @@ function findSymbolFromHeritageType(checker: ts.TypeChecker, type: ts.Expression
|
|||
/**
|
||||
* Returns the node's symbol and the `import` node (if the symbol resolved from a different module)
|
||||
*/
|
||||
function getRealNodeSymbol(checker: ts.TypeChecker, node: ts.Node): [ts.Symbol | null, ts.Declaration | null] {
|
||||
function getRealNodeSymbol(ts: typeof import('typescript'), checker: ts.TypeChecker, node: ts.Node): [ts.Symbol | null, ts.Declaration | null] {
|
||||
|
||||
// Use some TypeScript internals to avoid code duplication
|
||||
type ObjectLiteralElementWithName = ts.ObjectLiteralElement & { name: ts.PropertyName; parent: ts.ObjectLiteralExpression | ts.JsxAttributes };
|
||||
|
@ -913,7 +916,7 @@ function getRealNodeSymbol(checker: ts.TypeChecker, node: ts.Node): [ts.Symbol |
|
|||
}
|
||||
|
||||
/** Get the token whose text contains the position */
|
||||
function getTokenAtPosition(sourceFile: ts.SourceFile, position: number, allowPositionInLeadingTrivia: boolean, includeEndPosition: boolean): ts.Node {
|
||||
function getTokenAtPosition(ts: typeof import('typescript'), sourceFile: ts.SourceFile, position: number, allowPositionInLeadingTrivia: boolean, includeEndPosition: boolean): ts.Node {
|
||||
let current: ts.Node = sourceFile;
|
||||
outer: while (true) {
|
||||
// find the child that contains 'position'
|
||||
|
|
51
build/npm/dirs.js
Normal file
51
build/npm/dirs.js
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
// Complete list of directories where yarn should be executed to install node modules
|
||||
exports.dirs = [
|
||||
'',
|
||||
'build',
|
||||
'build/lib/watch',
|
||||
'extensions',
|
||||
'extensions/configuration-editing',
|
||||
'extensions/css-language-features',
|
||||
'extensions/css-language-features/server',
|
||||
'extensions/debug-auto-launch',
|
||||
'extensions/debug-server-ready',
|
||||
'extensions/emmet',
|
||||
'extensions/extension-editing',
|
||||
'extensions/git',
|
||||
'extensions/git-ui',
|
||||
'extensions/github',
|
||||
'extensions/github-authentication',
|
||||
'extensions/grunt',
|
||||
'extensions/gulp',
|
||||
'extensions/html-language-features',
|
||||
'extensions/html-language-features/server',
|
||||
'extensions/image-preview',
|
||||
'extensions/jake',
|
||||
'extensions/json-language-features',
|
||||
'extensions/json-language-features/server',
|
||||
'extensions/markdown-language-features',
|
||||
'extensions/merge-conflict',
|
||||
'extensions/microsoft-authentication',
|
||||
'extensions/npm',
|
||||
'extensions/php-language-features',
|
||||
'extensions/search-result',
|
||||
'extensions/simple-browser',
|
||||
'extensions/testing-editor-contributions',
|
||||
'extensions/typescript-language-features',
|
||||
'extensions/vscode-api-tests',
|
||||
'extensions/vscode-colorize-tests',
|
||||
'extensions/vscode-custom-editor-tests',
|
||||
'extensions/vscode-notebook-tests',
|
||||
'extensions/vscode-test-resolver',
|
||||
'remote',
|
||||
'remote/web',
|
||||
'test/automation',
|
||||
'test/integration/browser',
|
||||
'test/monaco',
|
||||
'test/smoke',
|
||||
];
|
|
@ -6,6 +6,7 @@
|
|||
const cp = require('child_process');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const { dirs } = require('./dirs');
|
||||
const yarn = process.platform === 'win32' ? 'yarn.cmd' : 'yarn';
|
||||
|
||||
/**
|
||||
|
@ -35,29 +36,39 @@ function yarnInstall(location, opts) {
|
|||
}
|
||||
}
|
||||
|
||||
yarnInstall('extensions'); // node modules shared by all extensions
|
||||
for (let dir of dirs) {
|
||||
|
||||
if (!(process.platform === 'win32' && (process.arch === 'arm64' || process.env['npm_config_arch'] === 'arm64'))) {
|
||||
const env = { ...process.env };
|
||||
if (process.env['VSCODE_REMOTE_CC']) { env['CC'] = process.env['VSCODE_REMOTE_CC']; }
|
||||
if (process.env['VSCODE_REMOTE_CXX']) { env['CXX'] = process.env['VSCODE_REMOTE_CXX']; }
|
||||
if (process.env['VSCODE_REMOTE_NODE_GYP']) { env['npm_config_node_gyp'] = process.env['VSCODE_REMOTE_NODE_GYP']; }
|
||||
|
||||
yarnInstall('remote', { env }); // node modules used by vscode server
|
||||
yarnInstall('remote/web'); // node modules used by vscode web
|
||||
}
|
||||
|
||||
const allExtensionFolders = fs.readdirSync('extensions');
|
||||
const extensions = allExtensionFolders.filter(e => {
|
||||
try {
|
||||
let packageJSON = JSON.parse(fs.readFileSync(path.join('extensions', e, 'package.json')).toString());
|
||||
return packageJSON && (packageJSON.dependencies || packageJSON.devDependencies);
|
||||
} catch (e) {
|
||||
return false;
|
||||
if (dir === '') {
|
||||
// `yarn` already executed in root
|
||||
continue;
|
||||
}
|
||||
});
|
||||
|
||||
extensions.forEach(extension => yarnInstall(`extensions/${extension}`, { ignoreEngines: true }));
|
||||
if (/^remote/.test(dir) && process.platform === 'win32' && (process.arch === 'arm64' || process.env['npm_config_arch'] === 'arm64')) {
|
||||
// windows arm: do not execute `yarn` on remote folder
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dir === 'build/lib/watch') {
|
||||
// node modules for watching, specific to host node version, not electron
|
||||
yarnInstallBuildDependencies();
|
||||
continue;
|
||||
}
|
||||
|
||||
let opts;
|
||||
|
||||
if (dir === 'remote') {
|
||||
// node modules used by vscode server
|
||||
const env = { ...process.env };
|
||||
if (process.env['VSCODE_REMOTE_CC']) { env['CC'] = process.env['VSCODE_REMOTE_CC']; }
|
||||
if (process.env['VSCODE_REMOTE_CXX']) { env['CXX'] = process.env['VSCODE_REMOTE_CXX']; }
|
||||
if (process.env['VSCODE_REMOTE_NODE_GYP']) { env['npm_config_node_gyp'] = process.env['VSCODE_REMOTE_NODE_GYP']; }
|
||||
opts = { env };
|
||||
} else if (/^extensions\//.test(dir)) {
|
||||
opts = { ignoreEngines: true };
|
||||
}
|
||||
|
||||
yarnInstall(dir, opts);
|
||||
}
|
||||
|
||||
function yarnInstallBuildDependencies() {
|
||||
// make sure we install the deps of build/lib/watch for the system installed
|
||||
|
@ -77,10 +88,4 @@ runtime "${runtime}"`;
|
|||
yarnInstall(watchPath);
|
||||
}
|
||||
|
||||
yarnInstall(`build`); // node modules required for build
|
||||
yarnInstall('test/automation'); // node modules required for smoketest
|
||||
yarnInstall('test/smoke'); // node modules required for smoketest
|
||||
yarnInstall('test/integration/browser'); // node modules required for integration
|
||||
yarnInstallBuildDependencies(); // node modules for watching, specific to host node version, not electron
|
||||
|
||||
cp.execSync('git config pull.rebase true');
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
"@types/gulp-json-editor": "^2.2.31",
|
||||
"@types/gulp-rename": "^0.0.33",
|
||||
"@types/gulp-sourcemaps": "^0.0.32",
|
||||
"@types/gulp-uglify": "^3.0.5",
|
||||
"@types/mime": "0.0.29",
|
||||
"@types/minimatch": "^3.0.3",
|
||||
"@types/minimist": "^1.2.1",
|
||||
|
@ -26,7 +25,6 @@
|
|||
"@types/pump": "^1.0.1",
|
||||
"@types/request": "^2.47.0",
|
||||
"@types/rimraf": "^2.0.4",
|
||||
"@types/terser": "^3.12.0",
|
||||
"@types/through": "^0.0.29",
|
||||
"@types/through2": "^2.0.34",
|
||||
"@types/underscore": "^1.8.9",
|
||||
|
@ -36,28 +34,18 @@
|
|||
"applicationinsights": "1.0.8",
|
||||
"azure-storage": "^2.1.0",
|
||||
"electron-osx-sign": "^0.4.16",
|
||||
"github-releases": "^0.4.1",
|
||||
"gulp-azure-storage": "^0.11.1",
|
||||
"gulp-bom": "^1.0.0",
|
||||
"gulp-gzip": "^1.4.2",
|
||||
"gulp-sourcemaps": "^1.11.0",
|
||||
"gulp-uglify": "^3.0.0",
|
||||
"esbuild": "^0.8.30",
|
||||
"iconv-lite-umd": "0.6.8",
|
||||
"jsonc-parser": "^2.3.0",
|
||||
"mime": "^1.4.1",
|
||||
"minimatch": "^3.0.4",
|
||||
"minimist": "^1.2.3",
|
||||
"request": "^2.85.0",
|
||||
"terser": "4.3.8",
|
||||
"source-map": "0.6.1",
|
||||
"typescript": "4.2.0-dev.20201207",
|
||||
"vsce": "1.48.0",
|
||||
"vscode-telemetry-extractor": "^1.6.0",
|
||||
"xml2js": "^0.4.17"
|
||||
"vsce": "1.48.0"
|
||||
},
|
||||
"scripts": {
|
||||
"compile": "tsc -p tsconfig.build.json",
|
||||
"watch": "tsc -p tsconfig.build.json --watch",
|
||||
"postinstall": "npm run compile",
|
||||
"npmCheckJs": "tsc --noEmit"
|
||||
}
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
|
|
2196
build/yarn.lock
2196
build/yarn.lock
File diff suppressed because it is too large
Load diff
|
@ -22,7 +22,6 @@
|
|||
"compile": "gulp compile-extension:css-language-features-client compile-extension:css-language-features-server",
|
||||
"watch": "gulp watch-extension:css-language-features-client watch-extension:css-language-features-server",
|
||||
"test": "node ../../node_modules/mocha/bin/mocha",
|
||||
"postinstall": "cd server && yarn install",
|
||||
"install-client-next": "yarn add vscode-languageclient@next"
|
||||
},
|
||||
"categories": [
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
"url": "https://github.com/microsoft/vscode-emmet"
|
||||
},
|
||||
"activationEvents": [
|
||||
"*",
|
||||
"onStartupFinished",
|
||||
"onCommand:emmet.expandAbbreviation",
|
||||
"onLanguage:html",
|
||||
"onLanguage:css",
|
||||
|
@ -430,14 +430,16 @@
|
|||
"deps": "yarn add vscode-emmet-helper"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^12.19.9"
|
||||
"@types/node": "^12.19.9",
|
||||
"emmet": "https://github.com/rzhao271/emmet.git#1b2df677d8925ef5ea6da9df8845968403979a0a"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emmetio/abbreviation": "^2.2.0",
|
||||
"@emmetio/css-parser": "ramya-rao-a/css-parser#vscode",
|
||||
"@emmetio/html-matcher": "^0.3.3",
|
||||
"@emmetio/math-expression": "^1.0.4",
|
||||
"image-size": "^0.5.2",
|
||||
"vscode-emmet-helper": "~2.0.0",
|
||||
"vscode-html-languageservice": "^3.0.3"
|
||||
"vscode-emmet-helper": "2.2.4-2",
|
||||
"vscode-languageserver-textdocument": "^1.0.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,16 +4,19 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { Node, HtmlNode, Rule, Property, Stylesheet } from 'EmmetNode';
|
||||
import { getEmmetHelper, getNode, getInnerRange, getMappingForIncludedLanguages, parseDocument, validate, getEmmetConfiguration, isStyleSheet, getEmmetMode, parsePartialStylesheet, isStyleAttribute, getEmbeddedCssNodeIfAny, allowedMimeTypesInScriptTag, toLSTextDocument } from './util';
|
||||
import { Node, HtmlNode, Rule, Property, Stylesheet } from 'EmmetFlatNode';
|
||||
import { getEmmetHelper, getFlatNode, getMappingForIncludedLanguages, validate, getEmmetConfiguration, isStyleSheet, getEmmetMode, parsePartialStylesheet, isStyleAttribute, getEmbeddedCssNodeIfAny, allowedMimeTypesInScriptTag, toLSTextDocument } from './util';
|
||||
import { getRootNode as parseDocument } from './parseDocument';
|
||||
import { MarkupAbbreviation } from 'emmet';
|
||||
// import { AbbreviationNode } from '@emmetio/abbreviation';
|
||||
|
||||
const trimRegex = /[\u00a0]*[\d#\-\*\u2022]+\.?/;
|
||||
const hexColorRegex = /^#[\da-fA-F]{0,6}$/;
|
||||
const inlineElements = ['a', 'abbr', 'acronym', 'applet', 'b', 'basefont', 'bdo',
|
||||
'big', 'br', 'button', 'cite', 'code', 'del', 'dfn', 'em', 'font', 'i',
|
||||
'iframe', 'img', 'input', 'ins', 'kbd', 'label', 'map', 'object', 'q',
|
||||
's', 'samp', 'select', 'small', 'span', 'strike', 'strong', 'sub', 'sup',
|
||||
'textarea', 'tt', 'u', 'var'];
|
||||
// const inlineElements = ['a', 'abbr', 'acronym', 'applet', 'b', 'basefont', 'bdo',
|
||||
// 'big', 'br', 'button', 'cite', 'code', 'del', 'dfn', 'em', 'font', 'i',
|
||||
// 'iframe', 'img', 'input', 'ins', 'kbd', 'label', 'map', 'object', 'q',
|
||||
// 's', 'samp', 'select', 'small', 'span', 'strike', 'strong', 'sub', 'sup',
|
||||
// 'textarea', 'tt', 'u', 'var'];
|
||||
|
||||
interface ExpandAbbreviationInput {
|
||||
syntax: string;
|
||||
|
@ -31,35 +34,35 @@ interface PreviewRangesWithContent {
|
|||
}
|
||||
|
||||
export function wrapWithAbbreviation(args: any) {
|
||||
return doWrapping(false, args);
|
||||
return doWrapping(true, args);
|
||||
}
|
||||
|
||||
export function wrapIndividualLinesWithAbbreviation(args: any) {
|
||||
return doWrapping(true, args);
|
||||
}
|
||||
|
||||
function doWrapping(individualLines: boolean, args: any) {
|
||||
function doWrapping(_: boolean, args: any) {
|
||||
if (!validate(false) || !vscode.window.activeTextEditor) {
|
||||
return;
|
||||
}
|
||||
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
if (individualLines) {
|
||||
if (editor.selections.length === 1 && editor.selection.isEmpty) {
|
||||
vscode.window.showInformationMessage('Select more than 1 line and try again.');
|
||||
return;
|
||||
}
|
||||
if (editor.selections.find(x => x.isEmpty)) {
|
||||
vscode.window.showInformationMessage('Select more than 1 line in each selection and try again.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
// if (individualLines) {
|
||||
// if (editor.selections.length === 1 && editor.selection.isEmpty) {
|
||||
// vscode.window.showInformationMessage('Select more than 1 line and try again.');
|
||||
// return;
|
||||
// }
|
||||
// if (editor.selections.find(x => x.isEmpty)) {
|
||||
// vscode.window.showInformationMessage('Select more than 1 line in each selection and try again.');
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
args = args || {};
|
||||
if (!args['language']) {
|
||||
args['language'] = editor.document.languageId;
|
||||
}
|
||||
const syntax = getSyntaxFromArgs(args) || 'html';
|
||||
const rootNode = parseDocument(editor.document, false);
|
||||
const rootNode = parseDocument(editor.document, true);
|
||||
|
||||
let inPreview = false;
|
||||
let currentValue = '';
|
||||
|
@ -68,35 +71,45 @@ function doWrapping(individualLines: boolean, args: any) {
|
|||
// Fetch general information for the succesive expansions. i.e. the ranges to replace and its contents
|
||||
const rangesToReplace: PreviewRangesWithContent[] = editor.selections.sort((a: vscode.Selection, b: vscode.Selection) => { return a.start.compareTo(b.start); }).map(selection => {
|
||||
let rangeToReplace: vscode.Range = selection.isReversed ? new vscode.Range(selection.active, selection.anchor) : selection;
|
||||
const document = editor.document;
|
||||
if (!rangeToReplace.isSingleLine && rangeToReplace.end.character === 0) {
|
||||
const previousLine = rangeToReplace.end.line - 1;
|
||||
const lastChar = editor.document.lineAt(previousLine).text.length;
|
||||
const lastChar = document.lineAt(previousLine).text.length;
|
||||
rangeToReplace = new vscode.Range(rangeToReplace.start, new vscode.Position(previousLine, lastChar));
|
||||
} else if (rangeToReplace.isEmpty) {
|
||||
const { active } = selection;
|
||||
const currentNode = getNode(rootNode, active, true);
|
||||
if (currentNode && (currentNode.start.line === active.line || currentNode.end.line === active.line)) {
|
||||
rangeToReplace = new vscode.Range(currentNode.start, currentNode.end);
|
||||
const activeOffset = document.offsetAt(active);
|
||||
const currentNode = getFlatNode(rootNode, activeOffset, true);
|
||||
if (currentNode) {
|
||||
const currentNodeStart = document.positionAt(currentNode.start);
|
||||
const currentNodeEnd = document.positionAt(currentNode.end);
|
||||
if (currentNodeStart.line === active.line || currentNodeEnd.line === active.line) {
|
||||
rangeToReplace = new vscode.Range(currentNodeStart, currentNodeEnd);
|
||||
}
|
||||
else {
|
||||
rangeToReplace = new vscode.Range(rangeToReplace.start.line, 0, rangeToReplace.start.line, document.lineAt(rangeToReplace.start.line).text.length);
|
||||
}
|
||||
} else {
|
||||
rangeToReplace = new vscode.Range(rangeToReplace.start.line, 0, rangeToReplace.start.line, editor.document.lineAt(rangeToReplace.start.line).text.length);
|
||||
rangeToReplace = new vscode.Range(rangeToReplace.start.line, 0, rangeToReplace.start.line, document.lineAt(rangeToReplace.start.line).text.length);
|
||||
}
|
||||
}
|
||||
|
||||
const firstLineOfSelection = editor.document.lineAt(rangeToReplace.start).text.substr(rangeToReplace.start.character);
|
||||
const firstLineOfSelection = document.lineAt(rangeToReplace.start).text.substr(rangeToReplace.start.character);
|
||||
const matches = firstLineOfSelection.match(/^(\s*)/);
|
||||
const extraWhitespaceSelected = matches ? matches[1].length : 0;
|
||||
rangeToReplace = new vscode.Range(rangeToReplace.start.line, rangeToReplace.start.character + extraWhitespaceSelected, rangeToReplace.end.line, rangeToReplace.end.character);
|
||||
|
||||
let textToWrapInPreview: string[];
|
||||
const textToReplace = editor.document.getText(rangeToReplace);
|
||||
if (individualLines) {
|
||||
textToWrapInPreview = textToReplace.split('\n').map(x => x.trim());
|
||||
} else {
|
||||
const wholeFirstLine = editor.document.lineAt(rangeToReplace.start).text;
|
||||
const otherMatches = wholeFirstLine.match(/^(\s*)/);
|
||||
const precedingWhitespace = otherMatches ? otherMatches[1] : '';
|
||||
textToWrapInPreview = rangeToReplace.isSingleLine ? [textToReplace] : ['\n\t' + textToReplace.split('\n' + precedingWhitespace).join('\n\t') + '\n'];
|
||||
}
|
||||
const textToReplace = document.getText(rangeToReplace);
|
||||
// if (individualLines) {
|
||||
// textToWrapInPreview = textToReplace.split('\n').map(x => x.trim());
|
||||
// } else {
|
||||
// the following assumes the lines are indented the same way
|
||||
const wholeFirstLine = document.lineAt(rangeToReplace.start).text;
|
||||
const otherMatches = wholeFirstLine.match(/^(\s*)/);
|
||||
const precedingWhitespace = otherMatches ? otherMatches[1] : '';
|
||||
textToWrapInPreview = rangeToReplace.isSingleLine ? [textToReplace] : textToReplace.split('\n' + precedingWhitespace).map(x => x.trimEnd());
|
||||
// }
|
||||
textToWrapInPreview = textToWrapInPreview.map(e => e.replace(/(\$\d)/g, '\\$1'));
|
||||
|
||||
return {
|
||||
|
@ -107,7 +120,7 @@ function doWrapping(individualLines: boolean, args: any) {
|
|||
};
|
||||
});
|
||||
|
||||
function revertPreview(): Thenable<any> {
|
||||
function revertPreview(): Thenable<boolean> {
|
||||
return editor.edit(builder => {
|
||||
for (const rangeToReplace of rangesToReplace) {
|
||||
builder.replace(rangeToReplace.previewRange, rangeToReplace.originalContent);
|
||||
|
@ -133,7 +146,8 @@ function doWrapping(individualLines: boolean, args: any) {
|
|||
const preceedingText = editor.document.getText(new vscode.Range(oldPreviewRange.start.line, 0, oldPreviewRange.start.line, oldPreviewRange.start.character));
|
||||
const indentPrefix = (preceedingText.match(/^(\s*)/) || ['', ''])[1];
|
||||
|
||||
let newText = expandedText.replace(/\n/g, '\n' + indentPrefix); // Adding indentation on each line of expanded text
|
||||
let newText = expandedText;
|
||||
newText = newText.replace(/\n/g, '\n' + indentPrefix); // Adding indentation on each line of expanded text
|
||||
newText = newText.replace(/\$\{[\d]*\}/g, '|'); // Removing Tabstops
|
||||
newText = newText.replace(/\$\{[\d]*(:[^}]*)?\}/g, (match) => { // Replacing Placeholders
|
||||
return match.replace(/^\$\{[\d]*:/, '').replace('}', '');
|
||||
|
@ -187,19 +201,21 @@ function doWrapping(individualLines: boolean, args: any) {
|
|||
|
||||
const { abbreviation, filter } = extractedResults;
|
||||
if (definitive) {
|
||||
const revertPromise = inPreview ? revertPreview() : Promise.resolve();
|
||||
const revertPromise = inPreview ? revertPreview() : Promise.resolve(true);
|
||||
return revertPromise.then(() => {
|
||||
const expandAbbrList: ExpandAbbreviationInput[] = rangesToReplace.map(rangesAndContent => {
|
||||
const rangeToReplace = rangesAndContent.originalRange;
|
||||
let textToWrap: string[];
|
||||
if (individualLines) {
|
||||
textToWrap = rangesAndContent.textToWrapInPreview;
|
||||
} else {
|
||||
textToWrap = rangeToReplace.isSingleLine ? ['$TM_SELECTED_TEXT'] : ['\n\t$TM_SELECTED_TEXT\n'];
|
||||
}
|
||||
// if (individualLines) {
|
||||
textToWrap = rangesAndContent.textToWrapInPreview;
|
||||
// } else {
|
||||
// // use the p tag as a dummy element to get Emmet to wrap the expression properly
|
||||
// textToWrap = rangeToReplace.isSingleLine ?
|
||||
// ['$TM_SELECTED_TEXT'] : ['<p>$TM_SELECTED_TEXT</p>'];
|
||||
// }
|
||||
return { syntax: syntax || '', abbreviation, rangeToReplace, textToWrap, filter };
|
||||
});
|
||||
return expandAbbreviationInRange(editor, expandAbbrList, !individualLines).then(() => { return true; });
|
||||
return expandAbbreviationInRange(editor, expandAbbrList, false).then(() => { return true; });
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -214,14 +230,15 @@ function doWrapping(individualLines: boolean, args: any) {
|
|||
if (value !== currentValue) {
|
||||
currentValue = value;
|
||||
makeChanges(value, false).then((out) => {
|
||||
if (typeof out === 'boolean') {
|
||||
inPreview = out;
|
||||
}
|
||||
inPreview = out;
|
||||
});
|
||||
}
|
||||
return '';
|
||||
}
|
||||
const abbreviationPromise: Thenable<string | undefined> = (args && args['abbreviation']) ? Promise.resolve(args['abbreviation']) : vscode.window.showInputBox({ prompt: 'Enter Abbreviation', validateInput: inputChanged });
|
||||
|
||||
const abbreviationPromise: Thenable<string | undefined> = (args && args['abbreviation']) ?
|
||||
Promise.resolve(args['abbreviation']) :
|
||||
vscode.window.showInputBox({ prompt: 'Enter Abbreviation', validateInput: inputChanged });
|
||||
return abbreviationPromise.then(inputAbbreviation => {
|
||||
return makeChanges(inputAbbreviation, true);
|
||||
});
|
||||
|
@ -327,7 +344,7 @@ export function expandEmmetAbbreviation(args: any): Thenable<boolean | undefined
|
|||
if (editor.selections.length === 1 && isStyleSheet(editor.document.languageId) && usePartialParsing && editor.document.lineCount > 1000) {
|
||||
rootNode = parsePartialStylesheet(editor.document, editor.selection.isReversed ? editor.selection.anchor : editor.selection.active);
|
||||
} else {
|
||||
rootNode = parseDocument(editor.document, false);
|
||||
rootNode = parseDocument(editor.document, true);
|
||||
}
|
||||
|
||||
return rootNode;
|
||||
|
@ -342,24 +359,25 @@ export function expandEmmetAbbreviation(args: any): Thenable<boolean | undefined
|
|||
if (!helper.isAbbreviationValid(syntax, abbreviation)) {
|
||||
return;
|
||||
}
|
||||
let currentNode = getNode(getRootNode(), position, true);
|
||||
const offset = editor.document.offsetAt(position);
|
||||
let currentNode = getFlatNode(getRootNode(), offset, true);
|
||||
let validateLocation = true;
|
||||
let syntaxToUse = syntax;
|
||||
|
||||
if (editor.document.languageId === 'html') {
|
||||
if (isStyleAttribute(currentNode, position)) {
|
||||
if (isStyleAttribute(currentNode, offset)) {
|
||||
syntaxToUse = 'css';
|
||||
validateLocation = false;
|
||||
} else {
|
||||
const embeddedCssNode = getEmbeddedCssNodeIfAny(editor.document, currentNode, position);
|
||||
if (embeddedCssNode) {
|
||||
currentNode = getNode(embeddedCssNode, position, true);
|
||||
currentNode = getFlatNode(embeddedCssNode, offset, true);
|
||||
syntaxToUse = 'css';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (validateLocation && !isValidLocationForEmmetAbbreviation(editor.document, getRootNode(), currentNode, syntaxToUse, position, rangeToReplace)) {
|
||||
if (validateLocation && !isValidLocationForEmmetAbbreviation(editor.document, getRootNode(), currentNode, syntaxToUse, offset, rangeToReplace)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -393,10 +411,10 @@ function fallbackTab(): Thenable<boolean | undefined> {
|
|||
* @param position position to validate
|
||||
* @param abbreviationRange The range of the abbreviation for which given position is being validated
|
||||
*/
|
||||
export function isValidLocationForEmmetAbbreviation(document: vscode.TextDocument, rootNode: Node | undefined, currentNode: Node | null, syntax: string, position: vscode.Position, abbreviationRange: vscode.Range): boolean {
|
||||
export function isValidLocationForEmmetAbbreviation(document: vscode.TextDocument, rootNode: Node | undefined, currentNode: Node | undefined, syntax: string, offset: number, abbreviationRange: vscode.Range): boolean {
|
||||
if (isStyleSheet(syntax)) {
|
||||
const stylesheet = <Stylesheet>rootNode;
|
||||
if (stylesheet && (stylesheet.comments || []).some(x => position.isAfterOrEqual(x.start) && position.isBeforeOrEqual(x.end))) {
|
||||
if (stylesheet && (stylesheet.comments || []).some(x => offset >= x.start && offset <= x.end)) {
|
||||
return false;
|
||||
}
|
||||
// Continue validation only if the file was parse-able and the currentNode has been found
|
||||
|
@ -419,14 +437,14 @@ export function isValidLocationForEmmetAbbreviation(document: vscode.TextDocumen
|
|||
const propertyNode = <Property>currentNode;
|
||||
if (propertyNode.terminatorToken
|
||||
&& propertyNode.separator
|
||||
&& position.isAfterOrEqual(propertyNode.separatorToken.end)
|
||||
&& position.isBeforeOrEqual(propertyNode.terminatorToken.start)
|
||||
&& offset >= propertyNode.separatorToken.end
|
||||
&& offset <= propertyNode.terminatorToken.start
|
||||
&& abbreviation.indexOf(':') === -1) {
|
||||
return hexColorRegex.test(abbreviation) || abbreviation === '!';
|
||||
}
|
||||
if (!propertyNode.terminatorToken
|
||||
&& propertyNode.separator
|
||||
&& position.isAfterOrEqual(propertyNode.separatorToken.end)
|
||||
&& offset >= propertyNode.separatorToken.end
|
||||
&& abbreviation.indexOf(':') === -1) {
|
||||
return hexColorRegex.test(abbreviation) || abbreviation === '!';
|
||||
}
|
||||
|
@ -444,7 +462,7 @@ export function isValidLocationForEmmetAbbreviation(document: vscode.TextDocumen
|
|||
const currentCssNode = <Rule>currentNode;
|
||||
|
||||
// Position is valid if it occurs after the `{` that marks beginning of rule contents
|
||||
if (position.isAfter(currentCssNode.contentStartToken.end)) {
|
||||
if (offset > currentCssNode.contentStartToken.end) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -453,12 +471,16 @@ export function isValidLocationForEmmetAbbreviation(document: vscode.TextDocumen
|
|||
// But we should assume it is a valid location for css properties under the parent rule
|
||||
if (currentCssNode.parent
|
||||
&& (currentCssNode.parent.type === 'rule' || currentCssNode.parent.type === 'at-rule')
|
||||
&& currentCssNode.selectorToken
|
||||
&& position.line !== currentCssNode.selectorToken.end.line
|
||||
&& currentCssNode.selectorToken.start.character === abbreviationRange.start.character
|
||||
&& currentCssNode.selectorToken.start.line === abbreviationRange.start.line
|
||||
) {
|
||||
return true;
|
||||
&& currentCssNode.selectorToken) {
|
||||
const position = document.positionAt(offset);
|
||||
const tokenStartPos = document.positionAt(currentCssNode.selectorToken.start);
|
||||
const tokenEndPos = document.positionAt(currentCssNode.selectorToken.end);
|
||||
if (position.line !== tokenEndPos.line
|
||||
&& tokenStartPos.character === abbreviationRange.start.character
|
||||
&& tokenStartPos.line === abbreviationRange.start.line
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -469,7 +491,7 @@ export function isValidLocationForEmmetAbbreviation(document: vscode.TextDocumen
|
|||
const escape = '\\';
|
||||
const question = '?';
|
||||
const currentHtmlNode = <HtmlNode>currentNode;
|
||||
let start = new vscode.Position(0, 0);
|
||||
let start = 0;
|
||||
|
||||
if (currentHtmlNode) {
|
||||
if (currentHtmlNode.name === 'script') {
|
||||
|
@ -487,27 +509,27 @@ export function isValidLocationForEmmetAbbreviation(document: vscode.TextDocumen
|
|||
return false;
|
||||
}
|
||||
|
||||
const innerRange = getInnerRange(currentHtmlNode);
|
||||
|
||||
// Fix for https://github.com/microsoft/vscode/issues/28829
|
||||
if (!innerRange || !innerRange.contains(position)) {
|
||||
if (!currentHtmlNode.open || !currentHtmlNode.close ||
|
||||
!(currentHtmlNode.open.end <= offset && offset <= currentHtmlNode.close.start)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fix for https://github.com/microsoft/vscode/issues/35128
|
||||
// Find the position up till where we will backtrack looking for unescaped < or >
|
||||
// to decide if current position is valid for emmet expansion
|
||||
start = innerRange.start;
|
||||
start = currentHtmlNode.open.end;
|
||||
let lastChildBeforePosition = currentHtmlNode.firstChild;
|
||||
while (lastChildBeforePosition) {
|
||||
if (lastChildBeforePosition.end.isAfter(position)) {
|
||||
if (lastChildBeforePosition.end > offset) {
|
||||
break;
|
||||
}
|
||||
start = lastChildBeforePosition.end;
|
||||
lastChildBeforePosition = lastChildBeforePosition.nextSibling;
|
||||
}
|
||||
}
|
||||
let textToBackTrack = document.getText(new vscode.Range(start.line, start.character, abbreviationRange.start.line, abbreviationRange.start.character));
|
||||
const startPos = document.positionAt(start);
|
||||
let textToBackTrack = document.getText(new vscode.Range(startPos.line, startPos.character, abbreviationRange.start.line, abbreviationRange.start.character));
|
||||
|
||||
// Worse case scenario is when cursor is inside a big chunk of text which needs to backtracked
|
||||
// Backtrack only 500 offsets to ensure we dont waste time doing this
|
||||
|
@ -582,7 +604,7 @@ function expandAbbreviationInRange(editor: vscode.TextEditor, expandAbbrList: Ex
|
|||
const insertPromises: Thenable<boolean>[] = [];
|
||||
if (!insertSameSnippet) {
|
||||
expandAbbrList.sort((a: ExpandAbbreviationInput, b: ExpandAbbreviationInput) => { return b.rangeToReplace.start.compareTo(a.rangeToReplace.start); }).forEach((expandAbbrInput: ExpandAbbreviationInput) => {
|
||||
let expandedText = expandAbbr(expandAbbrInput);
|
||||
const expandedText = expandAbbr(expandAbbrInput);
|
||||
if (expandedText) {
|
||||
insertPromises.push(editor.insertSnippet(new vscode.SnippetString(expandedText), expandAbbrInput.rangeToReplace, { undoStopBefore: false, undoStopAfter: false }));
|
||||
}
|
||||
|
@ -607,24 +629,26 @@ function expandAbbreviationInRange(editor: vscode.TextEditor, expandAbbrList: Ex
|
|||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Walks the tree rooted at root and apply function fn on each node.
|
||||
* if fn return false at any node, the further processing of tree is stopped.
|
||||
*/
|
||||
function walk(root: any, fn: ((node: any) => boolean)): boolean {
|
||||
let ctx = root;
|
||||
while (ctx) {
|
||||
// /*
|
||||
// * Walks the tree rooted at root and apply function fn on each node.
|
||||
// * if fn return false at any node, the further processing of tree is stopped.
|
||||
// */
|
||||
// function walk(root: AbbreviationNode, fn: ((node: AbbreviationNode) => boolean)): boolean {
|
||||
// if (fn(root) === false || walkChildren(root.children, fn) === false) {
|
||||
// return false;
|
||||
// }
|
||||
// return true;
|
||||
// }
|
||||
|
||||
const next = ctx.next;
|
||||
if (fn(ctx) === false || walk(ctx.firstChild, fn) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ctx = next;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
// function walkChildren(children: AbbreviationNode[], fn: ((node: AbbreviationNode) => boolean)): boolean {
|
||||
// for (let i = 0; i < children.length; i++) {
|
||||
// const child = children[i];
|
||||
// if (walk(child, fn) === false) {
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
// return true;
|
||||
// }
|
||||
|
||||
/**
|
||||
* Expands abbreviation as detailed in given input.
|
||||
|
@ -634,7 +658,7 @@ function expandAbbr(input: ExpandAbbreviationInput): string | undefined {
|
|||
const expandOptions = helper.getExpandOptions(input.syntax, getEmmetConfiguration(input.syntax), input.filter);
|
||||
|
||||
if (input.textToWrap) {
|
||||
if (input.filter && input.filter.indexOf('t') > -1) {
|
||||
if (input.filter && input.filter.includes('t')) {
|
||||
input.textToWrap = input.textToWrap.map(line => {
|
||||
return line.replace(trimRegex, '').trim();
|
||||
});
|
||||
|
@ -644,46 +668,47 @@ function expandAbbr(input: ExpandAbbreviationInput): string | undefined {
|
|||
// Below fixes https://github.com/microsoft/vscode/issues/29898
|
||||
// With this, Emmet formats inline elements as block elements
|
||||
// ensuring the wrapped multi line text does not get merged to a single line
|
||||
if (!input.rangeToReplace.isSingleLine) {
|
||||
expandOptions.profile['inlineBreak'] = 1;
|
||||
if (!input.rangeToReplace.isSingleLine && expandOptions.options) {
|
||||
expandOptions.options['output.inlineBreak'] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
let expandedText;
|
||||
try {
|
||||
// Expand the abbreviation
|
||||
if (input.textToWrap && !isStyleSheet(input.syntax)) {
|
||||
const parsedAbbr = <MarkupAbbreviation>helper.parseAbbreviation(input.abbreviation, expandOptions);
|
||||
// if (input.rangeToReplace.isSingleLine && input.textToWrap.length === 1) {
|
||||
// // Fetch rightmost element in the parsed abbreviation (i.e the element that will contain the wrapped text).
|
||||
// const wrappingNodeChildren = parsedAbbr.children;
|
||||
// let wrappingNode = wrappingNodeChildren[wrappingNodeChildren.length - 1];
|
||||
// while (wrappingNode && wrappingNode.children && wrappingNode.children.length > 0) {
|
||||
// wrappingNode = wrappingNode.children[wrappingNode.children.length - 1];
|
||||
// }
|
||||
|
||||
if (input.textToWrap) {
|
||||
const parsedAbbr = helper.parseAbbreviation(input.abbreviation, expandOptions);
|
||||
if (input.rangeToReplace.isSingleLine && input.textToWrap.length === 1) {
|
||||
|
||||
// Fetch rightmost element in the parsed abbreviation (i.e the element that will contain the wrapped text).
|
||||
let wrappingNode = parsedAbbr;
|
||||
while (wrappingNode && wrappingNode.children && wrappingNode.children.length > 0) {
|
||||
wrappingNode = wrappingNode.children[wrappingNode.children.length - 1];
|
||||
}
|
||||
|
||||
// If wrapping with a block element, insert newline in the text to wrap.
|
||||
if (wrappingNode && inlineElements.indexOf(wrappingNode.name) === -1 && (expandOptions['profile'].hasOwnProperty('format') ? expandOptions['profile'].format : true)) {
|
||||
wrappingNode.value = '\n\t' + wrappingNode.value + '\n';
|
||||
}
|
||||
}
|
||||
// // If wrapping with a block element, insert newline in the text to wrap.
|
||||
// // const format = expandOptions.options ? (expandOptions.options['output.format'] ?? true) : true;
|
||||
// // if (wrappingNode && wrappingNode.name && wrappingNode.value
|
||||
// // && inlineElements.indexOf(wrappingNode.name) === -1
|
||||
// // && format) {
|
||||
// // wrappingNode.value[0] = '\n\t' + wrappingNode.value[0] + '\n';
|
||||
// // }
|
||||
// }
|
||||
|
||||
// Below fixes https://github.com/microsoft/vscode/issues/78219
|
||||
// walk the tree and remove tags for empty values
|
||||
walk(parsedAbbr, node => {
|
||||
if (node.name !== null && node.value === '' && !node.isSelfClosing && node.children.length === 0) {
|
||||
node.name = '';
|
||||
node.value = '\n';
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
// walkChildren(parsedAbbr.children, node => {
|
||||
// if (node.name !== null && node.value && node.value[0] === '' && !node.selfClosing && node.children.length === 0) {
|
||||
// node.name = '';
|
||||
// node.value[0] = '\n';
|
||||
// }
|
||||
// return true;
|
||||
// });
|
||||
|
||||
expandedText = helper.expandAbbreviation(parsedAbbr, expandOptions);
|
||||
// All $anyword would have been escaped by the emmet helper.
|
||||
// Remove the escaping backslash from $TM_SELECTED_TEXT so that VS Code Snippet controller can treat it as a variable
|
||||
expandedText = expandedText.replace('\\$TM_SELECTED_TEXT', '$TM_SELECTED_TEXT');
|
||||
expandedText = expandedText.replace('<p>\\$TM_SELECTED_TEXT</p>', '$TM_SELECTED_TEXT');
|
||||
} else {
|
||||
expandedText = helper.expandAbbreviation(input.abbreviation, expandOptions);
|
||||
}
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { getHtmlNodeLS, offsetRangeToSelection, toLSTextDocument, validate } from './util';
|
||||
import { parseMarkupDocument } from './parseMarkupDocument';
|
||||
import { TextDocument as LSTextDocument } from 'vscode-html-languageservice';
|
||||
import { getHtmlFlatNode, offsetRangeToSelection, validate } from './util';
|
||||
import { getRootNode } from './parseDocument';
|
||||
import { HtmlNode as HtmlFlatNode } from 'EmmetFlatNode';
|
||||
|
||||
let balanceOutStack: Array<vscode.Selection[]> = [];
|
||||
let lastBalancedSelections: vscode.Selection[] = [];
|
||||
|
@ -24,16 +24,16 @@ function balance(out: boolean) {
|
|||
return;
|
||||
}
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
const document = toLSTextDocument(editor.document);
|
||||
const htmlDocument = parseMarkupDocument(document);
|
||||
if (!htmlDocument) {
|
||||
const document = editor.document;
|
||||
const rootNode = <HtmlFlatNode>getRootNode(document, true);
|
||||
if (!rootNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
const rangeFn = out ? getRangeToBalanceOut : getRangeToBalanceIn;
|
||||
let newSelections: vscode.Selection[] = [];
|
||||
editor.selections.forEach(selection => {
|
||||
const range = rangeFn(document, selection);
|
||||
const range = rangeFn(document, rootNode, selection);
|
||||
newSelections.push(range);
|
||||
});
|
||||
|
||||
|
@ -57,17 +57,18 @@ function balance(out: boolean) {
|
|||
lastBalancedSelections = editor.selections;
|
||||
}
|
||||
|
||||
function getRangeToBalanceOut(document: LSTextDocument, selection: vscode.Selection): vscode.Selection {
|
||||
const nodeToBalance = getHtmlNodeLS(document, selection.start, false);
|
||||
function getRangeToBalanceOut(document: vscode.TextDocument, rootNode: HtmlFlatNode, selection: vscode.Selection): vscode.Selection {
|
||||
const offset = document.offsetAt(selection.start);
|
||||
const nodeToBalance = getHtmlFlatNode(document.getText(), rootNode, offset, false);
|
||||
if (!nodeToBalance) {
|
||||
return selection;
|
||||
}
|
||||
if (!nodeToBalance.endTagStart || !nodeToBalance.startTagEnd) {
|
||||
if (!nodeToBalance.open || !nodeToBalance.close) {
|
||||
return offsetRangeToSelection(document, nodeToBalance.start, nodeToBalance.end);
|
||||
}
|
||||
|
||||
const innerSelection = offsetRangeToSelection(document, nodeToBalance.startTagEnd, nodeToBalance.endTagStart);
|
||||
const outerSelection = offsetRangeToSelection(document, nodeToBalance.start, nodeToBalance.end);
|
||||
const innerSelection = offsetRangeToSelection(document, nodeToBalance.open.end, nodeToBalance.close.start);
|
||||
const outerSelection = offsetRangeToSelection(document, nodeToBalance.open.start, nodeToBalance.close.end);
|
||||
|
||||
if (innerSelection.contains(selection) && !innerSelection.isEqual(selection)) {
|
||||
return innerSelection;
|
||||
|
@ -78,34 +79,35 @@ function getRangeToBalanceOut(document: LSTextDocument, selection: vscode.Select
|
|||
return selection;
|
||||
}
|
||||
|
||||
function getRangeToBalanceIn(document: LSTextDocument, selection: vscode.Selection): vscode.Selection {
|
||||
const nodeToBalance = getHtmlNodeLS(document, selection.start, true);
|
||||
function getRangeToBalanceIn(document: vscode.TextDocument, rootNode: HtmlFlatNode, selection: vscode.Selection): vscode.Selection {
|
||||
const offset = document.offsetAt(selection.start);
|
||||
const nodeToBalance = getHtmlFlatNode(document.getText(), rootNode, offset, true);
|
||||
if (!nodeToBalance) {
|
||||
return selection;
|
||||
}
|
||||
|
||||
const selectionStart = document.offsetAt(selection.start);
|
||||
const selectionEnd = document.offsetAt(selection.end);
|
||||
if (nodeToBalance.endTagStart !== undefined && nodeToBalance.startTagEnd !== undefined) {
|
||||
if (nodeToBalance.open && nodeToBalance.close) {
|
||||
const entireNodeSelected = selectionStart === nodeToBalance.start && selectionEnd === nodeToBalance.end;
|
||||
const startInOpenTag = selectionStart > nodeToBalance.start && selectionStart < nodeToBalance.startTagEnd;
|
||||
const startInCloseTag = selectionStart > nodeToBalance.endTagStart && selectionStart < nodeToBalance.end;
|
||||
const startInOpenTag = selectionStart > nodeToBalance.open.start && selectionStart < nodeToBalance.open.end;
|
||||
const startInCloseTag = selectionStart > nodeToBalance.close.start && selectionStart < nodeToBalance.close.end;
|
||||
|
||||
if (entireNodeSelected || startInOpenTag || startInCloseTag) {
|
||||
return offsetRangeToSelection(document, nodeToBalance.startTagEnd, nodeToBalance.endTagStart);
|
||||
return offsetRangeToSelection(document, nodeToBalance.open.end, nodeToBalance.close.start);
|
||||
}
|
||||
}
|
||||
|
||||
if (!nodeToBalance.children.length) {
|
||||
if (!nodeToBalance.firstChild) {
|
||||
return selection;
|
||||
}
|
||||
|
||||
const firstChild = nodeToBalance.children[0];
|
||||
const firstChild = nodeToBalance.firstChild;
|
||||
if (selectionStart === firstChild.start
|
||||
&& selectionEnd === firstChild.end
|
||||
&& firstChild.endTagStart !== undefined
|
||||
&& firstChild.startTagEnd !== undefined) {
|
||||
return offsetRangeToSelection(document, firstChild.startTagEnd, firstChild.endTagStart);
|
||||
&& firstChild.open
|
||||
&& firstChild.close) {
|
||||
return offsetRangeToSelection(document, firstChild.open.end, firstChild.close.start);
|
||||
}
|
||||
|
||||
return offsetRangeToSelection(document, firstChild.start, firstChild.end);
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
/* Based on @sergeche's work in his emmet plugin */
|
||||
|
||||
import { TextDocument, Position, Range, EndOfLine } from 'vscode';
|
||||
import { TextDocument } from 'vscode';
|
||||
|
||||
/**
|
||||
* A stream reader for VSCode's `TextDocument`
|
||||
|
@ -13,40 +13,37 @@ import { TextDocument, Position, Range, EndOfLine } from 'vscode';
|
|||
*/
|
||||
export class DocumentStreamReader {
|
||||
private document: TextDocument;
|
||||
private start: Position;
|
||||
private _eof: Position;
|
||||
private _sof: Position;
|
||||
public pos: Position;
|
||||
private _eol: string;
|
||||
|
||||
constructor(document: TextDocument, pos?: Position, limit?: Range) {
|
||||
private start: number;
|
||||
private _eof: number;
|
||||
private _sof: number;
|
||||
public pos: number;
|
||||
|
||||
constructor(document: TextDocument, pos?: number, limit?: [number, number]) {
|
||||
this.document = document;
|
||||
this.start = this.pos = pos ? pos : new Position(0, 0);
|
||||
this._sof = limit ? limit.start : new Position(0, 0);
|
||||
this._eof = limit ? limit.end : new Position(this.document.lineCount - 1, this._lineLength(this.document.lineCount - 1));
|
||||
this._eol = this.document.eol === EndOfLine.LF ? '\n' : '\r\n';
|
||||
this.start = this.pos = pos ? pos : 0;
|
||||
this._sof = limit ? limit[0] : 0;
|
||||
this._eof = limit ? limit[1] : document.getText().length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true only if the stream is at the start of the file.
|
||||
*/
|
||||
sof(): boolean {
|
||||
return this.pos.isBeforeOrEqual(this._sof);
|
||||
return this.pos <= this._sof;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true only if the stream is at the end of the file.
|
||||
*/
|
||||
eof(): boolean {
|
||||
return this.pos.isAfterOrEqual(this._eof);
|
||||
return this.pos >= this._eof;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new stream instance which is limited to given range for given document
|
||||
*/
|
||||
limit(start: Position, end: Position): DocumentStreamReader {
|
||||
return new DocumentStreamReader(this.document, start, new Range(start, end));
|
||||
limit(start: number, end: number): DocumentStreamReader {
|
||||
return new DocumentStreamReader(this.document, start, [start, end]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -57,8 +54,7 @@ export class DocumentStreamReader {
|
|||
if (this.eof()) {
|
||||
return NaN;
|
||||
}
|
||||
const line = this.document.lineAt(this.pos.line).text;
|
||||
return this.pos.character < line.length ? line.charCodeAt(this.pos.character) : this._eol.charCodeAt(this.pos.character - line.length);
|
||||
return this.document.getText().charCodeAt(this.pos);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -70,19 +66,12 @@ export class DocumentStreamReader {
|
|||
return NaN;
|
||||
}
|
||||
|
||||
const line = this.document.lineAt(this.pos.line).text;
|
||||
let code: number;
|
||||
if (this.pos.character < line.length) {
|
||||
code = line.charCodeAt(this.pos.character);
|
||||
this.pos = this.pos.translate(0, 1);
|
||||
} else {
|
||||
code = this._eol.charCodeAt(this.pos.character - line.length);
|
||||
this.pos = new Position(this.pos.line + 1, 0);
|
||||
}
|
||||
const code = this.document.getText().charCodeAt(this.pos);
|
||||
this.pos++;
|
||||
|
||||
if (this.eof()) {
|
||||
// restrict pos to eof, if in case it got moved beyond eof
|
||||
this.pos = new Position(this._eof.line, this._eof.character);
|
||||
this.pos = this._eof;
|
||||
}
|
||||
|
||||
return code;
|
||||
|
@ -92,20 +81,11 @@ export class DocumentStreamReader {
|
|||
* Backs up the stream n characters. Backing it up further than the
|
||||
* start of the current token will cause things to break, so be careful.
|
||||
*/
|
||||
backUp(n: number) {
|
||||
let row = this.pos.line;
|
||||
let column = this.pos.character;
|
||||
column -= (n || 1);
|
||||
|
||||
while (row >= 0 && column < 0) {
|
||||
row--;
|
||||
column += this._lineLength(row);
|
||||
backUp(n: number): number {
|
||||
this.pos -= n;
|
||||
if (this.pos < 0) {
|
||||
this.pos = 0;
|
||||
}
|
||||
|
||||
this.pos = row < 0 || column < 0
|
||||
? new Position(0, 0)
|
||||
: new Position(row, column);
|
||||
|
||||
return this.peek();
|
||||
}
|
||||
|
||||
|
@ -120,29 +100,18 @@ export class DocumentStreamReader {
|
|||
/**
|
||||
* Returns contents for given range
|
||||
*/
|
||||
substring(from: Position, to: Position): string {
|
||||
return this.document.getText(new Range(from, to));
|
||||
substring(from: number, to: number): string {
|
||||
return this.document.getText().substring(from, to);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates error object with current stream state
|
||||
*/
|
||||
error(message: string): Error {
|
||||
const err = new Error(`${message} at row ${this.pos.line}, column ${this.pos.character}`);
|
||||
|
||||
const err = new Error(`${message} at offset ${this.pos}`);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns line length of given row, including line ending
|
||||
*/
|
||||
_lineLength(row: number): number {
|
||||
if (row === this.document.lineCount - 1) {
|
||||
return this.document.lineAt(row).text.length;
|
||||
}
|
||||
return this.document.lineAt(row).text.length + this._eol.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* `match` can be a character code or a function that takes a character code
|
||||
* and returns a boolean. If the next character in the stream 'matches'
|
||||
|
@ -167,6 +136,6 @@ export class DocumentStreamReader {
|
|||
eatWhile(match: number | Function): boolean {
|
||||
const start = this.pos;
|
||||
while (!this.eof() && this.eat(match)) { }
|
||||
return !this.pos.isEqual(start);
|
||||
return this.pos !== start;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,17 +4,16 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { Node, Stylesheet } from 'EmmetNode';
|
||||
import { Node, Stylesheet } from 'EmmetFlatNode';
|
||||
import { isValidLocationForEmmetAbbreviation, getSyntaxFromArgs } from './abbreviationActions';
|
||||
import { getEmmetHelper, getMappingForIncludedLanguages, parsePartialStylesheet, getEmmetConfiguration, getEmmetMode, isStyleSheet, parseDocument, getNode, allowedMimeTypesInScriptTag, trimQuotes, toLSTextDocument } from './util';
|
||||
import { getLanguageService, TokenType, Range as LSRange } from 'vscode-html-languageservice';
|
||||
import { getEmmetHelper, getMappingForIncludedLanguages, parsePartialStylesheet, getEmmetConfiguration, getEmmetMode, isStyleSheet, getFlatNode, allowedMimeTypesInScriptTag, toLSTextDocument, getHtmlFlatNode } from './util';
|
||||
import { Range as LSRange } from 'vscode-languageserver-textdocument';
|
||||
import { getRootNode } from './parseDocument';
|
||||
|
||||
export class DefaultCompletionItemProvider implements vscode.CompletionItemProvider {
|
||||
|
||||
private lastCompletionType: string | undefined;
|
||||
|
||||
private htmlLS = getLanguageService();
|
||||
|
||||
public provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, _: vscode.CancellationToken, context: vscode.CompletionContext): Thenable<vscode.CompletionList | undefined> | undefined {
|
||||
const completionResult = this.provideCompletionItemsInternal(document, position, context);
|
||||
if (!completionResult) {
|
||||
|
@ -62,8 +61,8 @@ export class DefaultCompletionItemProvider implements vscode.CompletionItemProvi
|
|||
|
||||
const helper = getEmmetHelper();
|
||||
let validateLocation = syntax === 'html' || syntax === 'jsx' || syntax === 'xml';
|
||||
let rootNode: Node | undefined = undefined;
|
||||
let currentNode: Node | null = null;
|
||||
let rootNode: Node | undefined;
|
||||
let currentNode: Node | undefined;
|
||||
|
||||
const lsDoc = toLSTextDocument(document);
|
||||
position = document.validatePosition(position);
|
||||
|
@ -81,19 +80,16 @@ export class DefaultCompletionItemProvider implements vscode.CompletionItemProvi
|
|||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
if (validateLocation) {
|
||||
|
||||
const parsedLsDoc = this.htmlLS.parseHTMLDocument(lsDoc);
|
||||
const positionOffset = document.offsetAt(position);
|
||||
const node = parsedLsDoc.findNodeAt(positionOffset);
|
||||
|
||||
if (node.tag === 'script') {
|
||||
if (node.attributes && 'type' in node.attributes) {
|
||||
const rawTypeAttrValue = node.attributes['type'];
|
||||
if (rawTypeAttrValue) {
|
||||
const typeAttrValue = trimQuotes(rawTypeAttrValue);
|
||||
const emmetRootNode = getRootNode(document, true);
|
||||
const foundNode = getHtmlFlatNode(document.getText(), emmetRootNode, positionOffset, false);
|
||||
if (foundNode) {
|
||||
if (foundNode.name === 'script') {
|
||||
const typeNode = foundNode.attributes.find(attr => attr.name.toString() === 'type');
|
||||
if (typeNode) {
|
||||
const typeAttrValue = typeNode.value.toString();
|
||||
if (typeAttrValue === 'application/javascript' || typeAttrValue === 'text/javascript') {
|
||||
if (!getSyntaxFromArgs({ language: 'javascript' })) {
|
||||
return;
|
||||
|
@ -101,34 +97,19 @@ export class DefaultCompletionItemProvider implements vscode.CompletionItemProvi
|
|||
validateLocation = false;
|
||||
}
|
||||
}
|
||||
|
||||
else if (allowedMimeTypesInScriptTag.indexOf(trimQuotes(rawTypeAttrValue)) > -1) {
|
||||
else if (allowedMimeTypesInScriptTag.includes(typeAttrValue)) {
|
||||
validateLocation = false;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (node.tag === 'style') {
|
||||
syntax = 'css';
|
||||
validateLocation = false;
|
||||
} else {
|
||||
if (node.attributes && node.attributes['style']) {
|
||||
const scanner = this.htmlLS.createScanner(document.getText(), node.start);
|
||||
let tokenType = scanner.scan();
|
||||
let prevAttr = undefined;
|
||||
let styleAttrValueRange: [number, number] | undefined = undefined;
|
||||
while (tokenType !== TokenType.EOS && (scanner.getTokenEnd() <= positionOffset)) {
|
||||
tokenType = scanner.scan();
|
||||
if (tokenType === TokenType.AttributeName) {
|
||||
prevAttr = scanner.getTokenText();
|
||||
}
|
||||
else if (tokenType === TokenType.AttributeValue && prevAttr === 'style') {
|
||||
styleAttrValueRange = [scanner.getTokenOffset(), scanner.getTokenEnd()];
|
||||
}
|
||||
}
|
||||
if (prevAttr === 'style' && styleAttrValueRange && positionOffset > styleAttrValueRange[0] && positionOffset < styleAttrValueRange[1]) {
|
||||
else if (foundNode.name === 'style') {
|
||||
syntax = 'css';
|
||||
validateLocation = false;
|
||||
} else {
|
||||
const styleNode = foundNode.attributes.find(attr => attr.name.toString() === 'style');
|
||||
if (styleNode && styleNode.value.start <= positionOffset && positionOffset <= styleNode.value.end) {
|
||||
syntax = 'css';
|
||||
validateLocation = false;
|
||||
}
|
||||
|
@ -145,19 +126,18 @@ export class DefaultCompletionItemProvider implements vscode.CompletionItemProvi
|
|||
return;
|
||||
}
|
||||
|
||||
const offset = document.offsetAt(position);
|
||||
if (isStyleSheet(document.languageId) && context.triggerKind !== vscode.CompletionTriggerKind.TriggerForIncompleteCompletions) {
|
||||
validateLocation = true;
|
||||
let usePartialParsing = vscode.workspace.getConfiguration('emmet')['optimizeStylesheetParsing'] === true;
|
||||
rootNode = usePartialParsing && document.lineCount > 1000 ? parsePartialStylesheet(document, position) : <Stylesheet>parseDocument(document, false);
|
||||
rootNode = usePartialParsing && document.lineCount > 1000 ? parsePartialStylesheet(document, position) : <Stylesheet>getRootNode(document, true);
|
||||
if (!rootNode) {
|
||||
return;
|
||||
}
|
||||
currentNode = getNode(rootNode, position, true);
|
||||
currentNode = getFlatNode(rootNode, offset, true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (validateLocation && !isValidLocationForEmmetAbbreviation(document, rootNode, currentNode, syntax, position, toRange(extractAbbreviationResults.abbreviationRange))) {
|
||||
if (validateLocation && !isValidLocationForEmmetAbbreviation(document, rootNode, currentNode, syntax, offset, toRange(extractAbbreviationResults.abbreviationRange))) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,9 +17,9 @@ import { fetchEditPoint } from './editPoint';
|
|||
import { fetchSelectItem } from './selectItem';
|
||||
import { evaluateMathExpression } from './evaluateMathExpression';
|
||||
import { incrementDecrement } from './incrementDecrement';
|
||||
import { LANGUAGE_MODES, getMappingForIncludedLanguages, updateEmmetExtensionsPath, getPathBaseName, toLSTextDocument, getSyntaxes, getEmmetMode } from './util';
|
||||
import { LANGUAGE_MODES, getMappingForIncludedLanguages, updateEmmetExtensionsPath, getPathBaseName, getSyntaxes, getEmmetMode } from './util';
|
||||
import { reflectCssValue } from './reflectCssValue';
|
||||
import { addFileToMarkupParseCache, removeFileFromMarkupParseCache } from './parseMarkupDocument';
|
||||
import { addFileToParseCache, removeFileFromParseCache } from './parseDocument';
|
||||
|
||||
export function activateEmmetExtension(context: vscode.ExtensionContext) {
|
||||
registerCompletionProviders(context);
|
||||
|
@ -149,15 +149,17 @@ export function activateEmmetExtension(context: vscode.ExtensionContext) {
|
|||
|
||||
context.subscriptions.push(vscode.workspace.onDidOpenTextDocument((e) => {
|
||||
const emmetMode = getEmmetMode(e.languageId, []) ?? '';
|
||||
if (getSyntaxes().markup.includes(emmetMode)) {
|
||||
addFileToMarkupParseCache(toLSTextDocument(e));
|
||||
const syntaxes = getSyntaxes();
|
||||
if (syntaxes.markup.includes(emmetMode) || syntaxes.stylesheet.includes(emmetMode)) {
|
||||
addFileToParseCache(e);
|
||||
}
|
||||
}));
|
||||
|
||||
context.subscriptions.push(vscode.workspace.onDidCloseTextDocument((e) => {
|
||||
const emmetMode = getEmmetMode(e.languageId, []) ?? '';
|
||||
if (getSyntaxes().markup.includes(emmetMode)) {
|
||||
removeFileFromMarkupParseCache(toLSTextDocument(e));
|
||||
const syntaxes = getSyntaxes();
|
||||
if (syntaxes.markup.includes(emmetMode) || syntaxes.stylesheet.includes(emmetMode)) {
|
||||
removeFileFromParseCache(e);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
import * as vscode from 'vscode';
|
||||
import evaluate, { extract } from '@emmetio/math-expression';
|
||||
import { DocumentStreamReader } from './bufferStream';
|
||||
|
||||
export function evaluateMathExpression(): Thenable<boolean> {
|
||||
if (!vscode.window.activeTextEditor) {
|
||||
|
@ -15,13 +14,12 @@ export function evaluateMathExpression(): Thenable<boolean> {
|
|||
return Promise.resolve(false);
|
||||
}
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
const stream = new DocumentStreamReader(editor.document);
|
||||
return editor.edit(editBuilder => {
|
||||
editor.selections.forEach(selection => {
|
||||
// startpos always comes before endpos
|
||||
const startpos = selection.isReversed ? selection.active : selection.anchor;
|
||||
const endpos = selection.isReversed ? selection.anchor : selection.active;
|
||||
const selectionText = stream.substring(startpos, endpos);
|
||||
const selectionText = editor.document.getText(new vscode.Range(startpos, endpos));
|
||||
|
||||
try {
|
||||
if (selectionText) {
|
||||
|
@ -30,7 +28,7 @@ export function evaluateMathExpression(): Thenable<boolean> {
|
|||
editBuilder.replace(new vscode.Range(startpos, endpos), result);
|
||||
} else {
|
||||
// no selection made, extract expression from line
|
||||
const lineToSelectionEnd = stream.substring(new vscode.Position(selection.end.line, 0), endpos);
|
||||
const lineToSelectionEnd = editor.document.getText(new vscode.Range(new vscode.Position(selection.end.line, 0), endpos));
|
||||
const extractedIndices = extract(lineToSelectionEnd);
|
||||
if (!extractedIndices) {
|
||||
throw new Error('Invalid extracted indices');
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
// Based on @sergeche's work on the emmet plugin for atom
|
||||
// TODO: Move to https://github.com/emmetio/image-size
|
||||
|
||||
import * as path from 'path';
|
||||
import * as http from 'http';
|
||||
|
|
|
@ -4,8 +4,9 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { toLSTextDocument, validate, getHtmlNodeLS, offsetRangeToSelection } from './util';
|
||||
import { TextDocument as LSTextDocument } from 'vscode-html-languageservice';
|
||||
import { validate, getHtmlFlatNode, offsetRangeToSelection } from './util';
|
||||
import { getRootNode } from './parseDocument';
|
||||
import { HtmlNode as HtmlFlatNode } from 'EmmetFlatNode';
|
||||
|
||||
export function matchTag() {
|
||||
if (!validate(false) || !vscode.window.activeTextEditor) {
|
||||
|
@ -13,11 +14,15 @@ export function matchTag() {
|
|||
}
|
||||
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
const document = toLSTextDocument(editor.document);
|
||||
const document = editor.document;
|
||||
const rootNode = <HtmlFlatNode>getRootNode(document, true);
|
||||
if (!rootNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
let updatedSelections: vscode.Selection[] = [];
|
||||
editor.selections.forEach(selection => {
|
||||
const updatedSelection = getUpdatedSelections(document, selection.start);
|
||||
const updatedSelection = getUpdatedSelections(document, rootNode, selection.start);
|
||||
if (updatedSelection) {
|
||||
updatedSelections.push(updatedSelection);
|
||||
}
|
||||
|
@ -28,22 +33,21 @@ export function matchTag() {
|
|||
}
|
||||
}
|
||||
|
||||
function getUpdatedSelections(document: LSTextDocument, position: vscode.Position): vscode.Selection | undefined {
|
||||
const currentNode = getHtmlNodeLS(document, position, true);
|
||||
function getUpdatedSelections(document: vscode.TextDocument, rootNode: HtmlFlatNode, position: vscode.Position): vscode.Selection | undefined {
|
||||
const offset = document.offsetAt(position);
|
||||
const currentNode = getHtmlFlatNode(document.getText(), rootNode, offset, true);
|
||||
if (!currentNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
const offset = document.offsetAt(position);
|
||||
|
||||
// If no closing tag or cursor is between open and close tag, then no-op
|
||||
if (currentNode.endTagStart === undefined
|
||||
|| currentNode.startTagEnd === undefined
|
||||
|| (offset > currentNode.startTagEnd && offset < currentNode.endTagStart)) {
|
||||
// If no opening/closing tag or cursor is between open and close tag, then no-op
|
||||
if (!currentNode.open
|
||||
|| !currentNode.close
|
||||
|| (offset > currentNode.open.end && offset < currentNode.close.start)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Place cursor inside the close tag if cursor is inside the open tag, else place it inside the open tag
|
||||
const finalOffset = (offset <= currentNode.startTagEnd) ? currentNode.endTagStart + 2 : currentNode.start + 1;
|
||||
const finalOffset = (offset <= currentNode.open.end) ? currentNode.close.start + 2 : currentNode.start + 1;
|
||||
return offsetRangeToSelection(document, finalOffset, finalOffset);
|
||||
}
|
||||
|
|
|
@ -4,8 +4,9 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { Node } from 'EmmetNode';
|
||||
import { getNode, parseDocument, validate } from './util';
|
||||
import { Node } from 'EmmetFlatNode';
|
||||
import { getFlatNode, offsetRangeToVsRange, validate } from './util';
|
||||
import { getRootNode } from './parseDocument';
|
||||
|
||||
export function mergeLines() {
|
||||
if (!validate(false) || !vscode.window.activeTextEditor) {
|
||||
|
@ -14,14 +15,14 @@ export function mergeLines() {
|
|||
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
|
||||
let rootNode = parseDocument(editor.document);
|
||||
const rootNode = getRootNode(editor.document, true);
|
||||
if (!rootNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
return editor.edit(editBuilder => {
|
||||
editor.selections.reverse().forEach(selection => {
|
||||
let textEdit = getRangesToReplace(editor.document, selection, rootNode!);
|
||||
const textEdit = getRangesToReplace(editor.document, selection, rootNode);
|
||||
if (textEdit) {
|
||||
editBuilder.replace(textEdit.range, textEdit.newText);
|
||||
}
|
||||
|
@ -30,25 +31,36 @@ export function mergeLines() {
|
|||
}
|
||||
|
||||
function getRangesToReplace(document: vscode.TextDocument, selection: vscode.Selection, rootNode: Node): vscode.TextEdit | undefined {
|
||||
let startNodeToUpdate: Node | null;
|
||||
let endNodeToUpdate: Node | null;
|
||||
let startNodeToUpdate: Node | undefined;
|
||||
let endNodeToUpdate: Node | undefined;
|
||||
|
||||
const selectionStart = document.offsetAt(selection.start);
|
||||
const selectionEnd = document.offsetAt(selection.end);
|
||||
if (selection.isEmpty) {
|
||||
startNodeToUpdate = endNodeToUpdate = getNode(rootNode, selection.start, true);
|
||||
startNodeToUpdate = endNodeToUpdate = getFlatNode(rootNode, selectionStart, true);
|
||||
} else {
|
||||
startNodeToUpdate = getNode(rootNode, selection.start, true);
|
||||
endNodeToUpdate = getNode(rootNode, selection.end, true);
|
||||
startNodeToUpdate = getFlatNode(rootNode, selectionStart, true);
|
||||
endNodeToUpdate = getFlatNode(rootNode, selectionEnd, true);
|
||||
}
|
||||
|
||||
if (!startNodeToUpdate || !endNodeToUpdate || startNodeToUpdate.start.line === endNodeToUpdate.end.line) {
|
||||
if (!startNodeToUpdate || !endNodeToUpdate) {
|
||||
return;
|
||||
}
|
||||
|
||||
let rangeToReplace = new vscode.Range(startNodeToUpdate.start, endNodeToUpdate.end);
|
||||
let textToReplaceWith = document.lineAt(startNodeToUpdate.start.line).text.substr(startNodeToUpdate.start.character);
|
||||
for (let i = startNodeToUpdate.start.line + 1; i <= endNodeToUpdate.end.line; i++) {
|
||||
const startPos = document.positionAt(startNodeToUpdate.start);
|
||||
const startLine = startPos.line;
|
||||
const startChar = startPos.character;
|
||||
const endPos = document.positionAt(endNodeToUpdate.end);
|
||||
const endLine = endPos.line;
|
||||
if (startLine === endLine) {
|
||||
return;
|
||||
}
|
||||
|
||||
const rangeToReplace = offsetRangeToVsRange(document, startNodeToUpdate.start, endNodeToUpdate.end);
|
||||
let textToReplaceWith = document.lineAt(startLine).text.substr(startChar);
|
||||
for (let i = startLine + 1; i <= endLine; i++) {
|
||||
textToReplaceWith += document.lineAt(i).text.trim();
|
||||
}
|
||||
|
||||
return new vscode.TextEdit(rangeToReplace, textToReplaceWith);
|
||||
}
|
||||
}
|
||||
|
|
46
extensions/emmet/src/parseDocument.ts
Normal file
46
extensions/emmet/src/parseDocument.ts
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { TextDocument } from 'vscode';
|
||||
import { Node as FlatNode } from 'EmmetFlatNode';
|
||||
import parse from '@emmetio/html-matcher';
|
||||
import parseStylesheet from '@emmetio/css-parser';
|
||||
import { isStyleSheet } from './util';
|
||||
|
||||
type Pair<K, V> = {
|
||||
key: K;
|
||||
value: V;
|
||||
};
|
||||
|
||||
// Map(filename, Pair(fileVersion, rootNodeOfParsedContent))
|
||||
const _parseCache = new Map<string, Pair<number, FlatNode> | undefined>();
|
||||
|
||||
export function getRootNode(document: TextDocument, useCache: boolean): FlatNode {
|
||||
const key = document.uri.toString();
|
||||
const result = _parseCache.get(key);
|
||||
const documentVersion = document.version;
|
||||
if (useCache && result) {
|
||||
if (documentVersion === result.key) {
|
||||
return result.value;
|
||||
}
|
||||
}
|
||||
|
||||
const parseContent = isStyleSheet(document.languageId) ? parseStylesheet : parse;
|
||||
const rootNode = parseContent(document.getText());
|
||||
if (useCache) {
|
||||
_parseCache.set(key, { key: documentVersion, value: rootNode });
|
||||
}
|
||||
return rootNode;
|
||||
}
|
||||
|
||||
export function addFileToParseCache(document: TextDocument) {
|
||||
const filename = document.uri.toString();
|
||||
_parseCache.set(filename, undefined);
|
||||
}
|
||||
|
||||
export function removeFileFromParseCache(document: TextDocument) {
|
||||
const filename = document.uri.toString();
|
||||
_parseCache.delete(filename);
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { HTMLDocument, TextDocument as LSTextDocument } from 'vscode-html-languageservice';
|
||||
import { getLanguageService } from './util';
|
||||
|
||||
type Pair<K, V> = {
|
||||
key: K;
|
||||
value: V;
|
||||
};
|
||||
|
||||
// Map(filename, Pair(fileVersion, parsedContent))
|
||||
const _parseCache = new Map<string, Pair<number, HTMLDocument> | undefined>();
|
||||
|
||||
export function parseMarkupDocument(document: LSTextDocument, useCache: boolean = true): HTMLDocument {
|
||||
const languageService = getLanguageService();
|
||||
const key = document.uri;
|
||||
const result = _parseCache.get(key);
|
||||
const documentVersion = document.version;
|
||||
if (useCache && result) {
|
||||
if (documentVersion === result.key) {
|
||||
return result.value;
|
||||
}
|
||||
}
|
||||
|
||||
const parsedDocument = languageService.parseHTMLDocument(document);
|
||||
if (useCache) {
|
||||
_parseCache.set(key, { key: documentVersion, value: parsedDocument });
|
||||
}
|
||||
return parsedDocument;
|
||||
}
|
||||
|
||||
export function addFileToMarkupParseCache(document: LSTextDocument) {
|
||||
const filename = document.uri;
|
||||
_parseCache.set(filename, undefined);
|
||||
}
|
||||
|
||||
export function removeFileFromMarkupParseCache(document: LSTextDocument) {
|
||||
const filename = document.uri;
|
||||
_parseCache.delete(filename);
|
||||
}
|
|
@ -3,20 +3,20 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Range, window, TextEditor } from 'vscode';
|
||||
import { getCssPropertyFromRule, getCssPropertyFromDocument } from './util';
|
||||
import { Property, Rule } from 'EmmetNode';
|
||||
import { window, TextEditor } from 'vscode';
|
||||
import { getCssPropertyFromRule, getCssPropertyFromDocument, offsetRangeToVsRange } from './util';
|
||||
import { Property, Rule } from 'EmmetFlatNode';
|
||||
|
||||
const vendorPrefixes = ['-webkit-', '-moz-', '-ms-', '-o-', ''];
|
||||
|
||||
export function reflectCssValue(): Thenable<boolean> | undefined {
|
||||
let editor = window.activeTextEditor;
|
||||
const editor = window.activeTextEditor;
|
||||
if (!editor) {
|
||||
window.showInformationMessage('No editor is active.');
|
||||
return;
|
||||
}
|
||||
|
||||
let node = getCssPropertyFromDocument(editor, editor.selection.active);
|
||||
const node = getCssPropertyFromDocument(editor, editor.selection.active);
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
@ -45,10 +45,11 @@ function updateCSSNode(editor: TextEditor, property: Property): Thenable<boolean
|
|||
if (prefix === currentPrefix) {
|
||||
return;
|
||||
}
|
||||
let vendorProperty = getCssPropertyFromRule(rule, prefix + propertyName);
|
||||
const vendorProperty = getCssPropertyFromRule(rule, prefix + propertyName);
|
||||
if (vendorProperty) {
|
||||
builder.replace(new Range(vendorProperty.valueToken.start, vendorProperty.valueToken.end), propertyValue);
|
||||
const rangeToReplace = offsetRangeToVsRange(editor.document, vendorProperty.valueToken.start, vendorProperty.valueToken.end);
|
||||
builder.replace(rangeToReplace, propertyValue);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,16 +4,24 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { validate, getHtmlNodeLS, toLSTextDocument, offsetRangeToVsRange } from './util';
|
||||
import { getRootNode } from './parseDocument';
|
||||
import { validate, getHtmlFlatNode, offsetRangeToVsRange } from './util';
|
||||
import { HtmlNode as HtmlFlatNode } from 'EmmetFlatNode';
|
||||
|
||||
export function removeTag() {
|
||||
if (!validate(false) || !vscode.window.activeTextEditor) {
|
||||
return;
|
||||
}
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
const document = editor.document;
|
||||
const rootNode = <HtmlFlatNode>getRootNode(document, true);
|
||||
if (!rootNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
let finalRangesToRemove = editor.selections.reverse()
|
||||
.reduce<vscode.Range[]>((prev, selection) =>
|
||||
prev.concat(getRangesToRemove(editor.document, selection)), []);
|
||||
prev.concat(getRangesToRemove(editor.document, rootNode, selection)), []);
|
||||
|
||||
return editor.edit(editBuilder => {
|
||||
finalRangesToRemove.forEach(range => {
|
||||
|
@ -27,26 +35,32 @@ export function removeTag() {
|
|||
* It finds the node to remove based on the selection's start position
|
||||
* and then removes that node, reindenting the content in between.
|
||||
*/
|
||||
function getRangesToRemove(document: vscode.TextDocument, selection: vscode.Selection): vscode.Range[] {
|
||||
const lsDocument = toLSTextDocument(document);
|
||||
const nodeToUpdate = getHtmlNodeLS(lsDocument, selection.start, true);
|
||||
function getRangesToRemove(document: vscode.TextDocument, rootNode: HtmlFlatNode, selection: vscode.Selection): vscode.Range[] {
|
||||
const offset = document.offsetAt(selection.start);
|
||||
const nodeToUpdate = getHtmlFlatNode(document.getText(), rootNode, offset, true);
|
||||
if (!nodeToUpdate) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const openTagRange = offsetRangeToVsRange(lsDocument, nodeToUpdate.start, nodeToUpdate.startTagEnd ?? nodeToUpdate.end);
|
||||
let openTagRange: vscode.Range | undefined;
|
||||
if (nodeToUpdate.open) {
|
||||
openTagRange = offsetRangeToVsRange(document, nodeToUpdate.open.start, nodeToUpdate.open.end);
|
||||
}
|
||||
let closeTagRange: vscode.Range | undefined;
|
||||
if (nodeToUpdate.endTagStart !== undefined) {
|
||||
closeTagRange = offsetRangeToVsRange(lsDocument, nodeToUpdate.endTagStart, nodeToUpdate.end);
|
||||
if (nodeToUpdate.close) {
|
||||
closeTagRange = offsetRangeToVsRange(document, nodeToUpdate.close.start, nodeToUpdate.close.end);
|
||||
}
|
||||
|
||||
let rangesToRemove = [openTagRange];
|
||||
if (closeTagRange) {
|
||||
const indentAmountToRemove = calculateIndentAmountToRemove(document, openTagRange, closeTagRange);
|
||||
for (let i = openTagRange.start.line + 1; i < closeTagRange.start.line; i++) {
|
||||
rangesToRemove.push(new vscode.Range(i, 0, i, indentAmountToRemove));
|
||||
let rangesToRemove = [];
|
||||
if (openTagRange) {
|
||||
rangesToRemove.push(openTagRange);
|
||||
if (closeTagRange) {
|
||||
const indentAmountToRemove = calculateIndentAmountToRemove(document, openTagRange, closeTagRange);
|
||||
for (let i = openTagRange.start.line + 1; i < closeTagRange.start.line; i++) {
|
||||
rangesToRemove.push(new vscode.Range(i, 0, i, indentAmountToRemove));
|
||||
}
|
||||
rangesToRemove.push(closeTagRange);
|
||||
}
|
||||
rangesToRemove.push(closeTagRange);
|
||||
}
|
||||
return rangesToRemove;
|
||||
}
|
||||
|
|
|
@ -4,17 +4,19 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { validate, parseDocument, isStyleSheet } from './util';
|
||||
import { validate, isStyleSheet } from './util';
|
||||
import { nextItemHTML, prevItemHTML } from './selectItemHTML';
|
||||
import { nextItemStylesheet, prevItemStylesheet } from './selectItemStylesheet';
|
||||
import { HtmlNode, CssNode } from 'EmmetNode';
|
||||
import { HtmlNode, CssNode } from 'EmmetFlatNode';
|
||||
import { getRootNode } from './parseDocument';
|
||||
|
||||
export function fetchSelectItem(direction: string): void {
|
||||
if (!validate() || !vscode.window.activeTextEditor) {
|
||||
return;
|
||||
}
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
let rootNode = parseDocument(editor.document);
|
||||
const document = editor.document;
|
||||
const rootNode = getRootNode(document, true);
|
||||
if (!rootNode) {
|
||||
return;
|
||||
}
|
||||
|
@ -26,12 +28,16 @@ export function fetchSelectItem(direction: string): void {
|
|||
|
||||
let updatedSelection;
|
||||
if (isStyleSheet(editor.document.languageId)) {
|
||||
updatedSelection = direction === 'next' ? nextItemStylesheet(selectionStart, selectionEnd, <CssNode>rootNode!) : prevItemStylesheet(selectionStart, selectionEnd, <CssNode>rootNode!);
|
||||
updatedSelection = direction === 'next' ?
|
||||
nextItemStylesheet(document, selectionStart, selectionEnd, <CssNode>rootNode) :
|
||||
prevItemStylesheet(document, selectionStart, selectionEnd, <CssNode>rootNode);
|
||||
} else {
|
||||
updatedSelection = direction === 'next' ? nextItemHTML(selectionStart, selectionEnd, editor, <HtmlNode>rootNode!) : prevItemHTML(selectionStart, selectionEnd, editor, <HtmlNode>rootNode!);
|
||||
updatedSelection = direction === 'next' ?
|
||||
nextItemHTML(document, selectionStart, selectionEnd, <HtmlNode>rootNode) :
|
||||
prevItemHTML(document, selectionStart, selectionEnd, <HtmlNode>rootNode);
|
||||
}
|
||||
newSelections.push(updatedSelection ? updatedSelection : selection);
|
||||
});
|
||||
editor.selections = newSelections;
|
||||
editor.revealRange(editor.selections[editor.selections.length - 1]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,11 +4,12 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { getDeepestNode, findNextWord, findPrevWord, getHtmlNode, isNumber } from './util';
|
||||
import { HtmlNode } from 'EmmetNode';
|
||||
import { getDeepestFlatNode, findNextWord, findPrevWord, getHtmlFlatNode, offsetRangeToSelection } from './util';
|
||||
import { HtmlNode } from 'EmmetFlatNode';
|
||||
|
||||
export function nextItemHTML(selectionStart: vscode.Position, selectionEnd: vscode.Position, editor: vscode.TextEditor, rootNode: HtmlNode): vscode.Selection | undefined {
|
||||
let currentNode = getHtmlNode(editor.document, rootNode, selectionEnd, false);
|
||||
export function nextItemHTML(document: vscode.TextDocument, selectionStart: vscode.Position, selectionEnd: vscode.Position, rootNode: HtmlNode): vscode.Selection | undefined {
|
||||
const selectionEndOffset = document.offsetAt(selectionEnd);
|
||||
let currentNode = getHtmlFlatNode(document.getText(), rootNode, selectionEndOffset, false);
|
||||
let nextNode: HtmlNode | undefined = undefined;
|
||||
|
||||
if (!currentNode) {
|
||||
|
@ -17,13 +18,16 @@ export function nextItemHTML(selectionStart: vscode.Position, selectionEnd: vsco
|
|||
|
||||
if (currentNode.type !== 'comment') {
|
||||
// If cursor is in the tag name, select tag
|
||||
if (selectionEnd.isBefore(currentNode.open.start.translate(0, currentNode.name.length))) {
|
||||
return getSelectionFromNode(currentNode);
|
||||
if (currentNode.open &&
|
||||
selectionEndOffset < currentNode.open.start + currentNode.name.length) {
|
||||
return getSelectionFromNode(document, currentNode);
|
||||
}
|
||||
|
||||
// If cursor is in the open tag, look for attributes
|
||||
if (selectionEnd.isBefore(currentNode.open.end)) {
|
||||
let attrSelection = getNextAttribute(selectionStart, selectionEnd, currentNode);
|
||||
if (currentNode.open &&
|
||||
selectionEndOffset < currentNode.open.end) {
|
||||
const selectionStartOffset = document.offsetAt(selectionStart);
|
||||
const attrSelection = getNextAttribute(document, selectionStartOffset, selectionEndOffset, currentNode);
|
||||
if (attrSelection) {
|
||||
return attrSelection;
|
||||
}
|
||||
|
@ -31,12 +35,11 @@ export function nextItemHTML(selectionStart: vscode.Position, selectionEnd: vsco
|
|||
|
||||
// Get the first child of current node which is right after the cursor and is not a comment
|
||||
nextNode = currentNode.firstChild;
|
||||
while (nextNode && (selectionEnd.isAfterOrEqual(nextNode.end) || nextNode.type === 'comment')) {
|
||||
while (nextNode && (selectionEndOffset >= nextNode.end || nextNode.type === 'comment')) {
|
||||
nextNode = nextNode.nextSibling;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Get next sibling of current node which is not a comment. If none is found try the same on the parent
|
||||
while (!nextNode && currentNode) {
|
||||
if (currentNode.nextSibling) {
|
||||
|
@ -50,33 +53,36 @@ export function nextItemHTML(selectionStart: vscode.Position, selectionEnd: vsco
|
|||
}
|
||||
}
|
||||
|
||||
return nextNode && getSelectionFromNode(nextNode);
|
||||
return nextNode && getSelectionFromNode(document, nextNode);
|
||||
}
|
||||
|
||||
export function prevItemHTML(selectionStart: vscode.Position, selectionEnd: vscode.Position, editor: vscode.TextEditor, rootNode: HtmlNode): vscode.Selection | undefined {
|
||||
let currentNode = getHtmlNode(editor.document, rootNode, selectionStart, false);
|
||||
export function prevItemHTML(document: vscode.TextDocument, selectionStart: vscode.Position, selectionEnd: vscode.Position, rootNode: HtmlNode): vscode.Selection | undefined {
|
||||
const selectionStartOffset = document.offsetAt(selectionStart);
|
||||
let currentNode = getHtmlFlatNode(document.getText(), rootNode, selectionStartOffset, false);
|
||||
let prevNode: HtmlNode | undefined = undefined;
|
||||
|
||||
if (!currentNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentNode.type !== 'comment' && selectionStart.translate(0, -1).isAfter(currentNode.open.start)) {
|
||||
|
||||
if (selectionStart.isBefore(currentNode.open.end) || !currentNode.firstChild || selectionEnd.isBeforeOrEqual(currentNode.firstChild.start)) {
|
||||
const selectionEndOffset = document.offsetAt(selectionEnd);
|
||||
if (currentNode.open &&
|
||||
currentNode.type !== 'comment' &&
|
||||
selectionStartOffset - 1 > currentNode.open.start) {
|
||||
if (selectionStartOffset < currentNode.open.end || !currentNode.firstChild || selectionEndOffset <= currentNode.firstChild.start) {
|
||||
prevNode = currentNode;
|
||||
} else {
|
||||
// Select the child that appears just before the cursor and is not a comment
|
||||
prevNode = currentNode.firstChild;
|
||||
let oldOption: HtmlNode | undefined = undefined;
|
||||
while (prevNode.nextSibling && selectionStart.isAfterOrEqual(prevNode.nextSibling.end)) {
|
||||
while (prevNode.nextSibling && selectionStartOffset >= prevNode.nextSibling.end) {
|
||||
if (prevNode && prevNode.type !== 'comment') {
|
||||
oldOption = prevNode;
|
||||
}
|
||||
prevNode = prevNode.nextSibling;
|
||||
}
|
||||
|
||||
prevNode = <HtmlNode>getDeepestNode((prevNode && prevNode.type !== 'comment') ? prevNode : oldOption);
|
||||
prevNode = <HtmlNode>getDeepestFlatNode((prevNode && prevNode.type !== 'comment') ? prevNode : oldOption);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -84,7 +90,7 @@ export function prevItemHTML(selectionStart: vscode.Position, selectionEnd: vsco
|
|||
while (!prevNode && currentNode) {
|
||||
if (currentNode.previousSibling) {
|
||||
if (currentNode.previousSibling.type !== 'comment') {
|
||||
prevNode = <HtmlNode>getDeepestNode(currentNode.previousSibling);
|
||||
prevNode = <HtmlNode>getDeepestFlatNode(currentNode.previousSibling);
|
||||
} else {
|
||||
currentNode = currentNode.previousSibling;
|
||||
}
|
||||
|
@ -98,66 +104,66 @@ export function prevItemHTML(selectionStart: vscode.Position, selectionEnd: vsco
|
|||
return undefined;
|
||||
}
|
||||
|
||||
let attrSelection = getPrevAttribute(selectionStart, selectionEnd, prevNode);
|
||||
return attrSelection ? attrSelection : getSelectionFromNode(prevNode);
|
||||
const attrSelection = getPrevAttribute(document, selectionStartOffset, selectionEndOffset, prevNode);
|
||||
return attrSelection ? attrSelection : getSelectionFromNode(document, prevNode);
|
||||
}
|
||||
|
||||
function getSelectionFromNode(node: HtmlNode): vscode.Selection | undefined {
|
||||
function getSelectionFromNode(document: vscode.TextDocument, node: HtmlNode): vscode.Selection | undefined {
|
||||
if (node && node.open) {
|
||||
let selectionStart = (<vscode.Position>node.open.start).translate(0, 1);
|
||||
let selectionEnd = selectionStart.translate(0, node.name.length);
|
||||
|
||||
return new vscode.Selection(selectionStart, selectionEnd);
|
||||
const selectionStart = node.open.start + 1;
|
||||
const selectionEnd = selectionStart + node.name.length;
|
||||
return offsetRangeToSelection(document, selectionStart, selectionEnd);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getNextAttribute(selectionStart: vscode.Position, selectionEnd: vscode.Position, node: HtmlNode): vscode.Selection | undefined {
|
||||
|
||||
function getNextAttribute(document: vscode.TextDocument, selectionStart: number, selectionEnd: number, node: HtmlNode): vscode.Selection | undefined {
|
||||
if (!node.attributes || node.attributes.length === 0 || node.type === 'comment') {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const attr of node.attributes) {
|
||||
if (selectionEnd.isBefore(attr.start)) {
|
||||
if (selectionEnd < attr.start) {
|
||||
// select full attr
|
||||
return new vscode.Selection(attr.start, attr.end);
|
||||
return offsetRangeToSelection(document, attr.start, attr.end);
|
||||
}
|
||||
|
||||
if (!attr.value || (<vscode.Position>attr.value.start).isEqual(attr.value.end)) {
|
||||
if (!attr.value || attr.value.start === attr.value.end) {
|
||||
// No attr value to select
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((selectionStart.isEqual(attr.start) && selectionEnd.isEqual(attr.end)) || selectionEnd.isBefore(attr.value.start)) {
|
||||
if ((selectionStart === attr.start && selectionEnd === attr.end) ||
|
||||
selectionEnd < attr.value.start) {
|
||||
// cursor is in attr name, so select full attr value
|
||||
return new vscode.Selection(attr.value.start, attr.value.end);
|
||||
return offsetRangeToSelection(document, attr.value.start, attr.value.end);
|
||||
}
|
||||
|
||||
// Fetch the next word in the attr value
|
||||
|
||||
if (attr.value.toString().indexOf(' ') === -1) {
|
||||
// attr value does not have space, so no next word to find
|
||||
continue;
|
||||
}
|
||||
|
||||
let pos: number | undefined = undefined;
|
||||
if (selectionStart.isEqual(attr.value.start) && selectionEnd.isEqual(attr.value.end)) {
|
||||
if (selectionStart === attr.value.start && selectionEnd === attr.value.end) {
|
||||
pos = -1;
|
||||
}
|
||||
if (pos === undefined && selectionEnd.isBefore(attr.end)) {
|
||||
pos = selectionEnd.character - attr.value.start.character - 1;
|
||||
if (pos === undefined && selectionEnd < attr.end) {
|
||||
const selectionEndCharacter = document.positionAt(selectionEnd).character;
|
||||
const attrValueStartCharacter = document.positionAt(attr.value.start).character;
|
||||
pos = selectionEndCharacter - attrValueStartCharacter - 1;
|
||||
}
|
||||
|
||||
if (pos !== undefined) {
|
||||
let [newSelectionStartOffset, newSelectionEndOffset] = findNextWord(attr.value.toString(), pos);
|
||||
if (!isNumber(newSelectionStartOffset) || !isNumber(newSelectionEndOffset)) {
|
||||
const [newSelectionStartOffset, newSelectionEndOffset] = findNextWord(attr.value.toString(), pos);
|
||||
if (newSelectionStartOffset === undefined || newSelectionEndOffset === undefined) {
|
||||
return;
|
||||
}
|
||||
if (newSelectionStartOffset >= 0 && newSelectionEndOffset >= 0) {
|
||||
const newSelectionStart = (<vscode.Position>attr.value.start).translate(0, newSelectionStartOffset);
|
||||
const newSelectionEnd = (<vscode.Position>attr.value.start).translate(0, newSelectionEndOffset);
|
||||
return new vscode.Selection(newSelectionStart, newSelectionEnd);
|
||||
const newSelectionStart = attr.value.start + newSelectionStartOffset;
|
||||
const newSelectionEnd = attr.value.start + newSelectionEndOffset;
|
||||
return offsetRangeToSelection(document, newSelectionStart, newSelectionEnd);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -166,44 +172,44 @@ function getNextAttribute(selectionStart: vscode.Position, selectionEnd: vscode.
|
|||
return;
|
||||
}
|
||||
|
||||
function getPrevAttribute(selectionStart: vscode.Position, selectionEnd: vscode.Position, node: HtmlNode): vscode.Selection | undefined {
|
||||
|
||||
function getPrevAttribute(document: vscode.TextDocument, selectionStart: number, selectionEnd: number, node: HtmlNode): vscode.Selection | undefined {
|
||||
if (!node.attributes || node.attributes.length === 0 || node.type === 'comment') {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = node.attributes.length - 1; i >= 0; i--) {
|
||||
let attr = node.attributes[i];
|
||||
|
||||
if (selectionStart.isBeforeOrEqual(attr.start)) {
|
||||
const attr = node.attributes[i];
|
||||
if (selectionStart <= attr.start) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!attr.value || (<vscode.Position>attr.value.start).isEqual(attr.value.end) || selectionStart.isBefore(attr.value.start)) {
|
||||
if (!attr.value || attr.value.start === attr.value.end || selectionStart < attr.value.start) {
|
||||
// select full attr
|
||||
return new vscode.Selection(attr.start, attr.end);
|
||||
return offsetRangeToSelection(document, attr.start, attr.end);
|
||||
}
|
||||
|
||||
if (selectionStart.isEqual(attr.value.start)) {
|
||||
if (selectionEnd.isAfterOrEqual(attr.value.end)) {
|
||||
if (selectionStart === attr.value.start) {
|
||||
if (selectionEnd >= attr.value.end) {
|
||||
// select full attr
|
||||
return new vscode.Selection(attr.start, attr.end);
|
||||
return offsetRangeToSelection(document, attr.start, attr.end);
|
||||
}
|
||||
// select attr value
|
||||
return new vscode.Selection(attr.value.start, attr.value.end);
|
||||
return offsetRangeToSelection(document, attr.value.start, attr.value.end);
|
||||
}
|
||||
|
||||
// Fetch the prev word in the attr value
|
||||
|
||||
let pos = selectionStart.isAfter(attr.value.end) ? attr.value.toString().length : selectionStart.character - attr.value.start.character;
|
||||
let [newSelectionStartOffset, newSelectionEndOffset] = findPrevWord(attr.value.toString(), pos);
|
||||
if (!isNumber(newSelectionStartOffset) || !isNumber(newSelectionEndOffset)) {
|
||||
const selectionStartCharacter = document.positionAt(selectionStart).character;
|
||||
const attrValueStartCharacter = document.positionAt(attr.value.start).character;
|
||||
const pos = selectionStart > attr.value.end ? attr.value.toString().length :
|
||||
selectionStartCharacter - attrValueStartCharacter;
|
||||
const [newSelectionStartOffset, newSelectionEndOffset] = findPrevWord(attr.value.toString(), pos);
|
||||
if (newSelectionStartOffset === undefined || newSelectionEndOffset === undefined) {
|
||||
return;
|
||||
}
|
||||
if (newSelectionStartOffset >= 0 && newSelectionEndOffset >= 0) {
|
||||
const newSelectionStart = (<vscode.Position>attr.value.start).translate(0, newSelectionStartOffset);
|
||||
const newSelectionEnd = (<vscode.Position>attr.value.start).translate(0, newSelectionEndOffset);
|
||||
return new vscode.Selection(newSelectionStart, newSelectionEnd);
|
||||
const newSelectionStart = attr.value.start + newSelectionStartOffset;
|
||||
const newSelectionEnd = attr.value.start + newSelectionEndOffset;
|
||||
return offsetRangeToSelection(document, newSelectionStart, newSelectionEnd);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,11 +4,13 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { getDeepestNode, findNextWord, findPrevWord, getNode } from './util';
|
||||
import { Node, CssNode, Rule, Property } from 'EmmetNode';
|
||||
import { getDeepestFlatNode, findNextWord, findPrevWord, getFlatNode, offsetRangeToSelection } from './util';
|
||||
import { Node, CssNode, Rule, Property } from 'EmmetFlatNode';
|
||||
|
||||
export function nextItemStylesheet(startOffset: vscode.Position, endOffset: vscode.Position, rootNode: Node): vscode.Selection | undefined {
|
||||
let currentNode = <CssNode>getNode(rootNode, endOffset, true);
|
||||
export function nextItemStylesheet(document: vscode.TextDocument, startPosition: vscode.Position, endPosition: vscode.Position, rootNode: Node): vscode.Selection | undefined {
|
||||
const startOffset = document.offsetAt(startPosition);
|
||||
const endOffset = document.offsetAt(endPosition);
|
||||
let currentNode: CssNode | undefined = <CssNode>getFlatNode(rootNode, endOffset, true);
|
||||
if (!currentNode) {
|
||||
currentNode = <CssNode>rootNode;
|
||||
}
|
||||
|
@ -16,27 +18,31 @@ export function nextItemStylesheet(startOffset: vscode.Position, endOffset: vsco
|
|||
return;
|
||||
}
|
||||
// Full property is selected, so select full property value next
|
||||
if (currentNode.type === 'property' && startOffset.isEqual(currentNode.start) && endOffset.isEqual(currentNode.end)) {
|
||||
return getSelectionFromProperty(currentNode, startOffset, endOffset, true, 'next');
|
||||
if (currentNode.type === 'property' &&
|
||||
startOffset === currentNode.start &&
|
||||
endOffset === currentNode.end) {
|
||||
return getSelectionFromProperty(document, currentNode, startOffset, endOffset, true, 'next');
|
||||
}
|
||||
|
||||
// Part or whole of propertyValue is selected, so select the next word in the propertyValue
|
||||
if (currentNode.type === 'property' && startOffset.isAfterOrEqual((<Property>currentNode).valueToken.start) && endOffset.isBeforeOrEqual((<Property>currentNode).valueToken.end)) {
|
||||
let singlePropertyValue = getSelectionFromProperty(currentNode, startOffset, endOffset, false, 'next');
|
||||
if (currentNode.type === 'property' &&
|
||||
startOffset >= (<Property>currentNode).valueToken.start &&
|
||||
endOffset <= (<Property>currentNode).valueToken.end) {
|
||||
let singlePropertyValue = getSelectionFromProperty(document, currentNode, startOffset, endOffset, false, 'next');
|
||||
if (singlePropertyValue) {
|
||||
return singlePropertyValue;
|
||||
}
|
||||
}
|
||||
|
||||
// Cursor is in the selector or in a property
|
||||
if ((currentNode.type === 'rule' && endOffset.isBefore((<Rule>currentNode).selectorToken.end))
|
||||
|| (currentNode.type === 'property' && endOffset.isBefore((<Property>currentNode).valueToken.end))) {
|
||||
return getSelectionFromNode(currentNode);
|
||||
if ((currentNode.type === 'rule' && endOffset < (<Rule>currentNode).selectorToken.end)
|
||||
|| (currentNode.type === 'property' && endOffset < (<Property>currentNode).valueToken.end)) {
|
||||
return getSelectionFromNode(document, currentNode);
|
||||
}
|
||||
|
||||
// Get the first child of current node which is right after the cursor
|
||||
let nextNode = currentNode.firstChild;
|
||||
while (nextNode && endOffset.isAfterOrEqual(nextNode.end)) {
|
||||
while (nextNode && endOffset >= nextNode.end) {
|
||||
nextNode = nextNode.nextSibling;
|
||||
}
|
||||
|
||||
|
@ -46,12 +52,13 @@ export function nextItemStylesheet(startOffset: vscode.Position, endOffset: vsco
|
|||
currentNode = currentNode.parent;
|
||||
}
|
||||
|
||||
return getSelectionFromNode(nextNode);
|
||||
|
||||
return nextNode ? getSelectionFromNode(document, nextNode) : undefined;
|
||||
}
|
||||
|
||||
export function prevItemStylesheet(startOffset: vscode.Position, endOffset: vscode.Position, rootNode: CssNode): vscode.Selection | undefined {
|
||||
let currentNode = <CssNode>getNode(rootNode, startOffset, false);
|
||||
export function prevItemStylesheet(document: vscode.TextDocument, startPosition: vscode.Position, endPosition: vscode.Position, rootNode: CssNode): vscode.Selection | undefined {
|
||||
const startOffset = document.offsetAt(startPosition);
|
||||
const endOffset = document.offsetAt(endPosition);
|
||||
let currentNode = <CssNode>getFlatNode(rootNode, startOffset, false);
|
||||
if (!currentNode) {
|
||||
currentNode = rootNode;
|
||||
}
|
||||
|
@ -60,70 +67,80 @@ export function prevItemStylesheet(startOffset: vscode.Position, endOffset: vsco
|
|||
}
|
||||
|
||||
// Full property value is selected, so select the whole property next
|
||||
if (currentNode.type === 'property' && startOffset.isEqual((<Property>currentNode).valueToken.start) && endOffset.isEqual((<Property>currentNode).valueToken.end)) {
|
||||
return getSelectionFromNode(currentNode);
|
||||
if (currentNode.type === 'property' &&
|
||||
startOffset === (<Property>currentNode).valueToken.start &&
|
||||
endOffset === (<Property>currentNode).valueToken.end) {
|
||||
return getSelectionFromNode(document, currentNode);
|
||||
}
|
||||
|
||||
// Part of propertyValue is selected, so select the prev word in the propertyValue
|
||||
if (currentNode.type === 'property' && startOffset.isAfterOrEqual((<Property>currentNode).valueToken.start) && endOffset.isBeforeOrEqual((<Property>currentNode).valueToken.end)) {
|
||||
let singlePropertyValue = getSelectionFromProperty(currentNode, startOffset, endOffset, false, 'prev');
|
||||
if (currentNode.type === 'property' &&
|
||||
startOffset >= (<Property>currentNode).valueToken.start &&
|
||||
endOffset <= (<Property>currentNode).valueToken.end) {
|
||||
let singlePropertyValue = getSelectionFromProperty(document, currentNode, startOffset, endOffset, false, 'prev');
|
||||
if (singlePropertyValue) {
|
||||
return singlePropertyValue;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentNode.type === 'property' || !currentNode.firstChild || (currentNode.type === 'rule' && startOffset.isBeforeOrEqual(currentNode.firstChild.start))) {
|
||||
return getSelectionFromNode(currentNode);
|
||||
if (currentNode.type === 'property' || !currentNode.firstChild ||
|
||||
(currentNode.type === 'rule' && startOffset <= currentNode.firstChild.start)) {
|
||||
return getSelectionFromNode(document, currentNode);
|
||||
}
|
||||
|
||||
// Select the child that appears just before the cursor
|
||||
let prevNode = currentNode.firstChild;
|
||||
while (prevNode.nextSibling && startOffset.isAfterOrEqual(prevNode.nextSibling.end)) {
|
||||
let prevNode: CssNode | undefined = currentNode.firstChild;
|
||||
while (prevNode.nextSibling && startOffset >= prevNode.nextSibling.end) {
|
||||
prevNode = prevNode.nextSibling;
|
||||
}
|
||||
prevNode = <CssNode>getDeepestNode(prevNode);
|
||||
|
||||
return getSelectionFromProperty(prevNode, startOffset, endOffset, false, 'prev');
|
||||
prevNode = <CssNode | undefined>getDeepestFlatNode(prevNode);
|
||||
|
||||
return getSelectionFromProperty(document, prevNode, startOffset, endOffset, false, 'prev');
|
||||
}
|
||||
|
||||
|
||||
function getSelectionFromNode(node: Node): vscode.Selection | undefined {
|
||||
function getSelectionFromNode(document: vscode.TextDocument, node: Node | undefined): vscode.Selection | undefined {
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
let nodeToSelect = node.type === 'rule' ? (<Rule>node).selectorToken : node;
|
||||
return new vscode.Selection(nodeToSelect.start, nodeToSelect.end);
|
||||
const nodeToSelect = node.type === 'rule' ? (<Rule>node).selectorToken : node;
|
||||
return offsetRangeToSelection(document, nodeToSelect.start, nodeToSelect.end);
|
||||
}
|
||||
|
||||
|
||||
function getSelectionFromProperty(node: Node, selectionStart: vscode.Position, selectionEnd: vscode.Position, selectFullValue: boolean, direction: string): vscode.Selection | undefined {
|
||||
function getSelectionFromProperty(document: vscode.TextDocument, node: Node | undefined, selectionStart: number, selectionEnd: number, selectFullValue: boolean, direction: string): vscode.Selection | undefined {
|
||||
if (!node || node.type !== 'property') {
|
||||
return;
|
||||
}
|
||||
const propertyNode = <Property>node;
|
||||
|
||||
let propertyValue = propertyNode.valueToken.stream.substring(propertyNode.valueToken.start, propertyNode.valueToken.end);
|
||||
selectFullValue = selectFullValue || (direction === 'prev' && selectionStart.isEqual(propertyNode.valueToken.start) && selectionEnd.isBefore(propertyNode.valueToken.end));
|
||||
selectFullValue = selectFullValue ||
|
||||
(direction === 'prev' && selectionStart === propertyNode.valueToken.start && selectionEnd < propertyNode.valueToken.end);
|
||||
|
||||
if (selectFullValue) {
|
||||
return new vscode.Selection(propertyNode.valueToken.start, propertyNode.valueToken.end);
|
||||
return offsetRangeToSelection(document, propertyNode.valueToken.start, propertyNode.valueToken.end);
|
||||
}
|
||||
|
||||
let pos: number = -1;
|
||||
if (direction === 'prev') {
|
||||
if (selectionStart.isEqual(propertyNode.valueToken.start)) {
|
||||
if (selectionStart === propertyNode.valueToken.start) {
|
||||
return;
|
||||
}
|
||||
pos = selectionStart.isAfter(propertyNode.valueToken.end) ? propertyValue.length : selectionStart.character - propertyNode.valueToken.start.character;
|
||||
}
|
||||
|
||||
if (direction === 'next') {
|
||||
if (selectionEnd.isEqual(propertyNode.valueToken.end) && (selectionStart.isAfter(propertyNode.valueToken.start) || propertyValue.indexOf(' ') === -1)) {
|
||||
const selectionStartChar = document.positionAt(selectionStart).character;
|
||||
const tokenStartChar = document.positionAt(propertyNode.valueToken.start).character;
|
||||
pos = selectionStart > propertyNode.valueToken.end ? propertyValue.length :
|
||||
selectionStartChar - tokenStartChar;
|
||||
} else if (direction === 'next') {
|
||||
if (selectionEnd === propertyNode.valueToken.end &&
|
||||
(selectionStart > propertyNode.valueToken.start || !propertyValue.includes(' '))) {
|
||||
return;
|
||||
}
|
||||
pos = selectionEnd.isEqual(propertyNode.valueToken.end) ? -1 : selectionEnd.character - propertyNode.valueToken.start.character - 1;
|
||||
const selectionEndChar = document.positionAt(selectionEnd).character;
|
||||
const tokenStartChar = document.positionAt(propertyNode.valueToken.start).character;
|
||||
pos = selectionEnd === propertyNode.valueToken.end ? -1 :
|
||||
selectionEndChar - tokenStartChar - 1;
|
||||
}
|
||||
|
||||
|
||||
|
@ -132,8 +149,9 @@ function getSelectionFromProperty(node: Node, selectionStart: vscode.Position, s
|
|||
return;
|
||||
}
|
||||
|
||||
const newSelectionStart = (<vscode.Position>propertyNode.valueToken.start).translate(0, newSelectionStartOffset);
|
||||
const newSelectionEnd = (<vscode.Position>propertyNode.valueToken.start).translate(0, newSelectionEndOffset);
|
||||
const tokenStart = document.positionAt(propertyNode.valueToken.start);
|
||||
const newSelectionStart = tokenStart.translate(0, newSelectionStartOffset);
|
||||
const newSelectionEnd = tokenStart.translate(0, newSelectionEndOffset);
|
||||
|
||||
return new vscode.Selection(newSelectionStart, newSelectionEnd);
|
||||
}
|
||||
|
|
|
@ -4,8 +4,9 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { validate, getEmmetMode, getEmmetConfiguration, toLSTextDocument, getHtmlNodeLS, offsetRangeToVsRange } from './util';
|
||||
import { Node as LSNode, TextDocument as LSTextDocument } from 'vscode-html-languageservice';
|
||||
import { validate, getEmmetMode, getEmmetConfiguration, getHtmlFlatNode, offsetRangeToVsRange } from './util';
|
||||
import { HtmlNode as HtmlFlatNode } from 'EmmetFlatNode';
|
||||
import { getRootNode } from './parseDocument';
|
||||
|
||||
export function splitJoinTag() {
|
||||
if (!validate(false) || !vscode.window.activeTextEditor) {
|
||||
|
@ -13,29 +14,30 @@ export function splitJoinTag() {
|
|||
}
|
||||
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
const document = toLSTextDocument(editor.document);
|
||||
const document = editor.document;
|
||||
const rootNode = <HtmlFlatNode>getRootNode(editor.document, true);
|
||||
if (!rootNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
return editor.edit(editBuilder => {
|
||||
editor.selections.reverse().forEach(selection => {
|
||||
const nodeToUpdate = getHtmlNodeLS(document, selection.start, true);
|
||||
const documentText = document.getText();
|
||||
const offset = document.offsetAt(selection.start);
|
||||
const nodeToUpdate = getHtmlFlatNode(documentText, rootNode, offset, true);
|
||||
if (nodeToUpdate) {
|
||||
const textEdit = getRangesToReplace(document, nodeToUpdate);
|
||||
if (textEdit) {
|
||||
editBuilder.replace(textEdit.range, textEdit.newText);
|
||||
}
|
||||
editBuilder.replace(textEdit.range, textEdit.newText);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getRangesToReplace(document: LSTextDocument, nodeToUpdate: LSNode): vscode.TextEdit | undefined {
|
||||
function getRangesToReplace(document: vscode.TextDocument, nodeToUpdate: HtmlFlatNode): vscode.TextEdit {
|
||||
let rangeToReplace: vscode.Range;
|
||||
let textToReplaceWith: string;
|
||||
|
||||
if (!nodeToUpdate?.tag) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (nodeToUpdate.endTagStart === undefined || nodeToUpdate.startTagEnd === undefined) {
|
||||
if (!nodeToUpdate.open || !nodeToUpdate.close) {
|
||||
// Split Tag
|
||||
const nodeText = document.getText().substring(nodeToUpdate.start, nodeToUpdate.end);
|
||||
const m = nodeText.match(/(\s*\/)?>$/);
|
||||
|
@ -43,10 +45,10 @@ function getRangesToReplace(document: LSTextDocument, nodeToUpdate: LSNode): vsc
|
|||
const start = m ? end - m[0].length : end;
|
||||
|
||||
rangeToReplace = offsetRangeToVsRange(document, start, end);
|
||||
textToReplaceWith = `></${nodeToUpdate.tag}>`;
|
||||
textToReplaceWith = `></${nodeToUpdate.name}>`;
|
||||
} else {
|
||||
// Join Tag
|
||||
const start = nodeToUpdate.startTagEnd - 1;
|
||||
const start = nodeToUpdate.open.end - 1;
|
||||
const end = nodeToUpdate.end;
|
||||
rangeToReplace = offsetRangeToVsRange(document, start, end);
|
||||
textToReplaceWith = '/>';
|
||||
|
|
|
@ -7,15 +7,16 @@ import 'mocha';
|
|||
import * as assert from 'assert';
|
||||
import { withRandomFileEditor } from './testUtils';
|
||||
import * as vscode from 'vscode';
|
||||
import { parsePartialStylesheet, getNode } from '../util';
|
||||
import { parsePartialStylesheet, getFlatNode } from '../util';
|
||||
import { isValidLocationForEmmetAbbreviation } from '../abbreviationActions';
|
||||
|
||||
suite('Tests for partial parse of Stylesheets', () => {
|
||||
|
||||
function isValid(doc: vscode.TextDocument, range: vscode.Range, syntax: string): boolean {
|
||||
const rootNode = parsePartialStylesheet(doc, range.end);
|
||||
const currentNode = getNode(rootNode, range.end, true);
|
||||
return isValidLocationForEmmetAbbreviation(doc, rootNode, currentNode, syntax, range.end, range);
|
||||
const endOffset = doc.offsetAt(range.end);
|
||||
const currentNode = getFlatNode(rootNode, endOffset, true);
|
||||
return isValidLocationForEmmetAbbreviation(doc, rootNode, currentNode, syntax, endOffset, range);
|
||||
}
|
||||
|
||||
test('Ignore block comment inside rule', function (): any {
|
||||
|
@ -257,4 +258,4 @@ ment */{
|
|||
});
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
|
|
|
@ -26,7 +26,7 @@ suite('Tests for Toggle Comment action from Emmet (HTML)', () => {
|
|||
<div><li><span>Bye</span></li></div>
|
||||
</ul>
|
||||
<ul>
|
||||
<!--<li>Previously Commented Node</li>-->
|
||||
<!-- <li>Previously Commented Node</li> -->
|
||||
<li>Another Node</li>
|
||||
</ul>
|
||||
<span/>
|
||||
|
@ -47,24 +47,24 @@ suite('Tests for Toggle Comment action from Emmet (HTML)', () => {
|
|||
const expectedContents = `
|
||||
<div class="hello">
|
||||
<ul>
|
||||
<li><!--<span>Hello</span>--></li>
|
||||
<!--<li><span>There</span></li>-->
|
||||
<!--<div><li><span>Bye</span></li></div>-->
|
||||
<li><!-- <span>Hello</span> --></li>
|
||||
<!-- <li><span>There</span></li> -->
|
||||
<!-- <div><li><span>Bye</span></li></div> -->
|
||||
</ul>
|
||||
<!--<ul>
|
||||
<!-- <ul>
|
||||
<li>Previously Commented Node</li>
|
||||
<li>Another Node</li>
|
||||
</ul>-->
|
||||
</ul> -->
|
||||
<span/>
|
||||
<style>
|
||||
.boo {
|
||||
/*margin: 10px;*/
|
||||
/* margin: 10px; */
|
||||
padding: 20px;
|
||||
}
|
||||
/*.hoo {
|
||||
/* .hoo {
|
||||
margin: 10px;
|
||||
padding: 20px;
|
||||
}*/
|
||||
} */
|
||||
</style>
|
||||
</div>
|
||||
`;
|
||||
|
@ -89,24 +89,24 @@ suite('Tests for Toggle Comment action from Emmet (HTML)', () => {
|
|||
const expectedContents = `
|
||||
<div class="hello">
|
||||
<ul>
|
||||
<li><!--<span>Hello</span>--></li>
|
||||
<!--<li><span>There</span></li>-->
|
||||
<li><!-- <span>Hello</span> --></li>
|
||||
<!-- <li><span>There</span></li> -->
|
||||
<div><li><span>Bye</span></li></div>
|
||||
</ul>
|
||||
<!--<ul>
|
||||
<!-- <ul>
|
||||
<li>Previously Commented Node</li>
|
||||
<li>Another Node</li>
|
||||
</ul>-->
|
||||
</ul> -->
|
||||
<span/>
|
||||
<style>
|
||||
.boo {
|
||||
/*margin: 10px;*/
|
||||
/* margin: 10px; */
|
||||
padding: 20px;
|
||||
}
|
||||
/*.hoo {
|
||||
/* .hoo {
|
||||
margin: 10px;
|
||||
padding: 20px;
|
||||
}*/
|
||||
} */
|
||||
</style>
|
||||
</div>
|
||||
`;
|
||||
|
@ -130,19 +130,19 @@ suite('Tests for Toggle Comment action from Emmet (HTML)', () => {
|
|||
const expectedContents = `
|
||||
<div class="hello">
|
||||
<ul>
|
||||
<!--<li><span>Hello</span></li>
|
||||
<li><span>There</span></li>-->
|
||||
<!-- <li><span>Hello</span></li>
|
||||
<li><span>There</span></li> -->
|
||||
<div><li><span>Bye</span></li></div>
|
||||
</ul>
|
||||
<ul>
|
||||
<!--<li>Previously Commented Node</li>-->
|
||||
<!-- <li>Previously Commented Node</li> -->
|
||||
<li>Another Node</li>
|
||||
</ul>
|
||||
<span/>
|
||||
<style>
|
||||
.boo {
|
||||
/*margin: 10px;
|
||||
padding: 20px;*/
|
||||
/* margin: 10px;
|
||||
padding: 20px; */
|
||||
}
|
||||
.hoo {
|
||||
margin: 10px;
|
||||
|
@ -168,14 +168,14 @@ suite('Tests for Toggle Comment action from Emmet (HTML)', () => {
|
|||
const expectedContents = `
|
||||
<div class="hello">
|
||||
<ul>
|
||||
<!--<li><span>Hello</span></li>
|
||||
<li><span>There</span></li>-->
|
||||
<!-- <li><span>Hello</span></li>
|
||||
<li><span>There</span></li> -->
|
||||
<div><li><span>Bye</span></li></div>
|
||||
</ul>
|
||||
<!--<ul>
|
||||
<!-- <ul>
|
||||
<li>Previously Commented Node</li>
|
||||
<li>Another Node</li>
|
||||
</ul>-->
|
||||
</ul> -->
|
||||
<span/>
|
||||
<style>
|
||||
.boo {
|
||||
|
@ -206,16 +206,16 @@ suite('Tests for Toggle Comment action from Emmet (HTML)', () => {
|
|||
const expectedContents = `
|
||||
<div class="hello">
|
||||
<ul>
|
||||
<li><!--<span>Hello</span>--></li>
|
||||
<!--<li><span>There</span></li>-->
|
||||
<li><!-- <span>Hello</span> --></li>
|
||||
<!-- <li><span>There</span></li> -->
|
||||
<div><li><span>Bye</span></li></div>
|
||||
</ul>
|
||||
<!--<ul>
|
||||
<!-- <ul>
|
||||
<li>Previously Commented Node</li>
|
||||
<li>Another Node</li>
|
||||
</ul>-->
|
||||
</ul> -->
|
||||
<span/>
|
||||
<!--<style>
|
||||
<!-- <style>
|
||||
.boo {
|
||||
margin: 10px;
|
||||
padding: 20px;
|
||||
|
@ -224,7 +224,7 @@ suite('Tests for Toggle Comment action from Emmet (HTML)', () => {
|
|||
margin: 10px;
|
||||
padding: 20px;
|
||||
}
|
||||
</style>-->
|
||||
</style> -->
|
||||
</div>
|
||||
`;
|
||||
return withRandomFileEditor(contents, 'html', (editor, doc) => {
|
||||
|
@ -252,16 +252,16 @@ suite('Tests for Toggle Comment action from Emmet (HTML)', () => {
|
|||
const templateContents = `
|
||||
<script type="text/template">
|
||||
<li><span>Hello</span></li>
|
||||
<li><!--<span>There</span>--></li>
|
||||
<li><!-- <span>There</span> --></li>
|
||||
<div><li><span>Bye</span></li></div>
|
||||
<span/>
|
||||
</script>
|
||||
`;
|
||||
const expectedContents = `
|
||||
<script type="text/template">
|
||||
<!--<li><span>Hello</span></li>-->
|
||||
<!-- <li><span>Hello</span></li> -->
|
||||
<li><span>There</span></li>
|
||||
<div><li><!--<span>Bye</span>--></li></div>
|
||||
<div><li><!-- <span>Bye</span> --></li></div>
|
||||
<span/>
|
||||
</script>
|
||||
`;
|
||||
|
@ -298,13 +298,13 @@ suite('Tests for Toggle Comment action from Emmet (CSS)', () => {
|
|||
test('toggle comment with multiple cursors, but no selection (CSS)', () => {
|
||||
const expectedContents = `
|
||||
.one {
|
||||
/*margin: 10px;*/
|
||||
/* margin: 10px; */
|
||||
padding: 10px;
|
||||
}
|
||||
/*.two {
|
||||
/* .two {
|
||||
height: 42px;
|
||||
display: none;
|
||||
}*/
|
||||
} */
|
||||
.three {
|
||||
width: 42px;
|
||||
}`;
|
||||
|
@ -327,13 +327,13 @@ suite('Tests for Toggle Comment action from Emmet (CSS)', () => {
|
|||
test('toggle comment with multiple cursors and whole node selected (CSS)', () => {
|
||||
const expectedContents = `
|
||||
.one {
|
||||
/*margin: 10px;*/
|
||||
/*padding: 10px;*/
|
||||
/* margin: 10px; */
|
||||
/* padding: 10px; */
|
||||
}
|
||||
/*.two {
|
||||
/* .two {
|
||||
height: 42px;
|
||||
display: none;
|
||||
}*/
|
||||
} */
|
||||
.three {
|
||||
width: 42px;
|
||||
}`;
|
||||
|
@ -359,16 +359,16 @@ suite('Tests for Toggle Comment action from Emmet (CSS)', () => {
|
|||
test('toggle comment when multiple nodes of same parent are completely under single selection (CSS)', () => {
|
||||
const expectedContents = `
|
||||
.one {
|
||||
/* margin: 10px;
|
||||
padding: 10px;*/
|
||||
/* margin: 10px;
|
||||
padding: 10px; */
|
||||
}
|
||||
/*.two {
|
||||
/* .two {
|
||||
height: 42px;
|
||||
display: none;
|
||||
}
|
||||
.three {
|
||||
width: 42px;
|
||||
}*/`;
|
||||
} */`;
|
||||
return withRandomFileEditor(contents, 'css', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(2, 0, 3, 16), // 2 properties completely under a single selection along with whitespace
|
||||
|
@ -389,10 +389,10 @@ suite('Tests for Toggle Comment action from Emmet (CSS)', () => {
|
|||
const expectedContents = `
|
||||
.one {
|
||||
margin: 10px;
|
||||
/*padding: 10px;
|
||||
/* padding: 10px;
|
||||
}
|
||||
.two {
|
||||
height: 42px;*/
|
||||
height: 42px; */
|
||||
display: none;
|
||||
}
|
||||
.three {
|
||||
|
@ -417,10 +417,10 @@ suite('Tests for Toggle Comment action from Emmet (CSS)', () => {
|
|||
const expectedContents = `
|
||||
.one {
|
||||
margin: 10px;
|
||||
/*padding: 10px;
|
||||
/* padding: 10px;
|
||||
}
|
||||
.two {
|
||||
height: 42px;*/
|
||||
height: 42px; */
|
||||
display: none;
|
||||
}
|
||||
.three {
|
||||
|
@ -445,10 +445,10 @@ suite('Tests for Toggle Comment action from Emmet (CSS)', () => {
|
|||
const expectedContents = `
|
||||
.one {
|
||||
margin: 10px;
|
||||
/*padding: 10px;
|
||||
/* padding: 10px;
|
||||
}
|
||||
.two {
|
||||
height: 42px;*/
|
||||
height: 42px; */
|
||||
display: none;
|
||||
}
|
||||
.three {
|
||||
|
@ -473,10 +473,10 @@ suite('Tests for Toggle Comment action from Emmet (CSS)', () => {
|
|||
const expectedContents = `
|
||||
.one {
|
||||
margin: 10px;
|
||||
/*padding: 10px;
|
||||
/* padding: 10px;
|
||||
}
|
||||
.two {
|
||||
height: 42px;*/
|
||||
height: 42px; */
|
||||
display: none;
|
||||
}
|
||||
.three {
|
||||
|
@ -500,16 +500,16 @@ suite('Tests for Toggle Comment action from Emmet (CSS)', () => {
|
|||
test('toggle comment when multiple nodes of same parent are partially under single selection (CSS)', () => {
|
||||
const expectedContents = `
|
||||
.one {
|
||||
/*margin: 10px;
|
||||
padding: 10px;*/
|
||||
/* margin: 10px;
|
||||
padding: 10px; */
|
||||
}
|
||||
/*.two {
|
||||
/* .two {
|
||||
height: 42px;
|
||||
display: none;
|
||||
}
|
||||
.three {
|
||||
width: 42px;
|
||||
*/ }`;
|
||||
*/ }`;
|
||||
return withRandomFileEditor(contents, 'css', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(2, 7, 3, 10), // 2 properties partially under a single selection
|
||||
|
@ -549,14 +549,14 @@ suite('Tests for Toggle Comment action from Emmet in nested css (SCSS)', () => {
|
|||
test('toggle comment with multiple cursors selecting nested nodes (SCSS)', () => {
|
||||
const expectedContents = `
|
||||
.one {
|
||||
/*height: 42px;*/
|
||||
/* height: 42px; */
|
||||
|
||||
/*.two {
|
||||
/* .two {
|
||||
width: 42px;
|
||||
}*/
|
||||
} */
|
||||
|
||||
.three {
|
||||
/*padding: 10px;*/
|
||||
/* padding: 10px; */
|
||||
}
|
||||
}`;
|
||||
return withRandomFileEditor(contents, 'css', (editor, doc) => {
|
||||
|
@ -578,7 +578,7 @@ suite('Tests for Toggle Comment action from Emmet in nested css (SCSS)', () => {
|
|||
});
|
||||
test('toggle comment with multiple cursors selecting several nested nodes (SCSS)', () => {
|
||||
const expectedContents = `
|
||||
/*.one {
|
||||
/* .one {
|
||||
height: 42px;
|
||||
|
||||
.two {
|
||||
|
@ -588,7 +588,7 @@ suite('Tests for Toggle Comment action from Emmet in nested css (SCSS)', () => {
|
|||
.three {
|
||||
padding: 10px;
|
||||
}
|
||||
}*/`;
|
||||
} */`;
|
||||
return withRandomFileEditor(contents, 'css', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(1, 3, 1, 3), // cursor in the outside rule. And several cursors inside:
|
||||
|
@ -611,14 +611,14 @@ suite('Tests for Toggle Comment action from Emmet in nested css (SCSS)', () => {
|
|||
test('toggle comment with multiple cursors, but no selection (SCSS)', () => {
|
||||
const expectedContents = `
|
||||
.one {
|
||||
/*height: 42px;*/
|
||||
/* height: 42px; */
|
||||
|
||||
/*.two {
|
||||
/* .two {
|
||||
width: 42px;
|
||||
}*/
|
||||
} */
|
||||
|
||||
.three {
|
||||
/*padding: 10px;*/
|
||||
/* padding: 10px; */
|
||||
}
|
||||
}`;
|
||||
return withRandomFileEditor(contents, 'css', (editor, doc) => {
|
||||
|
@ -641,14 +641,14 @@ suite('Tests for Toggle Comment action from Emmet in nested css (SCSS)', () => {
|
|||
test('toggle comment with multiple cursors and whole node selected (CSS)', () => {
|
||||
const expectedContents = `
|
||||
.one {
|
||||
/*height: 42px;*/
|
||||
/* height: 42px; */
|
||||
|
||||
/*.two {
|
||||
/* .two {
|
||||
width: 42px;
|
||||
}*/
|
||||
} */
|
||||
|
||||
.three {
|
||||
/*padding: 10px;*/
|
||||
/* padding: 10px; */
|
||||
}
|
||||
}`;
|
||||
return withRandomFileEditor(contents, 'css', (editor, doc) => {
|
||||
|
@ -673,11 +673,11 @@ suite('Tests for Toggle Comment action from Emmet in nested css (SCSS)', () => {
|
|||
test('toggle comment when multiple nodes are completely under single selection (CSS)', () => {
|
||||
const expectedContents = `
|
||||
.one {
|
||||
/*height: 42px;
|
||||
/* height: 42px;
|
||||
|
||||
.two {
|
||||
width: 42px;
|
||||
}*/
|
||||
} */
|
||||
|
||||
.three {
|
||||
padding: 10px;
|
||||
|
@ -701,11 +701,11 @@ suite('Tests for Toggle Comment action from Emmet in nested css (SCSS)', () => {
|
|||
test('toggle comment when multiple nodes are partially under single selection (CSS)', () => {
|
||||
const expectedContents = `
|
||||
.one {
|
||||
/*height: 42px;
|
||||
/* height: 42px;
|
||||
|
||||
.two {
|
||||
width: 42px;
|
||||
*/ }
|
||||
*/ }
|
||||
|
||||
.three {
|
||||
padding: 10px;
|
||||
|
@ -726,4 +726,29 @@ suite('Tests for Toggle Comment action from Emmet in nested css (SCSS)', () => {
|
|||
});
|
||||
});
|
||||
|
||||
});
|
||||
test('toggle comment doesn\'t fail when start and end nodes differ HTML', () => {
|
||||
const contents = `
|
||||
<div>
|
||||
<p>Hello</p>
|
||||
</div>
|
||||
`;
|
||||
const expectedContents = `
|
||||
<!-- <div>
|
||||
<p>Hello</p>
|
||||
</div> -->
|
||||
`;
|
||||
return withRandomFileEditor(contents, 'html', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(1, 2, 2, 9), // <div> to <p> inclusive
|
||||
];
|
||||
|
||||
return toggleComment().then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return toggleComment().then(() => {
|
||||
assert.equal(doc.getText(), contents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,148 +3,147 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
// import 'mocha';
|
||||
// import * as assert from 'assert';
|
||||
// import { Selection } from 'vscode';
|
||||
// import { withRandomFileEditor, closeAllEditors } from './testUtils';
|
||||
// import { updateImageSize } from '../updateImageSize';
|
||||
import 'mocha';
|
||||
import * as assert from 'assert';
|
||||
import { Selection } from 'vscode';
|
||||
import { withRandomFileEditor, closeAllEditors } from './testUtils';
|
||||
import { updateImageSize } from '../updateImageSize';
|
||||
|
||||
// suite('Tests for Emmet actions on html tags', () => {
|
||||
// teardown(closeAllEditors);
|
||||
suite('Tests for Emmet actions on html tags', () => {
|
||||
teardown(closeAllEditors);
|
||||
|
||||
// test('update image css with multiple cursors in css file', () => {
|
||||
// const cssContents = `
|
||||
// .one {
|
||||
// margin: 10px;
|
||||
// padding: 10px;
|
||||
// background-image: url(https://github.com/microsoft/vscode/blob/master/resources/linux/code.png);
|
||||
// }
|
||||
// .two {
|
||||
// background-image: url(https://github.com/microsoft/vscode/blob/master/resources/linux/code.png);
|
||||
// height: 42px;
|
||||
// }
|
||||
// .three {
|
||||
// background-image: url(https://github.com/microsoft/vscode/blob/master/resources/linux/code.png);
|
||||
// width: 42px;
|
||||
// }
|
||||
// `;
|
||||
// const expectedContents = `
|
||||
// .one {
|
||||
// margin: 10px;
|
||||
// padding: 10px;
|
||||
// background-image: url(https://github.com/microsoft/vscode/blob/master/resources/linux/code.png);
|
||||
// width: 32px;
|
||||
// height: 32px;
|
||||
// }
|
||||
// .two {
|
||||
// background-image: url(https://github.com/microsoft/vscode/blob/master/resources/linux/code.png);
|
||||
// width: 32px;
|
||||
// height: 32px;
|
||||
// }
|
||||
// .three {
|
||||
// background-image: url(https://github.com/microsoft/vscode/blob/master/resources/linux/code.png);
|
||||
// height: 32px;
|
||||
// width: 32px;
|
||||
// }
|
||||
// `;
|
||||
// return withRandomFileEditor(cssContents, 'css', (editor, doc) => {
|
||||
// editor.selections = [
|
||||
// new Selection(4, 50, 4, 50),
|
||||
// new Selection(7, 50, 7, 50),
|
||||
// new Selection(11, 50, 11, 50)
|
||||
// ];
|
||||
test('update image css with multiple cursors in css file', () => {
|
||||
const cssContents = `
|
||||
.one {
|
||||
margin: 10px;
|
||||
padding: 10px;
|
||||
background-image: url(https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png);
|
||||
}
|
||||
.two {
|
||||
background-image: url(https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png);
|
||||
height: 42px;
|
||||
}
|
||||
.three {
|
||||
background-image: url(https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png);
|
||||
width: 42px;
|
||||
}
|
||||
`;
|
||||
const expectedContents = `
|
||||
.one {
|
||||
margin: 10px;
|
||||
padding: 10px;
|
||||
background-image: url(https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png);
|
||||
width: 1024px;
|
||||
height: 1024px;
|
||||
}
|
||||
.two {
|
||||
background-image: url(https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png);
|
||||
width: 1024px;
|
||||
height: 1024px;
|
||||
}
|
||||
.three {
|
||||
background-image: url(https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png);
|
||||
height: 1024px;
|
||||
width: 1024px;
|
||||
}
|
||||
`;
|
||||
return withRandomFileEditor(cssContents, 'css', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(4, 50, 4, 50),
|
||||
new Selection(7, 50, 7, 50),
|
||||
new Selection(11, 50, 11, 50)
|
||||
];
|
||||
|
||||
// return updateImageSize()!.then(() => {
|
||||
// assert.equal(doc.getText(), expectedContents);
|
||||
// return Promise.resolve();
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
return updateImageSize()!.then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// test('update image size in css in html file with multiple cursors', () => {
|
||||
// const htmlWithCssContents = `
|
||||
// <html>
|
||||
// <style>
|
||||
// .one {
|
||||
// margin: 10px;
|
||||
// padding: 10px;
|
||||
// background-image: url(https://github.com/microsoft/vscode/blob/master/resources/linux/code.png);
|
||||
// }
|
||||
// .two {
|
||||
// background-image: url(https://github.com/microsoft/vscode/blob/master/resources/linux/code.png);
|
||||
// height: 42px;
|
||||
// }
|
||||
// .three {
|
||||
// background-image: url(https://github.com/microsoft/vscode/blob/master/resources/linux/code.png);
|
||||
// width: 42px;
|
||||
// }
|
||||
// </style>
|
||||
// </html>
|
||||
// `;
|
||||
// const expectedContents = `
|
||||
// <html>
|
||||
// <style>
|
||||
// .one {
|
||||
// margin: 10px;
|
||||
// padding: 10px;
|
||||
// background-image: url(https://github.com/microsoft/vscode/blob/master/resources/linux/code.png);
|
||||
// width: 32px;
|
||||
// height: 32px;
|
||||
// }
|
||||
// .two {
|
||||
// background-image: url(https://github.com/microsoft/vscode/blob/master/resources/linux/code.png);
|
||||
// width: 32px;
|
||||
// height: 32px;
|
||||
// }
|
||||
// .three {
|
||||
// background-image: url(https://github.com/microsoft/vscode/blob/master/resources/linux/code.png);
|
||||
// height: 32px;
|
||||
// width: 32px;
|
||||
// }
|
||||
// </style>
|
||||
// </html>
|
||||
// `;
|
||||
// return withRandomFileEditor(htmlWithCssContents, 'html', (editor, doc) => {
|
||||
// editor.selections = [
|
||||
// new Selection(6, 50, 6, 50),
|
||||
// new Selection(9, 50, 9, 50),
|
||||
// new Selection(13, 50, 13, 50)
|
||||
// ];
|
||||
test('update image size in css in html file with multiple cursors', () => {
|
||||
const htmlWithCssContents = `
|
||||
<html>
|
||||
<style>
|
||||
.one {
|
||||
margin: 10px;
|
||||
padding: 10px;
|
||||
background-image: url(https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png);
|
||||
}
|
||||
.two {
|
||||
background-image: url(https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png);
|
||||
height: 42px;
|
||||
}
|
||||
.three {
|
||||
background-image: url(https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png);
|
||||
width: 42px;
|
||||
}
|
||||
</style>
|
||||
</html>
|
||||
`;
|
||||
const expectedContents = `
|
||||
<html>
|
||||
<style>
|
||||
.one {
|
||||
margin: 10px;
|
||||
padding: 10px;
|
||||
background-image: url(https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png);
|
||||
width: 1024px;
|
||||
height: 1024px;
|
||||
}
|
||||
.two {
|
||||
background-image: url(https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png);
|
||||
width: 1024px;
|
||||
height: 1024px;
|
||||
}
|
||||
.three {
|
||||
background-image: url(https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png);
|
||||
height: 1024px;
|
||||
width: 1024px;
|
||||
}
|
||||
</style>
|
||||
</html>
|
||||
`;
|
||||
return withRandomFileEditor(htmlWithCssContents, 'html', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(6, 50, 6, 50),
|
||||
new Selection(9, 50, 9, 50),
|
||||
new Selection(13, 50, 13, 50)
|
||||
];
|
||||
|
||||
// return updateImageSize()!.then(() => {
|
||||
// assert.equal(doc.getText(), expectedContents);
|
||||
// return Promise.resolve();
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
return updateImageSize()!.then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// test('update image size in img tag in html file with multiple cursors', () => {
|
||||
// const htmlwithimgtag = `
|
||||
// <html>
|
||||
// <img id="one" src="https://github.com/microsoft/vscode/blob/master/resources/linux/code.png" />
|
||||
// <img id="two" src="https://github.com/microsoft/vscode/blob/master/resources/linux/code.png" width="56" />
|
||||
// <img id="three" src="https://github.com/microsoft/vscode/blob/master/resources/linux/code.png" height="56" />
|
||||
// </html>
|
||||
// `;
|
||||
// const expectedContents = `
|
||||
// <html>
|
||||
// <img id="one" src="https://github.com/microsoft/vscode/blob/master/resources/linux/code.png" width="32" height="32" />
|
||||
// <img id="two" src="https://github.com/microsoft/vscode/blob/master/resources/linux/code.png" width="32" height="32" />
|
||||
// <img id="three" src="https://github.com/microsoft/vscode/blob/master/resources/linux/code.png" height="32" width="32" />
|
||||
// </html>
|
||||
// `;
|
||||
// return withRandomFileEditor(htmlwithimgtag, 'html', (editor, doc) => {
|
||||
// editor.selections = [
|
||||
// new Selection(2, 50, 2, 50),
|
||||
// new Selection(3, 50, 3, 50),
|
||||
// new Selection(4, 50, 4, 50)
|
||||
// ];
|
||||
test('update image size in img tag in html file with multiple cursors', () => {
|
||||
const htmlwithimgtag = `
|
||||
<html>
|
||||
<img id="one" src="https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png" />
|
||||
<img id="two" src="https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png" width="56" />
|
||||
<img id="three" src="https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png" height="56" />
|
||||
</html>
|
||||
`;
|
||||
const expectedContents = `
|
||||
<html>
|
||||
<img id="one" src="https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png" width="1024" height="1024" />
|
||||
<img id="two" src="https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png" width="1024" height="1024" />
|
||||
<img id="three" src="https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png" height="1024" width="1024" />
|
||||
</html>
|
||||
`;
|
||||
return withRandomFileEditor(htmlwithimgtag, 'html', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(2, 50, 2, 50),
|
||||
new Selection(3, 50, 3, 50),
|
||||
new Selection(4, 50, 4, 50)
|
||||
];
|
||||
|
||||
// return updateImageSize()!.then(() => {
|
||||
// assert.equal(doc.getText(), expectedContents);
|
||||
// return Promise.resolve();
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
|
||||
// });
|
||||
return updateImageSize()!.then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -9,13 +9,20 @@ import { Selection, workspace, ConfigurationTarget } from 'vscode';
|
|||
import { withRandomFileEditor, closeAllEditors } from './testUtils';
|
||||
import { wrapWithAbbreviation, wrapIndividualLinesWithAbbreviation } from '../abbreviationActions';
|
||||
|
||||
const htmlContentsForWrapTests = `
|
||||
const htmlContentsForBlockWrapTests = `
|
||||
<ul class="nav main">
|
||||
<li class="item1">img</li>
|
||||
<li class="item2">$hithere</li>
|
||||
</ul>
|
||||
`;
|
||||
|
||||
const htmlContentsForInlineWrapTests = `
|
||||
<ul class="nav main">
|
||||
<em class="item1">img</em>
|
||||
<em class="item2">$hithere</em>
|
||||
</ul>
|
||||
`;
|
||||
|
||||
const wrapBlockElementExpected = `
|
||||
<ul class="nav main">
|
||||
<div>
|
||||
|
@ -29,15 +36,19 @@ const wrapBlockElementExpected = `
|
|||
|
||||
const wrapInlineElementExpected = `
|
||||
<ul class="nav main">
|
||||
<span><li class="item1">img</li></span>
|
||||
<span><li class="item2">$hithere</li></span>
|
||||
<span><em class="item1">img</em></span>
|
||||
<span><em class="item2">$hithere</em></span>
|
||||
</ul>
|
||||
`;
|
||||
|
||||
const wrapSnippetExpected = `
|
||||
<ul class="nav main">
|
||||
<a href=""><li class="item1">img</li></a>
|
||||
<a href=""><li class="item2">$hithere</li></a>
|
||||
<a href="">
|
||||
<li class="item1">img</li>
|
||||
</a>
|
||||
<a href="">
|
||||
<li class="item2">$hithere</li>
|
||||
</a>
|
||||
</ul>
|
||||
`;
|
||||
|
||||
|
@ -56,10 +67,16 @@ const wrapMultiLineAbbrExpected = `
|
|||
</ul>
|
||||
`;
|
||||
|
||||
// technically a bug, but also a feature (requested behaviour)
|
||||
// https://github.com/microsoft/vscode/issues/78015
|
||||
const wrapInlineElementExpectedFormatFalse = `
|
||||
<ul class="nav main">
|
||||
<h1><li class="item1">img</li></h1>
|
||||
<h1><li class="item2">$hithere</li></h1>
|
||||
<h1>
|
||||
<li class="item1">img</li>
|
||||
</h1>
|
||||
<h1>
|
||||
<li class="item2">$hithere</li>
|
||||
</h1>
|
||||
</ul>
|
||||
`;
|
||||
|
||||
|
@ -73,51 +90,51 @@ suite('Tests for Wrap with Abbreviations', () => {
|
|||
const oldValueForSyntaxProfiles = workspace.getConfiguration('emmet').inspect('syntaxProfile');
|
||||
|
||||
test('Wrap with block element using multi cursor', () => {
|
||||
return testWrapWithAbbreviation(multiCursors, 'div', wrapBlockElementExpected);
|
||||
return testWrapWithAbbreviation(multiCursors, 'div', wrapBlockElementExpected, htmlContentsForBlockWrapTests);
|
||||
});
|
||||
|
||||
test('Wrap with inline element using multi cursor', () => {
|
||||
return testWrapWithAbbreviation(multiCursors, 'span', wrapInlineElementExpected);
|
||||
return testWrapWithAbbreviation(multiCursors, 'span', wrapInlineElementExpected, htmlContentsForInlineWrapTests);
|
||||
});
|
||||
|
||||
test('Wrap with snippet using multi cursor', () => {
|
||||
return testWrapWithAbbreviation(multiCursors, 'a', wrapSnippetExpected);
|
||||
return testWrapWithAbbreviation(multiCursors, 'a', wrapSnippetExpected, htmlContentsForBlockWrapTests);
|
||||
});
|
||||
|
||||
test('Wrap with multi line abbreviation using multi cursor', () => {
|
||||
return testWrapWithAbbreviation(multiCursors, 'ul>li', wrapMultiLineAbbrExpected);
|
||||
return testWrapWithAbbreviation(multiCursors, 'ul>li', wrapMultiLineAbbrExpected, htmlContentsForBlockWrapTests);
|
||||
});
|
||||
|
||||
test('Wrap with block element using multi cursor selection', () => {
|
||||
return testWrapWithAbbreviation(multiCursorsWithSelection, 'div', wrapBlockElementExpected);
|
||||
return testWrapWithAbbreviation(multiCursorsWithSelection, 'div', wrapBlockElementExpected, htmlContentsForBlockWrapTests);
|
||||
});
|
||||
|
||||
test('Wrap with inline element using multi cursor selection', () => {
|
||||
return testWrapWithAbbreviation(multiCursorsWithSelection, 'span', wrapInlineElementExpected);
|
||||
return testWrapWithAbbreviation(multiCursorsWithSelection, 'span', wrapInlineElementExpected, htmlContentsForInlineWrapTests);
|
||||
});
|
||||
|
||||
test('Wrap with snippet using multi cursor selection', () => {
|
||||
return testWrapWithAbbreviation(multiCursorsWithSelection, 'a', wrapSnippetExpected);
|
||||
return testWrapWithAbbreviation(multiCursorsWithSelection, 'a', wrapSnippetExpected, htmlContentsForBlockWrapTests);
|
||||
});
|
||||
|
||||
test('Wrap with multi line abbreviation using multi cursor selection', () => {
|
||||
return testWrapWithAbbreviation(multiCursorsWithSelection, 'ul>li', wrapMultiLineAbbrExpected);
|
||||
return testWrapWithAbbreviation(multiCursorsWithSelection, 'ul>li', wrapMultiLineAbbrExpected, htmlContentsForBlockWrapTests);
|
||||
});
|
||||
|
||||
test('Wrap with block element using multi cursor full line selection', () => {
|
||||
return testWrapWithAbbreviation(multiCursorsWithFullLineSelection, 'div', wrapBlockElementExpected);
|
||||
return testWrapWithAbbreviation(multiCursorsWithFullLineSelection, 'div', wrapBlockElementExpected, htmlContentsForBlockWrapTests);
|
||||
});
|
||||
|
||||
test('Wrap with inline element using multi cursor full line selection', () => {
|
||||
return testWrapWithAbbreviation(multiCursorsWithFullLineSelection, 'span', wrapInlineElementExpected);
|
||||
return testWrapWithAbbreviation(multiCursorsWithFullLineSelection, 'span', wrapInlineElementExpected, htmlContentsForInlineWrapTests);
|
||||
});
|
||||
|
||||
test('Wrap with snippet using multi cursor full line selection', () => {
|
||||
return testWrapWithAbbreviation(multiCursorsWithFullLineSelection, 'a', wrapSnippetExpected);
|
||||
return testWrapWithAbbreviation(multiCursorsWithFullLineSelection, 'a', wrapSnippetExpected, htmlContentsForBlockWrapTests);
|
||||
});
|
||||
|
||||
test('Wrap with multi line abbreviation using multi cursor full line selection', () => {
|
||||
return testWrapWithAbbreviation(multiCursorsWithFullLineSelection, 'ul>li', wrapMultiLineAbbrExpected);
|
||||
return testWrapWithAbbreviation(multiCursorsWithFullLineSelection, 'ul>li', wrapMultiLineAbbrExpected, htmlContentsForBlockWrapTests);
|
||||
});
|
||||
|
||||
test('Wrap with abbreviation and comment filter', () => {
|
||||
|
@ -128,15 +145,31 @@ suite('Tests for Wrap with Abbreviations', () => {
|
|||
`;
|
||||
const expectedContents = `
|
||||
<ul class="nav main">
|
||||
<li class="hello">
|
||||
line
|
||||
</li>
|
||||
<li class="hello">line</li>
|
||||
<!-- /.hello -->
|
||||
</ul>
|
||||
`;
|
||||
return testWrapWithAbbreviation([new Selection(2, 0, 2, 0)], 'li.hello|c', expectedContents, contents);
|
||||
});
|
||||
|
||||
test('Wrap with abbreviation link', () => {
|
||||
const contents = `
|
||||
<ul class="nav main">
|
||||
line
|
||||
</ul>
|
||||
`;
|
||||
const expectedContents = `
|
||||
<a href="https://example.com">
|
||||
<div>
|
||||
<ul class="nav main">
|
||||
line
|
||||
</ul>
|
||||
</div>
|
||||
</a>
|
||||
`;
|
||||
return testWrapWithAbbreviation([new Selection(1, 1, 1, 1)], 'a[href="https://example.com"]>div', expectedContents, contents);
|
||||
});
|
||||
|
||||
test('Wrap with abbreviation entire node when cursor is on opening tag', () => {
|
||||
const contents = `
|
||||
<div class="nav main">
|
||||
|
@ -192,8 +225,12 @@ suite('Tests for Wrap with Abbreviations', () => {
|
|||
const wrapIndividualLinesExpected = `
|
||||
<ul class="nav main">
|
||||
<ul>
|
||||
<li class="hello1"><li class="item1">This $10 is not a tabstop</li></li>
|
||||
<li class="hello2"><li class="item2">hi.there</li></li>
|
||||
<li class="hello1">
|
||||
<li class="item1">This $10 is not a tabstop</li>
|
||||
</li>
|
||||
<li class="hello2">
|
||||
<li class="item2">hi.there</li>
|
||||
</li>
|
||||
</ul>
|
||||
</ul>
|
||||
`;
|
||||
|
@ -210,8 +247,12 @@ suite('Tests for Wrap with Abbreviations', () => {
|
|||
const wrapIndividualLinesExpected = `
|
||||
<ul class="nav main">
|
||||
<ul>
|
||||
<li class="hello1"><li class="item1">img</li></li>
|
||||
<li class="hello2"><li class="item2">hi.there</li></li>
|
||||
<li class="hello1">
|
||||
<li class="item1">img</li>
|
||||
</li>
|
||||
<li class="hello2">
|
||||
<li class="item2">hi.there</li>
|
||||
</li>
|
||||
</ul>
|
||||
</ul>
|
||||
`;
|
||||
|
@ -228,9 +269,13 @@ suite('Tests for Wrap with Abbreviations', () => {
|
|||
const wrapIndividualLinesExpected = `
|
||||
<ul class="nav main">
|
||||
<ul>
|
||||
<li class="hello"><li class="item1">img</li></li>
|
||||
<li class="hello">
|
||||
<li class="item1">img</li>
|
||||
</li>
|
||||
<!-- /.hello -->
|
||||
<li class="hello"><li class="item2">hi.there</li></li>
|
||||
<li class="hello">
|
||||
<li class="item2">hi.there</li>
|
||||
</li>
|
||||
<!-- /.hello -->
|
||||
</ul>
|
||||
</ul>
|
||||
|
@ -257,9 +302,9 @@ suite('Tests for Wrap with Abbreviations', () => {
|
|||
});
|
||||
|
||||
test('Wrap with abbreviation and format set to false', () => {
|
||||
return workspace.getConfiguration('emmet').update('syntaxProfiles',{ 'html' : { 'format': false } } , ConfigurationTarget.Global).then(() => {
|
||||
return testWrapWithAbbreviation(multiCursors,'h1',wrapInlineElementExpectedFormatFalse).then(() => {
|
||||
return workspace.getConfiguration('emmet').update('syntaxProfiles',oldValueForSyntaxProfiles ? oldValueForSyntaxProfiles.globalValue : undefined, ConfigurationTarget.Global);
|
||||
return workspace.getConfiguration('emmet').update('syntaxProfiles', { 'html' : { 'format': false } }, ConfigurationTarget.Global).then(() => {
|
||||
return testWrapWithAbbreviation(multiCursors, 'h1', wrapInlineElementExpectedFormatFalse, htmlContentsForBlockWrapTests).then(() => {
|
||||
return workspace.getConfiguration('emmet').update('syntaxProfiles', oldValueForSyntaxProfiles ? oldValueForSyntaxProfiles.globalValue : undefined, ConfigurationTarget.Global);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -302,23 +347,27 @@ suite('Tests for Wrap with Abbreviations', () => {
|
|||
</ul>
|
||||
`;
|
||||
|
||||
return testWrapWithAbbreviation([new Selection(2,2,3,33)], '.hello', wrapMultiLineJsxExpected, htmlContentsForWrapTests, 'jsx');
|
||||
return testWrapWithAbbreviation([new Selection(2,2,3,33)], '.hello', wrapMultiLineJsxExpected, htmlContentsForBlockWrapTests, 'jsx');
|
||||
});
|
||||
|
||||
test('Wrap individual line with abbreviation uses className for jsx files', () => {
|
||||
const wrapIndividualLinesJsxExpected = `
|
||||
<ul class="nav main">
|
||||
<div className="hello1"><li class="item1">img</li></div>
|
||||
<div className="hello2"><li class="item2">$hithere</li></div>
|
||||
<div className="hello1">
|
||||
<li class="item1">img</li>
|
||||
</div>
|
||||
<div className="hello2">
|
||||
<li class="item2">$hithere</li>
|
||||
</div>
|
||||
</ul>
|
||||
`;
|
||||
|
||||
return testWrapIndividualLinesWithAbbreviation([new Selection(2,2,3,33)], '.hello$*', wrapIndividualLinesJsxExpected, htmlContentsForWrapTests, 'jsx');
|
||||
return testWrapIndividualLinesWithAbbreviation([new Selection(2,2,3,33)], '.hello$*', wrapIndividualLinesJsxExpected, htmlContentsForBlockWrapTests, 'jsx');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
function testWrapWithAbbreviation(selections: Selection[], abbreviation: string, expectedContents: string, input: string = htmlContentsForWrapTests, fileExtension: string = 'html'): Thenable<any> {
|
||||
function testWrapWithAbbreviation(selections: Selection[], abbreviation: string, expectedContents: string, input: string, fileExtension: string = 'html'): Thenable<any> {
|
||||
return withRandomFileEditor(input, fileExtension, (editor, _) => {
|
||||
editor.selections = selections;
|
||||
const promise = wrapWithAbbreviation({ abbreviation });
|
||||
|
@ -334,7 +383,7 @@ function testWrapWithAbbreviation(selections: Selection[], abbreviation: string,
|
|||
});
|
||||
}
|
||||
|
||||
function testWrapIndividualLinesWithAbbreviation(selections: Selection[], abbreviation: string, expectedContents: string, input: string = htmlContentsForWrapTests, fileExtension: string = 'html'): Thenable<any> {
|
||||
function testWrapIndividualLinesWithAbbreviation(selections: Selection[], abbreviation: string, expectedContents: string, input: string, fileExtension: string = 'html'): Thenable<any> {
|
||||
return withRandomFileEditor(input, fileExtension, (editor, _) => {
|
||||
editor.selections = selections;
|
||||
const promise = wrapIndividualLinesWithAbbreviation({ abbreviation });
|
||||
|
|
|
@ -4,22 +4,24 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { getNodesInBetween, getNode, getHtmlNode, parseDocument, sameNodes, isStyleSheet, validate } from './util';
|
||||
import { Node, Stylesheet, Rule } from 'EmmetNode';
|
||||
import { getNodesInBetween, getFlatNode, getHtmlFlatNode, sameNodes, isStyleSheet, validate, offsetRangeToVsRange, offsetRangeToSelection } from './util';
|
||||
import { Node, Stylesheet, Rule } from 'EmmetFlatNode';
|
||||
import parseStylesheet from '@emmetio/css-parser';
|
||||
import { DocumentStreamReader } from './bufferStream';
|
||||
import { getRootNode } from './parseDocument';
|
||||
|
||||
const startCommentStylesheet = '/*';
|
||||
const endCommentStylesheet = '*/';
|
||||
const startCommentHTML = '<!--';
|
||||
const endCommentHTML = '-->';
|
||||
let startCommentStylesheet: string;
|
||||
let endCommentStylesheet: string;
|
||||
let startCommentHTML: string;
|
||||
let endCommentHTML: string;
|
||||
|
||||
export function toggleComment(): Thenable<boolean> | undefined {
|
||||
if (!validate() || !vscode.window.activeTextEditor) {
|
||||
return;
|
||||
}
|
||||
setupCommentSpacing();
|
||||
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
let rootNode = parseDocument(editor.document);
|
||||
const rootNode = getRootNode(editor.document, true);
|
||||
if (!rootNode) {
|
||||
return;
|
||||
}
|
||||
|
@ -27,7 +29,7 @@ export function toggleComment(): Thenable<boolean> | undefined {
|
|||
return editor.edit(editBuilder => {
|
||||
let allEdits: vscode.TextEdit[][] = [];
|
||||
editor.selections.reverse().forEach(selection => {
|
||||
let edits = isStyleSheet(editor.document.languageId) ? toggleCommentStylesheet(selection, <Stylesheet>rootNode) : toggleCommentHTML(editor.document, selection, rootNode!);
|
||||
const edits = isStyleSheet(editor.document.languageId) ? toggleCommentStylesheet(editor.document, selection, <Stylesheet>rootNode) : toggleCommentHTML(editor.document, selection, rootNode!);
|
||||
if (edits.length > 0) {
|
||||
allEdits.push(edits);
|
||||
}
|
||||
|
@ -53,21 +55,25 @@ export function toggleComment(): Thenable<boolean> | undefined {
|
|||
function toggleCommentHTML(document: vscode.TextDocument, selection: vscode.Selection, rootNode: Node): vscode.TextEdit[] {
|
||||
const selectionStart = selection.isReversed ? selection.active : selection.anchor;
|
||||
const selectionEnd = selection.isReversed ? selection.anchor : selection.active;
|
||||
const selectionStartOffset = document.offsetAt(selectionStart);
|
||||
const selectionEndOffset = document.offsetAt(selectionEnd);
|
||||
const documentText = document.getText();
|
||||
|
||||
let startNode = getHtmlNode(document, rootNode, selectionStart, true);
|
||||
let endNode = getHtmlNode(document, rootNode, selectionEnd, true);
|
||||
const startNode = getHtmlFlatNode(documentText, rootNode, selectionStartOffset, true);
|
||||
const endNode = getHtmlFlatNode(documentText, rootNode, selectionEndOffset, true);
|
||||
|
||||
if (!startNode || !endNode) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (sameNodes(startNode, endNode) && startNode.name === 'style'
|
||||
&& startNode.open.end.isBefore(selectionStart)
|
||||
&& startNode.close.start.isAfter(selectionEnd)) {
|
||||
let buffer = new DocumentStreamReader(document, startNode.open.end, new vscode.Range(startNode.open.end, startNode.close.start));
|
||||
let cssRootNode = parseStylesheet(buffer);
|
||||
|
||||
return toggleCommentStylesheet(selection, cssRootNode);
|
||||
&& startNode.open && startNode.close
|
||||
&& startNode.open.end < selectionStartOffset
|
||||
&& startNode.close.start > selectionEndOffset) {
|
||||
const buffer = ' '.repeat(startNode.open.end) +
|
||||
documentText.substring(startNode.open.end, startNode.close.start);
|
||||
const cssRootNode = parseStylesheet(buffer);
|
||||
return toggleCommentStylesheet(document, selection, cssRootNode);
|
||||
}
|
||||
|
||||
let allNodes: Node[] = getNodesInBetween(startNode, endNode);
|
||||
|
@ -82,8 +88,8 @@ function toggleCommentHTML(document: vscode.TextDocument, selection: vscode.Sele
|
|||
}
|
||||
|
||||
|
||||
edits.push(new vscode.TextEdit(new vscode.Range(allNodes[0].start, allNodes[0].start), startCommentHTML));
|
||||
edits.push(new vscode.TextEdit(new vscode.Range(allNodes[allNodes.length - 1].end, allNodes[allNodes.length - 1].end), endCommentHTML));
|
||||
edits.push(new vscode.TextEdit(offsetRangeToVsRange(document, allNodes[0].start, allNodes[0].start), startCommentHTML));
|
||||
edits.push(new vscode.TextEdit(offsetRangeToVsRange(document, allNodes[allNodes.length - 1].end, allNodes[allNodes.length - 1].end), endCommentHTML));
|
||||
|
||||
return edits;
|
||||
}
|
||||
|
@ -93,10 +99,8 @@ function getRangesToUnCommentHTML(node: Node, document: vscode.TextDocument): vs
|
|||
|
||||
// If current node is commented, then uncomment and return
|
||||
if (node.type === 'comment') {
|
||||
|
||||
unCommentTextEdits.push(new vscode.TextEdit(new vscode.Range(node.start, node.start.translate(0, startCommentHTML.length)), ''));
|
||||
unCommentTextEdits.push(new vscode.TextEdit(new vscode.Range(node.end.translate(0, -endCommentHTML.length), node.end), ''));
|
||||
|
||||
unCommentTextEdits.push(new vscode.TextEdit(offsetRangeToVsRange(document, node.start, node.start + startCommentHTML.length), ''));
|
||||
unCommentTextEdits.push(new vscode.TextEdit(offsetRangeToVsRange(document, node.end - endCommentHTML.length, node.end), ''));
|
||||
return unCommentTextEdits;
|
||||
}
|
||||
|
||||
|
@ -108,32 +112,34 @@ function getRangesToUnCommentHTML(node: Node, document: vscode.TextDocument): vs
|
|||
return unCommentTextEdits;
|
||||
}
|
||||
|
||||
function toggleCommentStylesheet(selection: vscode.Selection, rootNode: Stylesheet): vscode.TextEdit[] {
|
||||
let selectionStart = selection.isReversed ? selection.active : selection.anchor;
|
||||
let selectionEnd = selection.isReversed ? selection.anchor : selection.active;
|
||||
function toggleCommentStylesheet(document: vscode.TextDocument, selection: vscode.Selection, rootNode: Stylesheet): vscode.TextEdit[] {
|
||||
const selectionStart = selection.isReversed ? selection.active : selection.anchor;
|
||||
const selectionEnd = selection.isReversed ? selection.anchor : selection.active;
|
||||
let selectionStartOffset = document.offsetAt(selectionStart);
|
||||
let selectionEndOffset = document.offsetAt(selectionEnd);
|
||||
|
||||
let startNode = getNode(rootNode, selectionStart, true);
|
||||
let endNode = getNode(rootNode, selectionEnd, true);
|
||||
const startNode = getFlatNode(rootNode, selectionStartOffset, true);
|
||||
const endNode = getFlatNode(rootNode, selectionEndOffset, true);
|
||||
|
||||
if (!selection.isEmpty) {
|
||||
selectionStart = adjustStartNodeCss(startNode, selectionStart, rootNode);
|
||||
selectionEnd = adjustEndNodeCss(endNode, selectionEnd, rootNode);
|
||||
selection = new vscode.Selection(selectionStart, selectionEnd);
|
||||
selectionStartOffset = adjustStartNodeCss(startNode, selectionStartOffset, rootNode);
|
||||
selectionEndOffset = adjustEndNodeCss(endNode, selectionEndOffset, rootNode);
|
||||
selection = offsetRangeToSelection(document, selectionStartOffset, selectionEndOffset);
|
||||
} else if (startNode) {
|
||||
selectionStart = startNode.start;
|
||||
selectionEnd = startNode.end;
|
||||
selection = new vscode.Selection(selectionStart, selectionEnd);
|
||||
selectionStartOffset = startNode.start;
|
||||
selectionEndOffset = startNode.end;
|
||||
selection = offsetRangeToSelection(document, selectionStartOffset, selectionEndOffset);
|
||||
}
|
||||
|
||||
// Uncomment the comments that intersect with the selection.
|
||||
let rangesToUnComment: vscode.Range[] = [];
|
||||
let edits: vscode.TextEdit[] = [];
|
||||
rootNode.comments.forEach(comment => {
|
||||
let commentRange = new vscode.Range(comment.start, comment.end);
|
||||
const commentRange = offsetRangeToVsRange(document, comment.start, comment.end);
|
||||
if (selection.intersection(commentRange)) {
|
||||
rangesToUnComment.push(commentRange);
|
||||
edits.push(new vscode.TextEdit(new vscode.Range(comment.start, comment.start.translate(0, startCommentStylesheet.length)), ''));
|
||||
edits.push(new vscode.TextEdit(new vscode.Range(comment.end.translate(0, -endCommentStylesheet.length), comment.end), ''));
|
||||
edits.push(new vscode.TextEdit(offsetRangeToVsRange(document, comment.start, comment.start + startCommentStylesheet.length), ''));
|
||||
edits.push(new vscode.TextEdit(offsetRangeToVsRange(document, comment.end - endCommentStylesheet.length, comment.end), ''));
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -145,20 +151,32 @@ function toggleCommentStylesheet(selection: vscode.Selection, rootNode: Styleshe
|
|||
new vscode.TextEdit(new vscode.Range(selection.start, selection.start), startCommentStylesheet),
|
||||
new vscode.TextEdit(new vscode.Range(selection.end, selection.end), endCommentStylesheet)
|
||||
];
|
||||
|
||||
|
||||
}
|
||||
|
||||
function adjustStartNodeCss(node: Node | null, pos: vscode.Position, rootNode: Stylesheet): vscode.Position {
|
||||
function setupCommentSpacing() {
|
||||
const config: boolean | undefined = vscode.workspace.getConfiguration('editor.comments').get('insertSpace');
|
||||
if (config) {
|
||||
startCommentStylesheet = '/* ';
|
||||
endCommentStylesheet = ' */';
|
||||
startCommentHTML = '<!-- ';
|
||||
endCommentHTML = ' -->';
|
||||
} else {
|
||||
startCommentStylesheet = '/*';
|
||||
endCommentStylesheet = '*/';
|
||||
startCommentHTML = '<!--';
|
||||
endCommentHTML = '-->';
|
||||
}
|
||||
}
|
||||
|
||||
function adjustStartNodeCss(node: Node | undefined, offset: number, rootNode: Stylesheet): number {
|
||||
for (const comment of rootNode.comments) {
|
||||
let commentRange = new vscode.Range(comment.start, comment.end);
|
||||
if (commentRange.contains(pos)) {
|
||||
return pos;
|
||||
if (comment.start <= offset && offset <= comment.end) {
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
|
||||
if (!node) {
|
||||
return pos;
|
||||
return offset;
|
||||
}
|
||||
|
||||
if (node.type === 'property') {
|
||||
|
@ -166,32 +184,31 @@ function adjustStartNodeCss(node: Node | null, pos: vscode.Position, rootNode: S
|
|||
}
|
||||
|
||||
const rule = <Rule>node;
|
||||
if (pos.isBefore(rule.contentStartToken.end) || !rule.firstChild) {
|
||||
if (offset < rule.contentStartToken.end || !rule.firstChild) {
|
||||
return rule.start;
|
||||
}
|
||||
|
||||
if (pos.isBefore(rule.firstChild.start)) {
|
||||
return pos;
|
||||
if (offset < rule.firstChild.start) {
|
||||
return offset;
|
||||
}
|
||||
|
||||
let newStartNode = rule.firstChild;
|
||||
while (newStartNode.nextSibling && pos.isAfter(newStartNode.end)) {
|
||||
while (newStartNode.nextSibling && offset > newStartNode.end) {
|
||||
newStartNode = newStartNode.nextSibling;
|
||||
}
|
||||
|
||||
return newStartNode.start;
|
||||
}
|
||||
|
||||
function adjustEndNodeCss(node: Node | null, pos: vscode.Position, rootNode: Stylesheet): vscode.Position {
|
||||
function adjustEndNodeCss(node: Node | undefined, offset: number, rootNode: Stylesheet): number {
|
||||
for (const comment of rootNode.comments) {
|
||||
let commentRange = new vscode.Range(comment.start, comment.end);
|
||||
if (commentRange.contains(pos)) {
|
||||
return pos;
|
||||
if (comment.start <= offset && offset <= comment.end) {
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
|
||||
if (!node) {
|
||||
return pos;
|
||||
return offset;
|
||||
}
|
||||
|
||||
if (node.type === 'property') {
|
||||
|
@ -199,16 +216,16 @@ function adjustEndNodeCss(node: Node | null, pos: vscode.Position, rootNode: Sty
|
|||
}
|
||||
|
||||
const rule = <Rule>node;
|
||||
if (pos.isEqual(rule.contentEndToken.end) || !rule.firstChild) {
|
||||
if (offset === rule.contentEndToken.end || !rule.firstChild) {
|
||||
return rule.end;
|
||||
}
|
||||
|
||||
if (pos.isAfter(rule.children[rule.children.length - 1].end)) {
|
||||
return pos;
|
||||
if (offset > rule.children[rule.children.length - 1].end) {
|
||||
return offset;
|
||||
}
|
||||
|
||||
let newEndNode = rule.children[rule.children.length - 1];
|
||||
while (newEndNode.previousSibling && pos.isBefore(newEndNode.start)) {
|
||||
while (newEndNode.previousSibling && offset < newEndNode.start) {
|
||||
newEndNode = newEndNode.previousSibling;
|
||||
}
|
||||
|
||||
|
|
89
extensions/emmet/src/typings/EmmetFlatNode.d.ts
vendored
Normal file
89
extensions/emmet/src/typings/EmmetFlatNode.d.ts
vendored
Normal file
|
@ -0,0 +1,89 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
declare module 'EmmetFlatNode' {
|
||||
export interface Node {
|
||||
start: number
|
||||
end: number
|
||||
type: string
|
||||
parent: Node | undefined
|
||||
firstChild: Node | undefined
|
||||
nextSibling: Node | undefined
|
||||
previousSibling: Node | undefined
|
||||
children: Node[]
|
||||
}
|
||||
|
||||
export interface Token {
|
||||
start: number
|
||||
end: number
|
||||
stream: BufferStream
|
||||
toString(): string
|
||||
}
|
||||
|
||||
export interface CssToken extends Token {
|
||||
size: number
|
||||
item(number: number): any
|
||||
type: string
|
||||
}
|
||||
|
||||
export interface HtmlToken extends Token {
|
||||
value: string
|
||||
}
|
||||
|
||||
export interface Attribute extends Token {
|
||||
name: Token
|
||||
value: Token
|
||||
}
|
||||
|
||||
export interface HtmlNode extends Node {
|
||||
name: string
|
||||
open: Token | undefined
|
||||
close: Token | undefined
|
||||
parent: HtmlNode | undefined
|
||||
firstChild: HtmlNode | undefined
|
||||
nextSibling: HtmlNode | undefined
|
||||
previousSibling: HtmlNode | undefined
|
||||
children: HtmlNode[]
|
||||
attributes: Attribute[]
|
||||
}
|
||||
|
||||
export interface CssNode extends Node {
|
||||
name: string
|
||||
parent: CssNode | undefined
|
||||
firstChild: CssNode | undefined
|
||||
nextSibling: CssNode | undefined
|
||||
previousSibling: CssNode | undefined
|
||||
children: CssNode[]
|
||||
}
|
||||
|
||||
export interface Rule extends CssNode {
|
||||
selectorToken: Token
|
||||
contentStartToken: Token
|
||||
contentEndToken: Token
|
||||
}
|
||||
|
||||
export interface Property extends CssNode {
|
||||
valueToken: Token
|
||||
separator: string
|
||||
parent: Rule
|
||||
terminatorToken: Token
|
||||
separatorToken: Token
|
||||
value: string
|
||||
}
|
||||
|
||||
export interface Stylesheet extends Node {
|
||||
comments: Token[]
|
||||
}
|
||||
|
||||
export interface BufferStream {
|
||||
peek(): number
|
||||
next(): number
|
||||
backUp(n: number): number
|
||||
current(): string
|
||||
substring(from: number, to: number): string
|
||||
eat(match: any): boolean
|
||||
eatWhile(match: any): boolean
|
||||
}
|
||||
}
|
|
@ -5,8 +5,10 @@
|
|||
|
||||
declare module '@emmetio/css-parser' {
|
||||
import { BufferStream, Stylesheet } from 'EmmetNode';
|
||||
import { Stylesheet as FlatStylesheet } from 'EmmetFlatNode';
|
||||
|
||||
function parseStylesheet(stream: BufferStream): Stylesheet;
|
||||
function parseStylesheet(stream: string): FlatStylesheet;
|
||||
|
||||
export default parseStylesheet;
|
||||
}
|
||||
|
|
|
@ -5,8 +5,10 @@
|
|||
|
||||
declare module '@emmetio/html-matcher' {
|
||||
import { BufferStream, HtmlNode } from 'EmmetNode';
|
||||
import { HtmlNode as HtmlFlatNode } from 'EmmetFlatNode';
|
||||
|
||||
function parse(stream: BufferStream): HtmlNode;
|
||||
function parse(stream: string): HtmlFlatNode;
|
||||
|
||||
export default parse;
|
||||
}
|
||||
|
|
|
@ -5,26 +5,26 @@
|
|||
|
||||
// Based on @sergeche's work on the emmet plugin for atom
|
||||
|
||||
import { TextEditor, Range, Position, window, TextEdit } from 'vscode';
|
||||
import { TextEditor, Position, window, TextEdit } from 'vscode';
|
||||
import * as path from 'path';
|
||||
import { getImageSize } from './imageSizeHelper';
|
||||
import { parseDocument, getNode, iterateCSSToken, getCssPropertyFromRule, isStyleSheet, validate } from './util';
|
||||
import { HtmlNode, CssToken, HtmlToken, Attribute, Property } from 'EmmetNode';
|
||||
import { getFlatNode, iterateCSSToken, getCssPropertyFromRule, isStyleSheet, validate, offsetRangeToVsRange } from './util';
|
||||
import { HtmlNode, CssToken, HtmlToken, Attribute, Property } from 'EmmetFlatNode';
|
||||
import { locateFile } from './locateFile';
|
||||
import parseStylesheet from '@emmetio/css-parser';
|
||||
import { DocumentStreamReader } from './bufferStream';
|
||||
import { getRootNode } from './parseDocument';
|
||||
|
||||
/**
|
||||
* Updates size of context image in given editor
|
||||
*/
|
||||
export function updateImageSize() {
|
||||
export function updateImageSize(): Promise<boolean> | undefined {
|
||||
if (!validate() || !window.activeTextEditor) {
|
||||
return;
|
||||
}
|
||||
const editor = window.activeTextEditor;
|
||||
|
||||
let allUpdatesPromise = editor.selections.reverse().map(selection => {
|
||||
let position = selection.isReversed ? selection.active : selection.anchor;
|
||||
const allUpdatesPromise = editor.selections.reverse().map(selection => {
|
||||
const position = selection.isReversed ? selection.active : selection.anchor;
|
||||
if (!isStyleSheet(editor.document.languageId)) {
|
||||
return updateImageSizeHTML(editor, position);
|
||||
} else {
|
||||
|
@ -71,15 +71,19 @@ function updateImageSizeHTML(editor: TextEditor, position: Position): Promise<Te
|
|||
|
||||
function updateImageSizeStyleTag(editor: TextEditor, position: Position): Promise<TextEdit[]> {
|
||||
const getPropertyInsiderStyleTag = (editor: TextEditor): Property | null => {
|
||||
const rootNode = parseDocument(editor.document);
|
||||
const currentNode = <HtmlNode>getNode(rootNode, position, true);
|
||||
const document = editor.document;
|
||||
const rootNode = getRootNode(document, true);
|
||||
const offset = document.offsetAt(position);
|
||||
const currentNode = <HtmlNode>getFlatNode(rootNode, offset, true);
|
||||
if (currentNode && currentNode.name === 'style'
|
||||
&& currentNode.open.end.isBefore(position)
|
||||
&& currentNode.close.start.isAfter(position)) {
|
||||
let buffer = new DocumentStreamReader(editor.document, currentNode.open.end, new Range(currentNode.open.end, currentNode.close.start));
|
||||
let rootNode = parseStylesheet(buffer);
|
||||
const node = getNode(rootNode, position, true);
|
||||
return (node && node.type === 'property') ? <Property>node : null;
|
||||
&& currentNode.open && currentNode.close
|
||||
&& currentNode.open.end < offset
|
||||
&& currentNode.close.start > offset) {
|
||||
const buffer = ' '.repeat(currentNode.open.end) +
|
||||
document.getText().substring(currentNode.open.end, currentNode.close.start);
|
||||
const innerRootNode = parseStylesheet(buffer);
|
||||
const innerNode = getFlatNode(innerRootNode, offset, true);
|
||||
return (innerNode && innerNode.type === 'property') ? <Property>innerNode : null;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
@ -96,7 +100,7 @@ function updateImageSizeCSSFile(editor: TextEditor, position: Position): Promise
|
|||
*/
|
||||
function updateImageSizeCSS(editor: TextEditor, position: Position, fetchNode: (editor: TextEditor, position: Position) => Property | null): Promise<TextEdit[]> {
|
||||
const node = fetchNode(editor, position);
|
||||
const src = node && getImageSrcCSS(node, position);
|
||||
const src = node && getImageSrcCSS(editor, node, position);
|
||||
|
||||
if (!src) {
|
||||
return Promise.reject(new Error('No valid image source'));
|
||||
|
@ -108,7 +112,7 @@ function updateImageSizeCSS(editor: TextEditor, position: Position, fetchNode: (
|
|||
// since this action is asynchronous, we have to ensure that editor wasn’t
|
||||
// changed and user didn’t moved caret outside <img> node
|
||||
const prop = fetchNode(editor, position);
|
||||
if (prop && getImageSrcCSS(prop, position) === src) {
|
||||
if (prop && getImageSrcCSS(editor, prop, position) === src) {
|
||||
return updateCSSNode(editor, prop, size.width, size.height);
|
||||
}
|
||||
return [];
|
||||
|
@ -121,8 +125,10 @@ function updateImageSizeCSS(editor: TextEditor, position: Position, fetchNode: (
|
|||
* be found
|
||||
*/
|
||||
function getImageHTMLNode(editor: TextEditor, position: Position): HtmlNode | null {
|
||||
const rootNode = parseDocument(editor.document);
|
||||
const node = <HtmlNode>getNode(rootNode, position, true);
|
||||
const document = editor.document;
|
||||
const rootNode = getRootNode(document, true);
|
||||
const offset = document.offsetAt(position);
|
||||
const node = <HtmlNode>getFlatNode(rootNode, offset, true);
|
||||
|
||||
return node && node.name.toLowerCase() === 'img' ? node : null;
|
||||
}
|
||||
|
@ -132,8 +138,10 @@ function getImageHTMLNode(editor: TextEditor, position: Position): HtmlNode | nu
|
|||
* be found
|
||||
*/
|
||||
function getImageCSSNode(editor: TextEditor, position: Position): Property | null {
|
||||
const rootNode = parseDocument(editor.document);
|
||||
const node = getNode(rootNode, position, true);
|
||||
const document = editor.document;
|
||||
const rootNode = getRootNode(document, true);
|
||||
const offset = document.offsetAt(position);
|
||||
const node = getFlatNode(rootNode, offset, true);
|
||||
return node && node.type === 'property' ? <Property>node : null;
|
||||
}
|
||||
|
||||
|
@ -152,11 +160,11 @@ function getImageSrcHTML(node: HtmlNode): string | undefined {
|
|||
/**
|
||||
* Returns image source from given `url()` token
|
||||
*/
|
||||
function getImageSrcCSS(node: Property | undefined, position: Position): string | undefined {
|
||||
function getImageSrcCSS(editor: TextEditor, node: Property | undefined, position: Position): string | undefined {
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
const urlToken = findUrlToken(node, position);
|
||||
const urlToken = findUrlToken(editor, node, position);
|
||||
if (!urlToken) {
|
||||
return;
|
||||
}
|
||||
|
@ -174,7 +182,12 @@ function getImageSrcCSS(node: Property | undefined, position: Position): string
|
|||
* Updates size of given HTML node
|
||||
*/
|
||||
function updateHTMLTag(editor: TextEditor, node: HtmlNode, width: number, height: number): TextEdit[] {
|
||||
const document = editor.document;
|
||||
const srcAttr = getAttribute(node, 'src');
|
||||
if (!srcAttr) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const widthAttr = getAttribute(node, 'width');
|
||||
const heightAttr = getAttribute(node, 'height');
|
||||
const quote = getAttributeQuote(editor, srcAttr);
|
||||
|
@ -186,15 +199,15 @@ function updateHTMLTag(editor: TextEditor, node: HtmlNode, width: number, height
|
|||
if (!widthAttr) {
|
||||
textToAdd += ` width=${quote}${width}${quote}`;
|
||||
} else {
|
||||
edits.push(new TextEdit(new Range(widthAttr.value.start, widthAttr.value.end), String(width)));
|
||||
edits.push(new TextEdit(offsetRangeToVsRange(document, widthAttr.value.start, widthAttr.value.end), String(width)));
|
||||
}
|
||||
if (!heightAttr) {
|
||||
textToAdd += ` height=${quote}${height}${quote}`;
|
||||
} else {
|
||||
edits.push(new TextEdit(new Range(heightAttr.value.start, heightAttr.value.end), String(height)));
|
||||
edits.push(new TextEdit(offsetRangeToVsRange(document, heightAttr.value.start, heightAttr.value.end), String(height)));
|
||||
}
|
||||
if (textToAdd) {
|
||||
edits.push(new TextEdit(new Range(endOfAttributes, endOfAttributes), textToAdd));
|
||||
edits.push(new TextEdit(offsetRangeToVsRange(document, endOfAttributes, endOfAttributes), textToAdd));
|
||||
}
|
||||
|
||||
return edits;
|
||||
|
@ -204,6 +217,7 @@ function updateHTMLTag(editor: TextEditor, node: HtmlNode, width: number, height
|
|||
* Updates size of given CSS rule
|
||||
*/
|
||||
function updateCSSNode(editor: TextEditor, srcProp: Property, width: number, height: number): TextEdit[] {
|
||||
const document = editor.document;
|
||||
const rule = srcProp.parent;
|
||||
const widthProp = getCssPropertyFromRule(rule, 'width');
|
||||
const heightProp = getCssPropertyFromRule(rule, 'height');
|
||||
|
@ -214,22 +228,22 @@ function updateCSSNode(editor: TextEditor, srcProp: Property, width: number, hei
|
|||
|
||||
let edits: TextEdit[] = [];
|
||||
if (!srcProp.terminatorToken) {
|
||||
edits.push(new TextEdit(new Range(srcProp.end, srcProp.end), ';'));
|
||||
edits.push(new TextEdit(offsetRangeToVsRange(document, srcProp.end, srcProp.end), ';'));
|
||||
}
|
||||
|
||||
let textToAdd = '';
|
||||
if (!widthProp) {
|
||||
textToAdd += `${before}width${separator}${width}px;`;
|
||||
} else {
|
||||
edits.push(new TextEdit(new Range(widthProp.valueToken.start, widthProp.valueToken.end), `${width}px`));
|
||||
edits.push(new TextEdit(offsetRangeToVsRange(document, widthProp.valueToken.start, widthProp.valueToken.end), `${width}px`));
|
||||
}
|
||||
if (!heightProp) {
|
||||
textToAdd += `${before}height${separator}${height}px;`;
|
||||
} else {
|
||||
edits.push(new TextEdit(new Range(heightProp.valueToken.start, heightProp.valueToken.end), `${height}px`));
|
||||
edits.push(new TextEdit(offsetRangeToVsRange(document, heightProp.valueToken.start, heightProp.valueToken.end), `${height}px`));
|
||||
}
|
||||
if (textToAdd) {
|
||||
edits.push(new TextEdit(new Range(srcProp.end, srcProp.end), textToAdd));
|
||||
edits.push(new TextEdit(offsetRangeToVsRange(document, srcProp.end, srcProp.end), textToAdd));
|
||||
}
|
||||
|
||||
return edits;
|
||||
|
@ -238,9 +252,9 @@ function updateCSSNode(editor: TextEditor, srcProp: Property, width: number, hei
|
|||
/**
|
||||
* Returns attribute object with `attrName` name from given HTML node
|
||||
*/
|
||||
function getAttribute(node: HtmlNode, attrName: string): Attribute {
|
||||
function getAttribute(node: HtmlNode, attrName: string): Attribute | undefined {
|
||||
attrName = attrName.toLowerCase();
|
||||
return node && (node.open as any).attributes.find((attr: any) => attr.name.value.toLowerCase() === attrName);
|
||||
return node && node.attributes.find(attr => attr.name.toString().toLowerCase() === attrName);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -248,18 +262,20 @@ function getAttribute(node: HtmlNode, attrName: string): Attribute {
|
|||
* string if attribute wasn’t quoted
|
||||
|
||||
*/
|
||||
function getAttributeQuote(editor: TextEditor, attr: any): string {
|
||||
const range = new Range(attr.value ? attr.value.end : attr.end, attr.end);
|
||||
return range.isEmpty ? '' : editor.document.getText(range);
|
||||
function getAttributeQuote(editor: TextEditor, attr: Attribute): string {
|
||||
const begin = attr.value ? attr.value.end : attr.end;
|
||||
const end = attr.end;
|
||||
return begin === end ? '' : editor.document.getText().substring(begin, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds 'url' token for given `pos` point in given CSS property `node`
|
||||
*/
|
||||
function findUrlToken(node: Property, pos: Position): CssToken | undefined {
|
||||
function findUrlToken(editor: TextEditor, node: Property, pos: Position): CssToken | undefined {
|
||||
const offset = editor.document.offsetAt(pos);
|
||||
for (let i = 0, il = (node as any).parsedValue.length, url; i < il; i++) {
|
||||
iterateCSSToken((node as any).parsedValue[i], (token: CssToken) => {
|
||||
if (token.type === 'url' && token.start.isBeforeOrEqual(pos) && token.end.isAfterOrEqual(pos)) {
|
||||
if (token.type === 'url' && token.start <= offset && token.end >= offset) {
|
||||
url = token;
|
||||
return false;
|
||||
}
|
||||
|
@ -279,9 +295,9 @@ function findUrlToken(node: Property, pos: Position): CssToken | undefined {
|
|||
function getPropertyDelimitor(editor: TextEditor, node: Property): string {
|
||||
let anchor;
|
||||
if (anchor = (node.previousSibling || node.parent.contentStartToken)) {
|
||||
return editor.document.getText(new Range(anchor.end, node.start));
|
||||
return editor.document.getText().substring(anchor.end, node.start);
|
||||
} else if (anchor = (node.nextSibling || node.parent.contentEndToken)) {
|
||||
return editor.document.getText(new Range(node.end, anchor.start));
|
||||
return editor.document.getText().substring(node.end, anchor.start);
|
||||
}
|
||||
|
||||
return '';
|
||||
|
|
|
@ -4,8 +4,9 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { getHtmlNodeLS, toLSTextDocument, validate } from './util';
|
||||
import { TextDocument as LSTextDocument, Node as LSNode } from 'vscode-html-languageservice';
|
||||
import { getHtmlFlatNode, validate } from './util';
|
||||
import { HtmlNode as HtmlFlatNode } from 'EmmetFlatNode';
|
||||
import { getRootNode } from './parseDocument';
|
||||
|
||||
export function updateTag(tagName: string): Thenable<boolean> | undefined {
|
||||
if (!validate(false) || !vscode.window.activeTextEditor) {
|
||||
|
@ -13,9 +14,15 @@ export function updateTag(tagName: string): Thenable<boolean> | undefined {
|
|||
}
|
||||
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
const document = editor.document;
|
||||
const rootNode = <HtmlFlatNode>getRootNode(document, true);
|
||||
if (!rootNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
const rangesToUpdate = editor.selections.reverse()
|
||||
.reduce<vscode.Range[]>((prev, selection) =>
|
||||
prev.concat(getRangesToUpdate(editor, selection)), []);
|
||||
prev.concat(getRangesToUpdate(document, selection, rootNode)), []);
|
||||
|
||||
return editor.edit(editBuilder => {
|
||||
rangesToUpdate.forEach(range => {
|
||||
|
@ -24,34 +31,25 @@ export function updateTag(tagName: string): Thenable<boolean> | undefined {
|
|||
});
|
||||
}
|
||||
|
||||
function getPositionFromOffset(offset: number | undefined, document: LSTextDocument): vscode.Position | undefined {
|
||||
if (offset === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
const pos = document.positionAt(offset);
|
||||
return new vscode.Position(pos.line, pos.character);
|
||||
}
|
||||
|
||||
function getRangesFromNode(node: LSNode, document: LSTextDocument): vscode.Range[] {
|
||||
const start = getPositionFromOffset(node.start, document)!;
|
||||
const startTagEnd = getPositionFromOffset(node.startTagEnd, document);
|
||||
const end = getPositionFromOffset(node.end, document)!;
|
||||
const endTagStart = getPositionFromOffset(node.endTagStart, document);
|
||||
|
||||
function getRangesFromNode(node: HtmlFlatNode, document: vscode.TextDocument): vscode.Range[] {
|
||||
let ranges: vscode.Range[] = [];
|
||||
if (startTagEnd) {
|
||||
if (node.open) {
|
||||
const start = document.positionAt(node.open.start);
|
||||
ranges.push(new vscode.Range(start.translate(0, 1),
|
||||
start.translate(0, 1).translate(0, node.tag!.length ?? 0)));
|
||||
start.translate(0, 1).translate(0, node.name.length)));
|
||||
}
|
||||
if (endTagStart) {
|
||||
if (node.close) {
|
||||
const endTagStart = document.positionAt(node.close.start);
|
||||
const end = document.positionAt(node.close.end);
|
||||
ranges.push(new vscode.Range(endTagStart.translate(0, 2), end.translate(0, -1)));
|
||||
}
|
||||
return ranges;
|
||||
}
|
||||
|
||||
function getRangesToUpdate(editor: vscode.TextEditor, selection: vscode.Selection): vscode.Range[] {
|
||||
const document = toLSTextDocument(editor.document);
|
||||
const nodeToUpdate = getHtmlNodeLS(document, selection.start, true);
|
||||
function getRangesToUpdate(document: vscode.TextDocument, selection: vscode.Selection, rootNode: HtmlFlatNode): vscode.Range[] {
|
||||
const documentText = document.getText();
|
||||
const offset = document.offsetAt(selection.start);
|
||||
const nodeToUpdate = getHtmlFlatNode(documentText, rootNode, offset, true);
|
||||
if (!nodeToUpdate) {
|
||||
return [];
|
||||
}
|
||||
|
|
|
@ -6,14 +6,13 @@
|
|||
import * as vscode from 'vscode';
|
||||
import parse from '@emmetio/html-matcher';
|
||||
import parseStylesheet from '@emmetio/css-parser';
|
||||
import { Node, HtmlNode, CssToken, Property, Rule, Stylesheet } from 'EmmetNode';
|
||||
import { Node as FlatNode, HtmlNode as HtmlFlatNode, Property as FlatProperty, Rule as FlatRule, CssToken as FlatCssToken, Stylesheet as FlatStylesheet } from 'EmmetFlatNode';
|
||||
import { DocumentStreamReader } from './bufferStream';
|
||||
import * as EmmetHelper from 'vscode-emmet-helper';
|
||||
import { Position as LSPosition, getLanguageService as getLanguageServiceInternal, LanguageService, LanguageServiceOptions, TextDocument as LSTextDocument, Node as LSNode } from 'vscode-html-languageservice';
|
||||
import { parseMarkupDocument } from './parseMarkupDocument';
|
||||
import { TextDocument as LSTextDocument } from 'vscode-languageserver-textdocument';
|
||||
import { getRootNode } from './parseDocument';
|
||||
|
||||
let _emmetHelper: typeof EmmetHelper;
|
||||
let _languageService: LanguageService;
|
||||
let _currentExtensionsPath: string | undefined = undefined;
|
||||
|
||||
let _homeDir: vscode.Uri | undefined;
|
||||
|
@ -33,16 +32,6 @@ export function getEmmetHelper() {
|
|||
return _emmetHelper;
|
||||
}
|
||||
|
||||
export function getLanguageService(options?: LanguageServiceOptions): LanguageService {
|
||||
if (!options) {
|
||||
if (!_languageService) {
|
||||
_languageService = getLanguageServiceInternal();
|
||||
}
|
||||
return _languageService;
|
||||
}
|
||||
return getLanguageServiceInternal(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update Emmet Helper to use user snippets from the extensionsPath setting
|
||||
*/
|
||||
|
@ -148,21 +137,6 @@ export function getEmmetMode(language: string, excludedLanguages: string[]): str
|
|||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given document using emmet parsing modules
|
||||
*/
|
||||
export function parseDocument(document: vscode.TextDocument, showError: boolean = true): Node | undefined {
|
||||
let parseContent = isStyleSheet(document.languageId) ? parseStylesheet : parse;
|
||||
try {
|
||||
return parseContent(new DocumentStreamReader(document));
|
||||
} catch (e) {
|
||||
if (showError) {
|
||||
vscode.window.showErrorMessage('Emmet: Failed to parse the file');
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const closeBrace = 125;
|
||||
const openBrace = 123;
|
||||
const slash = 47;
|
||||
|
@ -174,39 +148,41 @@ const star = 42;
|
|||
* @param document vscode.TextDocument
|
||||
* @param position vscode.Position
|
||||
*/
|
||||
export function parsePartialStylesheet(document: vscode.TextDocument, position: vscode.Position): Stylesheet | undefined {
|
||||
export function parsePartialStylesheet(document: vscode.TextDocument, position: vscode.Position): FlatStylesheet | undefined {
|
||||
const isCSS = document.languageId === 'css';
|
||||
let startPosition = new vscode.Position(0, 0);
|
||||
let endPosition = new vscode.Position(document.lineCount - 1, document.lineAt(document.lineCount - 1).text.length);
|
||||
const limitCharacter = document.offsetAt(position) - 5000;
|
||||
const limitPosition = limitCharacter > 0 ? document.positionAt(limitCharacter) : startPosition;
|
||||
const stream = new DocumentStreamReader(document, position);
|
||||
const positionOffset = document.offsetAt(position);
|
||||
let startOffset = 0;
|
||||
let endOffset = document.getText().length;
|
||||
const limitCharacter = positionOffset - 5000;
|
||||
const limitOffset = limitCharacter > 0 ? limitCharacter : startOffset;
|
||||
const stream = new DocumentStreamReader(document, positionOffset);
|
||||
|
||||
function findOpeningCommentBeforePosition(pos: vscode.Position): vscode.Position | undefined {
|
||||
let text = document.getText(new vscode.Range(0, 0, pos.line, pos.character));
|
||||
function findOpeningCommentBeforePosition(pos: number): number | undefined {
|
||||
const text = document.getText().substring(0, pos);
|
||||
let offset = text.lastIndexOf('/*');
|
||||
if (offset === -1) {
|
||||
return;
|
||||
}
|
||||
return document.positionAt(offset);
|
||||
return offset;
|
||||
}
|
||||
|
||||
function findClosingCommentAfterPosition(pos: vscode.Position): vscode.Position | undefined {
|
||||
let text = document.getText(new vscode.Range(pos.line, pos.character, document.lineCount - 1, document.lineAt(document.lineCount - 1).text.length));
|
||||
function findClosingCommentAfterPosition(pos: number): number | undefined {
|
||||
const text = document.getText().substring(pos);
|
||||
let offset = text.indexOf('*/');
|
||||
if (offset === -1) {
|
||||
return;
|
||||
}
|
||||
offset += 2 + document.offsetAt(pos);
|
||||
return document.positionAt(offset);
|
||||
offset += 2 + pos;
|
||||
return offset;
|
||||
}
|
||||
|
||||
function consumeLineCommentBackwards() {
|
||||
if (!isCSS && currentLine !== stream.pos.line) {
|
||||
currentLine = stream.pos.line;
|
||||
let startLineComment = document.lineAt(currentLine).text.indexOf('//');
|
||||
const posLineNumber = document.positionAt(stream.pos).line;
|
||||
if (!isCSS && currentLine !== posLineNumber) {
|
||||
currentLine = posLineNumber;
|
||||
const startLineComment = document.lineAt(currentLine).text.indexOf('//');
|
||||
if (startLineComment > -1) {
|
||||
stream.pos = new vscode.Position(currentLine, startLineComment);
|
||||
stream.pos = document.offsetAt(new vscode.Position(currentLine, startLineComment));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -214,7 +190,7 @@ export function parsePartialStylesheet(document: vscode.TextDocument, position:
|
|||
function consumeBlockCommentBackwards() {
|
||||
if (stream.peek() === slash) {
|
||||
if (stream.backUp(1) === star) {
|
||||
stream.pos = findOpeningCommentBeforePosition(stream.pos) || startPosition;
|
||||
stream.pos = findOpeningCommentBeforePosition(stream.pos) ?? startOffset;
|
||||
} else {
|
||||
stream.next();
|
||||
}
|
||||
|
@ -224,9 +200,10 @@ export function parsePartialStylesheet(document: vscode.TextDocument, position:
|
|||
function consumeCommentForwards() {
|
||||
if (stream.eat(slash)) {
|
||||
if (stream.eat(slash) && !isCSS) {
|
||||
stream.pos = new vscode.Position(stream.pos.line + 1, 0);
|
||||
const posLineNumber = document.positionAt(stream.pos).line;
|
||||
stream.pos = document.offsetAt(new vscode.Position(posLineNumber + 1, 0));
|
||||
} else if (stream.eat(star)) {
|
||||
stream.pos = findClosingCommentAfterPosition(stream.pos) || endPosition;
|
||||
stream.pos = findClosingCommentAfterPosition(stream.pos) ?? endOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -241,10 +218,10 @@ export function parsePartialStylesheet(document: vscode.TextDocument, position:
|
|||
}
|
||||
|
||||
if (!stream.eof()) {
|
||||
endPosition = stream.pos;
|
||||
endOffset = stream.pos;
|
||||
}
|
||||
|
||||
stream.pos = position;
|
||||
stream.pos = positionOffset;
|
||||
let openBracesToFind = 1;
|
||||
let currentLine = position.line;
|
||||
let exit = false;
|
||||
|
@ -260,7 +237,7 @@ export function parsePartialStylesheet(document: vscode.TextDocument, position:
|
|||
case closeBrace:
|
||||
if (isCSS) {
|
||||
stream.next();
|
||||
startPosition = stream.pos;
|
||||
startOffset = stream.pos;
|
||||
exit = true;
|
||||
} else {
|
||||
openBracesToFind++;
|
||||
|
@ -273,17 +250,17 @@ export function parsePartialStylesheet(document: vscode.TextDocument, position:
|
|||
break;
|
||||
}
|
||||
|
||||
if (position.line - stream.pos.line > 100 || stream.pos.isBeforeOrEqual(limitPosition)) {
|
||||
if (position.line - document.positionAt(stream.pos).line > 100
|
||||
|| stream.pos <= limitOffset) {
|
||||
exit = true;
|
||||
}
|
||||
}
|
||||
|
||||
// We are at an opening brace. We need to include its selector.
|
||||
currentLine = stream.pos.line;
|
||||
currentLine = document.positionAt(stream.pos).line;
|
||||
openBracesToFind = 0;
|
||||
let foundSelector = false;
|
||||
while (!exit && !stream.sof() && !foundSelector && openBracesToFind >= 0) {
|
||||
|
||||
consumeLineCommentBackwards();
|
||||
|
||||
const ch = stream.backUp(1);
|
||||
|
@ -309,12 +286,13 @@ export function parsePartialStylesheet(document: vscode.TextDocument, position:
|
|||
}
|
||||
|
||||
if (!stream.sof() && foundSelector) {
|
||||
startPosition = stream.pos;
|
||||
startOffset = stream.pos;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return parseStylesheet(new DocumentStreamReader(document, startPosition, new vscode.Range(startPosition, endPosition)));
|
||||
const buffer = ' '.repeat(startOffset) + document.getText().substring(startOffset, endOffset);
|
||||
return parseStylesheet(buffer);
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
|
@ -323,94 +301,70 @@ export function parsePartialStylesheet(document: vscode.TextDocument, position:
|
|||
/**
|
||||
* Returns node corresponding to given position in the given root node
|
||||
*/
|
||||
export function getNode(root: Node | undefined, position: vscode.Position, includeNodeBoundary: boolean) {
|
||||
export function getFlatNode(root: FlatNode | undefined, offset: number, includeNodeBoundary: boolean): FlatNode | undefined {
|
||||
if (!root) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
let currentNode = root.firstChild;
|
||||
let foundNode: Node | null = null;
|
||||
|
||||
while (currentNode) {
|
||||
const nodeStart: vscode.Position = currentNode.start;
|
||||
const nodeEnd: vscode.Position = currentNode.end;
|
||||
if ((nodeStart.isBefore(position) && nodeEnd.isAfter(position))
|
||||
|| (includeNodeBoundary && (nodeStart.isBeforeOrEqual(position) && nodeEnd.isAfterOrEqual(position)))) {
|
||||
|
||||
foundNode = currentNode;
|
||||
// Dig deeper
|
||||
currentNode = currentNode.firstChild;
|
||||
} else {
|
||||
currentNode = currentNode.nextSibling;
|
||||
function getFlatNodeChild(child: FlatNode | undefined): FlatNode | undefined {
|
||||
if (!child) {
|
||||
return;
|
||||
}
|
||||
const nodeStart = child.start;
|
||||
const nodeEnd = child.end;
|
||||
if ((nodeStart < offset && nodeEnd > offset)
|
||||
|| (includeNodeBoundary && nodeStart <= offset && nodeEnd >= offset)) {
|
||||
return getFlatNodeChildren(child.children) ?? child;
|
||||
}
|
||||
else if ('close' in <any>child) {
|
||||
// We have an HTML node in this case.
|
||||
// In case this node is an invalid unpaired HTML node,
|
||||
// we still want to search its children
|
||||
const htmlChild = <HtmlFlatNode>child;
|
||||
if (htmlChild.open && !htmlChild.close) {
|
||||
return getFlatNodeChildren(htmlChild.children);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
return foundNode;
|
||||
function getFlatNodeChildren(children: FlatNode[]): FlatNode | undefined {
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
const foundChild = getFlatNodeChild(children[i]);
|
||||
if (foundChild) {
|
||||
return foundChild;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
return getFlatNodeChildren(root.children);
|
||||
}
|
||||
|
||||
export const allowedMimeTypesInScriptTag = ['text/html', 'text/plain', 'text/x-template', 'text/template', 'text/ng-template'];
|
||||
|
||||
/**
|
||||
* Returns HTML node corresponding to given position in the given root node
|
||||
* Finds the HTML node within an HTML document at a given position
|
||||
* If position is inside a script tag of type template, then it will be parsed to find the inner HTML node as well
|
||||
*/
|
||||
export function getHtmlNode(document: vscode.TextDocument, root: Node | undefined, position: vscode.Position, includeNodeBoundary: boolean): HtmlNode | undefined {
|
||||
let currentNode = <HtmlNode>getNode(root, position, includeNodeBoundary);
|
||||
export function getHtmlFlatNode(documentText: string, root: FlatNode | undefined, offset: number, includeNodeBoundary: boolean): HtmlFlatNode | undefined {
|
||||
const currentNode: HtmlFlatNode | undefined = <HtmlFlatNode | undefined>getFlatNode(root, offset, includeNodeBoundary);
|
||||
if (!currentNode) { return; }
|
||||
|
||||
const isTemplateScript = currentNode.name === 'script' &&
|
||||
(currentNode.attributes &&
|
||||
currentNode.attributes.some(x => x.name.toString() === 'type'
|
||||
&& allowedMimeTypesInScriptTag.indexOf(x.value.toString()) > -1));
|
||||
|
||||
if (isTemplateScript && currentNode.close &&
|
||||
(position.isAfter(currentNode.open.end) && position.isBefore(currentNode.close.start))) {
|
||||
|
||||
let buffer = new DocumentStreamReader(document, currentNode.open.end, new vscode.Range(currentNode.open.end, currentNode.close.start));
|
||||
|
||||
try {
|
||||
let scriptInnerNodes = parse(buffer);
|
||||
currentNode = <HtmlNode>getNode(scriptInnerNodes, position, includeNodeBoundary) || currentNode;
|
||||
} catch (e) { }
|
||||
}
|
||||
|
||||
return currentNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the HTML node within an HTML document at a given position
|
||||
*/
|
||||
export function getHtmlNodeLS(document: LSTextDocument, position: vscode.Position, includeNodeBoundary: boolean): LSNode | undefined {
|
||||
const documentText = document.getText();
|
||||
const offset = document.offsetAt(position);
|
||||
let selectionStartOffset = offset;
|
||||
if (includeNodeBoundary && documentText.charAt(offset) === '<') {
|
||||
selectionStartOffset++;
|
||||
}
|
||||
else if (includeNodeBoundary && documentText.charAt(offset) === '>') {
|
||||
selectionStartOffset--;
|
||||
}
|
||||
return getHtmlNodeLSInternal(document, selectionStartOffset);
|
||||
}
|
||||
|
||||
function getHtmlNodeLSInternal(document: LSTextDocument, offset: number, isInTemplateNode: boolean = false): LSNode | undefined {
|
||||
const useCache = !isInTemplateNode;
|
||||
const parsedDocument = parseMarkupDocument(document, useCache);
|
||||
|
||||
const currentNode: LSNode = parsedDocument.findNodeAt(offset);
|
||||
if (!currentNode.tag) { return; }
|
||||
|
||||
const isTemplateScript = isNodeTemplateScriptLS(currentNode);
|
||||
&& allowedMimeTypesInScriptTag.includes(x.value.toString())));
|
||||
if (isTemplateScript
|
||||
&& currentNode.startTagEnd
|
||||
&& offset > currentNode.startTagEnd
|
||||
&& (!currentNode.endTagStart || offset < currentNode.endTagStart)) {
|
||||
&& currentNode.open
|
||||
&& offset > currentNode.open.end
|
||||
&& (!currentNode.close || offset < currentNode.close.start)) {
|
||||
// blank out the rest of the document and search for the node within
|
||||
const documentText = document.getText();
|
||||
const beforePadding = ' '.repeat(currentNode.startTagEnd);
|
||||
const scriptBodyText = beforePadding + documentText.substring(currentNode.startTagEnd, currentNode.endTagStart ?? currentNode.end);
|
||||
const scriptBodyDocument = LSTextDocument.create(document.uri, document.languageId, document.version, scriptBodyText);
|
||||
const scriptBodyNode = getHtmlNodeLSInternal(scriptBodyDocument, offset, true);
|
||||
const beforePadding = ' '.repeat(currentNode.open.end);
|
||||
const endToUse = currentNode.close ? currentNode.close.start : currentNode.end;
|
||||
const scriptBodyText = beforePadding + documentText.substring(currentNode.open.end, endToUse);
|
||||
const innerRoot: HtmlFlatNode = parse(scriptBodyText);
|
||||
const scriptBodyNode = getHtmlFlatNode(scriptBodyText, innerRoot, offset, includeNodeBoundary);
|
||||
if (scriptBodyNode) {
|
||||
scriptBodyNode.parent = currentNode;
|
||||
currentNode.children.push(scriptBodyNode);
|
||||
|
@ -420,55 +374,28 @@ function getHtmlNodeLSInternal(document: LSTextDocument, offset: number, isInTem
|
|||
return currentNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the node is a <script> node
|
||||
* that we want to search through and parse for more potential HTML nodes
|
||||
*/
|
||||
function isNodeTemplateScriptLS(node: LSNode): boolean {
|
||||
if (node.tag === 'script' && node.attributes && node.attributes['type']) {
|
||||
let scriptType = node.attributes['type'];
|
||||
scriptType = scriptType.substring(1, scriptType.length - 1);
|
||||
return allowedMimeTypesInScriptTag.includes(scriptType);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function toVsPosition(position: LSPosition): vscode.Position {
|
||||
return new vscode.Position(position.line, position.character);
|
||||
}
|
||||
|
||||
export function offsetRangeToSelection(document: LSTextDocument, start: number, end: number): vscode.Selection {
|
||||
export function offsetRangeToSelection(document: vscode.TextDocument, start: number, end: number): vscode.Selection {
|
||||
const startPos = document.positionAt(start);
|
||||
const endPos = document.positionAt(end);
|
||||
return new vscode.Selection(toVsPosition(startPos), toVsPosition(endPos));
|
||||
return new vscode.Selection(startPos, endPos);
|
||||
}
|
||||
|
||||
export function offsetRangeToVsRange(document: LSTextDocument, start: number, end: number): vscode.Range {
|
||||
export function offsetRangeToVsRange(document: vscode.TextDocument, start: number, end: number): vscode.Range {
|
||||
const startPos = document.positionAt(start);
|
||||
const endPos = document.positionAt(end);
|
||||
return new vscode.Range(toVsPosition(startPos), toVsPosition(endPos));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns inner range of an html node.
|
||||
*/
|
||||
export function getInnerRange(currentNode: HtmlNode): vscode.Range | undefined {
|
||||
if (!currentNode.close) {
|
||||
return undefined;
|
||||
}
|
||||
return new vscode.Range(currentNode.open.end, currentNode.close.start);
|
||||
return new vscode.Range(startPos, endPos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the deepest non comment node under given node
|
||||
*/
|
||||
export function getDeepestNode(node: Node | undefined): Node | undefined {
|
||||
export function getDeepestFlatNode(node: FlatNode | undefined): FlatNode | undefined {
|
||||
if (!node || !node.children || node.children.length === 0 || !node.children.find(x => x.type !== 'comment')) {
|
||||
return node;
|
||||
}
|
||||
for (let i = node.children.length - 1; i >= 0; i--) {
|
||||
if (node.children[i].type !== 'comment') {
|
||||
return getDeepestNode(node.children[i]);
|
||||
return getDeepestFlatNode(node.children[i]);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
|
@ -550,7 +477,7 @@ export function findPrevWord(propertyValue: string, pos: number): [number | unde
|
|||
return [newSelectionStart, newSelectionEnd];
|
||||
}
|
||||
|
||||
export function getNodesInBetween(node1: Node, node2: Node): Node[] {
|
||||
export function getNodesInBetween(node1: FlatNode, node2: FlatNode): FlatNode[] {
|
||||
// Same node
|
||||
if (sameNodes(node1, node2)) {
|
||||
return [node1];
|
||||
|
@ -559,41 +486,46 @@ export function getNodesInBetween(node1: Node, node2: Node): Node[] {
|
|||
// Not siblings
|
||||
if (!sameNodes(node1.parent, node2.parent)) {
|
||||
// node2 is ancestor of node1
|
||||
if (node2.start.isBefore(node1.start)) {
|
||||
if (node2.start < node1.start) {
|
||||
return [node2];
|
||||
}
|
||||
|
||||
// node1 is ancestor of node2
|
||||
if (node2.start.isBefore(node1.end)) {
|
||||
if (node2.start < node1.end) {
|
||||
return [node1];
|
||||
}
|
||||
|
||||
// Get the highest ancestor of node1 that should be commented
|
||||
while (node1.parent && node1.parent.end.isBefore(node2.start)) {
|
||||
while (node1.parent && node1.parent.end < node2.start) {
|
||||
node1 = node1.parent;
|
||||
}
|
||||
|
||||
// Get the highest ancestor of node2 that should be commented
|
||||
while (node2.parent && node2.parent.start.isAfter(node1.start)) {
|
||||
while (node2.parent && node2.parent.start > node1.start) {
|
||||
node2 = node2.parent;
|
||||
}
|
||||
}
|
||||
|
||||
const siblings: Node[] = [];
|
||||
let currentNode = node1;
|
||||
const siblings: FlatNode[] = [];
|
||||
let currentNode: FlatNode | undefined = node1;
|
||||
const position = node2.end;
|
||||
while (currentNode && position.isAfter(currentNode.start)) {
|
||||
while (currentNode && position > currentNode.start) {
|
||||
siblings.push(currentNode);
|
||||
currentNode = currentNode.nextSibling;
|
||||
}
|
||||
return siblings;
|
||||
}
|
||||
|
||||
export function sameNodes(node1: Node, node2: Node): boolean {
|
||||
export function sameNodes(node1: FlatNode | undefined, node2: FlatNode | undefined): boolean {
|
||||
// return true if they're both undefined
|
||||
if (!node1 && !node2) {
|
||||
return true;
|
||||
}
|
||||
// return false if only one of them is undefined
|
||||
if (!node1 || !node2) {
|
||||
return false;
|
||||
}
|
||||
return (<vscode.Position>node1.start).isEqual(node2.start) && (<vscode.Position>node1.end).isEqual(node2.end);
|
||||
return node1.start === node2.start && node1.end === node2.end;
|
||||
}
|
||||
|
||||
export function getEmmetConfiguration(syntax: string) {
|
||||
|
@ -629,7 +561,7 @@ export function getEmmetConfiguration(syntax: string) {
|
|||
* Itereates by each child, as well as nested child's children, in their order
|
||||
* and invokes `fn` for each. If `fn` function returns `false`, iteration stops
|
||||
*/
|
||||
export function iterateCSSToken(token: CssToken, fn: (x: any) => any): boolean {
|
||||
export function iterateCSSToken(token: FlatCssToken, fn: (x: any) => any): boolean {
|
||||
for (let i = 0, il = token.size; i < il; i++) {
|
||||
if (fn(token.item(i)) === false || iterateCSSToken(token.item(i), fn) === false) {
|
||||
return false;
|
||||
|
@ -641,51 +573,53 @@ export function iterateCSSToken(token: CssToken, fn: (x: any) => any): boolean {
|
|||
/**
|
||||
* Returns `name` CSS property from given `rule`
|
||||
*/
|
||||
export function getCssPropertyFromRule(rule: Rule, name: string): Property | undefined {
|
||||
return rule.children.find(node => node.type === 'property' && node.name === name) as Property;
|
||||
export function getCssPropertyFromRule(rule: FlatRule, name: string): FlatProperty | undefined {
|
||||
return rule.children.find(node => node.type === 'property' && node.name === name) as FlatProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns css property under caret in given editor or `null` if such node cannot
|
||||
* be found
|
||||
*/
|
||||
export function getCssPropertyFromDocument(editor: vscode.TextEditor, position: vscode.Position): Property | null {
|
||||
const rootNode = parseDocument(editor.document);
|
||||
const node = getNode(rootNode, position, true);
|
||||
export function getCssPropertyFromDocument(editor: vscode.TextEditor, position: vscode.Position): FlatProperty | null {
|
||||
const document = editor.document;
|
||||
const rootNode = getRootNode(document, true);
|
||||
const offset = document.offsetAt(position);
|
||||
const node = getFlatNode(rootNode, offset, true);
|
||||
|
||||
if (isStyleSheet(editor.document.languageId)) {
|
||||
return node && node.type === 'property' ? <Property>node : null;
|
||||
return node && node.type === 'property' ? <FlatProperty>node : null;
|
||||
}
|
||||
|
||||
let htmlNode = <HtmlNode>node;
|
||||
const htmlNode = <HtmlFlatNode>node;
|
||||
if (htmlNode
|
||||
&& htmlNode.name === 'style'
|
||||
&& htmlNode.open.end.isBefore(position)
|
||||
&& htmlNode.close.start.isAfter(position)) {
|
||||
let buffer = new DocumentStreamReader(editor.document, htmlNode.start, new vscode.Range(htmlNode.start, htmlNode.end));
|
||||
let rootNode = parseStylesheet(buffer);
|
||||
const node = getNode(rootNode, position, true);
|
||||
return (node && node.type === 'property') ? <Property>node : null;
|
||||
&& htmlNode.open && htmlNode.close
|
||||
&& htmlNode.open.end < offset
|
||||
&& htmlNode.close.start > offset) {
|
||||
const buffer = ' '.repeat(htmlNode.start) +
|
||||
document.getText().substring(htmlNode.start, htmlNode.end);
|
||||
const innerRootNode = parseStylesheet(buffer);
|
||||
const innerNode = getFlatNode(innerRootNode, offset, true);
|
||||
return (innerNode && innerNode.type === 'property') ? <FlatProperty>innerNode : null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
export function getEmbeddedCssNodeIfAny(document: vscode.TextDocument, currentNode: Node | null, position: vscode.Position): Node | undefined {
|
||||
export function getEmbeddedCssNodeIfAny(document: vscode.TextDocument, currentNode: FlatNode | undefined, position: vscode.Position): FlatNode | undefined {
|
||||
if (!currentNode) {
|
||||
return;
|
||||
}
|
||||
const currentHtmlNode = <HtmlNode>currentNode;
|
||||
if (currentHtmlNode && currentHtmlNode.close) {
|
||||
const innerRange = getInnerRange(currentHtmlNode);
|
||||
if (innerRange && innerRange.contains(position)) {
|
||||
const currentHtmlNode = <HtmlFlatNode>currentNode;
|
||||
if (currentHtmlNode && currentHtmlNode.open && currentHtmlNode.close) {
|
||||
const offset = document.offsetAt(position);
|
||||
if (currentHtmlNode.open.end <= offset && offset <= currentHtmlNode.close.start) {
|
||||
if (currentHtmlNode.name === 'style'
|
||||
&& currentHtmlNode.open.end.isBefore(position)
|
||||
&& currentHtmlNode.close.start.isAfter(position)
|
||||
|
||||
) {
|
||||
let buffer = new DocumentStreamReader(document, currentHtmlNode.open.end, new vscode.Range(currentHtmlNode.open.end, currentHtmlNode.close.start));
|
||||
&& currentHtmlNode.open.end < offset
|
||||
&& currentHtmlNode.close.start > offset) {
|
||||
const buffer = ' '.repeat(currentHtmlNode.open.end) + document.getText().substring(currentHtmlNode.open.end, currentHtmlNode.close.start);
|
||||
return parseStylesheet(buffer);
|
||||
}
|
||||
}
|
||||
|
@ -693,34 +627,17 @@ export function getEmbeddedCssNodeIfAny(document: vscode.TextDocument, currentNo
|
|||
return;
|
||||
}
|
||||
|
||||
export function isStyleAttribute(currentNode: Node | null, position: vscode.Position): boolean {
|
||||
export function isStyleAttribute(currentNode: FlatNode | undefined, offset: number): boolean {
|
||||
if (!currentNode) {
|
||||
return false;
|
||||
}
|
||||
const currentHtmlNode = <HtmlNode>currentNode;
|
||||
const currentHtmlNode = <HtmlFlatNode>currentNode;
|
||||
const index = (currentHtmlNode.attributes || []).findIndex(x => x.name.toString() === 'style');
|
||||
if (index === -1) {
|
||||
return false;
|
||||
}
|
||||
const styleAttribute = currentHtmlNode.attributes[index];
|
||||
return position.isAfterOrEqual(styleAttribute.value.start) && position.isBeforeOrEqual(styleAttribute.value.end);
|
||||
}
|
||||
|
||||
|
||||
export function trimQuotes(s: string) {
|
||||
if (s.length <= 1) {
|
||||
return s.replace(/['"]/, '');
|
||||
}
|
||||
|
||||
if (s[0] === `'` || s[0] === `"`) {
|
||||
s = s.slice(1);
|
||||
}
|
||||
|
||||
if (s[s.length - 1] === `'` || s[s.length - 1] === `"`) {
|
||||
s = s.slice(0, -1);
|
||||
}
|
||||
|
||||
return s;
|
||||
return offset >= styleAttribute.value.start && offset <= styleAttribute.value.end;
|
||||
}
|
||||
|
||||
export function isNumber(obj: any): obj is number {
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@emmetio/abbreviation@^2.0.2":
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@emmetio/abbreviation/-/abbreviation-2.0.2.tgz#e26d55d78c00cdeb2ef983e902c7ad55ed0b648d"
|
||||
integrity sha512-kpWg6jyR1YEj/yWceruvDj/fe1BhXqA0tGH3Z2ZiPFo8SDMH4JHg6FChqon5x0CCfLf4zVswrQa0gcZ4XtdRBQ==
|
||||
"@emmetio/abbreviation@^2.2.0":
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@emmetio/abbreviation/-/abbreviation-2.2.0.tgz#9f8dedbdb00e3136d6d37c6415375c82c0bb477f"
|
||||
integrity sha512-NPGVUmnr7cLj4i6MKS4c8NjuoIIJROrruJl/8nXsp2MdbDRHvtfq25foySvv/NbfqTQm+P9JzVLDD9JxGIpvkQ==
|
||||
dependencies:
|
||||
"@emmetio/scanner" "^1.0.0"
|
||||
|
||||
|
@ -54,16 +54,15 @@
|
|||
integrity sha1-Rs/+oRmgoAMxKiHC2bVijLX81EI=
|
||||
|
||||
"@types/node@^12.19.9":
|
||||
version "12.19.9"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.19.9.tgz#990ad687ad8b26ef6dcc34a4f69c33d40c95b679"
|
||||
integrity sha512-yj0DOaQeUrk3nJ0bd3Y5PeDRJ6W0r+kilosLA+dzF3dola/o9hxhMSg2sFvVcA2UHS5JSOsZp4S0c1OEXc4m1Q==
|
||||
version "12.19.12"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.19.12.tgz#04793c2afa4ce833a9972e4c476432e30f9df47b"
|
||||
integrity sha512-UwfL2uIU9arX/+/PRcIkT08/iBadGN2z6ExOROA2Dh5mAuWTBj6iJbQX4nekiV5H8cTrEG569LeX+HRco9Cbxw==
|
||||
|
||||
emmet@^2.1.5:
|
||||
version "2.1.6"
|
||||
resolved "https://registry.yarnpkg.com/emmet/-/emmet-2.1.6.tgz#425e0bcef6bf6e5eb758610f3e8d49f86a6fe877"
|
||||
integrity sha512-kfJMlze+k8jpX5CUx7xPYS83DxRNXuh8rQ98rQKnnf+wfo/KD+BG6pmpnEp5a7a1DWM9xmllKuOPfC7MeRmepQ==
|
||||
"emmet@https://github.com/rzhao271/emmet.git#1b2df677d8925ef5ea6da9df8845968403979a0a":
|
||||
version "2.3.0"
|
||||
resolved "https://github.com/rzhao271/emmet.git#1b2df677d8925ef5ea6da9df8845968403979a0a"
|
||||
dependencies:
|
||||
"@emmetio/abbreviation" "^2.0.2"
|
||||
"@emmetio/abbreviation" "^2.2.0"
|
||||
"@emmetio/css-abbreviation" "^2.1.2"
|
||||
|
||||
image-size@^0.5.2:
|
||||
|
@ -76,42 +75,27 @@ jsonc-parser@^2.3.0:
|
|||
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.3.1.tgz#59549150b133f2efacca48fe9ce1ec0659af2342"
|
||||
integrity sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==
|
||||
|
||||
vscode-emmet-helper@~2.0.0:
|
||||
version "2.0.9"
|
||||
resolved "https://registry.yarnpkg.com/vscode-emmet-helper/-/vscode-emmet-helper-2.0.9.tgz#16244c087cba4e379116f268384bb644649db6ad"
|
||||
integrity sha512-S6RjnR9gUicl8LsYnQAMNqqOxolud9gcj+NpPyEnxfxp1YIBuC9oetj6l6N9VMZBWu6tL77wmf+/EJsRx1PDPA==
|
||||
vscode-emmet-helper@2.2.4-2:
|
||||
version "2.2.4-2"
|
||||
resolved "https://registry.yarnpkg.com/vscode-emmet-helper/-/vscode-emmet-helper-2.2.4-2.tgz#8019188077a91dbe9a8d8c10c0b79369bb5c24d6"
|
||||
integrity sha512-7UTZXwt9M1xwaV72o2YgSBVoghtDtscTgYTOl1kiPkXN9OKiM4N52hcHFA1LlRtdTvIQd4PEkgaz57F9ZT/4kg==
|
||||
dependencies:
|
||||
emmet "^2.1.5"
|
||||
emmet "https://github.com/rzhao271/emmet.git#1b2df677d8925ef5ea6da9df8845968403979a0a"
|
||||
jsonc-parser "^2.3.0"
|
||||
vscode-languageserver-textdocument "^1.0.1"
|
||||
vscode-languageserver-types "^3.15.1"
|
||||
vscode-nls "^5.0.0"
|
||||
vscode-uri "^2.1.2"
|
||||
|
||||
vscode-html-languageservice@^3.0.3:
|
||||
version "3.1.4"
|
||||
resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-3.1.4.tgz#0316dff77ee38dc176f40560cbf55e4f64f4f433"
|
||||
integrity sha512-3M+bm+hNvwQcScVe5/ok9BXvctOiGJ4nlOkkFf+WKSDrYNkarZ/RByKOa1/iylbvZxJUPzbeziembWPe/dMvhw==
|
||||
dependencies:
|
||||
vscode-languageserver-textdocument "^1.0.1"
|
||||
vscode-languageserver-types "3.16.0-next.2"
|
||||
vscode-nls "^5.0.0"
|
||||
vscode-uri "^2.1.2"
|
||||
|
||||
vscode-languageserver-textdocument@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.1.tgz#178168e87efad6171b372add1dea34f53e5d330f"
|
||||
integrity sha512-UIcJDjX7IFkck7cSkNNyzIz5FyvpQfY7sdzVy+wkKN/BLaD4DQ0ppXQrKePomCxTS7RrolK1I0pey0bG9eh8dA==
|
||||
|
||||
vscode-languageserver-types@3.16.0-next.2:
|
||||
version "3.16.0-next.2"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.2.tgz#940bd15c992295a65eae8ab6b8568a1e8daa3083"
|
||||
integrity sha512-QjXB7CKIfFzKbiCJC4OWC8xUncLsxo19FzGVp/ADFvvi87PlmBSCAtZI5xwGjF5qE0xkLf0jjKUn3DzmpDP52Q==
|
||||
|
||||
vscode-languageserver-types@^3.15.1:
|
||||
version "3.15.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz#17be71d78d2f6236d414f0001ce1ef4d23e6b6de"
|
||||
integrity sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ==
|
||||
version "3.16.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz#ecf393fc121ec6974b2da3efb3155644c514e247"
|
||||
integrity sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==
|
||||
|
||||
vscode-nls@^5.0.0:
|
||||
version "5.0.0"
|
||||
|
|
|
@ -432,7 +432,11 @@ export class Git {
|
|||
if (options.recursive) {
|
||||
command.push('--recursive');
|
||||
}
|
||||
await this.exec(options.parentPath, command, { cancellationToken, onSpawn });
|
||||
await this.exec(options.parentPath, command, {
|
||||
cancellationToken,
|
||||
env: { 'GIT_HTTP_USER_AGENT': this.userAgent },
|
||||
onSpawn,
|
||||
});
|
||||
} catch (err) {
|
||||
if (err.stderr) {
|
||||
err.stderr = err.stderr.replace(/^Cloning.+$/m, '').trim();
|
||||
|
@ -1568,6 +1572,7 @@ export class Repository {
|
|||
const args = ['fetch'];
|
||||
const spawnOptions: SpawnOptions = {
|
||||
cancellationToken: options.cancellationToken,
|
||||
env: { 'GIT_HTTP_USER_AGENT': this.git.userAgent }
|
||||
};
|
||||
|
||||
if (options.remote) {
|
||||
|
@ -1589,7 +1594,7 @@ export class Repository {
|
|||
}
|
||||
|
||||
if (options.silent) {
|
||||
spawnOptions.env = { 'VSCODE_GIT_FETCH_SILENT': 'true' };
|
||||
spawnOptions.env!['VSCODE_GIT_FETCH_SILENT'] = 'true';
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -1626,7 +1631,10 @@ export class Repository {
|
|||
}
|
||||
|
||||
try {
|
||||
await this.run(args, options);
|
||||
await this.run(args, {
|
||||
cancellationToken: options.cancellationToken,
|
||||
env: { 'GIT_HTTP_USER_AGENT': this.git.userAgent }
|
||||
});
|
||||
} catch (err) {
|
||||
if (/^CONFLICT \([^)]+\): \b/m.test(err.stdout || '')) {
|
||||
err.gitErrorCode = GitErrorCodes.Conflict;
|
||||
|
@ -1694,10 +1702,8 @@ export class Repository {
|
|||
args.push(name);
|
||||
}
|
||||
|
||||
args.splice(0, 0, '-c', `http.userAgent=${this.git.userAgent}`);
|
||||
|
||||
try {
|
||||
await this.run(args);
|
||||
await this.run(args, { env: { 'GIT_HTTP_USER_AGENT': this.git.userAgent } });
|
||||
} catch (err) {
|
||||
if (/^error: failed to push some refs to\b/m.test(err.stderr || '')) {
|
||||
err.gitErrorCode = GitErrorCodes.PushRejected;
|
||||
|
@ -1909,11 +1915,11 @@ export class Repository {
|
|||
const fn = (line: string): Ref | null => {
|
||||
let match: RegExpExecArray | null;
|
||||
|
||||
if (match = /^refs\/heads\/([^ ]+) ([0-9a-f]{40}) ([0-9a-f]{40})?$/.exec(line)) {
|
||||
if (match = /^refs\/heads\/([^ ]+) ([0-9a-f]{40}) ?([0-9a-f]{40})?$/.exec(line)) {
|
||||
return { name: match[1], commit: match[2], type: RefType.Head };
|
||||
} else if (match = /^refs\/remotes\/([^/]+)\/([^ ]+) ([0-9a-f]{40}) ([0-9a-f]{40})?$/.exec(line)) {
|
||||
} else if (match = /^refs\/remotes\/([^/]+)\/([^ ]+) ?([0-9a-f]{40}) ([0-9a-f]{40})?$/.exec(line)) {
|
||||
return { name: `${match[1]}/${match[2]}`, commit: match[3], type: RefType.RemoteHead, remote: match[1] };
|
||||
} else if (match = /^refs\/tags\/([^ ]+) ([0-9a-f]{40}) ([0-9a-f]{40})?$/.exec(line)) {
|
||||
} else if (match = /^refs\/tags\/([^ ]+) ([0-9a-f]{40}) ?([0-9a-f]{40})?$/.exec(line)) {
|
||||
return { name: match[1], commit: match[3] ?? match[2], type: RefType.Tag };
|
||||
}
|
||||
|
||||
|
|
|
@ -79,7 +79,7 @@ export class Resource implements SourceControlResourceState {
|
|||
return this.resources[0];
|
||||
}
|
||||
|
||||
get rightUri(): Uri {
|
||||
get rightUri(): Uri | undefined {
|
||||
return this.resources[1];
|
||||
}
|
||||
|
||||
|
@ -88,7 +88,7 @@ export class Resource implements SourceControlResourceState {
|
|||
}
|
||||
|
||||
@memoize
|
||||
private get resources(): [Uri | undefined, Uri] {
|
||||
private get resources(): [Uri | undefined, Uri | undefined] {
|
||||
return this._commandResolver.getResources(this);
|
||||
}
|
||||
|
||||
|
@ -613,7 +613,7 @@ class ResourceCommandResolver {
|
|||
}
|
||||
}
|
||||
|
||||
getResources(resource: Resource): [Uri | undefined, Uri] {
|
||||
getResources(resource: Resource): [Uri | undefined, Uri | undefined] {
|
||||
for (const submodule of this.repository.submodules) {
|
||||
if (path.join(this.repository.root, submodule.path) === resource.resourceUri.fsPath) {
|
||||
return [undefined, toGitUri(resource.resourceUri, resource.resourceGroupType === ResourceGroupType.Index ? 'index' : 'wt', { submoduleOf: this.repository.root })];
|
||||
|
@ -641,7 +641,7 @@ class ResourceCommandResolver {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
private getRightResource(resource: Resource): Uri {
|
||||
private getRightResource(resource: Resource): Uri | undefined {
|
||||
switch (resource.type) {
|
||||
case Status.INDEX_MODIFIED:
|
||||
case Status.INDEX_ADDED:
|
||||
|
@ -677,7 +677,7 @@ class ResourceCommandResolver {
|
|||
return resource.resourceUri;
|
||||
}
|
||||
|
||||
throw new Error('Should never happen');
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private getTitle(resource: Resource): string {
|
||||
|
|
|
@ -124,4 +124,31 @@ suite('git smoke test', function () {
|
|||
assert.equal(repository.state.workingTreeChanges.length, 0);
|
||||
assert.equal(repository.state.indexChanges.length, 0);
|
||||
});
|
||||
|
||||
test('rename/delete conflict', async function () {
|
||||
cp.execSync('git branch test', { cwd });
|
||||
cp.execSync('git checkout test', { cwd });
|
||||
|
||||
fs.unlinkSync(file('app.js'));
|
||||
cp.execSync('git add .', { cwd });
|
||||
|
||||
await repository.commit('commit on test');
|
||||
cp.execSync('git checkout master', { cwd });
|
||||
|
||||
fs.renameSync(file('app.js'), file('rename.js'));
|
||||
cp.execSync('git add .', { cwd });
|
||||
await repository.commit('commit on master');
|
||||
|
||||
try {
|
||||
cp.execSync('git merge test', { cwd });
|
||||
} catch (e) { }
|
||||
|
||||
setTimeout(() => {
|
||||
commands.executeCommand('workbench.scm.focus');
|
||||
}, 2e3);
|
||||
|
||||
await new Promise(resolve => {
|
||||
setTimeout(resolve, 5e3);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -31,9 +31,10 @@ export type Keytar = {
|
|||
const SERVICE_ID = `github.auth`;
|
||||
|
||||
export class Keychain {
|
||||
constructor(private context: vscode.ExtensionContext) { }
|
||||
async setToken(token: string): Promise<void> {
|
||||
try {
|
||||
return await vscode.authentication.setPassword(SERVICE_ID, token);
|
||||
return await this.context.secrets.set(SERVICE_ID, token);
|
||||
} catch (e) {
|
||||
// Ignore
|
||||
Logger.error(`Setting token failed: ${e}`);
|
||||
|
@ -47,7 +48,7 @@ export class Keychain {
|
|||
|
||||
async getToken(): Promise<string | null | undefined> {
|
||||
try {
|
||||
return await vscode.authentication.getPassword(SERVICE_ID);
|
||||
return await this.context.secrets.get(SERVICE_ID);
|
||||
} catch (e) {
|
||||
// Ignore
|
||||
Logger.error(`Getting token failed: ${e}`);
|
||||
|
@ -57,7 +58,7 @@ export class Keychain {
|
|||
|
||||
async deleteToken(): Promise<void> {
|
||||
try {
|
||||
return await vscode.authentication.deletePassword(SERVICE_ID);
|
||||
return await this.context.secrets.delete(SERVICE_ID);
|
||||
} catch (e) {
|
||||
// Ignore
|
||||
Logger.error(`Deleting token failed: ${e}`);
|
||||
|
@ -85,5 +86,3 @@ export class Keychain {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const keychain = new Keychain();
|
||||
|
|
|
@ -14,7 +14,7 @@ export async function activate(context: vscode.ExtensionContext) {
|
|||
const telemetryReporter = new TelemetryReporter(name, version, aiKey);
|
||||
|
||||
context.subscriptions.push(vscode.window.registerUriHandler(uriHandler));
|
||||
const loginService = new GitHubAuthenticationProvider();
|
||||
const loginService = new GitHubAuthenticationProvider(context);
|
||||
|
||||
await loginService.initialize(context);
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import * as vscode from 'vscode';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { keychain } from './common/keychain';
|
||||
import { Keychain } from './common/keychain';
|
||||
import { GitHubServer, NETWORK_ERROR } from './githubServer';
|
||||
import Logger from './common/logger';
|
||||
|
||||
|
@ -26,6 +26,12 @@ export class GitHubAuthenticationProvider {
|
|||
private _sessions: vscode.AuthenticationSession[] = [];
|
||||
private _githubServer = new GitHubServer();
|
||||
|
||||
private _keychain: Keychain;
|
||||
|
||||
constructor(context: vscode.ExtensionContext) {
|
||||
this._keychain = new Keychain(context);
|
||||
}
|
||||
|
||||
public async initialize(context: vscode.ExtensionContext): Promise<void> {
|
||||
try {
|
||||
this._sessions = await this.readSessions();
|
||||
|
@ -34,7 +40,7 @@ export class GitHubAuthenticationProvider {
|
|||
// Ignore, network request failed
|
||||
}
|
||||
|
||||
context.subscriptions.push(vscode.authentication.onDidChangePassword(() => this.checkForUpdates()));
|
||||
context.subscriptions.push(context.secrets.onDidChange(() => this.checkForUpdates()));
|
||||
}
|
||||
|
||||
private async verifySessions(): Promise<void> {
|
||||
|
@ -101,7 +107,7 @@ export class GitHubAuthenticationProvider {
|
|||
}
|
||||
|
||||
private async readSessions(): Promise<vscode.AuthenticationSession[]> {
|
||||
const storedSessions = await keychain.getToken() || await keychain.tryMigrate();
|
||||
const storedSessions = await this._keychain.getToken() || await this._keychain.tryMigrate();
|
||||
if (storedSessions) {
|
||||
try {
|
||||
const sessionData: SessionData[] = JSON.parse(storedSessions);
|
||||
|
@ -132,7 +138,7 @@ export class GitHubAuthenticationProvider {
|
|||
}
|
||||
|
||||
Logger.error(`Error reading sessions: ${e}`);
|
||||
await keychain.deleteToken();
|
||||
await this._keychain.deleteToken();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -140,7 +146,7 @@ export class GitHubAuthenticationProvider {
|
|||
}
|
||||
|
||||
private async storeSessions(): Promise<void> {
|
||||
await keychain.setToken(JSON.stringify(this._sessions));
|
||||
await this._keychain.setToken(JSON.stringify(this._sessions));
|
||||
}
|
||||
|
||||
get sessions(): vscode.AuthenticationSession[] {
|
||||
|
|
|
@ -13,12 +13,31 @@ const localize = nls.loadMessageBundle();
|
|||
|
||||
type AutoDetect = 'on' | 'off';
|
||||
|
||||
function exists(file: string): Promise<boolean> {
|
||||
return new Promise<boolean>((resolve, _reject) => {
|
||||
fs.exists(file, (value) => {
|
||||
resolve(value);
|
||||
});
|
||||
});
|
||||
/**
|
||||
* Check if the given filename is a file.
|
||||
*
|
||||
* If returns false in case the file does not exist or
|
||||
* the file stats cannot be accessed/queried or it
|
||||
* is no file at all.
|
||||
*
|
||||
* @param filename
|
||||
* the filename to the checked
|
||||
* @returns
|
||||
* true in case the file exists, in any other case false.
|
||||
*/
|
||||
async function exists(filename: string): Promise<boolean> {
|
||||
try {
|
||||
|
||||
if ((await fs.promises.stat(filename)).isFile()) {
|
||||
return true;
|
||||
}
|
||||
} catch (ex) {
|
||||
// In case requesting the file statistics fail.
|
||||
// we assume it does not exist.
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function exec(command: string, options: cp.ExecOptions): Promise<{ stdout: string; stderr: string }> {
|
||||
|
@ -70,21 +89,23 @@ function showError() {
|
|||
}
|
||||
|
||||
async function findGulpCommand(rootPath: string): Promise<string> {
|
||||
let gulpCommand: string;
|
||||
let platform = process.platform;
|
||||
|
||||
if (platform === 'win32' && await exists(path.join(rootPath, 'node_modules', '.bin', 'gulp.cmd'))) {
|
||||
const globalGulp = path.join(process.env.APPDATA ? process.env.APPDATA : '', 'npm', 'gulp.cmd');
|
||||
if (await exists(globalGulp)) {
|
||||
gulpCommand = '"' + globalGulp + '"';
|
||||
} else {
|
||||
gulpCommand = path.join('.', 'node_modules', '.bin', 'gulp.cmd');
|
||||
return `"${globalGulp}"`;
|
||||
}
|
||||
} else if ((platform === 'linux' || platform === 'darwin') && await exists(path.join(rootPath, 'node_modules', '.bin', 'gulp'))) {
|
||||
gulpCommand = path.join('.', 'node_modules', '.bin', 'gulp');
|
||||
} else {
|
||||
gulpCommand = 'gulp';
|
||||
|
||||
return path.join('.', 'node_modules', '.bin', 'gulp.cmd');
|
||||
|
||||
}
|
||||
return gulpCommand;
|
||||
|
||||
if ((platform === 'linux' || platform === 'darwin') && await exists(path.join(rootPath, 'node_modules', '.bin', 'gulp'))) {
|
||||
return path.join('.', 'node_modules', '.bin', 'gulp');
|
||||
}
|
||||
|
||||
return 'gulp';
|
||||
}
|
||||
|
||||
interface GulpTaskDefinition extends vscode.TaskDefinition {
|
||||
|
@ -111,7 +132,7 @@ class FolderDetector {
|
|||
}
|
||||
|
||||
public start(): void {
|
||||
let pattern = path.join(this._workspaceFolder.uri.fsPath, '{node_modules,gulpfile{.babel.js,.js,.ts}}');
|
||||
let pattern = path.join(this._workspaceFolder.uri.fsPath, '{node_modules,gulpfile{.babel.js,.esm.js,.js,.mjs,.cjs,.ts}}');
|
||||
this.fileWatcher = vscode.workspace.createFileSystemWatcher(pattern);
|
||||
this.fileWatcher.onDidChange(() => this.promise = undefined);
|
||||
this.fileWatcher.onDidCreate(() => this.promise = undefined);
|
||||
|
@ -119,14 +140,15 @@ class FolderDetector {
|
|||
}
|
||||
|
||||
public async getTasks(): Promise<vscode.Task[]> {
|
||||
if (this.isEnabled()) {
|
||||
if (!this.promise) {
|
||||
this.promise = this.computeTasks();
|
||||
}
|
||||
return this.promise;
|
||||
} else {
|
||||
if (!this.isEnabled()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!this.promise) {
|
||||
this.promise = this.computeTasks();
|
||||
}
|
||||
|
||||
return this.promise;
|
||||
}
|
||||
|
||||
public async getTask(_task: vscode.Task): Promise<vscode.Task | undefined> {
|
||||
|
@ -140,21 +162,55 @@ class FolderDetector {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for a gulp entry point inside the given folder.
|
||||
*
|
||||
* Typically the entry point is a file named "gulpfile.js"
|
||||
*
|
||||
* It can also be a transposed gulp entry points, like gulp.babel.js or gulp.esm.js
|
||||
*
|
||||
* Additionally recent node version prefer the .mjs or .cjs extension over the .js.
|
||||
*
|
||||
* @param root
|
||||
* the folder which should be checked.
|
||||
*/
|
||||
private async hasGulpfile(root: string): Promise<boolean | undefined> {
|
||||
|
||||
for (const filename of await fs.promises.readdir(root)) {
|
||||
|
||||
const ext = path.extname(filename);
|
||||
if (ext !== '.js' && ext !== '.mjs' && ext !== '.cjs') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!exists(filename)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let basename = path.basename(filename, ext).toLowerCase();
|
||||
if (basename === 'gulpfile') {
|
||||
return true;
|
||||
}
|
||||
if (basename === 'gulpfile.esm') {
|
||||
return true;
|
||||
}
|
||||
if (basename === 'gulpfile.babel') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private async computeTasks(): Promise<vscode.Task[]> {
|
||||
let rootPath = this._workspaceFolder.uri.scheme === 'file' ? this._workspaceFolder.uri.fsPath : undefined;
|
||||
let emptyTasks: vscode.Task[] = [];
|
||||
if (!rootPath) {
|
||||
return emptyTasks;
|
||||
}
|
||||
let gulpfile = path.join(rootPath, 'gulpfile.js');
|
||||
if (!await exists(gulpfile)) {
|
||||
gulpfile = path.join(rootPath, 'Gulpfile.js');
|
||||
if (!await exists(gulpfile)) {
|
||||
gulpfile = path.join(rootPath, 'gulpfile.babel.js');
|
||||
if (!await exists(gulpfile)) {
|
||||
return emptyTasks;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.hasGulpfile(rootPath)) {
|
||||
return emptyTasks;
|
||||
}
|
||||
|
||||
let commandLine = `${await this._gulpCommand} --tasks-simple --no-color`;
|
||||
|
|
|
@ -40,7 +40,7 @@ namespace SemanticTokenLegendRequest {
|
|||
}
|
||||
|
||||
namespace SettingIds {
|
||||
export const linkedRename = 'editor.linkedRename';
|
||||
export const linkedEditing = 'editor.linkedEditing';
|
||||
export const formatEnable = 'html.format.enable';
|
||||
|
||||
}
|
||||
|
@ -284,25 +284,25 @@ export function startClient(context: ExtensionContext, newLanguageClient: Langua
|
|||
}
|
||||
});
|
||||
|
||||
const promptForTypeOnRenameKey = 'html.promptForTypeOnRename';
|
||||
const promptForTypeOnRename = extensions.getExtension('formulahendry.auto-rename-tag') !== undefined &&
|
||||
(context.globalState.get(promptForTypeOnRenameKey) !== false) &&
|
||||
!workspace.getConfiguration('editor', { languageId: 'html' }).get('linkedRename');
|
||||
|
||||
if (promptForTypeOnRename) {
|
||||
const activeEditorListener = window.onDidChangeActiveTextEditor(async e => {
|
||||
if (e && documentSelector.indexOf(e.document.languageId) !== -1) {
|
||||
context.globalState.update(promptForTypeOnRenameKey, false);
|
||||
activeEditorListener.dispose();
|
||||
const configure = localize('configureButton', 'Configure');
|
||||
const res = await window.showInformationMessage(localize('linkedRenameQuestion', 'VS Code now has built-in support for auto-renaming tags. Do you want to enable it?'), configure);
|
||||
if (res === configure) {
|
||||
commands.executeCommand('workbench.action.openSettings', SettingIds.linkedRename);
|
||||
const promptForLinkedEditingKey = 'html.promptForLinkedEditing';
|
||||
if (extensions.getExtension('formulahendry.auto-rename-tag') !== undefined && (context.globalState.get(promptForLinkedEditingKey) !== false)) {
|
||||
const config = workspace.getConfiguration('editor', { languageId: 'html' });
|
||||
if (!config.get('linkedEditing') && !config.get('renameOnType')) {
|
||||
const activeEditorListener = window.onDidChangeActiveTextEditor(async e => {
|
||||
if (e && documentSelector.indexOf(e.document.languageId) !== -1) {
|
||||
context.globalState.update(promptForLinkedEditingKey, false);
|
||||
activeEditorListener.dispose();
|
||||
const configure = localize('configureButton', 'Configure');
|
||||
const res = await window.showInformationMessage(localize('linkedEditingQuestion', 'VS Code now has built-in support for auto-renaming tags. Do you want to enable it?'), configure);
|
||||
if (res === configure) {
|
||||
commands.executeCommand('workbench.action.openSettings', SettingIds.linkedEditing);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
toDispose.push(activeEditorListener);
|
||||
});
|
||||
toDispose.push(activeEditorListener);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
toDispose.push();
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
"scripts": {
|
||||
"compile": "npx gulp compile-extension:html-language-features-client compile-extension:html-language-features-server",
|
||||
"watch": "npx gulp watch-extension:html-language-features-client watch-extension:html-language-features-server",
|
||||
"postinstall": "cd server && yarn install",
|
||||
"install-client-next": "yarn add vscode-languageclient@next"
|
||||
},
|
||||
"categories": [
|
||||
|
@ -148,17 +147,13 @@
|
|||
"description": "%html.format.wrapAttributesIndentSize.desc%"
|
||||
},
|
||||
"html.format.templating": {
|
||||
"type": [
|
||||
"boolean"
|
||||
],
|
||||
"type": "boolean",
|
||||
"scope": "resource",
|
||||
"default": false,
|
||||
"description": "%html.format.templating.desc%"
|
||||
},
|
||||
"html.format.unformattedContentDelimiter": {
|
||||
"type": [
|
||||
"string"
|
||||
],
|
||||
"type": "string",
|
||||
"scope": "resource",
|
||||
"default": "",
|
||||
"markdownDescription": "%html.format.unformattedContentDelimiter.desc%"
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"If you want to provide a fix or improvement, please create a pull request against the original repository.",
|
||||
"Once accepted there, we are happy to receive an update request."
|
||||
],
|
||||
"version": "https://github.com/microsoft/TypeScript-TmLanguage/commit/a755c6bad619daa8349fb23149eeeff220a06603",
|
||||
"version": "https://github.com/microsoft/TypeScript-TmLanguage/commit/398985941eb36cd270054a6e668d03fb9ef92e77",
|
||||
"name": "JavaScript (with React support)",
|
||||
"scopeName": "source.js",
|
||||
"patterns": [
|
||||
|
@ -2987,13 +2987,7 @@
|
|||
"end": "(?<=\\))|(?=[;),}\\]:?\\-\\+\\>]|\\|\\||\\&\\&|\\!\\=\\=|$|((?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))new(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.)))|((?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))function((\\s+[_$[:alpha:]][_$[:alnum:]]*)|(\\s*[\\(]))))",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#paren-expression"
|
||||
},
|
||||
{
|
||||
"include": "#class-declaration"
|
||||
},
|
||||
{
|
||||
"include": "#type"
|
||||
"include": "#expression"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -3359,7 +3353,7 @@
|
|||
"name": "keyword.operator.expression.typeof.js"
|
||||
}
|
||||
},
|
||||
"end": "(?=[,);}\\]=]|$|;|(?:^\\s*(?:abstract|async|class|const|declare|enum|export|function|import|interface|let|module|namespace|return|type|var)\\b))",
|
||||
"end": "(?=[,);}\\]=>]|$|;|(?:^\\s*(?:abstract|async|class|const|declare|enum|export|function|import|interface|let|module|namespace|return|type|var)\\b))",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#expression"
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"If you want to provide a fix or improvement, please create a pull request against the original repository.",
|
||||
"Once accepted there, we are happy to receive an update request."
|
||||
],
|
||||
"version": "https://github.com/microsoft/TypeScript-TmLanguage/commit/a755c6bad619daa8349fb23149eeeff220a06603",
|
||||
"version": "https://github.com/microsoft/TypeScript-TmLanguage/commit/398985941eb36cd270054a6e668d03fb9ef92e77",
|
||||
"name": "JavaScript (with React support)",
|
||||
"scopeName": "source.js.jsx",
|
||||
"patterns": [
|
||||
|
@ -2987,13 +2987,7 @@
|
|||
"end": "(?<=\\))|(?=[;),}\\]:?\\-\\+\\>]|\\|\\||\\&\\&|\\!\\=\\=|$|((?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))new(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.)))|((?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))function((\\s+[_$[:alpha:]][_$[:alnum:]]*)|(\\s*[\\(]))))",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#paren-expression"
|
||||
},
|
||||
{
|
||||
"include": "#class-declaration"
|
||||
},
|
||||
{
|
||||
"include": "#type"
|
||||
"include": "#expression"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -3359,7 +3353,7 @@
|
|||
"name": "keyword.operator.expression.typeof.js.jsx"
|
||||
}
|
||||
},
|
||||
"end": "(?=[,);}\\]=]|$|;|(?:^\\s*(?:abstract|async|class|const|declare|enum|export|function|import|interface|let|module|namespace|return|type|var)\\b))",
|
||||
"end": "(?=[,);}\\]=>]|$|;|(?:^\\s*(?:abstract|async|class|const|declare|enum|export|function|import|interface|let|module|namespace|return|type|var)\\b))",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#expression"
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue