mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-13 17:22:15 -05:00
Merge from vscode 8a997f7321ae6612fc0e6eb3eac4f358a6233bfb
This commit is contained in:
95
.github/workflows/ci.yml
vendored
95
.github/workflows/ci.yml
vendored
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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: |
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
DO NOT DELETE, USED BY INTEGRATION TESTS
|
||||
@@ -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"
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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%
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(/&&/g, '&');
|
||||
|
||||
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(/&&/g, '&');
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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!');
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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: [
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 (→ or ·) do not have the same width as .
|
||||
* 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); // · or word separator middle dot
|
||||
sb.write1(renderSpaceCharCode); // · or word separator middle dot
|
||||
}
|
||||
|
||||
charOffsetInPart += charWidth;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
@@ -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()))
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
's',
|
||||
0, 1, true, false,
|
||||
0, 1, true,
|
||||
's', 0
|
||||
);
|
||||
|
||||
@@ -163,7 +163,7 @@ suite('TextAreaState', () => {
|
||||
testDeduceInput(
|
||||
new TextAreaState('s', 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 {
|
||||
|
||||
@@ -42,6 +42,7 @@ export class TestConfiguration extends CommonEditorConfiguration {
|
||||
canUseHalfwidthRightwardsArrow: true,
|
||||
spaceWidth: 10,
|
||||
middotWidth: 10,
|
||||
wsmiddotWidth: 10,
|
||||
maxDigitWidth: 10,
|
||||
}, true);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
126
src/vs/monaco.d.ts
vendored
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ export interface IAccessibilityService {
|
||||
|
||||
alwaysUnderlineAccessKeys(): Promise<boolean>;
|
||||
isScreenReaderOptimized(): boolean;
|
||||
getAccessibilitySupport(): AccessibilitySupport;
|
||||
setAccessibilitySupport(accessibilitySupport: AccessibilitySupport): void;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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>][] {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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)) }));
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user