Merge from vscode 8a997f7321ae6612fc0e6eb3eac4f358a6233bfb

This commit is contained in:
ADS Merger
2020-02-11 07:08:19 +00:00
parent 0f934081e1
commit 085752f111
217 changed files with 2561 additions and 2063 deletions

View File

@@ -11,46 +11,49 @@ on:
- release/*
jobs:
# linux:
# runs-on: ubuntu-latest
# env:
# CHILD_CONCURRENCY: "1"
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# steps:
# - uses: actions/checkout@v1
# # TODO: rename azure-pipelines/linux/xvfb.init to github-actions
# - run: |
# sudo apt-get update
# sudo apt-get install -y libxkbfile-dev pkg-config libsecret-1-dev libxss1 dbus xvfb libgtk-3-0 libkrb5-dev # {{SQL CARBON EDIT}} add kerberos dep
# 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
# name: Setup Build Environment
# - uses: actions/setup-node@v1
# with:
# node-version: 10
# # TODO: cache node modules
# - run: yarn --frozen-lockfile
# name: Install Dependencies
# - run: yarn electron x64
# name: Download Electron
# - run: yarn gulp hygiene
# name: Run Hygiene Checks
# - run: yarn strict-vscode # {{SQL CARBON EDIT}} add step
# name: Run Strict Compile Options
# # - run: yarn monaco-compile-check {{SQL CARBON EDIT}} remove step
# # name: Run Monaco Editor Checks
# - run: yarn valid-layers-check
# name: Run Valid Layers Checks
# - run: yarn compile
# name: Compile Sources
# # - run: yarn download-builtin-extensions {{SQL CARBON EDIT}} remove step
# # name: Download Built-in Extensions
# - run: DISPLAY=:10 ./scripts/test.sh --tfs "Unit Tests"
# name: Run Unit Tests
# # - run: DISPLAY=:10 ./scripts/test-integration.sh --tfs "Integration Tests" {{SQL CARBON EDIT}} remove step
# # name: Run Integration Tests
linux:
runs-on: ubuntu-latest
env:
CHILD_CONCURRENCY: "1"
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v1
# TODO: rename azure-pipelines/linux/xvfb.init to github-actions
- run: |
sudo apt-get update
sudo apt-get install -y libxkbfile-dev pkg-config libsecret-1-dev libxss1 dbus xvfb libgtk-3-0 libkrb5-dev # {{SQL CARBON EDIT}} add kerberos dep
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
name: Setup Build Environment
- uses: actions/setup-node@v1
with:
node-version: 10
# TODO: cache node modules
- run: yarn --frozen-lockfile
name: Install Dependencies
- run: yarn electron x64
name: Download Electron
- run: yarn gulp hygiene
name: Run Hygiene Checks
- run: yarn strict-vscode # {{SQL CARBON EDIT}} add step
name: Run Strict Compile Options
# - run: yarn monaco-compile-check {{SQL CARBON EDIT}} remove step
# name: Run Monaco Editor Checks
- run: yarn valid-layers-check
name: Run Valid Layers Checks
- run: yarn compile
name: Compile Sources
# - run: yarn download-builtin-extensions {{SQL CARBON EDIT}} remove step
# name: Download Built-in Extensions
- run: DISPLAY=:10 ./scripts/test.sh --tfs "Unit Tests"
name: Run Unit Tests (Electron)
# Fails with cryptic error (e.g. https://github.com/microsoft/vscode/pull/90292/checks?check_run_id=433681926#step:13:9)
# - run: DISPLAY=:10 yarn test-browser --browser chromium
# name: Run Unit Tests (Browser)
# - run: DISPLAY=:10 ./scripts/test-integration.sh --tfs "Integration Tests" {{SQL CARBON EDIT}} remove step
# name: Run Integration Tests (Electron)
windows:
runs-on: windows-2016
@@ -82,9 +85,11 @@ jobs:
# - run: yarn download-builtin-extensions {{SQL CARBON EDIT}} remove step
# name: Download Built-in Extensions
- run: .\scripts\test.bat --tfs "Unit Tests"
name: Run Unit Tests
name: Run Unit Tests (Electron)
- run: yarn test-browser --browser chromium
name: Run Unit Tests (Browser)
# - run: .\scripts\test-integration.bat --tfs "Integration Tests" {{SQL CARBON EDIT}} remove step
# name: Run Integration Tests
# name: Run Integration Tests (Electron)
darwin:
runs-on: macos-latest
@@ -113,6 +118,8 @@ jobs:
# - run: yarn download-builtin-extensions {{SQL CARBON EDIT}} remove step
# name: Download Built-in Extensions
- run: ./scripts/test.sh --tfs "Unit Tests"
name: Run Unit Tests
name: Run Unit Tests (Electron)
- run: yarn test-browser --browser chromium --browser webkit
name: Run Unit Tests (Browser)
# - run: ./scripts/test-integration.sh --tfs "Integration Tests" {{SQL CARBON EDIT}} remove step
# name: Run Integration Tests
# name: Run Integration Tests (Electron)

View File

@@ -43,13 +43,13 @@ steps:
# displayName: Download Built-in Extensions
- script: |
./scripts/test.sh --tfs "Unit Tests"
displayName: Run Unit Tests
displayName: Run Unit Tests (Electron)
- script: |
yarn test-browser --browser chromium --browser webkit
displayName: Run Unit Tests (Browsers)
displayName: Run Unit Tests (Browser)
# - script: | {{SQL CARBON EDIT}} remove step
# ./scripts/test-integration.sh --tfs "Integration Tests"
# displayName: Run Integration Tests
# displayName: Run Integration Tests (Electron)
- task: PublishTestResults@2
displayName: Publish Tests Results
inputs:

View File

@@ -44,13 +44,6 @@ steps:
git config user.email "vscode@microsoft.com"
git config user.name "VSCode"
security create-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain
security default-keychain -s $(agent.tempdirectory)/buildagent.keychain
security unlock-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain
echo "$(macos-developer-certificate)" | base64 -D > $(agent.tempdirectory)/cert.p12
security import $(agent.tempdirectory)/cert.p12 -k $(agent.tempdirectory)/buildagent.keychain -P "$(macos-developer-certificate-key)" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k pwd $(agent.tempdirectory)/buildagent.keychain
displayName: Prepare tooling
- script: |
@@ -103,9 +96,13 @@ steps:
- script: |
set -e
./scripts/test.sh --build --tfs "Unit Tests"
# APP_NAME="`ls $(agent.builddirectory)/VSCode-darwin | head -n 1`"
# yarn smoketest -- --build "$(agent.builddirectory)/VSCode-darwin/$APP_NAME"
displayName: Run unit tests
displayName: Run unit tests (Electron)
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- script: |
set -e
yarn test-browser --build --browser chromium --browser webkit
displayName: Run unit tests (Browser)
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- script: |
@@ -118,22 +115,32 @@ steps:
INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME/Contents/MacOS/Electron" \
VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin" \
./scripts/test-integration.sh --build --tfs "Integration Tests"
displayName: Run integration tests
displayName: Run integration tests (Electron)
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- script: |
set -e
cd test/smoke
yarn compile
cd -
VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-darwin" \
yarn smoketest --web --headless
continueOnError: true
displayName: Run web smoke tests
./resources/server/test/test-web-integration.sh --browser webkit
displayName: Run integration tests (Browser)
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- script: |
set -e
VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-darwin" \
yarn smoketest --web --headless --browser webkit
continueOnError: true
displayName: Run smoke tests (Browser)
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- script: |
set -e
security create-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain
security default-keychain -s $(agent.tempdirectory)/buildagent.keychain
security unlock-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain
echo "$(macos-developer-certificate)" | base64 -D > $(agent.tempdirectory)/cert.p12
security import $(agent.tempdirectory)/cert.p12 -k $(agent.tempdirectory)/buildagent.keychain -P "$(macos-developer-certificate-key)" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k pwd $(agent.tempdirectory)/buildagent.keychain
codesign -s 99FM488X57 --deep --force --options runtime --entitlements build/azure-pipelines/darwin/entitlements.plist $(agent.builddirectory)/VSCode-darwin/*.app
displayName: Set Hardened Entitlements

View File

@@ -51,13 +51,13 @@ steps:
# displayName: Download Built-in Extensions
- script: |
DISPLAY=:10 ./scripts/test.sh --tfs "Unit Tests"
displayName: Run Unit Tests
displayName: Run Unit Tests (Electron)
- script: |
DISPLAY=:10 yarn test-browser --browser chromium
displayName: Run Unit Tests (Browser)
# - script: | {{SQL CARBON EDIT}} remove step
# DISPLAY=:10 ./scripts/test-integration.sh --tfs "Integration Tests"
# displayName: Run Integration Tests
# displayName: Run Integration Tests (Electron)
- task: PublishTestResults@2
displayName: Publish Tests Results
inputs:

View File

@@ -101,7 +101,13 @@ steps:
- script: |
set -e
DISPLAY=:10 ./scripts/test.sh --build --tfs "Unit Tests"
displayName: Run unit tests
displayName: Run unit tests (Electron)
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- script: |
set -e
DISPLAY=:10 yarn test-browser --build --browser chromium
displayName: Run unit tests (Browser)
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- script: |
@@ -114,8 +120,23 @@ steps:
INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME" \
VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-x64" \
DISPLAY=:10 ./scripts/test-integration.sh --build --tfs "Integration Tests"
# yarn smoketest -- --build "$(agent.builddirectory)/VSCode-linux-x64"
displayName: Run integration tests
displayName: Run integration tests (Electron)
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
# Fails due to weird error: Protocol error (Target.getBrowserContexts): Target closed.
# - script: |
# set -e
# VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-linux-x64" \
# DISPLAY=:10 ./resources/server/test/test-web-integration.sh --browser chromium
# displayName: Run integration tests (Browser)
# condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- script: |
set -e
VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-linux-x64" \
yarn smoketest --web --headless --browser firefox
continueOnError: true
displayName: Run smoke tests (Firefox)
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- script: |

View File

@@ -48,13 +48,13 @@ steps:
# displayName: Download Built-in Extensions
- powershell: |
.\scripts\test.bat --tfs "Unit Tests"
displayName: Run Unit Tests
displayName: Run Unit Tests (Electron)
- powershell: |
yarn test-browser --browser chromium --browser webkit
yarn test-browser --browser chromium
displayName: Run Unit Tests (Browser)
# - powershell: | {{SQL CARBON EDIT}} remove step
# .\scripts\test-integration.bat --tfs "Integration Tests"
# displayName: Run Integration Tests
# displayName: Run Integration Tests (Electron)
- task: PublishTestResults@2
displayName: Publish Tests Results
inputs:

View File

@@ -109,7 +109,14 @@ steps:
$ErrorActionPreference = "Stop"
exec { yarn electron $(VSCODE_ARCH) }
exec { .\scripts\test.bat --build --tfs "Unit Tests" }
displayName: Run unit tests
displayName: Run unit tests (Electron)
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- powershell: |
. build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop"
exec { yarn test-browser --build --browser chromium }
displayName: Run unit tests (Browser)
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- powershell: |
@@ -122,7 +129,22 @@ steps:
$AppProductJson = Get-Content -Raw -Path "$AppRoot\resources\app\product.json" | ConvertFrom-Json
$AppNameShort = $AppProductJson.nameShort
exec { $env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe"; $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH)"; .\scripts\test-integration.bat --build --tfs "Integration Tests" }
displayName: Run integration tests
displayName: Run integration tests (Electron)
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- powershell: |
. build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop"
exec { $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-web-win32-$(VSCODE_ARCH)"; .\resources\server\test\test-web-integration.bat --browser chromium }
displayName: Run integration tests (Browser)
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- powershell: |
. build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop"
exec { $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-web-win32-$(VSCODE_ARCH)"; yarn smoketest --web --headless --browser chromium }
continueOnError: true
displayName: Run smoke tests (Browser)
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1

View File

@@ -333,7 +333,7 @@ function sanitizePath(path: string): string {
return path.replace(/^([a-z]):\\/i, (_, letter) => `${letter.toUpperCase()}:\\`);
}
const COMMIT_FORMAT = '%H\n%aN\n%aE\n%at\n%P\n%B';
const COMMIT_FORMAT = '%H%n%aN%n%aE%n%at%n%P%n%B';
export class Git {
@@ -801,8 +801,8 @@ export class Repository {
}
async log(options?: LogOptions): Promise<Commit[]> {
const maxEntries = options && typeof options.maxEntries === 'number' && options.maxEntries > 0 ? options.maxEntries : 32;
const args = ['log', '-' + maxEntries, `--format:${COMMIT_FORMAT}`, '-z'];
const maxEntries = options?.maxEntries ?? 32;
const args = ['log', `-n${maxEntries}`, `--format=${COMMIT_FORMAT}`, '-z', '--'];
const result = await this.run(args);
if (result.exitCode) {
@@ -815,7 +815,7 @@ export class Repository {
async logFile(uri: Uri, options?: LogFileOptions): Promise<Commit[]> {
const maxEntries = options?.maxEntries ?? 32;
const args = ['log', `-${maxEntries}`, `--format=${COMMIT_FORMAT}`, '-z', '--', uri.fsPath];
const args = ['log', `-n${maxEntries}`, `--format=${COMMIT_FORMAT}`, '-z', '--', uri.fsPath];
const result = await this.run(args);
if (result.exitCode) {

View File

@@ -333,7 +333,7 @@
"dependencies": {
"highlight.js": "9.15.10",
"markdown-it": "^10.0.0",
"markdown-it-front-matter": "^0.1.2",
"markdown-it-front-matter": "^0.2.1",
"vscode-extension-telemetry": "0.1.1",
"vscode-nls": "^4.0.0"
},

View File

@@ -0,0 +1 @@
DO NOT DELETE, USED BY INTEGRATION TESTS

View File

@@ -2821,10 +2821,10 @@ map-visit@^1.0.0:
dependencies:
object-visit "^1.0.0"
markdown-it-front-matter@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/markdown-it-front-matter/-/markdown-it-front-matter-0.1.2.tgz#e50bf56e77e6a4f5ac4ffa894d4d45ccd9896b20"
integrity sha1-5Qv1bnfmpPWsT/qJTU1FzNmJayA=
markdown-it-front-matter@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/markdown-it-front-matter/-/markdown-it-front-matter-0.2.1.tgz#dca49a827bb3cebb0528452c1d87dff276eb28dc"
integrity sha512-ydUIqlKfDscRpRUTRcA3maeeUKn3Cl5EaKZSA+I/f0KOGCBurW7e+bbz59sxqkC3FA9Q2S2+t4mpkH9T0BCM6A==
markdown-it@^10.0.0:
version "10.0.0"

View File

@@ -75,11 +75,11 @@
"vscode-minimist": "^1.2.2",
"vscode-nsfw": "1.2.8",
"vscode-proxy-agent": "^0.5.2",
"vscode-ripgrep": "^1.5.7",
"vscode-ripgrep": "^1.5.8",
"vscode-sqlite3": "4.0.9",
"vscode-textmate": "4.4.0",
"xterm": "4.4.0",
"xterm-addon-search": "0.4.0",
"xterm-addon-search": "0.5.0",
"xterm-addon-unicode11": "0.1.1",
"xterm-addon-web-links": "0.2.1",
"xterm-addon-webgl": "0.5.0",

View File

@@ -18,10 +18,10 @@
"vscode-minimist": "^1.2.2",
"vscode-nsfw": "1.2.8",
"vscode-proxy-agent": "^0.5.2",
"vscode-ripgrep": "^1.5.7",
"vscode-ripgrep": "^1.5.8",
"vscode-textmate": "4.4.0",
"xterm": "4.4.0",
"xterm-addon-search": "0.4.0",
"xterm-addon-search": "0.5.0",
"xterm-addon-unicode11": "0.1.1",
"xterm-addon-web-links": "0.2.1",
"xterm-addon-webgl": "0.5.0",

View File

@@ -6,7 +6,7 @@
"semver-umd": "^5.5.5",
"vscode-textmate": "4.4.0",
"xterm": "4.4.0",
"xterm-addon-search": "0.4.0",
"xterm-addon-search": "0.5.0",
"xterm-addon-unicode11": "0.1.1",
"xterm-addon-web-links": "0.2.1",
"xterm-addon-webgl": "0.5.0"

View File

@@ -31,10 +31,10 @@ vscode-textmate@4.4.0:
dependencies:
oniguruma "^7.2.0"
xterm-addon-search@0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.4.0.tgz#a7beadb3caa7330eb31fb1f17d92de25537684a1"
integrity sha512-g07qb/Z4aSfrQ25e6Z6rz6KiExm2DvesQXkx+eA715VABBr5VM/9Jf0INoCiDSYy/nn7rpna+kXiGVJejIffKg==
xterm-addon-search@0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.5.0.tgz#cd3a2f8056084c28e236d4e732da37682010bcc2"
integrity sha512-zLVqVTrg5w2nk9fRj3UuVKCPo/dmFe/cLf3EM9Is5Dm6cgOoXmeo9eq2KgD8A0gquAflTFTf0ya2NaFmShHwyg==
xterm-addon-unicode11@0.1.1:
version "0.1.1"

View File

@@ -389,10 +389,10 @@ vscode-proxy-agent@^0.5.2:
https-proxy-agent "^2.2.3"
socks-proxy-agent "^4.0.1"
vscode-ripgrep@^1.5.7:
version "1.5.7"
resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.5.7.tgz#acb6b548af488a4bca5d0f1bb5faf761343289ce"
integrity sha512-/Vsz/+k8kTvui0q3O74pif9FK0nKopgFTiGNVvxicZANxtSA8J8gUE9GQ/4dpi7D/2yI/YVORszwVskFbz46hQ==
vscode-ripgrep@^1.5.8:
version "1.5.8"
resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.5.8.tgz#32cb33da6d1a9ca8f5de8c2813ed5114fd55fc11"
integrity sha512-l6Pv/t1Jk63RU+kEkMO04XxnNRYdyzuesizj9AzFpcfrUxxpAjEJBK1qO9Mov30UUGZl7uDUBn+uCv9koaHPPA==
vscode-textmate@4.4.0:
version "4.4.0"
@@ -413,10 +413,10 @@ vscode-windows-registry@1.0.2:
resolved "https://registry.yarnpkg.com/vscode-windows-registry/-/vscode-windows-registry-1.0.2.tgz#b863e704a6a69c50b3098a55fbddbe595b0c124a"
integrity sha512-/CLLvuOSM2Vme2z6aNyB+4Omd7hDxpf4Thrt8ImxnXeQtxzel2bClJpFQvQqK/s4oaXlkBKS7LqVLeZM+uSVIA==
xterm-addon-search@0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.4.0.tgz#a7beadb3caa7330eb31fb1f17d92de25537684a1"
integrity sha512-g07qb/Z4aSfrQ25e6Z6rz6KiExm2DvesQXkx+eA715VABBr5VM/9Jf0INoCiDSYy/nn7rpna+kXiGVJejIffKg==
xterm-addon-search@0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.5.0.tgz#cd3a2f8056084c28e236d4e732da37682010bcc2"
integrity sha512-zLVqVTrg5w2nk9fRj3UuVKCPo/dmFe/cLf3EM9Is5Dm6cgOoXmeo9eq2KgD8A0gquAflTFTf0ya2NaFmShHwyg==
xterm-addon-unicode11@0.1.1:
version "0.1.1"

View File

@@ -39,17 +39,20 @@ if "%INTEGRATION_TEST_ELECTRON_PATH%"=="" (
:: Tests in the extension host
call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-api-tests\testWorkspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\singlefolder-tests --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR%
if %errorlevel% neq 0 exit /b %errorlevel%
REM call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-api-tests\testWorkspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\singlefolder-tests --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR%
REM if %errorlevel% neq 0 exit /b %errorlevel%
call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-api-tests\testworkspace.code-workspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\workspace-tests --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR%
if %errorlevel% neq 0 exit /b %errorlevel%
REM call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-api-tests\testworkspace.code-workspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\workspace-tests --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR%
REM if %errorlevel% neq 0 exit /b %errorlevel%
call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-colorize-tests\test --extensionDevelopmentPath=%~dp0\..\extensions\vscode-colorize-tests --extensionTestsPath=%~dp0\..\extensions\vscode-colorize-tests\out --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR%
if %errorlevel% neq 0 exit /b %errorlevel%
REM call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-colorize-tests\test --extensionDevelopmentPath=%~dp0\..\extensions\vscode-colorize-tests --extensionTestsPath=%~dp0\..\extensions\vscode-colorize-tests\out --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR%
REM if %errorlevel% neq 0 exit /b %errorlevel%
:: call "%INTEGRATION_TEST_ELECTRON_PATH%" $%~dp0\..\extensions\emmet\test-fixtures --extensionDevelopmentPath=%~dp0\..\extensions\emmet --extensionTestsPath=%~dp0\..\extensions\emmet\out\test --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% .
:: if %errorlevel% neq 0 exit /b %errorlevel%
REM call "%INTEGRATION_TEST_ELECTRON_PATH%" $%~dp0\..\extensions\markdown-language-features\out\test\test-fixtures --extensionDevelopmentPath=%~dp0\..\extensions\markdown-language-features --extensionTestsPath=%~dp0\..\extensions\markdown-language-features\out\test --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% .
REM if %errorlevel% neq 0 exit /b %errorlevel%
REM call "%INTEGRATION_TEST_ELECTRON_PATH%" $%~dp0\..\extensions\emmet\out\test\test-fixtures --extensionDevelopmentPath=%~dp0\..\extensions\emmet --extensionTestsPath=%~dp0\..\extensions\emmet\out\test --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% .
REM if %errorlevel% neq 0 exit /b %errorlevel%
call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\azurecore\test-fixtures --extensionDevelopmentPath=%~dp0\..\extensions\azurecore --extensionTestsPath=%~dp0\..\extensions\azurecore\out\test --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR%
if %errorlevel% neq 0 exit /b %errorlevel%

View File

@@ -44,14 +44,12 @@ fi
# Tests in the extension host
# "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-api-tests/testWorkspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/singlefolder-tests --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --user-data-dir=$VSCODEUSERDATADIR
# "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-api-tests/testworkspace.code-workspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/workspace-tests --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --user-data-dir=$VSCODEUSERDATADIR
"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-colorize-tests/test --extensionDevelopmentPath=$ROOT/extensions/vscode-colorize-tests --extensionTestsPath=$ROOT/extensions/vscode-colorize-tests/out --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --user-data-dir=$VSCODEUSERDATADIR
"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/markdown-language-features/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/markdown-language-features --extensionTestsPath=$ROOT/extensions/markdown-language-features/out/test --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --user-data-dir=$VSCODEUSERDATADIR
"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/azurecore/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/azurecore --extensionTestsPath=$ROOT/extensions/azurecore/out/test --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --user-data-dir=$VSCODEUSERDATADIR
# "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-colorize-tests/test --extensionDevelopmentPath=$ROOT/extensions/vscode-colorize-tests --extensionTestsPath=$ROOT/extensions/vscode-colorize-tests/out --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --user-data-dir=$VSCODEUSERDATADIR
# "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/markdown-language-features/out/test/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/markdown-language-features --extensionTestsPath=$ROOT/extensions/markdown-language-features/out/test --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --user-data-dir=$VSCODEUSERDATADIR
# "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/emmet/out/test/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --user-data-dir=$VSCODEUSERDATADIR
# "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $(mktemp -d 2>/dev/null) --extensionDevelopmentPath=$ROOT/extensions/git --extensionTestsPath=$ROOT/extensions/git/out/test --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --user-data-dir=$VSCODEUSERDATADIR
mkdir -p $ROOT/extensions/emmet/test-fixtures
"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/emmet/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --user-data-dir=$VSCODEUSERDATADIR
rm -rf $ROOT/extensions/emmet/test-fixtures
"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/azurecore/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/azurecore --extensionTestsPath=$ROOT/extensions/azurecore/out/test --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --user-data-dir=$VSCODEUSERDATADIR
# Remote Integration Tests
if [ -f ./resources/server/test/test-remote-integration.sh ]; then

View File

@@ -35,7 +35,7 @@ import { UntitledNotebookInput } from 'sql/workbench/contrib/notebook/common/mod
import { FileNotebookInput } from 'sql/workbench/contrib/notebook/common/models/fileNotebookInput';
import { find } from 'vs/base/common/arrays';
import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService';
import { UntitledTextEditorInput } from 'vs/workbench/common/editor/untitledTextEditorInput';
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';

View File

@@ -19,7 +19,7 @@ import { IModelService } from 'vs/editor/common/services/modelService';
import { ComponentBase } from 'sql/workbench/browser/modelComponents/componentBase';
import { QueryTextEditor } from 'sql/workbench/browser/modelComponents/queryTextEditor';
import { ILogService } from 'vs/platform/log/common/log';
import { UntitledTextEditorInput } from 'vs/workbench/common/editor/untitledTextEditorInput';
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { IEditorProgressService } from 'vs/platform/progress/common/progress';
import { SimpleProgressIndicator } from 'sql/workbench/services/progress/browser/simpleProgressIndicator';

View File

@@ -21,7 +21,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { UntitledTextEditorInput } from 'vs/workbench/common/editor/untitledTextEditorInput';
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
/**

View File

@@ -33,7 +33,7 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
import { isUndefinedOrNull } from 'vs/base/common/types';
import { UntitledTextEditorInput } from 'vs/workbench/common/editor/untitledTextEditorInput';
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
import { LabelService } from 'vs/workbench/services/label/common/labelService';
class TestParsedArgs implements ParsedArgs {

View File

@@ -29,7 +29,7 @@ import {
import { TextResourceEditor } from 'vs/workbench/browser/parts/editor/textResourceEditor';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { UntitledTextEditorInput } from 'vs/workbench/common/editor/untitledTextEditorInput';
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
import { IFlexibleSash, HorizontalFlexibleSash } from 'sql/workbench/contrib/query/browser/flexibleSash';
import { EditDataResultsEditor } from 'sql/workbench/contrib/editData/browser/editDataResultsEditor';
import { EditDataResultsInput } from 'sql/workbench/contrib/editData/browser/editDataResultsInput';

View File

@@ -14,7 +14,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
import Severity from 'vs/base/common/severity';
import { EditDataResultsInput } from 'sql/workbench/contrib/editData/browser/editDataResultsInput';
import { IEditorViewState } from 'vs/editor/common/editorCommon';
import { UntitledTextEditorInput } from 'vs/workbench/common/editor/untitledTextEditorInput';
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
/**
* Input for the EditDataEditor.

View File

@@ -15,7 +15,7 @@ import { Registry } from 'vs/platform/registry/common/platform';
import * as path from 'vs/base/common/path';
import { ILanguageAssociationRegistry, Extensions as LanguageAssociationExtensions } from 'sql/workbench/services/languageAssociation/common/languageAssociation';
import { UntitledTextEditorInput } from 'vs/workbench/common/editor/untitledTextEditorInput';
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
const languageAssociationRegistry = Registry.as<ILanguageAssociationRegistry>(LanguageAssociationExtensions.LanguageAssociations);

View File

@@ -24,7 +24,7 @@ import { QueryEditorLanguageAssociation } from 'sql/workbench/contrib/query/comm
import { workbenchInstantiationService } from 'sql/workbench/test/workbenchTestServices';
import { NotebookEditorInputAssociation } from 'sql/workbench/contrib/notebook/common/models/nodebookInputFactory';
import { NotebookInput } from 'sql/workbench/contrib/notebook/browser/models/notebookInput';
import { UntitledTextEditorInput } from 'vs/workbench/common/editor/untitledTextEditorInput';
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
import { UntitledQueryEditorInput } from 'sql/workbench/contrib/query/common/untitledQueryEditorInput';
import { INotebookService } from 'sql/workbench/services/notebook/browser/notebookService';
import { NotebookServiceStub } from 'sql/workbench/contrib/notebook/test/stubs';

View File

@@ -30,8 +30,8 @@ import { ILogService } from 'vs/platform/log/common/log';
import { CollapseComponent } from 'sql/workbench/contrib/notebook/browser/cellViews/collapse.component';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { CellView } from 'sql/workbench/contrib/notebook/browser/cellViews/interfaces';
import { UntitledTextEditorInput } from 'vs/workbench/common/editor/untitledTextEditorInput';
import { UntitledTextEditorModel } from 'vs/workbench/common/editor/untitledTextEditorModel';
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
import { UntitledTextEditorModel } from 'vs/workbench/services/untitled/common/untitledTextEditorModel';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { IEditorProgressService } from 'vs/platform/progress/common/progress';
import { SimpleProgressIndicator } from 'sql/workbench/services/progress/browser/simpleProgressIndicator';

View File

@@ -26,8 +26,8 @@ import { Deferred } from 'sql/base/common/promise';
import { NotebookTextFileModel } from 'sql/workbench/contrib/notebook/browser/models/notebookTextFileModel';
import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfigurationService';
import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel';
import { UntitledTextEditorModel } from 'vs/workbench/common/editor/untitledTextEditorModel';
import { UntitledTextEditorInput } from 'vs/workbench/common/editor/untitledTextEditorInput';
import { UntitledTextEditorModel } from 'vs/workbench/services/untitled/common/untitledTextEditorModel';
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput';
import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput';
import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel';

View File

@@ -10,7 +10,7 @@ import { FILE_EDITOR_INPUT_ID } from 'vs/workbench/contrib/files/common/files';
import { FileNotebookInput } from 'sql/workbench/contrib/notebook/common/models/fileNotebookInput';
import { UntitledNotebookInput } from 'sql/workbench/contrib/notebook/common/models/untitledNotebookInput';
import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput';
import { UntitledTextEditorInput } from 'vs/workbench/common/editor/untitledTextEditorInput';
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
import { ILanguageAssociation } from 'sql/workbench/services/languageAssociation/common/languageAssociation';
import { NotebookInput } from 'sql/workbench/contrib/notebook/browser/models/notebookInput';

View File

@@ -9,7 +9,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { NotebookInput } from 'sql/workbench/contrib/notebook/browser/models/notebookInput';
import { INotebookService } from 'sql/workbench/services/notebook/browser/notebookService';
import { UntitledTextEditorInput } from 'vs/workbench/common/editor/untitledTextEditorInput';
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
export class UntitledNotebookInput extends NotebookInput {
public static ID: string = 'workbench.editorinputs.untitledNotebookInput';

View File

@@ -12,10 +12,10 @@ import { URI } from 'vs/base/common/uri';
import { UntitledNotebookInput } from 'sql/workbench/contrib/notebook/common/models/untitledNotebookInput';
import { FileNotebookInput } from 'sql/workbench/contrib/notebook/common/models/fileNotebookInput';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { UntitledTextEditorModel } from 'vs/workbench/common/editor/untitledTextEditorModel';
import { UntitledTextEditorModel } from 'vs/workbench/services/untitled/common/untitledTextEditorModel';
import { NodeStub, NotebookServiceStub } from 'sql/workbench/contrib/notebook/test/stubs';
import { basenameOrAuthority } from 'vs/base/common/resources';
import { UntitledTextEditorInput } from 'vs/workbench/common/editor/untitledTextEditorInput';
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
import { IExtensionService, NullExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { INotebookService, IProviderInfo } from 'sql/workbench/services/notebook/browser/notebookService';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';

View File

@@ -51,7 +51,7 @@ import { CellSelectionModel } from 'sql/base/browser/ui/table/plugins/cellSelect
import { handleCopyRequest } from 'sql/workbench/contrib/profiler/browser/profilerCopyHandler';
import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfigurationService';
import { find } from 'vs/base/common/arrays';
import { UntitledTextEditorInput } from 'vs/workbench/common/editor/untitledTextEditorInput';
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
import { attachTabbedPanelStyler } from 'sql/workbench/common/styler';
class BasicView implements IView {

View File

@@ -19,7 +19,7 @@ import { EditorOptions } from 'vs/workbench/common/editor';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { UntitledTextEditorInput } from 'vs/workbench/common/editor/untitledTextEditorInput';
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
class ProfilerResourceCodeEditor extends CodeEditorWidget {

View File

@@ -11,7 +11,7 @@ import { FILE_EDITOR_INPUT_ID } from 'vs/workbench/contrib/files/common/files';
import { UntitledQueryEditorInput } from 'sql/workbench/contrib/query/common/untitledQueryEditorInput';
import { FileQueryEditorInput } from 'sql/workbench/contrib/query/common/fileQueryEditorInput';
import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput';
import { UntitledTextEditorInput } from 'vs/workbench/common/editor/untitledTextEditorInput';
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
import { ILanguageAssociation } from 'sql/workbench/services/languageAssociation/common/languageAssociation';
import { QueryEditorInput } from 'sql/workbench/contrib/query/common/queryEditorInput';
import { getCurrentGlobalConnection } from 'sql/workbench/browser/taskUtilities';

View File

@@ -11,12 +11,10 @@ import { IQueryModelService } from 'sql/workbench/services/query/common/queryMod
import { IEncodingSupport, EncodingMode } from 'vs/workbench/common/editor';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService';
import { UntitledTextEditorInput } from 'vs/workbench/common/editor/untitledTextEditorInput';
import { UntitledTextEditorModel } from 'vs/workbench/common/editor/untitledTextEditorModel';
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
import { UntitledTextEditorModel } from 'vs/workbench/services/untitled/common/untitledTextEditorModel';
type PublicPart<T> = { [K in keyof T]: T[K] };
export class UntitledQueryEditorInput extends QueryEditorInput implements IEncodingSupport, PublicPart<UntitledTextEditorInput> {
export class UntitledQueryEditorInput extends QueryEditorInput implements IEncodingSupport {
public static readonly ID = 'workbench.editorInput.untitledQueryInput';

View File

@@ -32,7 +32,7 @@ import { URI } from 'vs/base/common/uri';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { TestConnectionManagementService } from 'sql/platform/connection/test/common/testConnectionManagementService';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { UntitledTextEditorInput } from 'vs/workbench/common/editor/untitledTextEditorInput';
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
import { LabelService } from 'vs/workbench/services/label/common/labelService';
suite('SQL QueryAction Tests', () => {

View File

@@ -20,7 +20,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { TestStorageService, TestFileService } from 'vs/workbench/test/browser/workbenchTestServices';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { UntitledTextEditorInput } from 'vs/workbench/common/editor/untitledTextEditorInput';
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
import { UntitledQueryEditorInput } from 'sql/workbench/contrib/query/common/untitledQueryEditorInput';
import { TestQueryModelService } from 'sql/workbench/services/query/test/common/testQueryModelService';
import { Event } from 'vs/base/common/event';

View File

@@ -19,7 +19,7 @@ import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { IConnectionManagementService, IConnectionCompletionOptions, IConnectionCallbacks, IConnectionResult } from 'sql/platform/connection/common/connectionManagement';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { UntitledQueryEditorInput } from 'sql/workbench/contrib/query/common/untitledQueryEditorInput';
import { UntitledTextEditorInput } from 'vs/workbench/common/editor/untitledTextEditorInput';
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
suite('Query Input Factory', () => {

View File

@@ -7,7 +7,7 @@ import { EditorInput, EditorModel, IEditorInput } from 'vs/workbench/common/edit
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { IFileService } from 'vs/platform/files/common/files';
import { URI } from 'vs/base/common/uri';
import { UntitledTextEditorInput } from 'vs/workbench/common/editor/untitledTextEditorInput';
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ILanguageAssociation } from 'sql/workbench/services/languageAssociation/common/languageAssociation';

View File

@@ -4,10 +4,10 @@
*--------------------------------------------------------------------------------------------*/
import { EditorInput } from 'vs/workbench/common/editor';
import { UntitledTextEditorInput } from 'vs/workbench/common/editor/untitledTextEditorInput';
import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput';
import { Extensions as ILanguageAssociationExtensions, ILanguageAssociationRegistry } from 'sql/workbench/services/languageAssociation/common/languageAssociation';
import { Registry } from 'vs/platform/registry/common/platform';
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
const languageRegistry = Registry.as<ILanguageAssociationRegistry>(ILanguageAssociationExtensions.LanguageAssociations);

View File

@@ -140,7 +140,7 @@ export class QueryEditorService implements IQueryEditorService {
let counter = 1;
// Get document name and check if it exists
let filePath = prefixFileName(counter);
while (this._untitledEditorService.exists(URI.from({ scheme: Schemas.untitled, path: filePath }))) {
while (this._untitledEditorService.get(URI.from({ scheme: Schemas.untitled, path: filePath }))) {
counter++;
filePath = prefixFileName(counter);
}

View File

@@ -5,6 +5,7 @@
import * as dom from 'vs/base/browser/dom';
import * as platform from 'vs/base/common/platform';
import * as browser from 'vs/base/browser/browser';
import { IframeUtils } from 'vs/base/browser/iframe';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
@@ -102,7 +103,7 @@ export class GlobalMouseMoveMonitor<R extends { buttons: number; }> implements I
for (const element of listenTo) {
this._hooks.add(dom.addDisposableThrottledListener(element, mouseMove,
(data: R) => {
if (data.buttons !== initialButtons) {
if (!browser.isIE && data.buttons !== initialButtons) {
// Buttons state has changed in the meantime
this.stopMonitoring(true);
return;

View File

@@ -9,7 +9,7 @@ import * as strings from 'vs/base/common/strings';
import { IActionRunner, IAction, Action, IActionViewItem } from 'vs/base/common/actions';
import { ActionBar, IActionViewItemProvider, ActionsOrientation, Separator, ActionViewItem, IActionViewItemOptions, BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar';
import { ResolvedKeybinding, KeyCode } from 'vs/base/common/keyCodes';
import { addClass, EventType, EventHelper, EventLike, removeTabIndexAndUpdateFocus, isAncestor, hasClass, addDisposableListener, removeClass, append, $, addClasses, removeClasses } from 'vs/base/browser/dom';
import { addClass, EventType, EventHelper, EventLike, removeTabIndexAndUpdateFocus, isAncestor, hasClass, addDisposableListener, removeClass, append, $, addClasses, removeClasses, clearNode } from 'vs/base/browser/dom';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { RunOnceScheduler } from 'vs/base/common/async';
import { DisposableStore } from 'vs/base/common/lifecycle';
@@ -464,7 +464,13 @@ class BaseMenuActionViewItem extends BaseActionViewItem {
}
updateLabel(): void {
if (!this.label) {
return;
}
if (this.options.label) {
clearNode(this.label);
let label = this.getAction().label;
if (label) {
const cleanLabel = cleanMnemonic(label);
@@ -472,9 +478,7 @@ class BaseMenuActionViewItem extends BaseActionViewItem {
label = cleanLabel;
}
if (this.label) {
this.label.setAttribute('aria-label', cleanLabel.replace(/&&/g, '&'));
}
this.label.setAttribute('aria-label', cleanLabel.replace(/&&/g, '&'));
const matches = MENU_MNEMONIC_REGEX.exec(label);
@@ -490,22 +494,25 @@ class BaseMenuActionViewItem extends BaseActionViewItem {
escMatch = MENU_ESCAPED_MNEMONIC_REGEX.exec(label);
}
const replaceDoubleEscapes = (str: string) => str.replace(/&amp;&amp;/g, '&amp;');
if (escMatch) {
label = `${label.substr(0, escMatch.index)}<u aria-hidden="true">${escMatch[3]}</u>${label.substr(escMatch.index + escMatch[0].length)}`;
this.label.append(
strings.ltrim(replaceDoubleEscapes(label.substr(0, escMatch.index)), ' '),
$('u', { 'aria-hidden': 'true' },
escMatch[3]),
strings.rtrim(replaceDoubleEscapes(label.substr(escMatch.index + escMatch[0].length)), ' '));
} else {
this.label.innerText = replaceDoubleEscapes(label).trim();
}
label = label.replace(/&amp;&amp;/g, '&amp;');
if (this.item) {
this.item.setAttribute('aria-keyshortcuts', (!!matches[1] ? matches[1] : matches[3]).toLocaleLowerCase());
}
} else {
label = label.replace(/&&/g, '&');
this.label.innerText = label.replace(/&&/g, '&').trim();
}
}
if (this.label) {
this.label.innerHTML = label.trim();
}
}
}

View File

@@ -360,7 +360,7 @@ function doWriteFileAndFlush(path: string, data: string | Buffer | Uint8Array, o
}
// Flush contents (not metadata) of the file to disk
fs.fdatasync(fd, (syncError: Error) => {
fs.fdatasync(fd, (syncError: Error | null) => {
// In some exotic setups it is well possible that node fails to sync
// In that case we disable flushing and warn to the console

View File

@@ -409,7 +409,7 @@ export function createQueuedSender(childProcess: cp.ChildProcess): IQueuedSender
return;
}
const result = childProcess.send(msg, (error: Error) => {
const result = childProcess.send(msg, (error: Error | null) => {
if (error) {
console.error(error); // unlikely to happen, best we can do is log this error
}

View File

@@ -440,7 +440,7 @@ export class IssueReporter extends Disposable {
sendWorkbenchCommand('workbench.action.reloadWindowWithExtensionsDisabled');
});
this.addEventListener('extensionBugsLink', 'click', (e: MouseEvent) => {
this.addEventListener('extensionBugsLink', 'click', (e: Event) => {
const url = (<HTMLElement>e.target).innerText;
shell.openExternal(url);
});

View File

@@ -12,7 +12,7 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment';
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
import { ExtensionManagementChannel, GlobalExtensionEnablementServiceClient } from 'vs/platform/extensionManagement/common/extensionManagementIpc';
import { ExtensionManagementChannel } from 'vs/platform/extensionManagement/common/extensionManagementIpc';
import { IExtensionManagementService, IExtensionGalleryService, IGlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService';
import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService';
@@ -49,7 +49,7 @@ import { IFileService } from 'vs/platform/files/common/files';
import { DiskFileSystemProvider } from 'vs/platform/files/electron-browser/diskFileSystemProvider';
import { Schemas } from 'vs/base/common/network';
import { IProductService } from 'vs/platform/product/common/productService';
import { IUserDataSyncService, IUserDataSyncStoreService, registerConfiguration, IUserDataSyncLogService, IUserDataSyncUtilService, ISettingsSyncService, IUserDataAuthTokenService } from 'vs/platform/userDataSync/common/userDataSync';
import { IUserDataSyncService, IUserDataSyncStoreService, registerConfiguration, IUserDataSyncLogService, IUserDataSyncUtilService, ISettingsSyncService, IUserDataAuthTokenService, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync';
import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService';
import { UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService';
import { UserDataSyncChannel, UserDataSyncUtilServiceClient, SettingsSyncChannel, UserDataAuthTokenServiceChannel, UserDataAutoSyncChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc';
@@ -61,6 +61,11 @@ import { KeytarCredentialsService } from 'vs/platform/credentials/node/credentia
import { UserDataAutoSyncService } from 'vs/platform/userDataSync/electron-browser/userDataAutoSyncService';
import { SettingsSynchroniser } from 'vs/platform/userDataSync/common/settingsSync';
import { UserDataAuthTokenService } from 'vs/platform/userDataSync/common/userDataAuthTokenService';
import { NativeStorageService } from 'vs/platform/storage/node/storageService';
import { GlobalStorageDatabaseChannelClient } from 'vs/platform/storage/node/storageIpc';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { GlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService';
import { UserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSyncEnablementService';
export interface ISharedProcessConfiguration {
readonly machineId: string;
@@ -100,7 +105,7 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
const onExit = () => disposables.dispose();
process.once('exit', onExit);
ipcRenderer.once('handshake:goodbye', onExit);
ipcRenderer.once('electron-main->shared-process: exit', onExit);
disposables.add(server);
@@ -119,6 +124,11 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
disposables.add(configurationService);
await configurationService.initialize();
const storageService = new NativeStorageService(new GlobalStorageDatabaseChannelClient(mainProcessService.getChannel('storage')), logService, environmentService);
await storageService.initialize();
services.set(IStorageService, storageService);
disposables.add(toDisposable(() => storageService.flush()));
services.set(IEnvironmentService, environmentService);
services.set(IProductService, { _serviceBrand: undefined, ...product });
services.set(ILogService, logService);
@@ -147,7 +157,7 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
instantiationService.invokeFunction(accessor => {
const services = new ServiceCollection();
const environmentService = accessor.get(IEnvironmentService);
const { appRoot, extensionsPath, extensionDevelopmentLocationURI: extensionDevelopmentLocationURI, isBuilt, installSourcePath } = environmentService;
const { appRoot, extensionsPath, extensionDevelopmentLocationURI, isBuilt, installSourcePath } = environmentService;
const telemetryLogService = new FollowerLogService(loggerClient, new SpdLogService('telemetry', environmentService.logsPath, initData.logLevel));
telemetryLogService.info('The below are logs for every telemetry event sent from VS Code once the log level is set to trace.');
telemetryLogService.info('===========================================================');
@@ -181,8 +191,9 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
services.set(IUserDataAuthTokenService, new SyncDescriptor(UserDataAuthTokenService));
services.set(IUserDataSyncLogService, new SyncDescriptor(UserDataSyncLogService));
services.set(IUserDataSyncUtilService, new UserDataSyncUtilServiceClient(server.getChannel('userDataSyncUtil', client => client.ctx !== 'main')));
services.set(IGlobalExtensionEnablementService, new GlobalExtensionEnablementServiceClient(server.getChannel('globalExtensionEnablement', client => client.ctx !== 'main')));
services.set(IGlobalExtensionEnablementService, new SyncDescriptor(GlobalExtensionEnablementService));
services.set(IUserDataSyncStoreService, new SyncDescriptor(UserDataSyncStoreService));
services.set(IUserDataSyncEnablementService, new SyncDescriptor(UserDataSyncEnablementService));
services.set(ISettingsSyncService, new SyncDescriptor(SettingsSynchroniser));
services.set(IUserDataSyncService, new SyncDescriptor(UserDataSyncService));
registerConfiguration();
@@ -271,13 +282,20 @@ function setupIPC(hook: string): Promise<Server> {
}
async function handshake(configuration: ISharedProcessConfiguration): Promise<void> {
// receive payload from electron-main to start things
const data = await new Promise<ISharedProcessInitData>(c => {
ipcRenderer.once('handshake:hey there', (_: any, r: ISharedProcessInitData) => c(r));
ipcRenderer.send('handshake:hello');
ipcRenderer.once('electron-main->shared-process: payload', (_: any, r: ISharedProcessInitData) => c(r));
// tell electron-main we are ready to receive payload
ipcRenderer.send('shared-process->electron-main: ready-for-payload');
});
// await IPC connection and signal this back to electron-main
const server = await setupIPC(data.sharedIPCHandle);
ipcRenderer.send('shared-process->electron-main: ipc-ready');
// await initialization and signal this back to electron-main
await main(server, data, configuration);
ipcRenderer.send('handshake:im ready');
ipcRenderer.send('shared-process->electron-main: init-done');
}

View File

@@ -364,7 +364,14 @@ export class CodeApplication extends Disposable {
// Spawn shared process after the first window has opened and 3s have passed
const sharedProcess = this.instantiationService.createInstance(SharedProcess, machineId, this.userEnv);
const sharedProcessClient = sharedProcess.whenReady().then(() => connect(this.environmentService.sharedIPCHandle, 'main'));
const sharedProcessClient = sharedProcess.whenIpcReady().then(() => {
this.logService.trace('Shared process: IPC ready');
return connect(this.environmentService.sharedIPCHandle, 'main');
});
const sharedProcessReady = sharedProcess.whenReady().then(() => {
this.logService.trace('Shared process: init ready');
return sharedProcessClient;
});
this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen).then(() => {
this._register(new RunOnceScheduler(async () => {
const userEnv = await getShellEnvironment(this.logService, this.environmentService);
@@ -374,7 +381,7 @@ export class CodeApplication extends Disposable {
});
// Services
const appInstantiationService = await this.createServices(machineId, trueMachineId, sharedProcess, sharedProcessClient);
const appInstantiationService = await this.createServices(machineId, trueMachineId, sharedProcess, sharedProcessReady);
// Create driver
if (this.environmentService.driverHandle) {
@@ -391,7 +398,7 @@ export class CodeApplication extends Disposable {
const windows = appInstantiationService.invokeFunction(accessor => this.openFirstWindow(accessor, electronIpcServer, sharedProcessClient));
// Post Open Windows Tasks
this.afterWindowOpen();
appInstantiationService.invokeFunction(this.afterWindowOpen.bind(this));
// Tracing: Stop tracing after windows are ready if enabled
if (this.environmentService.args.trace) {
@@ -424,7 +431,7 @@ export class CodeApplication extends Disposable {
return { machineId, trueMachineId };
}
private async createServices(machineId: string, trueMachineId: string | undefined, sharedProcess: SharedProcess, sharedProcessClient: Promise<Client<string>>): Promise<IInstantiationService> {
private async createServices(machineId: string, trueMachineId: string | undefined, sharedProcess: SharedProcess, sharedProcessReady: Promise<Client<string>>): Promise<IInstantiationService> {
const services = new ServiceCollection();
const fileService = this._register(new FileService(this.logService));
@@ -456,7 +463,7 @@ export class CodeApplication extends Disposable {
services.set(ISharedProcessMainService, new SyncDescriptor(SharedProcessMainService, [sharedProcess]));
services.set(ILaunchMainService, new SyncDescriptor(LaunchMainService));
const diagnosticsChannel = getDelayedChannel(sharedProcessClient.then(client => client.getChannel('diagnostics')));
const diagnosticsChannel = getDelayedChannel(sharedProcessReady.then(client => client.getChannel('diagnostics')));
services.set(IDiagnosticsService, new SyncDescriptor(DiagnosticsService, [diagnosticsChannel]));
services.set(IIssueService, new SyncDescriptor(IssueMainService, [machineId, this.userEnv]));
@@ -477,7 +484,7 @@ export class CodeApplication extends Disposable {
// Telemetry
if (!this.environmentService.isExtensionDevelopment && !this.environmentService.args['disable-telemetry'] && !!product.enableTelemetry) {
const channel = getDelayedChannel(sharedProcessClient.then(client => client.getChannel('telemetryAppender')));
const channel = getDelayedChannel(sharedProcessReady.then(client => client.getChannel('telemetryAppender')));
const appender = combinedAppender(new TelemetryAppenderClient(channel), new LogAppender(this.logService));
const commonProperties = resolveCommonProperties(product.commit, product.version, machineId, product.msftInternalDomains, this.environmentService.installSourcePath);
const piiPaths = this.environmentService.extensionsPath ? [this.environmentService.appRoot, this.environmentService.extensionsPath] : [this.environmentService.appRoot];
@@ -571,6 +578,7 @@ export class CodeApplication extends Disposable {
const storageMainService = accessor.get(IStorageMainService);
const storageChannel = this._register(new GlobalStorageDatabaseChannel(this.logService, storageMainService));
electronIpcServer.registerChannel('storage', storageChannel);
sharedProcessClient.then(client => client.registerChannel('storage', storageChannel));
const loggerChannel = new LoggerChannel(accessor.get(ILogService));
electronIpcServer.registerChannel('logger', loggerChannel);
@@ -706,13 +714,18 @@ export class CodeApplication extends Disposable {
return { fileUri: URI.file(path) };
}
private afterWindowOpen(): void {
private afterWindowOpen(accessor: ServicesAccessor): void {
// Signal phase: after window open
this.lifecycleMainService.phase = LifecycleMainPhase.AfterWindowOpen;
// Remote Authorities
this.handleRemoteAuthorities();
// Initialize update service
const updateService = accessor.get(IUpdateService);
if (updateService instanceof Win32UpdateService || updateService instanceof LinuxUpdateService || updateService instanceof DarwinUpdateService) {
updateService.initialize();
}
}
private handleRemoteAuthorities(): void {

View File

@@ -21,6 +21,8 @@ export class SharedProcess implements ISharedProcess {
private window: BrowserWindow | null = null;
private readonly _whenReady: Promise<void>;
constructor(
private readonly machineId: string,
private userEnv: NodeJS.ProcessEnv,
@@ -28,10 +30,13 @@ export class SharedProcess implements ISharedProcess {
@ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService,
@ILogService private readonly logService: ILogService,
@IThemeMainService private readonly themeMainService: IThemeMainService
) { }
) {
// overall ready promise when shared process signals initialization is done
this._whenReady = new Promise<void>(c => ipcMain.once('shared-process->electron-main: init-done', () => c(undefined)));
}
@memoize
private get _whenReady(): Promise<void> {
private get _whenIpcReady(): Promise<void> {
this.window = new BrowserWindow({
show: false,
backgroundColor: this.themeMainService.getBackgroundColor(),
@@ -98,16 +103,19 @@ export class SharedProcess implements ISharedProcess {
});
return new Promise<void>(c => {
const onHello = Event.once(Event.fromNodeEventEmitter(ipcMain, 'handshake:hello', ({ sender }: { sender: WebContents }) => sender));
disposables.add(onHello(sender => {
sender.send('handshake:hey there', {
// send payload once shared process is ready to receive it
disposables.add(Event.once(Event.fromNodeEventEmitter(ipcMain, 'shared-process->electron-main: ready-for-payload', ({ sender }: { sender: WebContents }) => sender))(sender => {
sender.send('electron-main->shared-process: payload', {
sharedIPCHandle: this.environmentService.sharedIPCHandle,
args: this.environmentService.args,
logLevel: this.logService.getLevel()
});
disposables.add(toDisposable(() => sender.send('handshake:goodbye')));
ipcMain.once('handshake:im ready', () => c(undefined));
// signal exit to shared process when we get disposed
disposables.add(toDisposable(() => sender.send('electron-main->shared-process: exit')));
// complete IPC-ready promise when shared process signals this to us
ipcMain.once('shared-process->electron-main: ipc-ready', () => c(undefined));
}));
});
}
@@ -122,6 +130,11 @@ export class SharedProcess implements ISharedProcess {
await this._whenReady;
}
async whenIpcReady(): Promise<void> {
await this.barrier.wait();
await this._whenIpcReady;
}
toggle(): void {
if (!this.window || this.window.isVisible()) {
this.hide();

View File

@@ -331,7 +331,7 @@ export async function main(argv: ParsedArgs): Promise<void> {
const envService = accessor.get(IEnvironmentService);
const stateService = accessor.get(IStateService);
const { appRoot, extensionsPath, extensionDevelopmentLocationURI: extensionDevelopmentLocationURI, isBuilt, installSourcePath } = envService;
const { appRoot, extensionsPath, extensionDevelopmentLocationURI, isBuilt, installSourcePath } = envService;
const services = new ServiceCollection();

View File

@@ -88,6 +88,7 @@ export interface ISerializedFontInfo {
readonly canUseHalfwidthRightwardsArrow: boolean;
readonly spaceWidth: number;
middotWidth: number;
wsmiddotWidth: number;
readonly maxDigitWidth: number;
}
@@ -161,6 +162,7 @@ class CSSBasedConfiguration extends Disposable {
// compatibility with older versions of VS Code which did not store this...
savedFontInfo.fontFeatureSettings = savedFontInfo.fontFeatureSettings || EditorFontLigatures.OFF;
savedFontInfo.middotWidth = savedFontInfo.middotWidth || savedFontInfo.spaceWidth;
savedFontInfo.wsmiddotWidth = savedFontInfo.wsmiddotWidth || savedFontInfo.spaceWidth;
const fontInfo = new FontInfo(savedFontInfo, false);
this._writeToCache(fontInfo, fontInfo);
}
@@ -186,6 +188,7 @@ class CSSBasedConfiguration extends Disposable {
canUseHalfwidthRightwardsArrow: readConfig.canUseHalfwidthRightwardsArrow,
spaceWidth: Math.max(readConfig.spaceWidth, 5),
middotWidth: Math.max(readConfig.middotWidth, 5),
wsmiddotWidth: Math.max(readConfig.wsmiddotWidth, 5),
maxDigitWidth: Math.max(readConfig.maxDigitWidth, 5),
}, false);
}
@@ -226,9 +229,12 @@ class CSSBasedConfiguration extends Disposable {
const rightwardsArrow = this.createRequest('→', CharWidthRequestType.Regular, all, monospace);
const halfwidthRightwardsArrow = this.createRequest('→', CharWidthRequestType.Regular, all, null);
// middle dot character
// U+00B7 - MIDDLE DOT
const middot = this.createRequest('·', CharWidthRequestType.Regular, all, monospace);
// U+2E31 - WORD SEPARATOR MIDDLE DOT
const wsmiddotWidth = this.createRequest(String.fromCharCode(0x2E31), CharWidthRequestType.Regular, all, null);
// monospace test: some characters
this.createRequest('|', CharWidthRequestType.Regular, all, monospace);
this.createRequest('/', CharWidthRequestType.Regular, all, monospace);
@@ -294,6 +300,7 @@ class CSSBasedConfiguration extends Disposable {
canUseHalfwidthRightwardsArrow: canUseHalfwidthRightwardsArrow,
spaceWidth: space.width,
middotWidth: middot.width,
wsmiddotWidth: wsmiddotWidth.width,
maxDigitWidth: maxDigitWidth
}, canTrustBrowserZoomLevel);
}
@@ -379,7 +386,11 @@ export class Configuration extends CommonEditorConfiguration {
emptySelectionClipboard: browser.isWebKit || browser.isFirefox,
pixelRatio: browser.getPixelRatio(),
zoomLevel: browser.getZoomLevel(),
accessibilitySupport: this.accessibilityService.isScreenReaderOptimized() ? AccessibilitySupport.Enabled : AccessibilitySupport.Disabled
accessibilitySupport: (
this.accessibilityService.isScreenReaderOptimized()
? AccessibilitySupport.Enabled
: this.accessibilityService.getAccessibilitySupport()
)
};
}

View File

@@ -57,19 +57,6 @@ export interface ITextAreaInputHost {
deduceModelPosition(viewAnchorPosition: Position, deltaOffset: number, lineFeedCnt: number): Position;
}
const enum TextAreaInputEventType {
none,
compositionstart,
compositionupdate,
compositionend,
input,
cut,
copy,
paste,
focus,
blur
}
interface CompositionEvent extends UIEvent {
readonly data: string;
readonly locale: string;
@@ -155,7 +142,6 @@ export class TextAreaInput extends Disposable {
private readonly _host: ITextAreaInputHost;
private readonly _textArea: TextAreaWrapper;
private _lastTextAreaEvent: TextAreaInputEventType;
private readonly _asyncTriggerCut: RunOnceScheduler;
private _textAreaState: TextAreaState;
@@ -169,7 +155,6 @@ export class TextAreaInput extends Disposable {
super();
this._host = host;
this._textArea = this._register(new TextAreaWrapper(textArea));
this._lastTextAreaEvent = TextAreaInputEventType.none;
this._asyncTriggerCut = this._register(new RunOnceScheduler(() => this._onCut.fire(), 0));
this._textAreaState = TextAreaState.EMPTY;
@@ -200,8 +185,6 @@ export class TextAreaInput extends Disposable {
}));
this._register(dom.addDisposableListener(textArea.domNode, 'compositionstart', (e: CompositionEvent) => {
this._lastTextAreaEvent = TextAreaInputEventType.compositionstart;
if (this._isDoingComposition) {
return;
}
@@ -218,10 +201,10 @@ export class TextAreaInput extends Disposable {
/**
* Deduce the typed input from a text area's value and the last observed state.
*/
const deduceInputFromTextAreaValue = (couldBeEmojiInput: boolean, couldBeTypingAtOffset0: boolean): [TextAreaState, ITypeData] => {
const deduceInputFromTextAreaValue = (couldBeEmojiInput: boolean): [TextAreaState, ITypeData] => {
const oldState = this._textAreaState;
const newState = TextAreaState.readFromTextArea(this._textArea);
return [newState, TextAreaState.deduceInput(oldState, newState, couldBeEmojiInput, couldBeTypingAtOffset0)];
return [newState, TextAreaState.deduceInput(oldState, newState, couldBeEmojiInput)];
};
/**
@@ -258,10 +241,8 @@ export class TextAreaInput extends Disposable {
};
this._register(dom.addDisposableListener(textArea.domNode, 'compositionupdate', (e: CompositionEvent) => {
this._lastTextAreaEvent = TextAreaInputEventType.compositionupdate;
if (compositionDataInValid(e.locale)) {
const [newState, typeInput] = deduceInputFromTextAreaValue(/*couldBeEmojiInput*/false, /*couldBeTypingAtOffset0*/false);
const [newState, typeInput] = deduceInputFromTextAreaValue(/*couldBeEmojiInput*/false);
this._textAreaState = newState;
this._onType.fire(typeInput);
this._onCompositionUpdate.fire(e);
@@ -275,7 +256,6 @@ export class TextAreaInput extends Disposable {
}));
this._register(dom.addDisposableListener(textArea.domNode, 'compositionend', (e: CompositionEvent) => {
this._lastTextAreaEvent = TextAreaInputEventType.compositionend;
// https://github.com/microsoft/monaco-editor/issues/1663
// On iOS 13.2, Chinese system IME randomly trigger an additional compositionend event with empty data
if (!this._isDoingComposition) {
@@ -283,7 +263,7 @@ export class TextAreaInput extends Disposable {
}
if (compositionDataInValid(e.locale)) {
// https://github.com/Microsoft/monaco-editor/issues/339
const [newState, typeInput] = deduceInputFromTextAreaValue(/*couldBeEmojiInput*/false, /*couldBeTypingAtOffset0*/false);
const [newState, typeInput] = deduceInputFromTextAreaValue(/*couldBeEmojiInput*/false);
this._textAreaState = newState;
this._onType.fire(typeInput);
} else {
@@ -307,10 +287,6 @@ export class TextAreaInput extends Disposable {
}));
this._register(dom.addDisposableListener(textArea.domNode, 'input', () => {
// We want to find out if this is the first `input` after a `focus`.
const previousEventWasFocus = (this._lastTextAreaEvent === TextAreaInputEventType.focus);
this._lastTextAreaEvent = TextAreaInputEventType.input;
// Pretend here we touched the text area, as the `input` event will most likely
// result in a `selectionchange` event which we want to ignore
this._textArea.setIgnoreSelectionChangeTime('received input event');
@@ -319,7 +295,7 @@ export class TextAreaInput extends Disposable {
return;
}
const [newState, typeInput] = deduceInputFromTextAreaValue(/*couldBeEmojiInput*/platform.isMacintosh, /*couldBeTypingAtOffset0*/previousEventWasFocus && platform.isMacintosh);
const [newState, typeInput] = deduceInputFromTextAreaValue(/*couldBeEmojiInput*/platform.isMacintosh);
if (typeInput.replaceCharCnt === 0 && typeInput.text.length === 1 && strings.isHighSurrogate(typeInput.text.charCodeAt(0))) {
// Ignore invalid input but keep it around for next time
return;
@@ -341,8 +317,6 @@ export class TextAreaInput extends Disposable {
// --- Clipboard operations
this._register(dom.addDisposableListener(textArea.domNode, 'cut', (e: ClipboardEvent) => {
this._lastTextAreaEvent = TextAreaInputEventType.cut;
// Pretend here we touched the text area, as the `cut` event will most likely
// result in a `selectionchange` event which we want to ignore
this._textArea.setIgnoreSelectionChangeTime('received cut event');
@@ -352,14 +326,10 @@ export class TextAreaInput extends Disposable {
}));
this._register(dom.addDisposableListener(textArea.domNode, 'copy', (e: ClipboardEvent) => {
this._lastTextAreaEvent = TextAreaInputEventType.copy;
this._ensureClipboardGetsEditorSelection(e);
}));
this._register(dom.addDisposableListener(textArea.domNode, 'paste', (e: ClipboardEvent) => {
this._lastTextAreaEvent = TextAreaInputEventType.paste;
// Pretend here we touched the text area, as the `paste` event will most likely
// result in a `selectionchange` event which we want to ignore
this._textArea.setIgnoreSelectionChangeTime('received paste event');
@@ -379,11 +349,9 @@ export class TextAreaInput extends Disposable {
}));
this._register(dom.addDisposableListener(textArea.domNode, 'focus', () => {
this._lastTextAreaEvent = TextAreaInputEventType.focus;
this._setHasFocus(true);
}));
this._register(dom.addDisposableListener(textArea.domNode, 'blur', () => {
this._lastTextAreaEvent = TextAreaInputEventType.blur;
this._setHasFocus(false);
}));
}
@@ -625,7 +593,8 @@ class ClipboardEventUtils {
if ((<any>window).clipboardData) {
e.preventDefault();
return (<any>window).clipboardData.getData('Text');
const text: string = (<any>window).clipboardData.getData('Text');
return [text, null];
}
throw new Error('ClipboardEventUtils.getTextData: Cannot use text data!');

View File

@@ -96,7 +96,7 @@ export class TextAreaState {
return new TextAreaState(text, 0, text.length, null, null);
}
public static deduceInput(previousState: TextAreaState, currentState: TextAreaState, couldBeEmojiInput: boolean, couldBeTypingAtOffset0: boolean): ITypeData {
public static deduceInput(previousState: TextAreaState, currentState: TextAreaState, couldBeEmojiInput: boolean): ITypeData {
if (!previousState) {
// This is the EMPTY state
return {
@@ -116,18 +116,6 @@ export class TextAreaState {
let currentSelectionStart = currentState.selectionStart;
let currentSelectionEnd = currentState.selectionEnd;
if (couldBeTypingAtOffset0 && previousValue.length > 0 && previousSelectionStart === previousSelectionEnd && currentSelectionStart === currentSelectionEnd) {
// See https://github.com/Microsoft/vscode/issues/42251
// where typing always happens at offset 0 in the textarea
// when using a custom title area in OSX and moving the window
if (!strings.startsWith(currentValue, previousValue) && strings.endsWith(currentValue, previousValue)) {
// Looks like something was typed at offset 0
// ==> pretend we placed the cursor at offset 0 to begin with...
previousSelectionStart = 0;
previousSelectionEnd = 0;
}
}
// Strip the previous suffix from the value (without interfering with the current selection)
const previousSuffix = previousValue.substring(previousSelectionEnd);
const currentSuffix = currentValue.substring(currentSelectionEnd);

View File

@@ -74,6 +74,7 @@ export class ViewLineOptions {
public readonly renderControlCharacters: boolean;
public readonly spaceWidth: number;
public readonly middotWidth: number;
public readonly wsmiddotWidth: number;
public readonly useMonospaceOptimizations: boolean;
public readonly canUseHalfwidthRightwardsArrow: boolean;
public readonly lineHeight: number;
@@ -88,6 +89,7 @@ export class ViewLineOptions {
this.renderControlCharacters = options.get(EditorOption.renderControlCharacters);
this.spaceWidth = fontInfo.spaceWidth;
this.middotWidth = fontInfo.middotWidth;
this.wsmiddotWidth = fontInfo.wsmiddotWidth;
this.useMonospaceOptimizations = (
fontInfo.isMonospace
&& !options.get(EditorOption.disableMonospaceOptimizations)
@@ -105,6 +107,7 @@ export class ViewLineOptions {
&& this.renderControlCharacters === other.renderControlCharacters
&& this.spaceWidth === other.spaceWidth
&& this.middotWidth === other.middotWidth
&& this.wsmiddotWidth === other.wsmiddotWidth
&& this.useMonospaceOptimizations === other.useMonospaceOptimizations
&& this.canUseHalfwidthRightwardsArrow === other.canUseHalfwidthRightwardsArrow
&& this.lineHeight === other.lineHeight
@@ -219,6 +222,7 @@ export class ViewLine implements IVisibleLine {
lineData.startVisibleColumn,
options.spaceWidth,
options.middotWidth,
options.wsmiddotWidth,
options.stopRenderingLineAfter,
options.renderWhitespace,
options.renderControlCharacters,

View File

@@ -8,7 +8,7 @@ import * as nls from 'vs/nls';
import * as dom from 'vs/base/browser/dom';
import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode';
import { ISashEvent, IVerticalSashLayoutProvider, Sash, SashState } from 'vs/base/browser/ui/sash/sash';
import { RunOnceScheduler, IntervalTimer } from 'vs/base/common/async';
import { RunOnceScheduler } from 'vs/base/common/async';
import { Color } from 'vs/base/common/color';
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
@@ -48,6 +48,7 @@ import { Constants } from 'vs/base/common/uint';
import { EditorExtensionsRegistry, IDiffEditorContributionDescription } from 'vs/editor/browser/editorExtensions';
import { onUnexpectedError } from 'vs/base/common/errors';
import { IEditorProgressService, IProgressRunner } from 'vs/platform/progress/common/progress';
import { ElementSizeObserver } from 'vs/editor/browser/config/elementSizeObserver';
interface IEditorDiffDecorations {
decorations: IModelDeltaDecoration[];
@@ -179,10 +180,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
private readonly _overviewDomElement: HTMLElement;
private readonly _overviewViewportDomElement: FastDomNode<HTMLElement>;
private _width: number;
private _height: number;
private _reviewHeight: number;
private readonly _measureDomElementToken: IntervalTimer | null;
private readonly _elementSizeObserver: ElementSizeObserver;
private readonly originalEditor: CodeEditorWidget;
private readonly _originalDomNode: HTMLElement;
@@ -329,9 +327,10 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
this._isVisible = true;
this._isHandlingScrollEvent = false;
this._width = 0;
this._height = 0;
this._reviewHeight = 0;
this._elementSizeObserver = this._register(new ElementSizeObserver(this._containerDomElement, undefined, () => this._onDidContainerSizeChanged()));
if (options.automaticLayout) {
this._elementSizeObserver.startObserving();
}
this._diffComputationResult = null;
@@ -360,12 +359,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
this._containerDomElement.appendChild(this._reviewPane.shadow.domNode);
this._containerDomElement.appendChild(this._reviewPane.actionBarContainer.domNode);
if (options.automaticLayout) {
this._measureDomElementToken = new IntervalTimer();
this._measureDomElementToken.cancelAndSet(() => this._measureDomElement(false), 100);
} else {
this._measureDomElementToken = null;
}
// enableSplitViewResizing
this._enableSplitViewResizing = true;
@@ -563,10 +557,6 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
this._beginUpdateDecorationsTimeout = -1;
}
if (this._measureDomElementToken) {
this._measureDomElementToken.dispose();
}
this._cleanViewZonesAndDecorations();
if (this._originalOverviewRuler) {
@@ -866,7 +856,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
}
public layout(dimension?: editorCommon.IDimension): void {
this._measureDomElement(false, dimension);
this._elementSizeObserver.observe(dimension);
}
public focus(): void {
@@ -907,35 +897,21 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
//------------ begin layouting methods
private _measureDomElement(forceDoLayoutCall: boolean, dimensions?: editorCommon.IDimension): void {
dimensions = dimensions || {
width: this._containerDomElement.clientWidth,
height: this._containerDomElement.clientHeight
};
if (dimensions.width <= 0) {
this._width = 0;
this._height = 0;
this._reviewHeight = 0;
return;
}
if (!forceDoLayoutCall && dimensions.width === this._width && dimensions.height === this._height) {
// Nothing has changed
return;
}
this._width = dimensions.width;
this._height = dimensions.height;
this._reviewHeight = this._reviewPane.isVisible() ? this._height : 0;
private _onDidContainerSizeChanged(): void {
this._doLayout();
}
private _getReviewHeight(): number {
return this._reviewPane.isVisible() ? this._elementSizeObserver.getHeight() : 0;
}
private _layoutOverviewRulers(): void {
if (!this._originalOverviewRuler || !this._modifiedOverviewRuler) {
return;
}
const height = this._elementSizeObserver.getHeight();
const reviewHeight = this._getReviewHeight();
let freeSpace = DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH - 2 * DiffEditorWidget.ONE_OVERVIEW_WIDTH;
let layoutInfo = this.modifiedEditor.getLayoutInfo();
if (layoutInfo) {
@@ -943,13 +919,13 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
top: 0,
width: DiffEditorWidget.ONE_OVERVIEW_WIDTH,
right: freeSpace + DiffEditorWidget.ONE_OVERVIEW_WIDTH,
height: (this._height - this._reviewHeight)
height: (height - reviewHeight)
});
this._modifiedOverviewRuler.setLayout({
top: 0,
right: 0,
width: DiffEditorWidget.ONE_OVERVIEW_WIDTH,
height: (this._height - this._reviewHeight)
height: (height - reviewHeight)
});
}
}
@@ -1095,33 +1071,38 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
}
public doLayout(): void {
this._measureDomElement(true);
this._elementSizeObserver.observe();
this._doLayout();
}
private _doLayout(): void {
const width = this._elementSizeObserver.getWidth();
const height = this._elementSizeObserver.getHeight();
const reviewHeight = this._getReviewHeight();
let splitPoint = this._strategy.layout();
this._originalDomNode.style.width = splitPoint + 'px';
this._originalDomNode.style.left = '0px';
this._modifiedDomNode.style.width = (this._width - splitPoint) + 'px';
this._modifiedDomNode.style.width = (width - splitPoint) + 'px';
this._modifiedDomNode.style.left = splitPoint + 'px';
this._overviewDomElement.style.top = '0px';
this._overviewDomElement.style.height = (this._height - this._reviewHeight) + 'px';
this._overviewDomElement.style.height = (height - reviewHeight) + 'px';
this._overviewDomElement.style.width = DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH + 'px';
this._overviewDomElement.style.left = (this._width - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH) + 'px';
this._overviewDomElement.style.left = (width - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH) + 'px';
this._overviewViewportDomElement.setWidth(DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH);
this._overviewViewportDomElement.setHeight(30);
this.originalEditor.layout({ width: splitPoint, height: (this._height - this._reviewHeight) });
this.modifiedEditor.layout({ width: this._width - splitPoint - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH, height: (this._height - this._reviewHeight) });
this.originalEditor.layout({ width: splitPoint, height: (height - reviewHeight) });
this.modifiedEditor.layout({ width: width - splitPoint - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH, height: (height - reviewHeight) });
if (this._originalOverviewRuler || this._modifiedOverviewRuler) {
this._layoutOverviewRulers();
}
this._reviewPane.layout(this._height - this._reviewHeight, this._width, this._reviewHeight);
this._reviewPane.layout(height - reviewHeight, width, reviewHeight);
this._layoutOverviewViewport();
}
@@ -1162,11 +1143,11 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
private _createDataSource(): IDataSource {
return {
getWidth: () => {
return this._width;
return this._elementSizeObserver.getWidth();
},
getHeight: () => {
return (this._height - this._reviewHeight);
return (this._elementSizeObserver.getHeight() - this._getReviewHeight());
},
getContainerDomNode: () => {
@@ -1200,7 +1181,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
}
// Just do a layout, the strategy might need it
this._measureDomElement(true);
this._doLayout();
}
private _getLineChangeAtOrBeforeLineNumber(lineNumber: number, startLineNumberExtractor: (lineChange: editorCommon.ILineChange) => number): editorCommon.ILineChange | null {
@@ -2181,6 +2162,7 @@ class InlineViewZonesComputer extends ViewZonesComputer {
0,
fontInfo.spaceWidth,
fontInfo.middotWidth,
fontInfo.wsmiddotWidth,
options.get(EditorOption.stopRenderingLineAfter),
options.get(EditorOption.renderWhitespace),
options.get(EditorOption.renderControlCharacters),

View File

@@ -783,6 +783,7 @@ export class DiffReview extends Disposable {
0,
fontInfo.spaceWidth,
fontInfo.middotWidth,
fontInfo.wsmiddotWidth,
options.get(EditorOption.stopRenderingLineAfter),
options.get(EditorOption.renderWhitespace),
options.get(EditorOption.renderControlCharacters),

View File

@@ -366,6 +366,10 @@ export interface IEditorOptions {
* Defaults to 10 (ms)
*/
quickSuggestionsDelay?: number;
/**
* Controls the spacing around the editor.
*/
padding?: IEditorPaddingOptions;
/**
* Parameter hint options.
*/
@@ -2082,6 +2086,65 @@ function _multiCursorModifierFromString(multiCursorModifier: 'ctrlCmd' | 'alt'):
//#endregion
//#region padding
/**
* Configuration options for editor padding
*/
export interface IEditorPaddingOptions {
/**
* Spacing between top edge of editor and first line.
*/
top?: number;
/**
* Spacing between bottom edge of editor and last line.
*/
bottom?: number;
}
export interface InternalEditorPaddingOptions {
readonly top: number;
readonly bottom: number;
}
class EditorPadding extends BaseEditorOption<EditorOption.padding, InternalEditorPaddingOptions> {
constructor() {
super(
EditorOption.padding, 'padding', { top: 0, bottom: 0 },
{
'editor.padding.top': {
type: 'number',
default: 0,
minimum: 0,
maximum: 1000,
description: nls.localize('padding.top', "Controls the amount of space between the top edge of the editor and the first line.")
},
'editor.padding.bottom': {
type: 'number',
default: 0,
minimum: 0,
maximum: 1000,
description: nls.localize('padding.bottom', "Controls the amount of space between the bottom edge of the editor and the last line.")
}
}
);
}
public validate(_input: any): InternalEditorPaddingOptions {
if (typeof _input !== 'object') {
return this.defaultValue;
}
const input = _input as IEditorPaddingOptions;
return {
top: EditorIntOption.clampedInt(input.top, 0, 0, 1000),
bottom: EditorIntOption.clampedInt(input.bottom, 0, 0, 1000)
};
}
}
//#endregion
//#region parameterHints
/**
@@ -3188,6 +3251,7 @@ export const enum EditorOption {
occurrencesHighlight,
overviewRulerBorder,
overviewRulerLanes,
padding,
parameterHints,
peekWidgetDefaultFocus,
definitionLinkOpensInPeek,
@@ -3575,6 +3639,7 @@ export const EditorOptions = {
EditorOption.overviewRulerLanes, 'overviewRulerLanes',
3, 0, 3
)),
padding: register(new EditorPadding()),
parameterHints: register(new EditorParameterHints()),
peekWidgetDefaultFocus: register(new EditorStringEnumOption(
EditorOption.peekWidgetDefaultFocus, 'peekWidgetDefaultFocus',
@@ -3634,7 +3699,7 @@ export const EditorOptions = {
)),
renderWhitespace: register(new EditorStringEnumOption(
EditorOption.renderWhitespace, 'renderWhitespace',
'none' as 'none' | 'boundary' | 'selection' | 'all',
'selection' as 'selection' | 'none' | 'boundary' | 'all',
['none', 'boundary', 'selection', 'all'] as const,
{
enumDescriptions: [

View File

@@ -135,6 +135,7 @@ export class FontInfo extends BareFontInfo {
readonly canUseHalfwidthRightwardsArrow: boolean;
readonly spaceWidth: number;
readonly middotWidth: number;
readonly wsmiddotWidth: number;
readonly maxDigitWidth: number;
/**
@@ -154,6 +155,7 @@ export class FontInfo extends BareFontInfo {
canUseHalfwidthRightwardsArrow: boolean;
spaceWidth: number;
middotWidth: number;
wsmiddotWidth: number;
maxDigitWidth: number;
}, isTrusted: boolean) {
super(opts);
@@ -164,6 +166,7 @@ export class FontInfo extends BareFontInfo {
this.canUseHalfwidthRightwardsArrow = opts.canUseHalfwidthRightwardsArrow;
this.spaceWidth = opts.spaceWidth;
this.middotWidth = opts.middotWidth;
this.wsmiddotWidth = opts.wsmiddotWidth;
this.maxDigitWidth = opts.maxDigitWidth;
}
@@ -183,6 +186,7 @@ export class FontInfo extends BareFontInfo {
&& this.canUseHalfwidthRightwardsArrow === other.canUseHalfwidthRightwardsArrow
&& this.spaceWidth === other.spaceWidth
&& this.middotWidth === other.middotWidth
&& this.wsmiddotWidth === other.wsmiddotWidth
&& this.maxDigitWidth === other.maxDigitWidth
);
}

View File

@@ -61,6 +61,11 @@ export interface ITextEditorModel extends IEditorModel {
* Figure out if this model is resolved or not.
*/
isResolved(): this is IResolvedTextEditorModel;
/**
* The mode id of the text model if known.
*/
getMode(): string | undefined;
}
export interface IResolvedTextEditorModel extends ITextEditorModel {

View File

@@ -229,53 +229,54 @@ export enum EditorOption {
occurrencesHighlight = 61,
overviewRulerBorder = 62,
overviewRulerLanes = 63,
parameterHints = 64,
peekWidgetDefaultFocus = 65,
definitionLinkOpensInPeek = 66,
quickSuggestions = 67,
quickSuggestionsDelay = 68,
readOnly = 69,
renderControlCharacters = 70,
renderIndentGuides = 71,
renderFinalNewline = 72,
renderLineHighlight = 73,
renderValidationDecorations = 74,
renderWhitespace = 75,
revealHorizontalRightPadding = 76,
roundedSelection = 77,
rulers = 78,
scrollbar = 79,
scrollBeyondLastColumn = 80,
scrollBeyondLastLine = 81,
scrollPredominantAxis = 82,
selectionClipboard = 83,
selectionHighlight = 84,
selectOnLineNumbers = 85,
showFoldingControls = 86,
showUnused = 87,
snippetSuggestions = 88,
smoothScrolling = 89,
stopRenderingLineAfter = 90,
suggest = 91,
suggestFontSize = 92,
suggestLineHeight = 93,
suggestOnTriggerCharacters = 94,
suggestSelection = 95,
tabCompletion = 96,
useTabStops = 97,
wordSeparators = 98,
wordWrap = 99,
wordWrapBreakAfterCharacters = 100,
wordWrapBreakBeforeCharacters = 101,
wordWrapColumn = 102,
wordWrapMinified = 103,
wrappingIndent = 104,
wrappingStrategy = 105,
editorClassName = 106,
pixelRatio = 107,
tabFocusMode = 108,
layoutInfo = 109,
wrappingInfo = 110
padding = 64,
parameterHints = 65,
peekWidgetDefaultFocus = 66,
definitionLinkOpensInPeek = 67,
quickSuggestions = 68,
quickSuggestionsDelay = 69,
readOnly = 70,
renderControlCharacters = 71,
renderIndentGuides = 72,
renderFinalNewline = 73,
renderLineHighlight = 74,
renderValidationDecorations = 75,
renderWhitespace = 76,
revealHorizontalRightPadding = 77,
roundedSelection = 78,
rulers = 79,
scrollbar = 80,
scrollBeyondLastColumn = 81,
scrollBeyondLastLine = 82,
scrollPredominantAxis = 83,
selectionClipboard = 84,
selectionHighlight = 85,
selectOnLineNumbers = 86,
showFoldingControls = 87,
showUnused = 88,
snippetSuggestions = 89,
smoothScrolling = 90,
stopRenderingLineAfter = 91,
suggest = 92,
suggestFontSize = 93,
suggestLineHeight = 94,
suggestOnTriggerCharacters = 95,
suggestSelection = 96,
tabCompletion = 97,
useTabStops = 98,
wordSeparators = 99,
wordWrap = 100,
wordWrapBreakAfterCharacters = 101,
wordWrapBreakBeforeCharacters = 102,
wordWrapColumn = 103,
wordWrapMinified = 104,
wrappingIndent = 105,
wrappingStrategy = 106,
editorClassName = 107,
pixelRatio = 108,
tabFocusMode = 109,
layoutInfo = 110,
wrappingInfo = 111
}
/**

View File

@@ -111,8 +111,10 @@ export class LinesLayout {
private _minWidth: number;
private _lineCount: number;
private _lineHeight: number;
private _paddingTop: number;
private _paddingBottom: number;
constructor(lineCount: number, lineHeight: number) {
constructor(lineCount: number, lineHeight: number, paddingTop: number, paddingBottom: number) {
this._instanceId = strings.singleLetterHash(++LinesLayout.INSTANCE_COUNT);
this._pendingChanges = new PendingChanges();
this._lastWhitespaceId = 0;
@@ -121,6 +123,8 @@ export class LinesLayout {
this._minWidth = -1; /* marker for not being computed */
this._lineCount = lineCount;
this._lineHeight = lineHeight;
this._paddingTop = paddingTop;
this._paddingBottom = paddingBottom;
}
/**
@@ -158,6 +162,14 @@ export class LinesLayout {
this._lineHeight = lineHeight;
}
/**
* Changes the padding used to calculate vertical offsets.
*/
public setPadding(paddingTop: number, paddingBottom: number): void {
this._paddingTop = paddingTop;
this._paddingBottom = paddingBottom;
}
/**
* Set the number of lines.
*
@@ -404,7 +416,8 @@ export class LinesLayout {
this._checkPendingChanges();
const linesHeight = this._lineHeight * this._lineCount;
const whitespacesHeight = this.getWhitespacesTotalHeight();
return linesHeight + whitespacesHeight;
return linesHeight + whitespacesHeight + this._paddingTop + this._paddingBottom;
}
/**
@@ -495,7 +508,7 @@ export class LinesLayout {
const previousWhitespacesHeight = this.getWhitespaceAccumulatedHeightBeforeLineNumber(lineNumber);
return previousLinesHeight + previousWhitespacesHeight;
return previousLinesHeight + previousWhitespacesHeight + this._paddingTop;
}
/**
@@ -719,7 +732,7 @@ export class LinesLayout {
} else {
previousWhitespacesHeight = 0;
}
return previousLinesHeight + previousWhitespacesHeight;
return previousLinesHeight + previousWhitespacesHeight + this._paddingTop;
}
public getWhitespaceIndexAtOrAfterVerticallOffset(verticalOffset: number): number {

View File

@@ -161,8 +161,9 @@ export class ViewLayout extends Disposable implements IViewLayout {
this._configuration = configuration;
const options = this._configuration.options;
const layoutInfo = options.get(EditorOption.layoutInfo);
const padding = options.get(EditorOption.padding);
this._linesLayout = new LinesLayout(lineCount, options.get(EditorOption.lineHeight));
this._linesLayout = new LinesLayout(lineCount, options.get(EditorOption.lineHeight), padding.top, padding.bottom);
this._scrollable = this._register(new EditorScrollable(0, scheduleAtNextAnimationFrame));
this._configureSmoothScrollDuration();
@@ -202,6 +203,10 @@ export class ViewLayout extends Disposable implements IViewLayout {
if (e.hasChanged(EditorOption.lineHeight)) {
this._linesLayout.setLineHeight(options.get(EditorOption.lineHeight));
}
if (e.hasChanged(EditorOption.padding)) {
const padding = options.get(EditorOption.padding);
this._linesLayout.setPadding(padding.top, padding.bottom);
}
if (e.hasChanged(EditorOption.layoutInfo)) {
const layoutInfo = options.get(EditorOption.layoutInfo);
const width = layoutInfo.contentWidth;

View File

@@ -68,7 +68,8 @@ export class RenderLineInput {
public readonly tabSize: number;
public readonly startVisibleColumn: number;
public readonly spaceWidth: number;
public readonly middotWidth: number;
public readonly renderSpaceWidth: number;
public readonly renderSpaceCharCode: number;
public readonly stopRenderingLineAfter: number;
public readonly renderWhitespace: RenderWhitespace;
public readonly renderControlCharacters: boolean;
@@ -94,6 +95,7 @@ export class RenderLineInput {
startVisibleColumn: number,
spaceWidth: number,
middotWidth: number,
wsmiddotWidth: number,
stopRenderingLineAfter: number,
renderWhitespace: 'none' | 'boundary' | 'selection' | 'all',
renderControlCharacters: boolean,
@@ -112,7 +114,6 @@ export class RenderLineInput {
this.tabSize = tabSize;
this.startVisibleColumn = startVisibleColumn;
this.spaceWidth = spaceWidth;
this.middotWidth = middotWidth;
this.stopRenderingLineAfter = stopRenderingLineAfter;
this.renderWhitespace = (
renderWhitespace === 'all'
@@ -126,6 +127,16 @@ export class RenderLineInput {
this.renderControlCharacters = renderControlCharacters;
this.fontLigatures = fontLigatures;
this.selectionsOnLine = selectionsOnLine && selectionsOnLine.sort((a, b) => a.startOffset < b.startOffset ? -1 : 1);
const wsmiddotDiff = Math.abs(wsmiddotWidth - spaceWidth);
const middotDiff = Math.abs(middotWidth - spaceWidth);
if (wsmiddotDiff < middotDiff) {
this.renderSpaceWidth = wsmiddotWidth;
this.renderSpaceCharCode = 0x2E31; // U+2E31 - WORD SEPARATOR MIDDLE DOT
} else {
this.renderSpaceWidth = middotWidth;
this.renderSpaceCharCode = 0xB7; // U+00B7 - MIDDLE DOT
}
}
private sameSelection(otherSelections: LineRange[] | null): boolean {
@@ -162,6 +173,8 @@ export class RenderLineInput {
&& this.tabSize === other.tabSize
&& this.startVisibleColumn === other.startVisibleColumn
&& this.spaceWidth === other.spaceWidth
&& this.renderSpaceWidth === other.renderSpaceWidth
&& this.renderSpaceCharCode === other.renderSpaceCharCode
&& this.stopRenderingLineAfter === other.stopRenderingLineAfter
&& this.renderWhitespace === other.renderWhitespace
&& this.renderControlCharacters === other.renderControlCharacters
@@ -383,7 +396,7 @@ class ResolvedRenderLineInput {
public readonly startVisibleColumn: number,
public readonly containsRTL: boolean,
public readonly spaceWidth: number,
public readonly middotWidth: number,
public readonly renderSpaceCharCode: number,
public readonly renderWhitespace: RenderWhitespace,
public readonly renderControlCharacters: boolean,
) {
@@ -392,7 +405,6 @@ class ResolvedRenderLineInput {
}
function resolveRenderLineInput(input: RenderLineInput): ResolvedRenderLineInput {
const useMonospaceOptimizations = input.useMonospaceOptimizations;
const lineContent = input.lineContent;
let isOverflowing: boolean;
@@ -408,7 +420,7 @@ function resolveRenderLineInput(input: RenderLineInput): ResolvedRenderLineInput
let tokens = transformAndRemoveOverflowing(input.lineTokens, input.fauxIndentLength, len);
if (input.renderWhitespace === RenderWhitespace.All || input.renderWhitespace === RenderWhitespace.Boundary || (input.renderWhitespace === RenderWhitespace.Selection && !!input.selectionsOnLine)) {
tokens = _applyRenderWhitespace(lineContent, len, input.continuesWithWrappedLine, tokens, input.fauxIndentLength, input.tabSize, input.startVisibleColumn, useMonospaceOptimizations, input.selectionsOnLine, input.renderWhitespace === RenderWhitespace.Boundary);
tokens = _applyRenderWhitespace(input, lineContent, len, tokens);
}
let containsForeignElements = ForeignElementType.None;
if (input.lineDecorations.length > 0) {
@@ -431,7 +443,7 @@ function resolveRenderLineInput(input: RenderLineInput): ResolvedRenderLineInput
}
return new ResolvedRenderLineInput(
useMonospaceOptimizations,
input.useMonospaceOptimizations,
input.canUseHalfwidthRightwardsArrow,
lineContent,
len,
@@ -443,7 +455,7 @@ function resolveRenderLineInput(input: RenderLineInput): ResolvedRenderLineInput
input.startVisibleColumn,
input.containsRTL,
input.spaceWidth,
input.middotWidth,
input.renderSpaceCharCode,
input.renderWhitespace,
input.renderControlCharacters
);
@@ -553,7 +565,16 @@ function splitLargeTokens(lineContent: string, tokens: LinePart[], onlyAtSpaces:
* Moreover, a token is created for every visual indent because on some fonts the glyphs used for rendering whitespace (&rarr; or &middot;) do not have the same width as &nbsp;.
* The rendering phase will generate `style="width:..."` for these tokens.
*/
function _applyRenderWhitespace(lineContent: string, len: number, continuesWithWrappedLine: boolean, tokens: LinePart[], fauxIndentLength: number, tabSize: number, startVisibleColumn: number, useMonospaceOptimizations: boolean, selections: LineRange[] | null, onlyBoundary: boolean): LinePart[] {
function _applyRenderWhitespace(input: RenderLineInput, lineContent: string, len: number, tokens: LinePart[]): LinePart[] {
const continuesWithWrappedLine = input.continuesWithWrappedLine;
const fauxIndentLength = input.fauxIndentLength;
const tabSize = input.tabSize;
const startVisibleColumn = input.startVisibleColumn;
const useMonospaceOptimizations = input.useMonospaceOptimizations;
const selections = input.selectionsOnLine;
const onlyBoundary = (input.renderWhitespace === RenderWhitespace.Boundary);
const generateLinePartForEachWhitespace = (input.renderSpaceWidth !== input.spaceWidth);
let result: LinePart[] = [], resultLen = 0;
let tokenIndex = 0;
@@ -616,7 +637,14 @@ function _applyRenderWhitespace(lineContent: string, len: number, continuesWithW
// was in whitespace token
if (!isInWhitespace || (!useMonospaceOptimizations && tmpIndent >= tabSize)) {
// leaving whitespace token or entering a new indent
result[resultLen++] = new LinePart(charIndex, 'mtkw');
if (generateLinePartForEachWhitespace) {
const lastEndIndex = (resultLen > 0 ? result[resultLen - 1].endIndex : fauxIndentLength);
for (let i = lastEndIndex + 1; i <= charIndex; i++) {
result[resultLen++] = new LinePart(i, 'mtkw');
}
} else {
result[resultLen++] = new LinePart(charIndex, 'mtkw');
}
tmpIndent = tmpIndent % tabSize;
}
} else {
@@ -661,7 +689,18 @@ function _applyRenderWhitespace(lineContent: string, len: number, continuesWithW
}
}
result[resultLen++] = new LinePart(len, generateWhitespace ? 'mtkw' : tokenType);
if (generateWhitespace) {
if (generateLinePartForEachWhitespace) {
const lastEndIndex = (resultLen > 0 ? result[resultLen - 1].endIndex : fauxIndentLength);
for (let i = lastEndIndex + 1; i <= len; i++) {
result[resultLen++] = new LinePart(i, 'mtkw');
}
} else {
result[resultLen++] = new LinePart(len, 'mtkw');
}
} else {
result[resultLen++] = new LinePart(len, tokenType);
}
return result;
}
@@ -739,13 +778,10 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render
const startVisibleColumn = input.startVisibleColumn;
const containsRTL = input.containsRTL;
const spaceWidth = input.spaceWidth;
const middotWidth = input.middotWidth;
const renderSpaceCharCode = input.renderSpaceCharCode;
const renderWhitespace = input.renderWhitespace;
const renderControlCharacters = input.renderControlCharacters;
// use U+2E31 - WORD SEPARATOR MIDDLE DOT or U+00B7 - MIDDLE DOT
const spaceRenderWhitespaceCharacter = (middotWidth > spaceWidth ? 0x2E31 : 0xB7);
const characterMapping = new CharacterMapping(len + 1, parts.length);
let charIndex = 0;
@@ -815,7 +851,7 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render
} else { // must be CharCode.Space
charWidth = 1;
sb.write1(spaceRenderWhitespaceCharacter); // &middot; or word separator middle dot
sb.write1(renderSpaceCharCode); // &middot; or word separator middle dot
}
charOffsetInPart += charWidth;

View File

@@ -611,7 +611,14 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
return;
} else {
const scrollAdjustment = this._getHeight();
let scrollAdjustment = this._getHeight();
// if the editor has top padding, factor that into the zone height
scrollAdjustment -= this._codeEditor.getOption(EditorOption.padding).top;
if (scrollAdjustment <= 0) {
return;
}
viewZone.heightInPx = scrollAdjustment;
this._viewZoneId = accessor.addZone(viewZone);

View File

@@ -304,7 +304,7 @@ export class MarkerController implements IEditorContribution {
model.currentMarker = marker;
}
private _onMarkerChanged(changedResources: URI[]): void {
private _onMarkerChanged(changedResources: readonly URI[]): void {
const editorModel = this._editor.getModel();
if (!editorModel) {
return;

View File

@@ -134,10 +134,10 @@ class MessageWidget {
detailsElement.appendChild(codeElement);
} else {
this._codeLink = dom.$('a.code-link');
this._codeLink.setAttribute('href', `${code.link.toString()}`);
this._codeLink.setAttribute('href', `${code.target.toString()}`);
this._codeLink.onclick = (e) => {
this._openerService.open(code.link);
this._openerService.open(code.target);
e.preventDefault();
e.stopPropagation();
};

View File

@@ -516,10 +516,10 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
sourceElement.innerText = source;
}
this._codeLink = dom.append(sourceAndCodeElement, $('a.code-link'));
this._codeLink.setAttribute('href', code.link.toString());
this._codeLink.setAttribute('href', code.target.toString());
this._codeLink.onclick = (e) => {
this._openerService.open(code.link);
this._openerService.open(code.target);
e.preventDefault();
e.stopPropagation();
};

View File

@@ -468,6 +468,7 @@ export class SnippetSession {
// that ensures the primiary cursor stays primary despite not being
// the one with lowest start position
edits[idx] = EditOperation.replace(snippetSelection, snippet.toString());
edits[idx].identifier = { major: idx, minor: 0 }; // mark the edit so only our undo edits will be used to generate end cursors
snippets[idx] = new OneSnippet(editor, snippet, offset);
}
@@ -507,7 +508,11 @@ export class SnippetSession {
if (this._snippets[0].hasPlaceholder) {
return this._move(true);
} else {
return undoEdits.map(edit => Selection.fromPositions(edit.range.getEndPosition()));
return (
undoEdits
.filter(edit => !!edit.identifier) // only use our undo edits
.map(edit => Selection.fromPositions(edit.range.getEndPosition()))
);
}
});
this._editor.revealRange(this._editor.getSelections()[0]);
@@ -529,7 +534,11 @@ export class SnippetSession {
if (this._snippets[0].hasPlaceholder) {
return this._move(undefined);
} else {
return undoEdits.map(edit => Selection.fromPositions(edit.range.getEndPosition()));
return (
undoEdits
.filter(edit => !!edit.identifier) // only use our undo edits
.map(edit => Selection.fromPositions(edit.range.getEndPosition()))
);
}
});
}

View File

@@ -11,6 +11,7 @@ import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKe
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { NullLogService } from 'vs/platform/log/common/log';
import { Handler } from 'vs/editor/common/editorCommon';
import { CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands';
suite('SnippetController2', function () {
@@ -428,4 +429,13 @@ suite('SnippetController2', function () {
assertSelections(editor, new Selection(2, 5, 2, 5));
assertContextKeys(contextKeys, false, false, false);
});
test('issue #90135: confusing trim whitespace edits', function () {
const ctrl = new SnippetController2(editor, logService, contextKeys);
model.setValue('');
CoreEditingCommands.Tab.runEditorCommand(null, editor, null);
ctrl.insert('\nfoo');
assertSelections(editor, new Selection(2, 8, 2, 8));
});
});

View File

@@ -127,6 +127,7 @@ export class Colorizer {
0,
0,
0,
0,
-1,
'none',
false,
@@ -197,6 +198,7 @@ function _fakeColorize(lines: string[], tabSize: number): string {
0,
0,
0,
0,
-1,
'none',
false,
@@ -236,6 +238,7 @@ function _actualColorize(lines: string[], tabSize: number, tokenizationSupport:
0,
0,
0,
0,
-1,
'none',
false,

View File

@@ -84,6 +84,10 @@ export class SimpleModel implements IResolvedTextEditorModel {
public isResolved(): boolean {
return true;
}
public getMode(): string | undefined {
return this.model.getModeId();
}
}
export interface IOpenEditorDelegate {

View File

@@ -90,7 +90,7 @@ export interface IGlobalEditorOptions {
tabSize?: number;
/**
* Insert spaces when pressing `Tab`.
* This setting is overridden based on the file contents when detectIndentation` is on.
* This setting is overridden based on the file contents when `detectIndentation` is on.
* Defaults to true.
*/
insertSpaces?: boolean;

View File

@@ -501,7 +501,7 @@ export class MonarchTokenizer implements modes.ITokenizationSupport {
}
let result = line.search(regex);
if (result === -1) {
if (result === -1 || (result !== 0 && rule.matchOnlyAtLineStart)) {
continue;
}

View File

@@ -123,7 +123,7 @@ suite('TextAreaState', () => {
textArea.dispose();
});
function testDeduceInput(prevState: TextAreaState | null, value: string, selectionStart: number, selectionEnd: number, couldBeEmojiInput: boolean, couldBeTypingAtOffset0: boolean, expected: string, expectedCharReplaceCnt: number): void {
function testDeduceInput(prevState: TextAreaState | null, value: string, selectionStart: number, selectionEnd: number, couldBeEmojiInput: boolean, expected: string, expectedCharReplaceCnt: number): void {
prevState = prevState || TextAreaState.EMPTY;
let textArea = new MockTextAreaWrapper();
@@ -132,7 +132,7 @@ suite('TextAreaState', () => {
textArea._selectionEnd = selectionEnd;
let newState = TextAreaState.readFromTextArea(textArea);
let actual = TextAreaState.deduceInput(prevState, newState, couldBeEmojiInput, couldBeTypingAtOffset0);
let actual = TextAreaState.deduceInput(prevState, newState, couldBeEmojiInput);
assert.equal(actual.text, expected);
assert.equal(actual.replaceCharCnt, expectedCharReplaceCnt);
@@ -153,7 +153,7 @@ suite('TextAreaState', () => {
testDeduceInput(
TextAreaState.EMPTY,
'',
0, 1, true, false,
0, 1, true,
'', 0
);
@@ -163,7 +163,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('', 0, 1, null, null),
'せ',
0, 1, true, false,
0, 1, true,
'せ', 1
);
@@ -173,7 +173,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('せ', 0, 1, null, null),
'せn',
0, 2, true, false,
0, 2, true,
'せn', 1
);
@@ -183,7 +183,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('せn', 0, 2, null, null),
'せん',
0, 2, true, false,
0, 2, true,
'せん', 2
);
@@ -193,7 +193,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('せん', 0, 2, null, null),
'せんs',
0, 3, true, false,
0, 3, true,
'せんs', 2
);
@@ -203,7 +203,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('せんs', 0, 3, null, null),
'せんせ',
0, 3, true, false,
0, 3, true,
'せんせ', 3
);
@@ -213,7 +213,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('せんせ', 0, 3, null, null),
'せんせ',
0, 3, true, false,
0, 3, true,
'せんせ', 3
);
@@ -223,7 +223,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('せんせ', 0, 3, null, null),
'せんせい',
0, 4, true, false,
0, 4, true,
'せんせい', 3
);
@@ -233,7 +233,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('せんせい', 0, 4, null, null),
'せんせい',
4, 4, true, false,
4, 4, true,
'', 0
);
});
@@ -252,7 +252,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('せんせい', 0, 4, null, null),
'せんせい',
0, 4, true, false,
0, 4, true,
'せんせい', 4
);
@@ -262,7 +262,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('せんせい', 0, 4, null, null),
'先生',
0, 2, true, false,
0, 2, true,
'先生', 4
);
@@ -272,7 +272,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('先生', 0, 2, null, null),
'先生',
2, 2, true, false,
2, 2, true,
'', 0
);
});
@@ -281,7 +281,7 @@ suite('TextAreaState', () => {
testDeduceInput(
null,
'a',
0, 1, true, false,
0, 1, true,
'a', 0
);
});
@@ -290,7 +290,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState(']\n', 1, 2, null, null),
']\n',
2, 2, true, false,
2, 2, true,
'\n', 0
);
});
@@ -299,7 +299,7 @@ suite('TextAreaState', () => {
testDeduceInput(
null,
'a',
1, 1, true, false,
1, 1, true,
'a', 0
);
});
@@ -308,7 +308,7 @@ suite('TextAreaState', () => {
testDeduceInput(
TextAreaState.EMPTY,
'a',
0, 1, true, false,
0, 1, true,
'a', 0
);
});
@@ -317,7 +317,7 @@ suite('TextAreaState', () => {
testDeduceInput(
TextAreaState.EMPTY,
'a',
1, 1, true, false,
1, 1, true,
'a', 0
);
});
@@ -326,7 +326,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('Hello world!', 0, 12, null, null),
'H',
1, 1, true, false,
1, 1, true,
'H', 0
);
});
@@ -335,7 +335,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('Hello world!', 12, 12, null, null),
'Hello world!a',
13, 13, true, false,
13, 13, true,
'a', 0
);
});
@@ -344,7 +344,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('Hello world!', 0, 0, null, null),
'aHello world!',
1, 1, true, false,
1, 1, true,
'a', 0
);
});
@@ -353,7 +353,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('Hello world!', 6, 11, null, null),
'Hello other!',
11, 11, true, false,
11, 11, true,
'other', 0
);
});
@@ -362,7 +362,7 @@ suite('TextAreaState', () => {
testDeduceInput(
TextAreaState.EMPTY,
'これは',
3, 3, true, false,
3, 3, true,
'これは', 0
);
});
@@ -371,7 +371,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('Hello world!', 0, 0, null, null),
'Aello world!',
1, 1, true, false,
1, 1, true,
'A', 0
);
});
@@ -380,7 +380,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('Hello world!', 5, 5, null, null),
'Hellö world!',
4, 5, true, false,
4, 5, true,
'ö', 0
);
});
@@ -389,7 +389,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('Hello world!', 5, 5, null, null),
'Hellöö world!',
5, 5, true, false,
5, 5, true,
'öö', 1
);
});
@@ -398,7 +398,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('Hello world!', 5, 5, null, null),
'Helöö world!',
5, 5, true, false,
5, 5, true,
'öö', 2
);
});
@@ -407,7 +407,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('Hello world!', 5, 5, null, null),
'Hellö world!',
5, 5, true, false,
5, 5, true,
'ö', 1
);
});
@@ -416,7 +416,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('a', 0, 1, null, null),
'a',
1, 1, true, false,
1, 1, true,
'a', 0
);
});
@@ -425,7 +425,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('x x', 0, 1, null, null),
'x x',
1, 1, true, false,
1, 1, true,
'x', 0
);
});
@@ -455,7 +455,7 @@ suite('TextAreaState', () => {
'some6 text',
'some7 text'
].join('\n'),
4, 4, true, false,
4, 4, true,
'📅', 0
);
});
@@ -469,7 +469,7 @@ suite('TextAreaState', () => {
null, null
),
'some💊1 text',
6, 6, true, false,
6, 6, true,
'💊', 0
);
});
@@ -483,7 +483,7 @@ suite('TextAreaState', () => {
null, null
),
'qwertyu\nasdfghj\nzxcvbnm🎈',
25, 25, true, false,
25, 25, true,
'🎈', 0
);
});
@@ -498,39 +498,11 @@ suite('TextAreaState', () => {
null, null
),
'some⌨1 text',
6, 6, true, false,
6, 6, true,
'⌨️', 0
);
});
test('issue #42251: Minor issue, character swapped when typing', () => {
// Typing on OSX occurs at offset 0 after moving the window using the custom (non-native) titlebar.
testDeduceInput(
new TextAreaState(
'ab',
2, 2,
null, null
),
'cab',
1, 1, true, true,
'c', 0
);
});
test('issue #49480: Double curly braces inserted', () => {
// Characters get doubled
testDeduceInput(
new TextAreaState(
'aa',
2, 2,
null, null
),
'aaa',
3, 3, true, true,
'a', 0
);
});
suite('PagedScreenReaderStrategy', () => {
function testPagedScreenReaderStrategy(lines: string[], selection: Selection, expected: TextAreaState): void {

View File

@@ -42,6 +42,7 @@ export class TestConfiguration extends CommonEditorConfiguration {
canUseHalfwidthRightwardsArrow: true,
spaceWidth: 10,
middotWidth: 10,
wsmiddotWidth: 10,
maxDigitWidth: 10,
}, true);
}

View File

@@ -28,7 +28,7 @@ suite('Editor ViewLayout - LinesLayout', () => {
test('LinesLayout 1', () => {
// Start off with 10 lines
let linesLayout = new LinesLayout(10, 10);
let linesLayout = new LinesLayout(10, 10, 0, 0);
// lines: [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
// whitespace: -
@@ -137,7 +137,7 @@ suite('Editor ViewLayout - LinesLayout', () => {
test('LinesLayout 2', () => {
// Start off with 10 lines and one whitespace after line 2, of height 5
let linesLayout = new LinesLayout(10, 1);
let linesLayout = new LinesLayout(10, 1, 0, 0);
let a = insertWhitespace(linesLayout, 2, 0, 5, 0);
// 10 lines
@@ -232,8 +232,103 @@ suite('Editor ViewLayout - LinesLayout', () => {
assert.equal(linesLayout.getVerticalOffsetForLineNumber(10), 9);
});
test('LinesLayout Padding', () => {
// Start off with 10 lines
let linesLayout = new LinesLayout(10, 10, 15, 20);
// lines: [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
// whitespace: -
assert.equal(linesLayout.getLinesTotalHeight(), 135);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(1), 15);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(2), 25);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(3), 35);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(4), 45);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(5), 55);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(6), 65);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(7), 75);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(8), 85);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(9), 95);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(10), 105);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(0), 1);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(10), 1);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(15), 1);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(24), 1);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(25), 2);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(34), 2);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(35), 3);
// Add whitespace of height 5px after 2nd line
insertWhitespace(linesLayout, 2, 0, 5, 0);
// lines: [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
// whitespace: a(2,5)
assert.equal(linesLayout.getLinesTotalHeight(), 140);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(1), 15);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(2), 25);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(3), 40);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(4), 50);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(0), 1);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(10), 1);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(25), 2);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(34), 2);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(35), 3);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(39), 3);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(40), 3);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(41), 3);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(49), 3);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(50), 4);
// Add two more whitespaces of height 5px
insertWhitespace(linesLayout, 3, 0, 5, 0);
insertWhitespace(linesLayout, 4, 0, 5, 0);
// lines: [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
// whitespace: a(2,5), b(3, 5), c(4, 5)
assert.equal(linesLayout.getLinesTotalHeight(), 150);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(1), 15);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(2), 25);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(3), 40);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(4), 55);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(5), 70);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(6), 80);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(0), 1);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(15), 1);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(24), 1);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(30), 2);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(35), 3);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(39), 3);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(40), 3);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(49), 3);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(50), 4);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(54), 4);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(55), 4);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(64), 4);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(65), 5);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(69), 5);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(70), 5);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(80), 6);
assert.equal(linesLayout.getVerticalOffsetForWhitespaceIndex(0), 35); // 35 -> 40
assert.equal(linesLayout.getVerticalOffsetForWhitespaceIndex(1), 50); // 50 -> 55
assert.equal(linesLayout.getVerticalOffsetForWhitespaceIndex(2), 65);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(0), 0);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(34), 0);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(35), 0);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(39), 0);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(40), 1);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(49), 1);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(50), 1);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(54), 1);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(55), 2);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(64), 2);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(65), 2);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(70), -1);
});
test('LinesLayout getLineNumberAtOrAfterVerticalOffset', () => {
let linesLayout = new LinesLayout(10, 1);
let linesLayout = new LinesLayout(10, 1, 0, 0);
insertWhitespace(linesLayout, 6, 0, 10, 0);
// 10 lines
@@ -282,7 +377,7 @@ suite('Editor ViewLayout - LinesLayout', () => {
});
test('LinesLayout getCenteredLineInViewport', () => {
let linesLayout = new LinesLayout(10, 1);
let linesLayout = new LinesLayout(10, 1, 0, 0);
insertWhitespace(linesLayout, 6, 0, 10, 0);
// 10 lines
@@ -365,7 +460,7 @@ suite('Editor ViewLayout - LinesLayout', () => {
});
test('LinesLayout getLinesViewportData 1', () => {
let linesLayout = new LinesLayout(10, 10);
let linesLayout = new LinesLayout(10, 10, 0, 0);
insertWhitespace(linesLayout, 6, 0, 100, 0);
// 10 lines
@@ -498,7 +593,7 @@ suite('Editor ViewLayout - LinesLayout', () => {
});
test('LinesLayout getLinesViewportData 2 & getWhitespaceViewportData', () => {
let linesLayout = new LinesLayout(10, 10);
let linesLayout = new LinesLayout(10, 10, 0, 0);
let a = insertWhitespace(linesLayout, 6, 0, 100, 0);
let b = insertWhitespace(linesLayout, 7, 0, 50, 0);
@@ -569,7 +664,7 @@ suite('Editor ViewLayout - LinesLayout', () => {
});
test('LinesLayout getWhitespaceAtVerticalOffset', () => {
let linesLayout = new LinesLayout(10, 10);
let linesLayout = new LinesLayout(10, 10, 0, 0);
let a = insertWhitespace(linesLayout, 6, 0, 100, 0);
let b = insertWhitespace(linesLayout, 7, 0, 50, 0);
@@ -612,7 +707,7 @@ suite('Editor ViewLayout - LinesLayout', () => {
test('LinesLayout', () => {
const linesLayout = new LinesLayout(100, 20);
const linesLayout = new LinesLayout(100, 20, 0, 0);
// Insert a whitespace after line number 2, of height 10
const a = insertWhitespace(linesLayout, 2, 0, 10, 0);
@@ -963,7 +1058,7 @@ suite('Editor ViewLayout - LinesLayout', () => {
});
test('LinesLayout changeWhitespaceAfterLineNumber & getFirstWhitespaceIndexAfterLineNumber', () => {
const linesLayout = new LinesLayout(100, 20);
const linesLayout = new LinesLayout(100, 20, 0, 0);
const a = insertWhitespace(linesLayout, 0, 0, 1, 0);
const b = insertWhitespace(linesLayout, 7, 0, 1, 0);
@@ -1087,7 +1182,7 @@ suite('Editor ViewLayout - LinesLayout', () => {
});
test('LinesLayout Bug', () => {
const linesLayout = new LinesLayout(100, 20);
const linesLayout = new LinesLayout(100, 20, 0, 0);
const a = insertWhitespace(linesLayout, 0, 0, 1, 0);
const b = insertWhitespace(linesLayout, 7, 0, 1, 0);

View File

@@ -40,6 +40,7 @@ suite('viewLineRenderer.renderLine', () => {
0,
0,
0,
0,
-1,
'none',
false,
@@ -92,6 +93,7 @@ suite('viewLineRenderer.renderLine', () => {
0,
0,
0,
0,
-1,
'none',
false,
@@ -147,6 +149,7 @@ suite('viewLineRenderer.renderLine', () => {
0,
10,
10,
10,
6,
'boundary',
false,
@@ -241,6 +244,7 @@ suite('viewLineRenderer.renderLine', () => {
0,
10,
10,
10,
-1,
'boundary',
false,
@@ -306,6 +310,7 @@ suite('viewLineRenderer.renderLine', () => {
0,
10,
10,
10,
-1,
'none',
false,
@@ -371,6 +376,7 @@ suite('viewLineRenderer.renderLine', () => {
0,
10,
10,
10,
-1,
'none',
false,
@@ -413,6 +419,7 @@ suite('viewLineRenderer.renderLine', () => {
0,
10,
10,
10,
-1,
'none',
false,
@@ -446,6 +453,7 @@ suite('viewLineRenderer.renderLine', () => {
0,
10,
10,
10,
-1,
'none',
false,
@@ -549,6 +557,7 @@ suite('viewLineRenderer.renderLine', () => {
0,
10,
10,
10,
-1,
'none',
false,
@@ -590,6 +599,7 @@ suite('viewLineRenderer.renderLine', () => {
0,
10,
10,
10,
-1,
'none',
false,
@@ -622,6 +632,7 @@ suite('viewLineRenderer.renderLine', () => {
0,
10,
10,
10,
-1,
'none',
false,
@@ -671,6 +682,7 @@ suite('viewLineRenderer.renderLine', () => {
0,
10,
10,
10,
-1,
'none',
false,
@@ -755,6 +767,7 @@ suite('viewLineRenderer.renderLine 2', () => {
0,
10,
10,
10,
-1,
renderWhitespace,
false,
@@ -783,6 +796,7 @@ suite('viewLineRenderer.renderLine 2', () => {
0,
10,
10,
10,
-1,
'none',
false,
@@ -825,6 +839,7 @@ suite('viewLineRenderer.renderLine 2', () => {
0,
10,
10,
10,
-1,
'none',
false,
@@ -1242,6 +1257,7 @@ suite('viewLineRenderer.renderLine 2', () => {
0,
10,
10,
10,
-1,
'none',
false,
@@ -1285,6 +1301,7 @@ suite('viewLineRenderer.renderLine 2', () => {
0,
10,
10,
10,
-1,
'all',
false,
@@ -1320,6 +1337,7 @@ suite('viewLineRenderer.renderLine 2', () => {
0,
10,
10,
10,
-1,
'all',
false,
@@ -1356,6 +1374,7 @@ suite('viewLineRenderer.renderLine 2', () => {
0,
10,
10,
10,
-1,
'all',
false,
@@ -1388,6 +1407,7 @@ suite('viewLineRenderer.renderLine 2', () => {
0,
10,
10,
10,
10000,
'none',
false,
@@ -1424,6 +1444,7 @@ suite('viewLineRenderer.renderLine 2', () => {
0,
10,
10,
10,
10000,
'none',
false,
@@ -1460,6 +1481,7 @@ suite('viewLineRenderer.renderLine 2', () => {
0,
10,
10,
10,
10000,
'none',
false,
@@ -1493,6 +1515,7 @@ suite('viewLineRenderer.renderLine 2', () => {
0,
10,
10,
10,
10000,
'none',
false,
@@ -1525,6 +1548,7 @@ suite('viewLineRenderer.renderLine 2', () => {
0,
10,
10,
10,
10000,
'all',
false,
@@ -1563,6 +1587,7 @@ suite('viewLineRenderer.renderLine 2', () => {
0,
10,
10,
10,
10000,
'none',
false,
@@ -1595,6 +1620,7 @@ suite('viewLineRenderer.renderLine 2', () => {
0,
10,
10,
10,
10000,
'none',
false,
@@ -1629,6 +1655,7 @@ suite('viewLineRenderer.renderLine 2', () => {
0,
10,
10,
10,
10000,
'none',
false,
@@ -1662,6 +1689,7 @@ suite('viewLineRenderer.renderLine 2', () => {
0,
10,
10,
10,
10000,
'boundary',
false,
@@ -1693,6 +1721,7 @@ suite('viewLineRenderer.renderLine 2', () => {
0,
10,
10,
10,
10000,
'none',
false,
@@ -1728,6 +1757,7 @@ suite('viewLineRenderer.renderLine 2', () => {
0,
10,
10,
10,
10000,
'none',
false,
@@ -1759,6 +1789,7 @@ suite('viewLineRenderer.renderLine 2', () => {
0,
10,
10,
10,
-1,
'none',
false,

View File

@@ -58,6 +58,7 @@ function getLineBreakData(factory: ILineBreaksComputerFactory, tabSize: number,
canUseHalfwidthRightwardsArrow: true,
spaceWidth: 7,
middotWidth: 7,
wsmiddotWidth: 7,
maxDigitWidth: 7
}, false);
const lineBreaksComputer = factory.createLineBreaksComputer(fontInfo, tabSize, breakAfter, wrappingIndent);

126
src/vs/monaco.d.ts vendored
View File

@@ -1067,7 +1067,7 @@ declare namespace monaco.editor {
tabSize?: number;
/**
* Insert spaces when pressing `Tab`.
* This setting is overridden based on the file contents when detectIndentation` is on.
* This setting is overridden based on the file contents when `detectIndentation` is on.
* Defaults to true.
*/
insertSpaces?: boolean;
@@ -1184,7 +1184,7 @@ declare namespace monaco.editor {
severity: MarkerSeverity;
code?: string | {
value: string;
link: Uri;
target: Uri;
};
message: string;
source?: string;
@@ -1202,7 +1202,7 @@ declare namespace monaco.editor {
export interface IMarkerData {
code?: string | {
value: string;
link: Uri;
target: Uri;
};
severity: MarkerSeverity;
message: string;
@@ -2841,6 +2841,10 @@ declare namespace monaco.editor {
* Defaults to 10 (ms)
*/
quickSuggestionsDelay?: number;
/**
* Controls the spacing around the editor.
*/
padding?: IEditorPaddingOptions;
/**
* Parameter hint options.
*/
@@ -3395,6 +3399,25 @@ declare namespace monaco.editor {
export type EditorMinimapOptions = Readonly<Required<IEditorMinimapOptions>>;
/**
* Configuration options for editor padding
*/
export interface IEditorPaddingOptions {
/**
* Spacing between top edge of editor and first line.
*/
top?: number;
/**
* Spacing between bottom edge of editor and last line.
*/
bottom?: number;
}
export interface InternalEditorPaddingOptions {
readonly top: number;
readonly bottom: number;
}
/**
* Configuration options for parameter hints
*/
@@ -3763,53 +3786,54 @@ declare namespace monaco.editor {
occurrencesHighlight = 61,
overviewRulerBorder = 62,
overviewRulerLanes = 63,
parameterHints = 64,
peekWidgetDefaultFocus = 65,
definitionLinkOpensInPeek = 66,
quickSuggestions = 67,
quickSuggestionsDelay = 68,
readOnly = 69,
renderControlCharacters = 70,
renderIndentGuides = 71,
renderFinalNewline = 72,
renderLineHighlight = 73,
renderValidationDecorations = 74,
renderWhitespace = 75,
revealHorizontalRightPadding = 76,
roundedSelection = 77,
rulers = 78,
scrollbar = 79,
scrollBeyondLastColumn = 80,
scrollBeyondLastLine = 81,
scrollPredominantAxis = 82,
selectionClipboard = 83,
selectionHighlight = 84,
selectOnLineNumbers = 85,
showFoldingControls = 86,
showUnused = 87,
snippetSuggestions = 88,
smoothScrolling = 89,
stopRenderingLineAfter = 90,
suggest = 91,
suggestFontSize = 92,
suggestLineHeight = 93,
suggestOnTriggerCharacters = 94,
suggestSelection = 95,
tabCompletion = 96,
useTabStops = 97,
wordSeparators = 98,
wordWrap = 99,
wordWrapBreakAfterCharacters = 100,
wordWrapBreakBeforeCharacters = 101,
wordWrapColumn = 102,
wordWrapMinified = 103,
wrappingIndent = 104,
wrappingStrategy = 105,
editorClassName = 106,
pixelRatio = 107,
tabFocusMode = 108,
layoutInfo = 109,
wrappingInfo = 110
padding = 64,
parameterHints = 65,
peekWidgetDefaultFocus = 66,
definitionLinkOpensInPeek = 67,
quickSuggestions = 68,
quickSuggestionsDelay = 69,
readOnly = 70,
renderControlCharacters = 71,
renderIndentGuides = 72,
renderFinalNewline = 73,
renderLineHighlight = 74,
renderValidationDecorations = 75,
renderWhitespace = 76,
revealHorizontalRightPadding = 77,
roundedSelection = 78,
rulers = 79,
scrollbar = 80,
scrollBeyondLastColumn = 81,
scrollBeyondLastLine = 82,
scrollPredominantAxis = 83,
selectionClipboard = 84,
selectionHighlight = 85,
selectOnLineNumbers = 86,
showFoldingControls = 87,
showUnused = 88,
snippetSuggestions = 89,
smoothScrolling = 90,
stopRenderingLineAfter = 91,
suggest = 92,
suggestFontSize = 93,
suggestLineHeight = 94,
suggestOnTriggerCharacters = 95,
suggestSelection = 96,
tabCompletion = 97,
useTabStops = 98,
wordSeparators = 99,
wordWrap = 100,
wordWrapBreakAfterCharacters = 101,
wordWrapBreakBeforeCharacters = 102,
wordWrapColumn = 103,
wordWrapMinified = 104,
wrappingIndent = 105,
wrappingStrategy = 106,
editorClassName = 107,
pixelRatio = 108,
tabFocusMode = 109,
layoutInfo = 110,
wrappingInfo = 111
}
export const EditorOptions: {
acceptSuggestionOnCommitCharacter: IEditorOption<EditorOption.acceptSuggestionOnCommitCharacter, boolean>;
@@ -3876,6 +3900,7 @@ declare namespace monaco.editor {
occurrencesHighlight: IEditorOption<EditorOption.occurrencesHighlight, boolean>;
overviewRulerBorder: IEditorOption<EditorOption.overviewRulerBorder, boolean>;
overviewRulerLanes: IEditorOption<EditorOption.overviewRulerLanes, number>;
padding: IEditorOption<EditorOption.padding, InternalEditorPaddingOptions>;
parameterHints: IEditorOption<EditorOption.parameterHints, InternalParameterHintOptions>;
peekWidgetDefaultFocus: IEditorOption<EditorOption.peekWidgetDefaultFocus, 'tree' | 'editor'>;
definitionLinkOpensInPeek: IEditorOption<EditorOption.definitionLinkOpensInPeek, boolean>;
@@ -4669,6 +4694,7 @@ declare namespace monaco.editor {
readonly canUseHalfwidthRightwardsArrow: boolean;
readonly spaceWidth: number;
readonly middotWidth: number;
readonly wsmiddotWidth: number;
readonly maxDigitWidth: number;
}

View File

@@ -16,6 +16,7 @@ export interface IAccessibilityService {
alwaysUnderlineAccessKeys(): Promise<boolean>;
isScreenReaderOptimized(): boolean;
getAccessibilitySupport(): AccessibilitySupport;
setAccessibilitySupport(accessibilitySupport: AccessibilitySupport): void;
}

View File

@@ -42,6 +42,10 @@ export class AccessibilityService extends Disposable implements IAccessibilitySe
return config === 'on' || (config === 'auto' && this._accessibilitySupport === AccessibilitySupport.Enabled);
}
getAccessibilitySupport(): AccessibilitySupport {
return this._accessibilitySupport;
}
alwaysUnderlineAccessKeys(): Promise<boolean> {
return Promise.resolve(false);
}

View File

@@ -324,10 +324,10 @@ export class SyncActionDescriptor {
public static create<Services extends BrandedService[]>(ctor: { new(id: string, label: string, ...services: Services): Action },
id: string, label: string | undefined, keybindings?: IKeybindings, keybindingContext?: ContextKeyExpr, keybindingWeight?: number
): SyncActionDescriptor {
return new SyncActionDescriptor(ctor as IConstructorSignature2<string, string, Action>, id, label, keybindings, keybindingContext, keybindingWeight);
return new SyncActionDescriptor(ctor as IConstructorSignature2<string, string | undefined, Action>, id, label, keybindings, keybindingContext, keybindingWeight);
}
private constructor(ctor: IConstructorSignature2<string, string, Action>,
private constructor(ctor: IConstructorSignature2<string, string | undefined, Action>,
id: string, label: string | undefined, keybindings?: IKeybindings, keybindingContext?: ContextKeyExpr, keybindingWeight?: number
) {
this._id = id;

View File

@@ -223,8 +223,6 @@ export interface IGlobalExtensionEnablementService {
enableExtension(extension: IExtensionIdentifier, source?: string): Promise<boolean>;
disableExtension(extension: IExtensionIdentifier, source?: string): Promise<boolean>;
// Async method until storage service is available in shared process
getDisabledExtensionsAsync(): Promise<IExtensionIdentifier[]>;
}
export const ExtensionsLabel = localize('extensions', "Extensions");

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
import { IExtensionManagementService, ILocalExtension, InstallExtensionEvent, DidInstallExtensionEvent, IGalleryExtension, DidUninstallExtensionEvent, IExtensionIdentifier, IGalleryMetadata, IReportedExtension, IGlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionManagementService, ILocalExtension, InstallExtensionEvent, DidInstallExtensionEvent, IGalleryExtension, DidUninstallExtensionEvent, IExtensionIdentifier, IGalleryMetadata, IReportedExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
import { Event } from 'vs/base/common/event';
import { URI, UriComponents } from 'vs/base/common/uri';
import { IURITransformer, DefaultURITransformer, transformAndReviveIncomingURIs } from 'vs/base/common/uriIpc';
@@ -130,53 +130,3 @@ export class ExtensionManagementChannelClient implements IExtensionManagementSer
return Promise.resolve(this.channel.call('getExtensionsReport'));
}
}
export class GlobalExtensionEnablementServiceChannel implements IServerChannel {
constructor(private readonly service: IGlobalExtensionEnablementService) { }
listen(_: unknown, event: string): Event<any> {
switch (event) {
case 'onDidChangeEnablement': return this.service.onDidChangeEnablement;
}
throw new Error(`Event not found: ${event}`);
}
call(context: any, command: string, args?: any): Promise<any> {
switch (command) {
case 'getDisabledExtensionsAsync': return Promise.resolve(this.service.getDisabledExtensions());
case 'enableExtension': return this.service.enableExtension(args[0]);
case 'disableExtension': return this.service.disableExtension(args[0]);
}
throw new Error('Invalid call');
}
}
export class GlobalExtensionEnablementServiceClient implements IGlobalExtensionEnablementService {
_serviceBrand: undefined;
get onDidChangeEnablement(): Event<{ readonly extensions: IExtensionIdentifier[], readonly source?: string }> { return this.channel.listen('onDidChangeEnablement'); }
constructor(private readonly channel: IChannel) {
}
getDisabledExtensionsAsync(): Promise<IExtensionIdentifier[]> {
return this.channel.call('getDisabledExtensionsAsync');
}
enableExtension(extension: IExtensionIdentifier): Promise<boolean> {
return this.channel.call('enableExtension', [extension]);
}
disableExtension(extension: IExtensionIdentifier): Promise<boolean> {
return this.channel.call('disableExtension', [extension]);
}
getDisabledExtensions(): IExtensionIdentifier[] {
throw new Error('not supported');
}
}

View File

@@ -8,8 +8,8 @@ import { ServiceIdentifier, BrandedService } from './instantiation';
const _registry: [ServiceIdentifier<any>, SyncDescriptor<any>][] = [];
export function registerSingleton<T, Services extends BrandedService[]>(id: ServiceIdentifier<T>, ctor: { new(...services: Services): T }, supportsDelayedInstantiation?: boolean): void {
_registry.push([id, new SyncDescriptor<T>(ctor, [], supportsDelayedInstantiation)]);
export function registerSingleton<T, Services extends BrandedService[]>(id: ServiceIdentifier<T>, ctor: new (...services: Services) => T, supportsDelayedInstantiation?: boolean): void {
_registry.push([id, new SyncDescriptor<T>(ctor as new (...args: any[]) => T, [], supportsDelayedInstantiation)]);
}
export function getSingletonServiceDescriptors(): [ServiceIdentifier<any>, SyncDescriptor<any>][] {

View File

@@ -64,7 +64,7 @@ export class InstantiationService implements IInstantiationService {
return result;
}
};
return fn.apply(undefined, [accessor, ...args]);
return fn(accessor, ...args);
} finally {
_done = true;
_trace.stop();

View File

@@ -87,7 +87,7 @@ export namespace MarkerSeverity {
* A structure defining a problem/warning/etc.
*/
export interface IMarkerData {
code?: string | { value: string; link: URI };
code?: string | { value: string; target: URI };
severity: MarkerSeverity;
message: string;
source?: string;
@@ -108,7 +108,7 @@ export interface IMarker {
owner: string;
resource: URI;
severity: MarkerSeverity;
code?: string | { value: string; link: URI };
code?: string | { value: string; target: URI };
message: string;
source?: string;
startLineNumber: number;

View File

@@ -284,14 +284,12 @@ export class Menubar {
this.setMenuById(editMenu, 'Edit');
menubar.append(editMenuItem);
// {{SQL CARBON EDIT}} - Disable unused menus
// // Selection
// const selectionMenu = new Menu();
// const selectionMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mSelection', comment: ['&& denotes a mnemonic'] }, "&&Selection")), submenu: selectionMenu });
// Selection
/*const selectionMenu = new Menu();
const selectionMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mSelection', comment: ['&& denotes a mnemonic'] }, "&&Selection")), submenu: selectionMenu });
// this.setMenuById(selectionMenu, 'Selection');
// menubar.append(selectionMenuItem);
// {{SQL CARBON EDIT}} - End
this.setMenuById(selectionMenu, 'Selection');
menubar.append(selectionMenuItem); {{SQL CARBON EDIT}} - Disable unused menus */
// View
const viewMenu = new Menu();
@@ -300,32 +298,26 @@ export class Menubar {
this.setMenuById(viewMenu, 'View');
menubar.append(viewMenuItem);
// {{SQL CARBON EDIT}} - Disable unused menus
// // Go
// const gotoMenu = new Menu();
// const gotoMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mGoto', comment: ['&& denotes a mnemonic'] }, "&&Go")), submenu: gotoMenu });
// Go
/* const gotoMenu = new Menu();
const gotoMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mGoto', comment: ['&& denotes a mnemonic'] }, "&&Go")), submenu: gotoMenu });
// this.setMenuById(gotoMenu, 'Go');
// menubar.append(gotoMenuItem);
// {{SQL CARBON EDIT}} - End
this.setMenuById(gotoMenu, 'Go');
menubar.append(gotoMenuItem); {{SQL CARBON EDIT}} - Disable unused menus */
// {{SQL CARBON EDIT}} - Disable unused menus
// // Debug
// const debugMenu = new Menu();
// const debugMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mDebug', comment: ['&& denotes a mnemonic'] }, "&&Debug")), submenu: debugMenu });
// Debug
/*const debugMenu = new Menu();
const debugMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mRun', comment: ['&& denotes a mnemonic'] }, "&&Run")), submenu: debugMenu });
// this.setMenuById(debugMenu, 'Debug');
// menubar.append(debugMenuItem);
// {{SQL CARBON EDIT}} - End
this.setMenuById(debugMenu, 'Run');
menubar.append(debugMenuItem); {{SQL CARBON EDIT}} - Disable unused menus */
// {{SQL CARBON EDIT}} - Disable unused menus
// // Terminal
// const terminalMenu = new Menu();
// const terminalMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mTerminal', comment: ['&& denotes a mnemonic'] }, "&&Terminal")), submenu: terminalMenu });
// Terminal
/*const terminalMenu = new Menu();
const terminalMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mTerminal', comment: ['&& denotes a mnemonic'] }, "&&Terminal")), submenu: terminalMenu });
// this.setMenuById(terminalMenu, 'Terminal');
// menubar.append(terminalMenuItem);
// {{SQL CARBON EDIT}} - End
this.setMenuById(terminalMenu, 'Terminal');
menubar.append(terminalMenuItem); {{SQL CARBON EDIT}} - Disable unused menus */
// Mac: Window
let macWindowMenuItem: MenuItem | undefined;

View File

@@ -15,7 +15,7 @@ import { copy, exists, mkdirp, writeFile } from 'vs/base/node/pfs';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IWorkspaceInitializationPayload, isWorkspaceIdentifier, isSingleFolderWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces';
import { onUnexpectedError } from 'vs/base/common/errors';
import { assertIsDefined, assertAllDefined } from 'vs/base/common/types';
import { assertIsDefined } from 'vs/base/common/types';
import { RunOnceScheduler, runWhenIdle } from 'vs/base/common/async';
export class NativeStorageService extends Disposable implements IStorageService {
@@ -238,14 +238,11 @@ export class NativeStorageService extends Disposable implements IStorageService
}
async logStorage(): Promise<void> {
const [workspaceStorage, workspaceStoragePath] = assertAllDefined(this.workspaceStorage, this.workspaceStoragePath);
const result = await Promise.all([
return logStorage(
this.globalStorage.items,
workspaceStorage.items
]);
logStorage(result[0], result[1], this.environmentService.globalStorageHome, workspaceStoragePath);
this.workspaceStorage ? this.workspaceStorage.items : new Map<string, string>(), // Shared process storage does not has workspace storage
this.environmentService.globalStorageHome,
this.workspaceStoragePath || '');
}
async migrate(toWorkspace: IWorkspaceInitializationPayload): Promise<void> {

View File

@@ -401,22 +401,22 @@ function registerDefaultClassifications(): void {
registerTokenType('namespace', nls.localize('namespace', "Style for namespaces."), [['entity.name.namespace']]);
registerTokenType('type', nls.localize('type', "Style for types."), [['entity.name.type'], ['support.type'], ['support.class']]);
registerTokenType('struct', nls.localize('struct', "Style for structs."), [['storage.type.struct']], 'type');
registerTokenType('class', nls.localize('class', "Style for classes."), [['entity.name.type.class']], 'type');
registerTokenType('interface', nls.localize('interface', "Style for interfaces."), [['entity.name.type.interface']], 'type');
registerTokenType('enum', nls.localize('enum', "Style for enums."), [['entity.name.type.enum']], 'type');
registerTokenType('typeParameter', nls.localize('typeParameter', "Style for type parameters."), [['entity.name.type', 'meta.type.parameters']], 'type');
registerTokenType('type', nls.localize('type', "Style for types."), [['entity.name.type'], ['support.type']]);
registerTokenType('struct', nls.localize('struct', "Style for structs."), [['storage.type.struct']]);
registerTokenType('class', nls.localize('class', "Style for classes."), [['entity.name.type.class'], ['support.class']]);
registerTokenType('interface', nls.localize('interface', "Style for interfaces."), [['entity.name.type.interface']]);
registerTokenType('enum', nls.localize('enum', "Style for enums."), [['entity.name.type.enum']]);
registerTokenType('typeParameter', nls.localize('typeParameter', "Style for type parameters."), [['entity.name.type.parameter']]);
registerTokenType('function', nls.localize('function', "Style for functions"), [['entity.name.function'], ['support.function']]);
registerTokenType('member', nls.localize('member', "Style for member"), [['entity.name.function.member'], ['support.function']]);
registerTokenType('macro', nls.localize('macro', "Style for macros."), [['entity.name.other.preprocessor.macro']], 'function');
registerTokenType('macro', nls.localize('macro', "Style for macros."), [['entity.name.other.preprocessor.macro']]);
registerTokenType('variable', nls.localize('variable', "Style for variables."), [['variable.other.readwrite'], ['entity.name.variable']]);
registerTokenType('parameter', nls.localize('parameter', "Style for parameters."), [['variable.parameter']], 'variable');
registerTokenType('property', nls.localize('property', "Style for properties."), [['variable.other.property']], 'variable');
registerTokenType('enumMember', nls.localize('enumMember', "Style for enum members."), [['variable.other.enummember']], 'variable');
registerTokenType('event', nls.localize('event', "Style for events."), [['variable.other.event']], 'variable');
registerTokenType('parameter', nls.localize('parameter', "Style for parameters."), [['variable.parameter']]);
registerTokenType('property', nls.localize('property', "Style for properties."), [['variable.other.property']]);
registerTokenType('enumMember', nls.localize('enumMember', "Style for enum members."), [['variable.other.enummember']]);
registerTokenType('event', nls.localize('event', "Style for events."), [['variable.other.event']]);
registerTokenType('label', nls.localize('labels', "Style for labels. "), undefined);

View File

@@ -26,7 +26,7 @@ export abstract class AbstractUpdateService implements IUpdateService {
_serviceBrand: undefined;
protected readonly url: string | undefined;
protected url: string | undefined;
private _state: State = State.Uninitialized;
@@ -49,7 +49,14 @@ export abstract class AbstractUpdateService implements IUpdateService {
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@IRequestService protected requestService: IRequestService,
@ILogService protected logService: ILogService,
) {
) { }
/**
* This must be called before any other call. This is a performance
* optimization, to avoid using extra CPU cycles before first window open.
* https://github.com/microsoft/vscode/issues/89784
*/
initialize(): void {
if (!this.environmentService.isBuilt) {
return; // updates are never enabled when running out of sources
}
@@ -173,6 +180,7 @@ export abstract class AbstractUpdateService implements IUpdateService {
if (!this.url) {
return Promise.resolve(undefined);
}
return this.requestService.request({ url: this.url }, CancellationToken.None).then(context => {
// The update server replies with 204 (No Content) when no
// update is available - that's all we want to know.

View File

@@ -36,6 +36,10 @@ export class DarwinUpdateService extends AbstractUpdateService {
@ILogService logService: ILogService
) {
super(lifecycleMainService, configurationService, environmentService, requestService, logService);
}
initialize(): void {
super.initialize();
this.onRawError(this.onError, this, this.disposables);
this.onRawUpdateAvailable(this.onUpdateAvailable, this, this.disposables);
this.onRawUpdateDownloaded(this.onUpdateDownloaded, this, this.disposables);

View File

@@ -70,6 +70,10 @@ export class Win32UpdateService extends AbstractUpdateService {
@IFileService private readonly fileService: IFileService
) {
super(lifecycleMainService, configurationService, environmentService, requestService, logService);
}
initialize(): void {
super.initialize();
if (getUpdateType() === UpdateType.Setup) {
/* __GDPR__
@@ -82,7 +86,7 @@ export class Win32UpdateService extends AbstractUpdateService {
"target" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
telemetryService.publicLog('update:win32SetupTarget', { target: product.target });
this.telemetryService.publicLog('update:win32SetupTarget', { target: product.target });
}
}

View File

@@ -58,7 +58,7 @@ export class ElectronURLListener {
return url;
});
const onOpenUrl = Event.filter(Event.map(onOpenElectronUrl, uriFromRawUrl), uri => !!uri);
const onOpenUrl = Event.filter<URI | null, URI>(Event.map(onOpenElectronUrl, uriFromRawUrl), (uri): uri is URI => !!uri);
onOpenUrl(this.urlService.open, this.urlService, this.disposables);
const isWindowReady = windowsMainService.getWindows()

View File

@@ -4,15 +4,22 @@
*--------------------------------------------------------------------------------------------*/
import { Disposable } from 'vs/base/common/lifecycle';
import { IFileService, IFileContent } from 'vs/platform/files/common/files';
import { IFileService, IFileContent, FileChangesEvent, FileSystemProviderError, FileSystemProviderErrorCode, FileOperationResult, FileOperationError } from 'vs/platform/files/common/files';
import { VSBuffer } from 'vs/base/common/buffer';
import { URI } from 'vs/base/common/uri';
import { SyncSource, SyncStatus, IUserData, IUserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSync';
import { SyncSource, SyncStatus, IUserData, IUserDataSyncStoreService, UserDataSyncErrorCode, UserDataSyncError, IUserDataSyncLogService, IUserDataSyncUtilService, ResourceKey, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { joinPath, dirname } from 'vs/base/common/resources';
import { toLocalISOString } from 'vs/base/common/date';
import { ThrottledDelayer } from 'vs/base/common/async';
import { ThrottledDelayer, CancelablePromise } from 'vs/base/common/async';
import { Emitter, Event } from 'vs/base/common/event';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { ParseError, parse } from 'vs/base/common/json';
import { FormattingOptions } from 'vs/base/common/jsonFormatter';
type SyncConflictsClassification = {
source?: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
};
export abstract class AbstractSynchroniser extends Disposable {
@@ -34,6 +41,9 @@ export abstract class AbstractSynchroniser extends Disposable {
@IFileService protected readonly fileService: IFileService,
@IEnvironmentService environmentService: IEnvironmentService,
@IUserDataSyncStoreService protected readonly userDataSyncStoreService: IUserDataSyncStoreService,
@IUserDataSyncEnablementService protected readonly userDataSyncEnablementService: IUserDataSyncEnablementService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IUserDataSyncLogService protected readonly logService: IUserDataSyncLogService,
) {
super();
this.syncFolder = joinPath(environmentService.userDataSyncHome, source);
@@ -43,20 +53,53 @@ export abstract class AbstractSynchroniser extends Disposable {
protected setStatus(status: SyncStatus): void {
if (this._status !== status) {
const oldStatus = this._status;
this._status = status;
this._onDidChangStatus.fire(status);
if (status === SyncStatus.HasConflicts) {
// Log to telemetry when there is a sync conflict
this.telemetryService.publicLog2<{ source: string }, SyncConflictsClassification>('sync/conflictsDetected', { source: this.source });
}
if (oldStatus === SyncStatus.HasConflicts && status === SyncStatus.Idle) {
// Log to telemetry when conflicts are resolved
this.telemetryService.publicLog2<{ source: string }, SyncConflictsClassification>('sync/conflictsResolved', { source: this.source });
}
}
}
protected get enabled(): boolean { return this.userDataSyncEnablementService.isResourceEnabled(this.resourceKey); }
async sync(ref?: string): Promise<void> {
if (!this.enabled) {
this.logService.info(`${this.source}: Skipped synchronizing ${this.source.toLowerCase()} as it is disabled.`);
return;
}
if (this.status === SyncStatus.HasConflicts) {
this.logService.info(`${this.source}: Skipped synchronizing ${this.source.toLowerCase()} as there are conflicts.`);
return;
}
if (this.status === SyncStatus.Syncing) {
this.logService.info(`${this.source}: Skipped synchronizing ${this.source.toLowerCase()} as it is running already.`);
return;
}
this.logService.trace(`${this.source}: Started synchronizing ${this.source.toLowerCase()}...`);
this.setStatus(SyncStatus.Syncing);
const lastSyncUserData = await this.getLastSyncUserData();
const remoteUserData = ref && lastSyncUserData && lastSyncUserData.ref === ref ? lastSyncUserData : await this.getRemoteUserData(lastSyncUserData);
return this.doSync(remoteUserData, lastSyncUserData);
}
async hasPreviouslySynced(): Promise<boolean> {
const lastSyncData = await this.getLastSyncUserData();
return !!lastSyncData;
}
async hasRemoteData(): Promise<boolean> {
async getRemoteContent(): Promise<string | null> {
const lastSyncData = await this.getLastSyncUserData();
const remoteUserData = await this.getRemoteUserData(lastSyncData);
return remoteUserData.content !== null;
return remoteUserData.content;
}
async resetLocal(): Promise<void> {
@@ -79,11 +122,11 @@ export abstract class AbstractSynchroniser extends Disposable {
}
protected async getRemoteUserData(lastSyncData: IUserData | null): Promise<IUserData> {
return this.userDataSyncStoreService.read(this.getRemoteDataResourceKey(), lastSyncData, this.source);
return this.userDataSyncStoreService.read(this.resourceKey, lastSyncData, this.source);
}
protected async updateRemoteUserData(content: string, ref: string | null): Promise<string> {
return this.userDataSyncStoreService.write(this.getRemoteDataResourceKey(), content, ref, this.source);
return this.userDataSyncStoreService.write(this.resourceKey, content, ref, this.source);
}
protected async backupLocal(content: VSBuffer): Promise<void> {
@@ -101,21 +144,56 @@ export abstract class AbstractSynchroniser extends Disposable {
}
}
protected abstract getRemoteDataResourceKey(): string;
abstract readonly resourceKey: ResourceKey;
protected abstract doSync(remoteUserData: IUserData, lastSyncUserData: IUserData | null): Promise<void>;
}
export interface IFileSyncPreviewResult {
readonly fileContent: IFileContent | null;
readonly remoteUserData: IUserData;
readonly lastSyncUserData: IUserData | null;
readonly content: string | null;
readonly hasLocalChanged: boolean;
readonly hasRemoteChanged: boolean;
readonly hasConflicts: boolean;
}
export abstract class AbstractFileSynchroniser extends AbstractSynchroniser {
protected syncPreviewResultPromise: CancelablePromise<IFileSyncPreviewResult> | null = null;
constructor(
protected readonly file: URI,
readonly source: SyncSource,
source: SyncSource,
@IFileService fileService: IFileService,
@IEnvironmentService environmentService: IEnvironmentService,
@IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService,
@IUserDataSyncEnablementService userDataSyncEnablementService: IUserDataSyncEnablementService,
@ITelemetryService telemetryService: ITelemetryService,
@IUserDataSyncLogService logService: IUserDataSyncLogService,
) {
super(source, fileService, environmentService, userDataSyncStoreService);
super(source, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService);
this._register(this.fileService.watch(dirname(file)));
this._register(Event.filter(this.fileService.onFileChanges, e => e.contains(file))(() => this._onDidChangeLocal.fire()));
this._register(this.fileService.onFileChanges(e => this.onFileChanges(e)));
}
async stop(): Promise<void> {
this.cancel();
this.logService.trace(`${this.source}: Stopped synchronizing ${this.source.toLowerCase()}.`);
try {
await this.fileService.del(this.conflictsPreviewResource);
} catch (e) { /* ignore */ }
this.setStatus(SyncStatus.Idle);
}
async getRemoteContent(preview?: boolean): Promise<string | null> {
if (preview) {
if (this.syncPreviewResultPromise) {
const result = await this.syncPreviewResultPromise;
return result.remoteUserData ? result.remoteUserData.content : null;
}
}
return super.getRemoteContent();
}
protected async getLocalFileContent(): Promise<IFileContent | null> {
@@ -127,14 +205,87 @@ export abstract class AbstractFileSynchroniser extends AbstractSynchroniser {
}
protected async updateLocalFileContent(newContent: string, oldContent: IFileContent | null): Promise<void> {
if (oldContent) {
// file exists already
await this.backupLocal(oldContent.value);
await this.fileService.writeFile(this.file, VSBuffer.fromString(newContent), oldContent);
} else {
// file does not exist
await this.fileService.createFile(this.file, VSBuffer.fromString(newContent), { overwrite: false });
try {
if (oldContent) {
// file exists already
await this.backupLocal(oldContent.value);
await this.fileService.writeFile(this.file, VSBuffer.fromString(newContent), oldContent);
} else {
// file does not exist
await this.fileService.createFile(this.file, VSBuffer.fromString(newContent), { overwrite: false });
}
} catch (e) {
if ((e instanceof FileSystemProviderError && e.code === FileSystemProviderErrorCode.FileExists) ||
(e instanceof FileOperationError && e.fileOperationResult === FileOperationResult.FILE_MODIFIED_SINCE)) {
throw new UserDataSyncError(e.message, UserDataSyncErrorCode.LocalPreconditionFailed);
} else {
throw e;
}
}
}
private onFileChanges(e: FileChangesEvent): void {
if (!e.contains(this.file)) {
return;
}
if (!this.enabled) {
return;
}
// Sync again if local file has changed and current status is in conflicts
if (this.status === SyncStatus.HasConflicts) {
this.syncPreviewResultPromise?.then(result => {
this.cancel();
this.doSync(result.remoteUserData, result.lastSyncUserData);
});
}
// Otherwise fire change event
else {
this._onDidChangeLocal.fire();
}
}
protected cancel(): void {
if (this.syncPreviewResultPromise) {
this.syncPreviewResultPromise.cancel();
this.syncPreviewResultPromise = null;
}
}
protected abstract readonly conflictsPreviewResource: URI;
}
export abstract class AbstractJsonFileSynchroniser extends AbstractFileSynchroniser {
constructor(
file: URI,
source: SyncSource,
@IFileService fileService: IFileService,
@IEnvironmentService environmentService: IEnvironmentService,
@IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService,
@IUserDataSyncEnablementService userDataSyncEnablementService: IUserDataSyncEnablementService,
@ITelemetryService telemetryService: ITelemetryService,
@IUserDataSyncLogService logService: IUserDataSyncLogService,
@IUserDataSyncUtilService protected readonly userDataSyncUtilService: IUserDataSyncUtilService,
) {
super(file, source, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService);
}
protected hasErrors(content: string): boolean {
const parseErrors: ParseError[] = [];
parse(content, parseErrors, { allowEmptyContent: true, allowTrailingComma: true });
return parseErrors.length > 0;
}
private _formattingOptions: Promise<FormattingOptions> | undefined = undefined;
protected getFormattingOptions(): Promise<FormattingOptions> {
if (!this._formattingOptions) {
this._formattingOptions = this.userDataSyncUtilService.resolveFormattingOptions(this.file);
}
return this._formattingOptions;
}
}

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IUserData, UserDataSyncError, UserDataSyncErrorCode, SyncStatus, IUserDataSyncStoreService, ISyncExtension, IUserDataSyncLogService, IUserDataSynchroniser, SyncSource } from 'vs/platform/userDataSync/common/userDataSync';
import { IUserData, UserDataSyncError, UserDataSyncErrorCode, SyncStatus, IUserDataSyncStoreService, ISyncExtension, IUserDataSyncLogService, IUserDataSynchroniser, SyncSource, ResourceKey, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync';
import { Event } from 'vs/base/common/event';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IExtensionManagementService, IExtensionGalleryService, IGlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement';
@@ -15,6 +15,7 @@ import { localize } from 'vs/nls';
import { merge } from 'vs/platform/userDataSync/common/extensionsMerge';
import { isNonEmptyArray } from 'vs/base/common/arrays';
import { AbstractSynchroniser } from 'vs/platform/userDataSync/common/abstractSynchronizer';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
interface ISyncPreviewResult {
readonly added: ISyncExtension[];
@@ -32,17 +33,21 @@ interface ILastSyncUserData extends IUserData {
export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUserDataSynchroniser {
readonly resourceKey: ResourceKey = 'extensions';
constructor(
@IEnvironmentService environmentService: IEnvironmentService,
@IFileService fileService: IFileService,
@IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService,
@IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService,
@IGlobalExtensionEnablementService private readonly extensionEnablementService: IGlobalExtensionEnablementService,
@IUserDataSyncLogService private readonly logService: IUserDataSyncLogService,
@IUserDataSyncLogService logService: IUserDataSyncLogService,
@IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IUserDataSyncEnablementService userDataSyncEnablementService: IUserDataSyncEnablementService,
@ITelemetryService telemetryService: ITelemetryService,
) {
super(SyncSource.Extensions, fileService, environmentService, userDataSyncStoreService);
super(SyncSource.Extensions, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService);
this._register(
Event.debounce(
Event.any<any>(
@@ -52,10 +57,8 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
() => undefined, 500)(() => this._onDidChangeLocal.fire()));
}
protected getRemoteDataResourceKey(): string { return 'extensions'; }
async pull(): Promise<void> {
if (!this.configurationService.getValue<boolean>('sync.enableExtensions')) {
if (!this.enabled) {
this.logService.info('Extensions: Skipped pulling extensions as it is disabled.');
return;
}
@@ -88,7 +91,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
}
async push(): Promise<void> {
if (!this.configurationService.getValue<boolean>('sync.enableExtensions')) {
if (!this.enabled) {
this.logService.info('Extensions: Skipped pushing extensions as it is disabled.');
return;
}
@@ -112,46 +115,16 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
}
async sync(): Promise<void> {
if (!this.configurationService.getValue<boolean>('sync.enableExtensions')) {
this.logService.trace('Extensions: Skipping synchronizing extensions as it is disabled.');
return;
}
async sync(ref?: string): Promise<void> {
if (!this.extensionGalleryService.isEnabled()) {
this.logService.trace('Extensions: Skipping synchronizing extensions as gallery is disabled.');
this.logService.info('Extensions: Skipping synchronizing extensions as gallery is disabled.');
return;
}
if (this.status !== SyncStatus.Idle) {
this.logService.trace('Extensions: Skipping synchronizing extensions as it is running already.');
return;
}
this.logService.trace('Extensions: Started synchronizing extensions...');
this.setStatus(SyncStatus.Syncing);
try {
const previewResult = await this.getPreview();
await this.apply(previewResult);
} catch (e) {
this.setStatus(SyncStatus.Idle);
if (e instanceof UserDataSyncError && e.code === UserDataSyncErrorCode.Rejected) {
// Rejected as there is a new remote version. Syncing again,
this.logService.info('Extensions: Failed to synchronize extensions as there is a new remote version available. Synchronizing again...');
return this.sync();
}
throw e;
}
this.logService.trace('Extensions: Finished synchronizing extensions.');
this.setStatus(SyncStatus.Idle);
return super.sync(ref);
}
async stop(): Promise<void> { }
async restart(): Promise<void> {
throw new Error('Extensions: Conflicts should not occur');
}
accept(content: string): Promise<void> {
throw new Error('Extensions: Conflicts should not occur');
}
@@ -172,14 +145,29 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
return null;
}
private async getPreview(): Promise<ISyncPreviewResult> {
const lastSyncUserData = await this.getLastSyncUserData<ILastSyncUserData>();
protected async doSync(remoteUserData: IUserData, lastSyncUserData: ILastSyncUserData | null): Promise<void> {
try {
const previewResult = await this.getPreview(remoteUserData, lastSyncUserData);
await this.apply(previewResult);
} catch (e) {
this.setStatus(SyncStatus.Idle);
if (e instanceof UserDataSyncError && e.code === UserDataSyncErrorCode.RemotePreconditionFailed) {
// Rejected as there is a new remote version. Syncing again,
this.logService.info('Extensions: Failed to synchronize extensions as there is a new remote version available. Synchronizing again...');
return this.sync();
}
throw e;
}
this.logService.trace('Extensions: Finished synchronizing extensions.');
this.setStatus(SyncStatus.Idle);
}
private async getPreview(remoteUserData: IUserData, lastSyncUserData: ILastSyncUserData | null): Promise<ISyncPreviewResult> {
const remoteExtensions: ISyncExtension[] = remoteUserData.content ? JSON.parse(remoteUserData.content) : null;
const lastSyncExtensions: ISyncExtension[] | null = lastSyncUserData ? JSON.parse(lastSyncUserData.content!) : null;
const skippedExtensions: ISyncExtension[] = lastSyncUserData ? lastSyncUserData.skippedExtensions || [] : [];
const remoteUserData = await this.getRemoteUserData(lastSyncUserData);
const remoteExtensions: ISyncExtension[] = remoteUserData.content ? JSON.parse(remoteUserData.content) : null;
const localExtensions = await this.getLocalExtensions();
if (remoteExtensions) {
@@ -202,7 +190,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
const hasChanges = added.length || removed.length || updated.length || remote;
if (!hasChanges) {
this.logService.trace('Extensions: No changes found during synchronizing extensions.');
this.logService.info('Extensions: No changes found during synchronizing extensions.');
}
if (added.length || removed.length || updated.length) {
@@ -255,7 +243,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
} else {
this.logService.trace('Extensions: Disabling extension...', e.identifier.id);
await this.extensionEnablementService.disableExtension(e.identifier);
this.logService.info('Extensions: Disabled extension.', e.identifier.id);
this.logService.info('Extensions: Disabled extension', e.identifier.id);
}
removeFromSkipped.push(e.identifier);
return;
@@ -307,7 +295,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
private async getLocalExtensions(): Promise<ISyncExtension[]> {
const installedExtensions = await this.extensionManagementService.getInstalled();
const disabledExtensions = await this.extensionEnablementService.getDisabledExtensionsAsync();
const disabledExtensions = await this.extensionEnablementService.getDisabledExtensions();
return installedExtensions
.map(({ identifier }) => ({ identifier, enabled: !disabledExtensions.some(disabledExtension => areSameExtensions(disabledExtension, identifier)) }));
}

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IUserData, UserDataSyncError, UserDataSyncErrorCode, SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IGlobalState, SyncSource, IUserDataSynchroniser } from 'vs/platform/userDataSync/common/userDataSync';
import { IUserData, UserDataSyncError, UserDataSyncErrorCode, SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IGlobalState, SyncSource, IUserDataSynchroniser, ResourceKey, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync';
import { VSBuffer } from 'vs/base/common/buffer';
import { Event } from 'vs/base/common/event';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
@@ -12,9 +12,9 @@ import { IFileService } from 'vs/platform/files/common/files';
import { IStringDictionary } from 'vs/base/common/collections';
import { edit } from 'vs/platform/userDataSync/common/content';
import { merge } from 'vs/platform/userDataSync/common/globalStateMerge';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { parse } from 'vs/base/common/json';
import { AbstractSynchroniser } from 'vs/platform/userDataSync/common/abstractSynchronizer';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
const argvProperties: string[] = ['locale'];
@@ -27,22 +27,23 @@ interface ISyncPreviewResult {
export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUserDataSynchroniser {
readonly resourceKey: ResourceKey = 'globalState';
constructor(
@IFileService fileService: IFileService,
@IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService,
@IUserDataSyncLogService private readonly logService: IUserDataSyncLogService,
@IUserDataSyncLogService logService: IUserDataSyncLogService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IUserDataSyncEnablementService userDataSyncEnablementService: IUserDataSyncEnablementService,
@ITelemetryService telemetryService: ITelemetryService,
) {
super(SyncSource.GlobalState, fileService, environmentService, userDataSyncStoreService);
super(SyncSource.GlobalState, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService);
this._register(this.fileService.watch(dirname(this.environmentService.argvResource)));
this._register(Event.filter(this.fileService.onFileChanges, e => e.contains(this.environmentService.argvResource))(() => this._onDidChangeLocal.fire()));
}
protected getRemoteDataResourceKey(): string { return 'globalState'; }
async pull(): Promise<void> {
if (!this.configurationService.getValue<boolean>('sync.enableUIState')) {
if (!this.enabled) {
this.logService.info('UI State: Skipped pulling ui state as it is disabled.');
return;
}
@@ -73,7 +74,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
}
async push(): Promise<void> {
if (!this.configurationService.getValue<boolean>('sync.enableUIState')) {
if (!this.enabled) {
this.logService.info('UI State: Skipped pushing UI State as it is disabled.');
return;
}
@@ -96,43 +97,8 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
}
async sync(): Promise<void> {
if (!this.configurationService.getValue<boolean>('sync.enableUIState')) {
this.logService.trace('UI State: Skipping synchronizing UI state as it is disabled.');
return;
}
if (this.status !== SyncStatus.Idle) {
this.logService.trace('UI State: Skipping synchronizing ui state as it is running already.');
return;
}
this.logService.trace('UI State: Started synchronizing ui state...');
this.setStatus(SyncStatus.Syncing);
try {
const result = await this.getPreview();
await this.apply(result);
this.logService.trace('UI State: Finished synchronizing ui state.');
} catch (e) {
this.setStatus(SyncStatus.Idle);
if (e instanceof UserDataSyncError && e.code === UserDataSyncErrorCode.Rejected) {
// Rejected as there is a new remote version. Syncing again,
this.logService.info('UI State: Failed to synchronize ui state as there is a new remote version available. Synchronizing again...');
return this.sync();
}
throw e;
} finally {
this.setStatus(SyncStatus.Idle);
}
}
async stop(): Promise<void> { }
async restart(): Promise<void> {
throw new Error('UI State: Conflicts should not occur');
}
accept(content: string): Promise<void> {
throw new Error('UI State: Conflicts should not occur');
}
@@ -153,12 +119,27 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
return null;
}
private async getPreview(): Promise<ISyncPreviewResult> {
const lastSyncUserData = await this.getLastSyncUserData();
const lastSyncGlobalState = lastSyncUserData && lastSyncUserData.content ? JSON.parse(lastSyncUserData.content) : null;
protected async doSync(remoteUserData: IUserData, lastSyncUserData: IUserData | null): Promise<void> {
try {
const result = await this.getPreview(remoteUserData, lastSyncUserData);
await this.apply(result);
this.logService.trace('UI State: Finished synchronizing ui state.');
} catch (e) {
this.setStatus(SyncStatus.Idle);
if (e instanceof UserDataSyncError && e.code === UserDataSyncErrorCode.RemotePreconditionFailed) {
// Rejected as there is a new remote version. Syncing again,
this.logService.info('UI State: Failed to synchronize ui state as there is a new remote version available. Synchronizing again...');
return this.sync();
}
throw e;
} finally {
this.setStatus(SyncStatus.Idle);
}
}
const remoteUserData = await this.getRemoteUserData(lastSyncUserData);
private async getPreview(remoteUserData: IUserData, lastSyncUserData: IUserData | null, ): Promise<ISyncPreviewResult> {
const remoteGlobalState: IGlobalState = remoteUserData.content ? JSON.parse(remoteUserData.content) : null;
const lastSyncGlobalState = lastSyncUserData && lastSyncUserData.content ? JSON.parse(lastSyncUserData.content) : null;
const localGloablState = await this.getLocalGlobalState();
@@ -178,7 +159,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
const hasChanges = local || remote;
if (!hasChanges) {
this.logService.trace('UI State: No changes found during synchronizing ui state.');
this.logService.info('UI State: No changes found during synchronizing ui state.');
}
if (local) {
@@ -208,26 +189,32 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
private async getLocalGlobalState(): Promise<IGlobalState> {
const argv: IStringDictionary<any> = {};
const storage: IStringDictionary<any> = {};
try {
const content = await this.fileService.readFile(this.environmentService.argvResource);
const argvValue: IStringDictionary<any> = parse(content.value.toString());
for (const argvProperty of argvProperties) {
if (argvValue[argvProperty] !== undefined) {
argv[argvProperty] = argvValue[argvProperty];
}
const argvContent: string = await this.getLocalArgvContent();
const argvValue: IStringDictionary<any> = parse(argvContent);
for (const argvProperty of argvProperties) {
if (argvValue[argvProperty] !== undefined) {
argv[argvProperty] = argvValue[argvProperty];
}
} catch (error) { }
}
return { argv, storage };
}
private async getLocalArgvContent(): Promise<string> {
try {
const content = await this.fileService.readFile(this.environmentService.argvResource);
return content.value.toString();
} catch (error) { }
return '{}';
}
private async writeLocalGlobalState(globalState: IGlobalState): Promise<void> {
const content = await this.fileService.readFile(this.environmentService.argvResource);
let argvContent = content.value.toString();
const argvContent = await this.getLocalArgvContent();
let content = argvContent;
for (const argvProperty of Object.keys(globalState.argv)) {
argvContent = edit(argvContent, [argvProperty], globalState.argv[argvProperty], {});
content = edit(content, [argvProperty], globalState.argv[argvProperty], {});
}
if (argvContent !== content.value.toString()) {
await this.fileService.writeFile(this.environmentService.argvResource, VSBuffer.fromString(argvContent));
if (argvContent !== content) {
await this.fileService.writeFile(this.environmentService.argvResource, VSBuffer.fromString(content));
}
}

View File

@@ -3,21 +3,22 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IFileService, FileSystemProviderErrorCode, FileSystemProviderError, IFileContent, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
import { IUserData, UserDataSyncError, UserDataSyncErrorCode, SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSyncUtilService, SyncSource, IUserDataSynchroniser } from 'vs/platform/userDataSync/common/userDataSync';
import { IFileService, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
import { UserDataSyncError, UserDataSyncErrorCode, SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSyncUtilService, SyncSource, IUserDataSynchroniser, IUserData, ResourceKey, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync';
import { merge } from 'vs/platform/userDataSync/common/keybindingsMerge';
import { VSBuffer } from 'vs/base/common/buffer';
import { parse, ParseError } from 'vs/base/common/json';
import { parse } from 'vs/base/common/json';
import { localize } from 'vs/nls';
import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
import { createCancelablePromise } from 'vs/base/common/async';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { CancellationToken } from 'vs/base/common/cancellation';
import { OS, OperatingSystem } from 'vs/base/common/platform';
import { isUndefined } from 'vs/base/common/types';
import { FormattingOptions } from 'vs/base/common/jsonFormatter';
import { isNonEmptyArray } from 'vs/base/common/arrays';
import { AbstractFileSynchroniser } from 'vs/platform/userDataSync/common/abstractSynchronizer';
import { IFileSyncPreviewResult, AbstractJsonFileSynchroniser } from 'vs/platform/userDataSync/common/abstractSynchronizer';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { URI } from 'vs/base/common/uri';
interface ISyncContent {
mac?: string;
@@ -26,34 +27,26 @@ interface ISyncContent {
all?: string;
}
interface ISyncPreviewResult {
readonly fileContent: IFileContent | null;
readonly remoteUserData: IUserData;
readonly lastSyncUserData: IUserData | null;
readonly hasLocalChanged: boolean;
readonly hasRemoteChanged: boolean;
readonly hasConflicts: boolean;
}
export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implements IUserDataSynchroniser {
export class KeybindingsSynchroniser extends AbstractFileSynchroniser implements IUserDataSynchroniser {
private syncPreviewResultPromise: CancelablePromise<ISyncPreviewResult> | null = null;
readonly resourceKey: ResourceKey = 'keybindings';
protected get conflictsPreviewResource(): URI { return this.environmentService.keybindingsSyncPreviewResource; }
constructor(
@IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService,
@IUserDataSyncLogService private readonly logService: IUserDataSyncLogService,
@IUserDataSyncLogService logService: IUserDataSyncLogService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IUserDataSyncEnablementService userDataSyncEnablementService: IUserDataSyncEnablementService,
@IFileService fileService: IFileService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@IUserDataSyncUtilService private readonly userDataSyncUtilService: IUserDataSyncUtilService,
@IUserDataSyncUtilService userDataSyncUtilService: IUserDataSyncUtilService,
@ITelemetryService telemetryService: ITelemetryService,
) {
super(environmentService.keybindingsResource, SyncSource.Keybindings, fileService, environmentService, userDataSyncStoreService);
super(environmentService.keybindingsResource, SyncSource.Keybindings, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService, userDataSyncUtilService);
}
protected getRemoteDataResourceKey(): string { return 'keybindings'; }
async pull(): Promise<void> {
if (!this.configurationService.getValue<boolean>('sync.enableKeybindings')) {
if (!this.enabled) {
this.logService.info('Keybindings: Skipped pulling keybindings as it is disabled.');
return;
}
@@ -66,18 +59,18 @@ export class KeybindingsSynchroniser extends AbstractFileSynchroniser implements
const lastSyncUserData = await this.getLastSyncUserData();
const remoteUserData = await this.getRemoteUserData(lastSyncUserData);
const remoteContent = remoteUserData.content !== null ? this.getKeybindingsContentFromSyncContent(remoteUserData.content) : null;
const content = remoteUserData.content !== null ? this.getKeybindingsContentFromSyncContent(remoteUserData.content) : null;
if (remoteContent !== null) {
await this.fileService.writeFile(this.environmentService.keybindingsSyncPreviewResource, VSBuffer.fromString(remoteContent));
if (content !== null) {
const fileContent = await this.getLocalFileContent();
this.syncPreviewResultPromise = createCancelablePromise(() => Promise.resolve<ISyncPreviewResult>({
this.syncPreviewResultPromise = createCancelablePromise(() => Promise.resolve<IFileSyncPreviewResult>({
fileContent,
remoteUserData,
lastSyncUserData,
content,
hasConflicts: false,
hasLocalChanged: true,
hasRemoteChanged: false,
remoteUserData,
lastSyncUserData
}));
await this.apply();
}
@@ -95,7 +88,7 @@ export class KeybindingsSynchroniser extends AbstractFileSynchroniser implements
}
async push(): Promise<void> {
if (!this.configurationService.getValue<boolean>('sync.enableKeybindings')) {
if (!this.enabled) {
this.logService.info('Keybindings: Skipped pushing keybindings as it is disabled.');
return;
}
@@ -111,16 +104,16 @@ export class KeybindingsSynchroniser extends AbstractFileSynchroniser implements
if (fileContent !== null) {
const lastSyncUserData = await this.getLastSyncUserData();
const remoteUserData = await this.getRemoteUserData(lastSyncUserData);
await this.fileService.writeFile(this.environmentService.settingsSyncPreviewResource, fileContent.value);
this.syncPreviewResultPromise = createCancelablePromise(() => Promise.resolve<ISyncPreviewResult>({
this.syncPreviewResultPromise = createCancelablePromise(() => Promise.resolve<IFileSyncPreviewResult>({
fileContent,
hasConflicts: false,
remoteUserData,
lastSyncUserData,
content: fileContent.value.toString(),
hasLocalChanged: false,
hasRemoteChanged: true,
remoteUserData,
lastSyncUserData
hasConflicts: false,
}));
await this.apply(undefined, true);
await this.apply(true);
}
// No local exists to push
@@ -135,53 +128,13 @@ export class KeybindingsSynchroniser extends AbstractFileSynchroniser implements
}
async sync(): Promise<void> {
if (!this.configurationService.getValue<boolean>('sync.enableKeybindings')) {
this.logService.trace('Keybindings: Skipping synchronizing keybindings as it is disabled.');
return;
}
if (this.status !== SyncStatus.Idle) {
this.logService.trace('Keybindings: Skipping synchronizing keybindings as it is running already.');
return;
}
this.logService.trace('Keybindings: Started synchronizing keybindings...');
this.setStatus(SyncStatus.Syncing);
return this.doSync();
}
async stop(): Promise<void> {
if (this.syncPreviewResultPromise) {
this.syncPreviewResultPromise.cancel();
this.syncPreviewResultPromise = null;
this.logService.trace('Keybindings: Stopped synchronizing keybindings.');
}
await this.fileService.del(this.environmentService.keybindingsSyncPreviewResource);
this.setStatus(SyncStatus.Idle);
}
async restart(): Promise<void> {
if (this.status === SyncStatus.HasConflicts) {
this.syncPreviewResultPromise!.cancel();
this.syncPreviewResultPromise = null;
await this.doSync();
}
}
async accept(content: string): Promise<void> {
if (this.status === SyncStatus.HasConflicts) {
try {
await this.apply(content, true);
this.setStatus(SyncStatus.Idle);
} catch (e) {
if ((e instanceof FileSystemProviderError && e.code === FileSystemProviderErrorCode.FileExists) ||
(e instanceof FileOperationError && e.fileOperationResult === FileOperationResult.FILE_MODIFIED_SINCE)) {
throw new UserDataSyncError('Failed to resolve conflicts as there is a new local version available.', UserDataSyncErrorCode.NewLocal);
}
throw e;
}
const preview = await this.syncPreviewResultPromise!;
this.cancel();
this.syncPreviewResultPromise = createCancelablePromise(async () => ({ ...preview, content }));
await this.apply(true);
this.setStatus(SyncStatus.Idle);
}
}
@@ -202,22 +155,14 @@ export class KeybindingsSynchroniser extends AbstractFileSynchroniser implements
return false;
}
async getRemoteContent(): Promise<string | null> {
let content: string | null | undefined = null;
if (this.syncPreviewResultPromise) {
const preview = await this.syncPreviewResultPromise;
content = preview.remoteUserData?.content;
} else {
const lastSyncData = await this.getLastSyncUserData();
const remoteUserData = await this.getRemoteUserData(lastSyncData);
content = remoteUserData.content;
}
return content ? this.getKeybindingsContentFromSyncContent(content) : null;
async getRemoteContent(preview?: boolean): Promise<string | null> {
const content = await super.getRemoteContent(preview);
return content !== null ? this.getKeybindingsContentFromSyncContent(content) : null;
}
private async doSync(): Promise<void> {
protected async doSync(remoteUserData: IUserData, lastSyncUserData: IUserData | null): Promise<void> {
try {
const result = await this.getPreview();
const result = await this.getPreview(remoteUserData, lastSyncUserData);
if (result.hasConflicts) {
this.logService.info('Keybindings: Detected conflicts while synchronizing keybindings.');
this.setStatus(SyncStatus.HasConflicts);
@@ -232,44 +177,32 @@ export class KeybindingsSynchroniser extends AbstractFileSynchroniser implements
} catch (e) {
this.syncPreviewResultPromise = null;
this.setStatus(SyncStatus.Idle);
if (e instanceof UserDataSyncError && e.code === UserDataSyncErrorCode.Rejected) {
// Rejected as there is a new remote version. Syncing again,
this.logService.info('Keybindings: Failed to synchronize keybindings as there is a new remote version available. Synchronizing again...');
return this.sync();
}
if ((e instanceof FileSystemProviderError && e.code === FileSystemProviderErrorCode.FileExists) ||
(e instanceof FileOperationError && e.fileOperationResult === FileOperationResult.FILE_MODIFIED_SINCE)) {
// Rejected as there is a new local version. Syncing again.
this.logService.info('Keybindings: Failed to synchronize keybindings as there is a new local version available. Synchronizing again...');
return this.sync();
if (e instanceof UserDataSyncError) {
switch (e.code) {
case UserDataSyncErrorCode.RemotePreconditionFailed:
// Rejected as there is a new remote version. Syncing again,
this.logService.info('Keybindings: Failed to synchronize keybindings as there is a new remote version available. Synchronizing again...');
return this.sync();
case UserDataSyncErrorCode.LocalPreconditionFailed:
// Rejected as there is a new local version. Syncing again.
this.logService.info('Keybindings: Failed to synchronize keybindings as there is a new local version available. Synchronizing again...');
return this.sync(remoteUserData.ref);
}
}
throw e;
}
}
private async apply(content?: string, forcePush?: boolean): Promise<void> {
private async apply(forcePush?: boolean): Promise<void> {
if (!this.syncPreviewResultPromise) {
return;
}
let { fileContent, remoteUserData, lastSyncUserData, hasLocalChanged, hasRemoteChanged } = await this.syncPreviewResultPromise;
let { fileContent, remoteUserData, lastSyncUserData, content, hasLocalChanged, hasRemoteChanged } = await this.syncPreviewResultPromise;
if (content === undefined) {
if (await this.fileService.exists(this.environmentService.keybindingsSyncPreviewResource)) {
const keybindingsPreivew = await this.fileService.readFile(this.environmentService.keybindingsSyncPreviewResource);
content = keybindingsPreivew.value.toString();
}
}
if (content !== undefined) {
if (content !== null) {
if (this.hasErrors(content)) {
const error = new Error(localize('errorInvalidKeybindings', "Unable to sync keybindings. Please resolve conflicts without any errors/warnings and try again."));
this.logService.error(error);
throw error;
}
if (!hasLocalChanged && !hasRemoteChanged) {
this.logService.trace('Keybindings: No changes found during synchronizing keybindings.');
throw new UserDataSyncError(localize('errorInvalidSettings', "Unable to sync keybindings as there are errors/warning in keybindings file."), UserDataSyncErrorCode.LocalInvalidContent, this.source);
}
if (hasLocalChanged) {
@@ -287,14 +220,16 @@ export class KeybindingsSynchroniser extends AbstractFileSynchroniser implements
}
// Delete the preview
await this.fileService.del(this.environmentService.keybindingsSyncPreviewResource);
try {
await this.fileService.del(this.conflictsPreviewResource);
} catch (e) { /* ignore */ }
} else {
this.logService.trace('Keybindings: No changes found during synchronizing keybindings.');
this.logService.info('Keybindings: No changes found during synchronizing keybindings.');
}
if (lastSyncUserData?.ref !== remoteUserData.ref && (content !== undefined || fileContent !== null)) {
if (lastSyncUserData?.ref !== remoteUserData.ref && (content !== null || fileContent !== null)) {
this.logService.trace('Keybindings: Updating last synchronized keybindings...');
const lastSyncContent = this.updateSyncContent(content !== undefined ? content : fileContent!.value.toString(), null);
const lastSyncContent = this.updateSyncContent(content !== null ? content : fileContent!.value.toString(), null);
await this.updateLastSyncUserData({ ref: remoteUserData.ref, content: lastSyncContent });
this.logService.info('Keybindings: Updated last synchronized keybindings');
}
@@ -302,36 +237,29 @@ export class KeybindingsSynchroniser extends AbstractFileSynchroniser implements
this.syncPreviewResultPromise = null;
}
private hasErrors(content: string): boolean {
const parseErrors: ParseError[] = [];
parse(content, parseErrors, { allowEmptyContent: true, allowTrailingComma: true });
return parseErrors.length > 0;
}
private getPreview(): Promise<ISyncPreviewResult> {
private getPreview(remoteUserData: IUserData, lastSyncUserData: IUserData | null): Promise<IFileSyncPreviewResult> {
if (!this.syncPreviewResultPromise) {
this.syncPreviewResultPromise = createCancelablePromise(token => this.generatePreview(token));
this.syncPreviewResultPromise = createCancelablePromise(token => this.generatePreview(remoteUserData, lastSyncUserData, token));
}
return this.syncPreviewResultPromise;
}
private async generatePreview(token: CancellationToken): Promise<ISyncPreviewResult> {
const lastSyncUserData = await this.getLastSyncUserData();
const lastSyncContent = lastSyncUserData && lastSyncUserData.content ? this.getKeybindingsContentFromSyncContent(lastSyncUserData.content) : null;
const remoteUserData = await this.getRemoteUserData(lastSyncUserData);
private async generatePreview(remoteUserData: IUserData, lastSyncUserData: IUserData | null, token: CancellationToken): Promise<IFileSyncPreviewResult> {
const remoteContent = remoteUserData.content ? this.getKeybindingsContentFromSyncContent(remoteUserData.content) : null;
const lastSyncContent = lastSyncUserData && lastSyncUserData.content ? this.getKeybindingsContentFromSyncContent(lastSyncUserData.content) : null;
// Get file content last to get the latest
const fileContent = await this.getLocalFileContent();
const formattingOptions = await this.getFormattingOptions();
let content: string | null = null;
let hasLocalChanged: boolean = false;
let hasRemoteChanged: boolean = false;
let hasConflicts: boolean = false;
let previewContent = null;
if (remoteContent) {
const localContent: string = fileContent ? fileContent.value.toString() : '[]';
if (this.hasErrors(localContent)) {
this.logService.error('Keybindings: Unable to sync keybindings as there are errors/warning in keybindings file.');
return { fileContent, remoteUserData, lastSyncUserData, hasLocalChanged, hasRemoteChanged, hasConflicts };
throw new UserDataSyncError(localize('errorInvalidSettings', "Unable to sync keybindings as there are errors/warning in keybindings file."), UserDataSyncErrorCode.LocalInvalidContent, this.source);
}
if (!lastSyncContent // First time sync
@@ -339,14 +267,13 @@ export class KeybindingsSynchroniser extends AbstractFileSynchroniser implements
|| lastSyncContent !== remoteContent // Remote has forwarded
) {
this.logService.trace('Keybindings: Merging remote keybindings with local keybindings...');
const formattingOptions = await this.getFormattingOptions();
const result = await merge(localContent, remoteContent, lastSyncContent, formattingOptions, this.userDataSyncUtilService);
// Sync only if there are changes
if (result.hasChanges) {
hasLocalChanged = result.mergeContent !== localContent;
hasRemoteChanged = result.mergeContent !== remoteContent;
content = result.mergeContent;
hasConflicts = result.hasConflicts;
previewContent = result.mergeContent;
hasLocalChanged = hasConflicts || result.mergeContent !== localContent;
hasRemoteChanged = hasConflicts || result.mergeContent !== remoteContent;
}
}
}
@@ -354,23 +281,15 @@ export class KeybindingsSynchroniser extends AbstractFileSynchroniser implements
// First time syncing to remote
else if (fileContent) {
this.logService.trace('Keybindings: Remote keybindings does not exist. Synchronizing keybindings for the first time.');
content = fileContent.value.toString();
hasRemoteChanged = true;
previewContent = fileContent.value.toString();
}
if (previewContent && !token.isCancellationRequested) {
await this.fileService.writeFile(this.environmentService.keybindingsSyncPreviewResource, VSBuffer.fromString(previewContent));
if (content && !token.isCancellationRequested) {
await this.fileService.writeFile(this.environmentService.keybindingsSyncPreviewResource, VSBuffer.fromString(content));
}
return { fileContent, remoteUserData, lastSyncUserData, hasLocalChanged, hasRemoteChanged, hasConflicts };
}
private _formattingOptions: Promise<FormattingOptions> | undefined = undefined;
private getFormattingOptions(): Promise<FormattingOptions> {
if (!this._formattingOptions) {
this._formattingOptions = this.userDataSyncUtilService.resolveFormattingOptions(this.environmentService.keybindingsResource);
}
return this._formattingOptions;
return { fileContent, remoteUserData, lastSyncUserData, content, hasLocalChanged, hasRemoteChanged, hasConflicts };
}
private getKeybindingsContentFromSyncContent(syncContent: string): string | null {

View File

@@ -10,7 +10,7 @@ import { values } from 'vs/base/common/map';
import { IStringDictionary } from 'vs/base/common/collections';
import { FormattingOptions, Edit, getEOL } from 'vs/base/common/jsonFormatter';
import * as contentUtil from 'vs/platform/userDataSync/common/content';
import { IConflictSetting, DEFAULT_IGNORED_SETTINGS } from 'vs/platform/userDataSync/common/userDataSync';
import { IConflictSetting, CONFIGURATION_SYNC_STORE_KEY } from 'vs/platform/userDataSync/common/userDataSync';
import { firstIndex } from 'vs/base/common/arrays';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { startsWith } from 'vs/base/common/strings';
@@ -42,7 +42,7 @@ export function getIgnoredSettings(configurationService: IConfigurationService,
}
}
}
return [...DEFAULT_IGNORED_SETTINGS, ...added].filter(setting => removed.indexOf(setting) === -1);
return [CONFIGURATION_SYNC_STORE_KEY, ...added].filter(setting => removed.indexOf(setting) === -1);
}

View File

@@ -3,40 +3,31 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IFileService, FileSystemProviderErrorCode, FileSystemProviderError, IFileContent, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
import { IUserData, UserDataSyncError, UserDataSyncErrorCode, SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSyncUtilService, IConflictSetting, ISettingsSyncService, CONFIGURATION_SYNC_STORE_KEY, SyncSource } from 'vs/platform/userDataSync/common/userDataSync';
import { IFileService, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
import { UserDataSyncError, UserDataSyncErrorCode, SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSyncUtilService, IConflictSetting, ISettingsSyncService, CONFIGURATION_SYNC_STORE_KEY, SyncSource, IUserData, ResourceKey, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync';
import { VSBuffer } from 'vs/base/common/buffer';
import { parse, ParseError } from 'vs/base/common/json';
import { parse } from 'vs/base/common/json';
import { localize } from 'vs/nls';
import { Emitter, Event } from 'vs/base/common/event';
import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
import { createCancelablePromise } from 'vs/base/common/async';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { CancellationToken } from 'vs/base/common/cancellation';
import { updateIgnoredSettings, merge, getIgnoredSettings } from 'vs/platform/userDataSync/common/settingsMerge';
import { FormattingOptions } from 'vs/base/common/jsonFormatter';
import * as arrays from 'vs/base/common/arrays';
import * as objects from 'vs/base/common/objects';
import { isEmptyObject, isUndefinedOrNull } from 'vs/base/common/types';
import { isEmptyObject } from 'vs/base/common/types';
import { edit } from 'vs/platform/userDataSync/common/content';
import { AbstractFileSynchroniser } from 'vs/platform/userDataSync/common/abstractSynchronizer';
import { IFileSyncPreviewResult, AbstractJsonFileSynchroniser } from 'vs/platform/userDataSync/common/abstractSynchronizer';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { URI } from 'vs/base/common/uri';
interface ISyncPreviewResult {
readonly fileContent: IFileContent | null;
readonly remoteUserData: IUserData;
readonly lastSyncUserData: IUserData | null;
readonly content: string | null;
readonly hasLocalChanged: boolean;
readonly hasRemoteChanged: boolean;
readonly hasConflicts: boolean;
readonly conflictSettings: IConflictSetting[];
}
export class SettingsSynchroniser extends AbstractFileSynchroniser implements ISettingsSyncService {
export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implements ISettingsSyncService {
_serviceBrand: any;
private syncPreviewResultPromise: CancelablePromise<ISyncPreviewResult> | null = null;
readonly resourceKey: ResourceKey = 'settings';
protected get conflictsPreviewResource(): URI { return this.environmentService.settingsSyncPreviewResource; }
private _conflicts: IConflictSetting[] = [];
get conflicts(): IConflictSetting[] { return this._conflicts; }
@@ -47,15 +38,15 @@ export class SettingsSynchroniser extends AbstractFileSynchroniser implements IS
@IFileService fileService: IFileService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService,
@IUserDataSyncLogService private readonly logService: IUserDataSyncLogService,
@IUserDataSyncUtilService private readonly userDataSyncUtilService: IUserDataSyncUtilService,
@IUserDataSyncLogService logService: IUserDataSyncLogService,
@IUserDataSyncUtilService userDataSyncUtilService: IUserDataSyncUtilService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IUserDataSyncEnablementService userDataSyncEnablementService: IUserDataSyncEnablementService,
@ITelemetryService telemetryService: ITelemetryService,
) {
super(environmentService.settingsResource, SyncSource.Settings, fileService, environmentService, userDataSyncStoreService);
super(environmentService.settingsResource, SyncSource.Settings, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService, userDataSyncUtilService);
}
protected getRemoteDataResourceKey(): string { return 'settings'; }
protected setStatus(status: SyncStatus): void {
super.setStatus(status);
if (this.status !== SyncStatus.HasConflicts) {
@@ -73,7 +64,7 @@ export class SettingsSynchroniser extends AbstractFileSynchroniser implements IS
}
async pull(): Promise<void> {
if (!this.configurationService.getValue<boolean>('sync.enableSettings')) {
if (!this.enabled) {
this.logService.info('Settings: Skipped pulling settings as it is disabled.');
return;
}
@@ -92,7 +83,7 @@ export class SettingsSynchroniser extends AbstractFileSynchroniser implements IS
const formatUtils = await this.getFormattingOptions();
// Update ignored settings from local file content
const content = updateIgnoredSettings(remoteUserData.content, fileContent ? fileContent.value.toString() : '{}', getIgnoredSettings(this.configurationService), formatUtils);
this.syncPreviewResultPromise = createCancelablePromise(() => Promise.resolve<ISyncPreviewResult>({
this.syncPreviewResultPromise = createCancelablePromise(() => Promise.resolve<IFileSyncPreviewResult>({
fileContent,
remoteUserData,
lastSyncUserData,
@@ -100,7 +91,6 @@ export class SettingsSynchroniser extends AbstractFileSynchroniser implements IS
hasLocalChanged: true,
hasRemoteChanged: false,
hasConflicts: false,
conflictSettings: [],
}));
await this.apply();
@@ -118,7 +108,7 @@ export class SettingsSynchroniser extends AbstractFileSynchroniser implements IS
}
async push(): Promise<void> {
if (!this.configurationService.getValue<boolean>('sync.enableSettings')) {
if (!this.enabled) {
this.logService.info('Settings: Skipped pushing settings as it is disabled.');
return;
}
@@ -138,7 +128,7 @@ export class SettingsSynchroniser extends AbstractFileSynchroniser implements IS
const lastSyncUserData = await this.getLastSyncUserData();
const remoteUserData = await this.getRemoteUserData(lastSyncUserData);
this.syncPreviewResultPromise = createCancelablePromise(() => Promise.resolve<ISyncPreviewResult>({
this.syncPreviewResultPromise = createCancelablePromise(() => Promise.resolve<IFileSyncPreviewResult>({
fileContent,
remoteUserData,
lastSyncUserData,
@@ -146,7 +136,6 @@ export class SettingsSynchroniser extends AbstractFileSynchroniser implements IS
hasRemoteChanged: true,
hasLocalChanged: false,
hasConflicts: false,
conflictSettings: [],
}));
await this.apply(true);
@@ -163,32 +152,6 @@ export class SettingsSynchroniser extends AbstractFileSynchroniser implements IS
}
}
async sync(): Promise<void> {
if (!this.configurationService.getValue<boolean>('sync.enableSettings')) {
this.logService.trace('Settings: Skipping synchronizing settings as it is disabled.');
return;
}
if (this.status !== SyncStatus.Idle) {
this.logService.trace('Settings: Skipping synchronizing settings as it is running already.');
return;
}
this.logService.trace('Settings: Started synchronizing settings...');
this.setStatus(SyncStatus.Syncing);
return this.doSync([]);
}
async stop(): Promise<void> {
if (this.syncPreviewResultPromise) {
this.syncPreviewResultPromise.cancel();
this.syncPreviewResultPromise = null;
this.logService.trace('Settings: Stopped synchronizing settings.');
}
await this.fileService.del(this.environmentService.settingsSyncPreviewResource);
this.setStatus(SyncStatus.Idle);
}
async hasLocalData(): Promise<boolean> {
try {
const localFileContent = await this.getLocalFileContent();
@@ -209,62 +172,39 @@ export class SettingsSynchroniser extends AbstractFileSynchroniser implements IS
}
async getRemoteContent(preview?: boolean): Promise<string | null> {
let content: string | null | undefined = null;
if (this.syncPreviewResultPromise) {
const preview = await this.syncPreviewResultPromise;
content = preview.remoteUserData?.content;
} else {
const lastSyncData = await this.getLastSyncUserData();
const remoteUserData = await this.getRemoteUserData(lastSyncData);
content = remoteUserData.content;
}
if (preview && !isUndefinedOrNull(content)) {
let content = await super.getRemoteContent(preview);
if (preview && content !== null) {
const formatUtils = await this.getFormattingOptions();
// remove ignored settings from the remote content for preview
content = updateIgnoredSettings(content, '{}', getIgnoredSettings(this.configurationService), formatUtils);
}
return content !== undefined ? content : null;
}
async restart(): Promise<void> {
if (this.status === SyncStatus.HasConflicts) {
this.syncPreviewResultPromise!.cancel();
this.syncPreviewResultPromise = null;
await this.doSync([]);
}
return content;
}
async accept(content: string): Promise<void> {
if (this.status === SyncStatus.HasConflicts) {
try {
const preview = await this.syncPreviewResultPromise!;
const formatUtils = await this.getFormattingOptions();
// Add ignored settings from local file content
content = updateIgnoredSettings(content, preview.fileContent ? preview.fileContent.value.toString() : '{}', getIgnoredSettings(this.configurationService), formatUtils);
this.syncPreviewResultPromise = createCancelablePromise(async () => ({ ...preview, content }));
await this.apply(true);
this.setStatus(SyncStatus.Idle);
} catch (e) {
if ((e instanceof FileSystemProviderError && e.code === FileSystemProviderErrorCode.FileExists) ||
(e instanceof FileOperationError && e.fileOperationResult === FileOperationResult.FILE_MODIFIED_SINCE)) {
throw new UserDataSyncError('Failed to resolve conflicts as there is a new local version available.', UserDataSyncErrorCode.NewLocal);
}
throw e;
}
const preview = await this.syncPreviewResultPromise!;
this.cancel();
const formatUtils = await this.getFormattingOptions();
// Add ignored settings from local file content
content = updateIgnoredSettings(content, preview.fileContent ? preview.fileContent.value.toString() : '{}', getIgnoredSettings(this.configurationService), formatUtils);
this.syncPreviewResultPromise = createCancelablePromise(async () => ({ ...preview, content }));
await this.apply(true);
this.setStatus(SyncStatus.Idle);
}
}
async resolveSettingsConflicts(resolvedConflicts: { key: string, value: any | undefined }[]): Promise<void> {
if (this.status === SyncStatus.HasConflicts) {
this.syncPreviewResultPromise!.cancel();
this.syncPreviewResultPromise = null;
await this.doSync(resolvedConflicts);
const preview = await this.syncPreviewResultPromise!;
this.cancel();
await this.doSync(preview.remoteUserData, preview.lastSyncUserData, resolvedConflicts);
}
}
private async doSync(resolvedConflicts: { key: string, value: any | undefined }[]): Promise<void> {
protected async doSync(remoteUserData: IUserData, lastSyncUserData: IUserData | null, resolvedConflicts: { key: string, value: any | undefined }[] = []): Promise<void> {
try {
const result = await this.getPreview(resolvedConflicts);
const result = await this.getPreview(remoteUserData, lastSyncUserData, resolvedConflicts);
if (result.hasConflicts) {
this.logService.info('Settings: Detected conflicts while synchronizing settings.');
this.setStatus(SyncStatus.HasConflicts);
@@ -279,16 +219,17 @@ export class SettingsSynchroniser extends AbstractFileSynchroniser implements IS
} catch (e) {
this.syncPreviewResultPromise = null;
this.setStatus(SyncStatus.Idle);
if (e instanceof UserDataSyncError && e.code === UserDataSyncErrorCode.Rejected) {
// Rejected as there is a new remote version. Syncing again,
this.logService.info('Settings: Failed to synchronize settings as there is a new remote version available. Synchronizing again...');
return this.sync();
}
if ((e instanceof FileSystemProviderError && e.code === FileSystemProviderErrorCode.FileExists) ||
(e instanceof FileOperationError && e.fileOperationResult === FileOperationResult.FILE_MODIFIED_SINCE)) {
// Rejected as there is a new local version. Syncing again.
this.logService.info('Settings: Failed to synchronize settings as there is a new local version available. Synchronizing again...');
return this.sync();
if (e instanceof UserDataSyncError) {
switch (e.code) {
case UserDataSyncErrorCode.RemotePreconditionFailed:
// Rejected as there is a new remote version. Syncing again,
this.logService.info('Settings: Failed to synchronize settings as there is a new remote version available. Synchronizing again...');
return this.sync();
case UserDataSyncErrorCode.LocalPreconditionFailed:
// Rejected as there is a new local version. Syncing again.
this.logService.info('Settings: Failed to synchronize settings as there is a new local version available. Synchronizing again...');
return this.sync(remoteUserData.ref);
}
}
throw e;
}
@@ -304,9 +245,7 @@ export class SettingsSynchroniser extends AbstractFileSynchroniser implements IS
if (content !== null) {
if (this.hasErrors(content)) {
const error = new Error(localize('errorInvalidSettings', "Unable to sync settings. Please resolve conflicts without any errors/warnings and try again."));
this.logService.error(error);
throw error;
throw new UserDataSyncError(localize('errorInvalidSettings', "Unable to sync settings as there are errors/warning in settings file."), UserDataSyncErrorCode.LocalInvalidContent, this.source);
}
if (hasLocalChanged) {
@@ -325,9 +264,11 @@ export class SettingsSynchroniser extends AbstractFileSynchroniser implements IS
}
// Delete the preview
await this.fileService.del(this.environmentService.settingsSyncPreviewResource);
try {
await this.fileService.del(this.conflictsPreviewResource);
} catch (e) { /* ignore */ }
} else {
this.logService.trace('Settings: No changes found during synchronizing settings.');
this.logService.info('Settings: No changes found during synchronizing settings.');
}
if (lastSyncUserData?.ref !== remoteUserData.ref) {
@@ -339,25 +280,16 @@ export class SettingsSynchroniser extends AbstractFileSynchroniser implements IS
this.syncPreviewResultPromise = null;
}
private hasErrors(content: string): boolean {
const parseErrors: ParseError[] = [];
parse(content, parseErrors, { allowEmptyContent: true, allowTrailingComma: true });
return parseErrors.length > 0;
}
private getPreview(resolvedConflicts: { key: string, value: any }[]): Promise<ISyncPreviewResult> {
private getPreview(remoteUserData: IUserData, lastSyncUserData: IUserData | null, resolvedConflicts: { key: string, value: any }[]): Promise<IFileSyncPreviewResult> {
if (!this.syncPreviewResultPromise) {
this.syncPreviewResultPromise = createCancelablePromise(token => this.generatePreview(resolvedConflicts, token));
this.syncPreviewResultPromise = createCancelablePromise(token => this.generatePreview(remoteUserData, lastSyncUserData, resolvedConflicts, token));
}
return this.syncPreviewResultPromise;
}
private async generatePreview(resolvedConflicts: { key: string, value: any }[], token: CancellationToken): Promise<ISyncPreviewResult> {
const lastSyncUserData = await this.getLastSyncUserData();
const remoteUserData = await this.getRemoteUserData(lastSyncUserData);
// Get file content last to get the latest
protected async generatePreview(remoteUserData: IUserData, lastSyncUserData: IUserData | null, resolvedConflicts: { key: string, value: any }[], token: CancellationToken): Promise<IFileSyncPreviewResult> {
const fileContent = await this.getLocalFileContent();
const formatUtils = await this.getFormattingOptions();
const formattingOptions = await this.getFormattingOptions();
let content: string | null = null;
let hasLocalChanged: boolean = false;
@@ -370,12 +302,12 @@ export class SettingsSynchroniser extends AbstractFileSynchroniser implements IS
// No action when there are errors
if (this.hasErrors(localContent)) {
this.logService.error('Settings: Unable to sync settings as there are errors/warning in settings file.');
throw new UserDataSyncError(localize('errorInvalidSettings', "Unable to sync settings as there are errors/warning in settings file."), UserDataSyncErrorCode.LocalInvalidContent, this.source);
}
else {
this.logService.trace('Settings: Merging remote settings with local settings...');
const result = merge(localContent, remoteUserData.content, lastSyncUserData ? lastSyncUserData.content : null, getIgnoredSettings(this.configurationService), resolvedConflicts, formatUtils);
const result = merge(localContent, remoteUserData.content, lastSyncUserData ? lastSyncUserData.content : null, getIgnoredSettings(this.configurationService), resolvedConflicts, formattingOptions);
content = result.localContent || result.remoteContent;
hasLocalChanged = result.localContent !== null;
hasRemoteChanged = result.remoteContent !== null;
@@ -393,20 +325,12 @@ export class SettingsSynchroniser extends AbstractFileSynchroniser implements IS
if (content && !token.isCancellationRequested) {
// Remove the ignored settings from the preview.
const previewContent = updateIgnoredSettings(content, '{}', getIgnoredSettings(this.configurationService), formatUtils);
const previewContent = updateIgnoredSettings(content, '{}', getIgnoredSettings(this.configurationService), formattingOptions);
await this.fileService.writeFile(this.environmentService.settingsSyncPreviewResource, VSBuffer.fromString(previewContent));
}
this.setConflicts(conflictSettings);
return { fileContent, remoteUserData, lastSyncUserData, content, hasLocalChanged, hasRemoteChanged, conflictSettings, hasConflicts };
}
private _formattingOptions: Promise<FormattingOptions> | undefined = undefined;
private getFormattingOptions(): Promise<FormattingOptions> {
if (!this._formattingOptions) {
this._formattingOptions = this.userDataSyncUtilService.resolveFormattingOptions(this.environmentService.settingsResource);
}
return this._formattingOptions;
return { fileContent, remoteUserData, lastSyncUserData, content, hasLocalChanged, hasRemoteChanged, hasConflicts };
}
}

Some files were not shown because too many files have changed in this diff Show More