mirror of
https://github.com/microsoft/vscode.git
synced 2026-09-12 11:39:57 +01:00
Merge remote-tracking branch 'origin/master' into joao/uri-handler
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
---
|
||||
name: Question
|
||||
about: The issue tracker is not for questions. Please ask questions on https://stackoverflow.com/questions/tagged/vscode.
|
||||
about: The issue tracker is not for questions. Please ask questions on https://stackoverflow.com/questions/tagged/visual-studio-code.
|
||||
|
||||
---
|
||||
|
||||
🚨 The issue tracker is not for questions 🚨
|
||||
|
||||
If you have a question, please ask it on https://stackoverflow.com/questions/tagged/vscode.
|
||||
If you have a question, please ask it on https://stackoverflow.com/questions/tagged/visual-studio-code.
|
||||
|
||||
+74
-12
@@ -1,43 +1,69 @@
|
||||
{
|
||||
perform: true,
|
||||
alwaysRequireAssignee: false,
|
||||
labelsRequiringAssignee: [ feature-request ],
|
||||
labelsRequiringAssignee: [],
|
||||
autoAssignees: {
|
||||
L10N: [],
|
||||
VIM: [],
|
||||
api: {
|
||||
assignees: [ jrieken ],
|
||||
assignLabel: false
|
||||
},
|
||||
color-picker: [],
|
||||
css-less-sass: [ aeschli ],
|
||||
cli: [],
|
||||
color-palette: [],
|
||||
config: [],
|
||||
css-less-scss: [ aeschli ],
|
||||
debug-console: [],
|
||||
debug: {
|
||||
assignees: [ weinand ],
|
||||
assignLabel: false
|
||||
},
|
||||
diff-editor: [],
|
||||
dropdown: [],
|
||||
editor: [],
|
||||
editor-1000-limit: [],
|
||||
editor-autoclosing: [],
|
||||
editor-autoindent: [],
|
||||
editor-brackets: [],
|
||||
editor-clipboard: [],
|
||||
editor-code-actions: [],
|
||||
editor-code-lens: [],
|
||||
editor-color-picker: [],
|
||||
editor-colors: [],
|
||||
editor-columnselect: [],
|
||||
editor-commands: [],
|
||||
editor-contrib: [],
|
||||
editor-core: [],
|
||||
editor-find-widget: [],
|
||||
editor-drag-and-drop: [],
|
||||
editor-find: [],
|
||||
editor-folding: [],
|
||||
editor-hover: [],
|
||||
editor-ime: [],
|
||||
editor-input: [],
|
||||
editor-ligatures: [],
|
||||
editor-links: [],
|
||||
editor-minimap: [],
|
||||
editor-multicursor: [],
|
||||
editor-parameter-hints: [],
|
||||
editor-rendering: [],
|
||||
editor-smooth: [],
|
||||
editor-symbols: [],
|
||||
editor-textbuffer: [],
|
||||
editor-wrapping: [],
|
||||
emmet: [ ramya-rao-a ],
|
||||
error-list: [],
|
||||
explorer-custom: [],
|
||||
extension-host: [],
|
||||
extensions: [],
|
||||
file-decorations: [],
|
||||
file-encoding: {
|
||||
assignees: [],
|
||||
assignLabel: false
|
||||
},
|
||||
file-explorer: {
|
||||
assignees: [ isidorn ],
|
||||
assignLabel: false
|
||||
},
|
||||
file-glob: [],
|
||||
file-io: {
|
||||
assignees: [],
|
||||
assignLabel: false
|
||||
@@ -46,45 +72,73 @@
|
||||
assignees: [],
|
||||
assignLabel: false
|
||||
},
|
||||
file-explorer: {
|
||||
assignees: [ isidorn ],
|
||||
assignLabel: false
|
||||
},
|
||||
format: [],
|
||||
formatting: [],
|
||||
git: [ joaomoreno ],
|
||||
grammar: [],
|
||||
hot-exit: [ Tyriar ],
|
||||
html: [ aeschli ],
|
||||
i18n: [],
|
||||
install-update: [],
|
||||
integrated-terminal: [ Tyriar ],
|
||||
integration-test: [],
|
||||
intellisense-config: [],
|
||||
issue-reporter: [ RMacfarlane ],
|
||||
javascript: [ mjbvz ],
|
||||
json: [],
|
||||
keybindings: [],
|
||||
keyboard-layout: [],
|
||||
keybindings: [],
|
||||
keybindings-editor: [],
|
||||
lang-diagnostics: [],
|
||||
languages basic: [],
|
||||
list: [],
|
||||
log: [],
|
||||
markdown: [ mjbvz ],
|
||||
marketplace: [],
|
||||
menus: [],
|
||||
merge-conflict: [ chrmarti ],
|
||||
multi-root: {
|
||||
assignees: [],
|
||||
assignLabel: false
|
||||
},
|
||||
os-integration: [],
|
||||
outline: [],
|
||||
output: [],
|
||||
perf-profile: [],
|
||||
perf-bloat: [],
|
||||
perf-startup: [],
|
||||
php: [ roblourens ],
|
||||
proxy: [],
|
||||
quick-pick: [ chrmarti ],
|
||||
release-notes: [],
|
||||
remote: {
|
||||
assignees: [ jrieken ],
|
||||
assignLabel: false
|
||||
},
|
||||
rename: [],
|
||||
run-as-admin: [],
|
||||
samples: [],
|
||||
scm: [],
|
||||
search: [ roblourens ],
|
||||
search-replace: [],
|
||||
settings-editor: [],
|
||||
shared-process: [],
|
||||
smart-select: [],
|
||||
smoke-test: [],
|
||||
snippets: {
|
||||
assignees: [ jrieken ],
|
||||
assignLabel: false
|
||||
},
|
||||
suggest: [],
|
||||
tasks: [ dbaeumer ],
|
||||
telemetry: [],
|
||||
themes: [],
|
||||
tokenization: [],
|
||||
tree: [],
|
||||
typescript: [ mjbvz ],
|
||||
unit-test: [],
|
||||
uri: [],
|
||||
ux: [],
|
||||
vscode-build: [],
|
||||
webview: [],
|
||||
workbench: {
|
||||
assignees: [],
|
||||
assignLabel: false
|
||||
@@ -109,6 +163,10 @@
|
||||
assignees: [],
|
||||
assignLabel: false
|
||||
},
|
||||
workbench-grid: {
|
||||
assignees: [],
|
||||
assignLabel: false
|
||||
},
|
||||
workbench-history: {
|
||||
assignees: [],
|
||||
assignLabel: false
|
||||
@@ -145,6 +203,10 @@
|
||||
assignees: [],
|
||||
assignLabel: false
|
||||
},
|
||||
workbench-views: {
|
||||
assignees: [],
|
||||
assignLabel: false
|
||||
},
|
||||
workbench-welcome: [ chrmarti ]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
[
|
||||
{
|
||||
"name": "ms-vscode.node-debug",
|
||||
"version": "1.25.3",
|
||||
"version": "1.26.0",
|
||||
"repo": "https://github.com/Microsoft/vscode-node-debug"
|
||||
},
|
||||
{
|
||||
"name": "ms-vscode.node-debug2",
|
||||
"version": "1.25.4",
|
||||
"version": "1.26.0",
|
||||
"repo": "https://github.com/Microsoft/vscode-node-debug2"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -82,6 +82,7 @@ const indentationFilter = [
|
||||
'!build/{lib,tslintRules}/**/*.js',
|
||||
'!build/**/*.sh',
|
||||
'!build/tfs/**/*.js',
|
||||
'!build/tfs/**/*.config',
|
||||
'!**/Dockerfile',
|
||||
'!extensions/markdown-language-features/media/*.js'
|
||||
];
|
||||
@@ -104,6 +105,7 @@ const copyrightFilter = [
|
||||
'!**/*.code-workspace',
|
||||
'!build/**/*.init',
|
||||
'!resources/linux/snap/snapcraft.yaml',
|
||||
'!resources/linux/snap/electron-launch',
|
||||
'!resources/win32/bin/code.js',
|
||||
'!extensions/markdown-language-features/media/highlight.css',
|
||||
'!extensions/html-language-features/server/src/modes/typescript/*',
|
||||
|
||||
@@ -71,7 +71,7 @@ const vscodeResources = [
|
||||
'out-build/paths.js',
|
||||
'out-build/vs/**/*.{svg,png,cur,html}',
|
||||
'out-build/vs/base/common/performance.js',
|
||||
'out-build/vs/base/node/{stdForkStart.js,terminateProcess.sh, cpuUsage.sh}',
|
||||
'out-build/vs/base/node/{stdForkStart.js,terminateProcess.sh,cpuUsage.sh}',
|
||||
'out-build/vs/base/browser/ui/octiconLabel/octicons/**',
|
||||
'out-build/vs/workbench/browser/media/*-theme.css',
|
||||
'out-build/vs/workbench/electron-browser/bootstrap/**',
|
||||
@@ -265,10 +265,17 @@ function packageTask(platform, arch, opts) {
|
||||
const packageJsonStream = gulp.src(['package.json'], { base: '.' })
|
||||
.pipe(json({ name, version }));
|
||||
|
||||
const settingsSearchBuildId = getSettingsSearchBuildId(packageJson);
|
||||
const date = new Date().toISOString();
|
||||
const productJsonUpdate = { commit, date, checksums };
|
||||
|
||||
try {
|
||||
productJsonUpdate.settingsSearchBuildId = getSettingsSearchBuildId(packageJson);
|
||||
} catch (err) {
|
||||
console.warn(err);
|
||||
}
|
||||
|
||||
const productJsonStream = gulp.src(['product.json'], { base: '.' })
|
||||
.pipe(json({ commit, date, checksums, settingsSearchBuildId }));
|
||||
.pipe(json(productJsonUpdate));
|
||||
|
||||
const license = gulp.src(['LICENSES.chromium.html', 'LICENSE.txt', 'ThirdPartyNotices.txt', 'licenses/**'], { base: '.' });
|
||||
|
||||
@@ -508,6 +515,10 @@ gulp.task('generate-vscode-configuration', () => {
|
||||
return reject(new Error('$AGENT_BUILDDIRECTORY not set'));
|
||||
}
|
||||
|
||||
if (process.env.VSCODE_QUALITY !== 'insider' && process.env.VSCODE_QUALITY !== 'stable') {
|
||||
return resolve();
|
||||
}
|
||||
|
||||
const userDataDir = path.join(os.tmpdir(), 'tmpuserdata');
|
||||
const extensionsDir = path.join(os.tmpdir(), 'tmpextdir');
|
||||
const appName = process.env.VSCODE_QUALITY === 'insider' ? 'Visual\\ Studio\\ Code\\ -\\ Insiders.app' : 'Visual\\ Studio\\ Code.app';
|
||||
|
||||
@@ -26,6 +26,7 @@ const zipPath = arch => path.join(zipDir(arch), `VSCode-win32-${arch}.zip`);
|
||||
const setupDir = (arch, target) => path.join(repoPath, '.build', `win32-${arch}`, `${target}-setup`);
|
||||
const issPath = path.join(__dirname, 'win32', 'code.iss');
|
||||
const innoSetupPath = path.join(path.dirname(path.dirname(require.resolve('innosetup-compiler'))), 'bin', 'ISCC.exe');
|
||||
const signPS1 = path.join(repoPath, 'build', 'tfs', 'win32', 'sign.ps1');
|
||||
|
||||
function packageInnoSetup(iss, options, cb) {
|
||||
options = options || {};
|
||||
@@ -42,7 +43,11 @@ function packageInnoSetup(iss, options, cb) {
|
||||
keys.forEach(key => assert(typeof definitions[key] === 'string', `Missing value for '${key}' in Inno Setup package step`));
|
||||
|
||||
const defs = keys.map(key => `/d${key}=${definitions[key]}`);
|
||||
const args = [iss].concat(defs);
|
||||
const args = [
|
||||
iss,
|
||||
...defs,
|
||||
`/sesrp=powershell.exe -ExecutionPolicy bypass ${signPS1} $f`
|
||||
];
|
||||
|
||||
cp.spawn(innoSetupPath, args, { stdio: ['ignore', 'inherit', 'inherit'] })
|
||||
.on('error', cb)
|
||||
@@ -74,14 +79,15 @@ function buildWin32Setup(arch, target) {
|
||||
DirName: product.win32DirName,
|
||||
Version: pkg.version,
|
||||
RawVersion: pkg.version.replace(/-\w+$/, ''),
|
||||
NameVersion: product.win32NameVersion,
|
||||
NameVersion: product.win32NameVersion + (target === 'user' ? ' (User)' : ''),
|
||||
ExeBasename: product.nameShort,
|
||||
RegValueName: product.win32RegValueName,
|
||||
ShellNameShort: product.win32ShellNameShort,
|
||||
AppMutex: product.win32MutexName,
|
||||
Arch: arch,
|
||||
AppId: arch === 'ia32' ? ia32AppId : x64AppId,
|
||||
IncompatibleAppId: arch === 'ia32' ? x64AppId : ia32AppId,
|
||||
IncompatibleTargetAppId: arch === 'ia32' ? product.win32AppId : product.win32x64AppId,
|
||||
IncompatibleArchAppId: arch === 'ia32' ? x64AppId : ia32AppId,
|
||||
AppUserId: product.win32AppUserModelId,
|
||||
ArchitecturesAllowed: arch === 'ia32' ? '' : 'x64',
|
||||
ArchitecturesInstallIn64BitMode: arch === 'ia32' ? '' : 'x64',
|
||||
@@ -108,7 +114,7 @@ defineWin32SetupTasks('x64', 'user');
|
||||
|
||||
function archiveWin32Setup(arch) {
|
||||
return cb => {
|
||||
const args = ['a', '-tzip', zipPath(arch), '.', '-r'];
|
||||
const args = ['a', '-tzip', zipPath(arch), '-x!CodeSignSummary*.md', '.', '-r'];
|
||||
|
||||
cp.spawn(_7z, args, { stdio: 'inherit', cwd: buildPath(arch) })
|
||||
.on('error', cb)
|
||||
|
||||
@@ -78,6 +78,10 @@
|
||||
"name": "vs/workbench/parts/logs",
|
||||
"project": "vscode-workbench"
|
||||
},
|
||||
{
|
||||
"name": "vs/workbench/parts/navigation",
|
||||
"project": "vscode-workbench"
|
||||
},
|
||||
{
|
||||
"name": "vs/workbench/parts/output",
|
||||
"project": "vscode-workbench"
|
||||
|
||||
+19
-81
@@ -125,91 +125,29 @@ phases:
|
||||
]
|
||||
SessionTimeout: 120
|
||||
|
||||
- task: NuGetCommand@2
|
||||
displayName: Install ESRPClient.exe
|
||||
inputs:
|
||||
restoreSolution: 'build\tfs\win32\ESRPClient\packages.config'
|
||||
feedsToUse: config
|
||||
nugetConfigPath: 'build\tfs\win32\ESRPClient\NuGet.config'
|
||||
externalFeedCredentials: 3fc0b7f7-da09-4ae7-a9c8-d69824b1819b
|
||||
restoreDirectory: packages
|
||||
|
||||
- task: ESRPImportCertTask@1
|
||||
displayName: Import ESRP Request Signing Certificate
|
||||
inputs:
|
||||
ESRP: 'ESRP CodeSign'
|
||||
|
||||
- powershell: |
|
||||
$ErrorActionPreference = "Stop"
|
||||
.\build\tfs\win32\import-esrp-auth-cert.ps1 -AuthCertificateBase64 $(ESRP_AUTH_CERTIFICATE) -AuthCertificateKey $(ESRP_AUTH_CERTIFICATE_KEY)
|
||||
displayName: Import ESRP Auth Certificate
|
||||
|
||||
- powershell: |
|
||||
$ErrorActionPreference = "Stop"
|
||||
npm run gulp -- "vscode-win32-$(VSCODE_ARCH)-archive" "vscode-win32-$(VSCODE_ARCH)-system-setup" "vscode-win32-$(VSCODE_ARCH)-user-setup"
|
||||
|
||||
- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1
|
||||
inputs:
|
||||
ConnectedServiceName: 'ESRP CodeSign'
|
||||
FolderPath: '$(agent.builddirectory)'
|
||||
Pattern: VSCodeSetup.exe
|
||||
signConfigType: inlineSignParams
|
||||
inlineOperation: |
|
||||
[
|
||||
{
|
||||
"keyCode": "CP-229803",
|
||||
"operationSetCode": "SigntoolSign",
|
||||
"parameters": [
|
||||
{
|
||||
"parameterName": "OpusName",
|
||||
"parameterValue": "VS Code"
|
||||
},
|
||||
{
|
||||
"parameterName": "OpusInfo",
|
||||
"parameterValue": "https://code.visualstudio.com/"
|
||||
},
|
||||
{
|
||||
"parameterName": "PageHash",
|
||||
"parameterValue": "/NPH"
|
||||
},
|
||||
{
|
||||
"parameterName": "TimeStamp",
|
||||
"parameterValue": "/t \"http://ts4096.gtm.microsoft.com/TSS/AuthenticodeTS\""
|
||||
}
|
||||
],
|
||||
"toolName": "sign",
|
||||
"toolVersion": "1.0"
|
||||
},
|
||||
{
|
||||
"keyCode": "CP-230012",
|
||||
"operationSetCode": "SigntoolSign",
|
||||
"parameters": [
|
||||
{
|
||||
"parameterName": "OpusName",
|
||||
"parameterValue": "VS Code"
|
||||
},
|
||||
{
|
||||
"parameterName": "OpusInfo",
|
||||
"parameterValue": "https://code.visualstudio.com/"
|
||||
},
|
||||
{
|
||||
"parameterName": "Append",
|
||||
"parameterValue": "/as"
|
||||
},
|
||||
{
|
||||
"parameterName": "FileDigest",
|
||||
"parameterValue": "/fd \"SHA256\""
|
||||
},
|
||||
{
|
||||
"parameterName": "PageHash",
|
||||
"parameterValue": "/NPH"
|
||||
},
|
||||
{
|
||||
"parameterName": "TimeStamp",
|
||||
"parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
|
||||
}
|
||||
],
|
||||
"toolName": "sign",
|
||||
"toolVersion": "1.0"
|
||||
},
|
||||
{
|
||||
"keyCode": "CP-230012",
|
||||
"operationSetCode": "SigntoolVerify",
|
||||
"parameters": [
|
||||
{
|
||||
"parameterName": "VerifyAll",
|
||||
"parameterValue": "/all"
|
||||
}
|
||||
],
|
||||
"toolName": "sign",
|
||||
"toolVersion": "1.0"
|
||||
}
|
||||
]
|
||||
SessionTimeout: 120
|
||||
|
||||
- powershell: |
|
||||
$ErrorActionPreference = "Stop"
|
||||
$Repo = "$(pwd)"
|
||||
$Root = "$Repo\.."
|
||||
$SystemExe = "$Repo\.build\win32-$(VSCODE_ARCH)\system-setup\VSCodeSetup.exe"
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<add key="ESRP" value="https://microsoft.pkgs.visualstudio.com/_packaging/ESRP/nuget/v3/index.json" />
|
||||
</packageSources>
|
||||
</configuration>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="EsrpClient" version="1.0.27" />
|
||||
</packages>
|
||||
@@ -0,0 +1,14 @@
|
||||
Param(
|
||||
[string]$AuthCertificateBase64,
|
||||
[string]$AuthCertificateKey
|
||||
)
|
||||
|
||||
# Import auth certificate
|
||||
$AuthCertificateFileName = [System.IO.Path]::GetTempFileName()
|
||||
$AuthCertificateBytes = [Convert]::FromBase64String($AuthCertificateBase64)
|
||||
[IO.File]::WriteAllBytes($AuthCertificateFileName, $AuthCertificateBytes)
|
||||
$AuthCertificate = Import-PfxCertificate -FilePath $AuthCertificateFileName -CertStoreLocation Cert:\LocalMachine\My -Password (ConvertTo-SecureString $AuthCertificateKey -AsPlainText -Force)
|
||||
rm $AuthCertificateFileName
|
||||
$ESRPAuthCertificateSubjectName = $AuthCertificate.Subject
|
||||
|
||||
Write-Output ("##vso[task.setvariable variable=ESRPAuthCertificateSubjectName;]$ESRPAuthCertificateSubjectName")
|
||||
@@ -0,0 +1,82 @@
|
||||
function Create-TmpJson($Obj) {
|
||||
$FileName = [System.IO.Path]::GetTempFileName()
|
||||
ConvertTo-Json -Depth 100 $Obj | Out-File -Encoding UTF8 $FileName
|
||||
return $FileName
|
||||
}
|
||||
|
||||
$Auth = Create-TmpJson @{
|
||||
Version = "1.0.0"
|
||||
AuthenticationType = "AAD_CERT"
|
||||
ClientId = $env:ESRPClientId
|
||||
AuthCert = @{
|
||||
SubjectName = $env:ESRPAuthCertificateSubjectName
|
||||
StoreLocation = "LocalMachine"
|
||||
StoreName = "My"
|
||||
}
|
||||
RequestSigningCert = @{
|
||||
SubjectName = $env:ESRPCertificateSubjectName
|
||||
StoreLocation = "LocalMachine"
|
||||
StoreName = "My"
|
||||
}
|
||||
}
|
||||
|
||||
$Policy = Create-TmpJson @{
|
||||
Version = "1.0.0"
|
||||
}
|
||||
|
||||
$Input = Create-TmpJson @{
|
||||
Version = "1.0.0"
|
||||
SignBatches = @(
|
||||
@{
|
||||
SourceLocationType = "UNC"
|
||||
SignRequestFiles = @(
|
||||
@{
|
||||
SourceLocation = $args[0]
|
||||
}
|
||||
)
|
||||
SigningInfo = @{
|
||||
Operations = @(
|
||||
@{
|
||||
KeyCode = "CP-229803"
|
||||
OperationCode = "SigntoolSign"
|
||||
Parameters = @{
|
||||
OpusName = "VS Code"
|
||||
OpusInfo = "https://code.visualstudio.com/"
|
||||
PageHash = "/NPH"
|
||||
TimeStamp = "/t `"http://ts4096.gtm.microsoft.com/TSS/AuthenticodeTS`""
|
||||
}
|
||||
ToolName = "sign"
|
||||
ToolVersion = "1.0"
|
||||
},
|
||||
@{
|
||||
KeyCode = "CP-230012"
|
||||
OperationCode = "SigntoolSign"
|
||||
Parameters = @{
|
||||
OpusName = "VS Code"
|
||||
OpusInfo = "https://code.visualstudio.com/"
|
||||
Append = "/as"
|
||||
FileDigest = "/fd `"SHA256`""
|
||||
PageHash = "/NPH"
|
||||
TimeStamp = "/tr `"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer`" /td sha256"
|
||||
}
|
||||
ToolName = "sign"
|
||||
ToolVersion = "1.0"
|
||||
},
|
||||
@{
|
||||
KeyCode = "CP-230012"
|
||||
OperationCode = "SigntoolVerify"
|
||||
Parameters = @{
|
||||
VerifyAll = "/all"
|
||||
}
|
||||
ToolName = "sign"
|
||||
ToolVersion = "1.0"
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
$Output = [System.IO.Path]::GetTempFileName()
|
||||
$ScriptPath = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
|
||||
& "$ScriptPath\ESRPClient\packages\EsrpClient.1.0.27\tools\ESRPClient.exe" Sign -a $Auth -p $Policy -i $Input -o $Output
|
||||
+28
-28
@@ -545,33 +545,6 @@
|
||||
],
|
||||
"isProd": true
|
||||
},
|
||||
{
|
||||
"name": "retep998/winapi-rs",
|
||||
"version": "0.3.4",
|
||||
"repositoryUrl": "https://github.com/retep998/winapi-rs",
|
||||
"licenseDetail": [
|
||||
"Copyright (c) 2015 The winapi-rs Developers",
|
||||
"",
|
||||
"Permission is hereby granted, free of charge, to any person obtaining a copy",
|
||||
"of this software and associated documentation files (the \"Software\"), to deal",
|
||||
"in the Software without restriction, including without limitation the rights",
|
||||
"to use, copy, modify, merge, publish, distribute, sublicense, and/or sell",
|
||||
"copies of the Software, and to permit persons to whom the Software is",
|
||||
"furnished to do so, subject to the following conditions:",
|
||||
"",
|
||||
"The above copyright notice and this permission notice shall be included in all",
|
||||
"copies or substantial portions of the Software.",
|
||||
"",
|
||||
"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR",
|
||||
"IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,",
|
||||
"FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE",
|
||||
"AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER",
|
||||
"LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,",
|
||||
"OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE",
|
||||
"SOFTWARE."
|
||||
],
|
||||
"isProd": true
|
||||
},
|
||||
{
|
||||
"name": "retep998/winapi-rs",
|
||||
"version": "0.2.8",
|
||||
@@ -707,6 +680,33 @@
|
||||
],
|
||||
"isProd": true
|
||||
},
|
||||
{
|
||||
"name": "retep998/winapi-rs",
|
||||
"version": "0.3.4",
|
||||
"repositoryUrl": "https://github.com/retep998/winapi-rs",
|
||||
"licenseDetail": [
|
||||
"Copyright (c) 2015 The winapi-rs Developers",
|
||||
"",
|
||||
"Permission is hereby granted, free of charge, to any person obtaining a copy",
|
||||
"of this software and associated documentation files (the \"Software\"), to deal",
|
||||
"in the Software without restriction, including without limitation the rights",
|
||||
"to use, copy, modify, merge, publish, distribute, sublicense, and/or sell",
|
||||
"copies of the Software, and to permit persons to whom the Software is",
|
||||
"furnished to do so, subject to the following conditions:",
|
||||
"",
|
||||
"The above copyright notice and this permission notice shall be included in all",
|
||||
"copies or substantial portions of the Software.",
|
||||
"",
|
||||
"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR",
|
||||
"IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,",
|
||||
"FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE",
|
||||
"AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER",
|
||||
"LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,",
|
||||
"OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE",
|
||||
"SOFTWARE."
|
||||
],
|
||||
"isProd": true
|
||||
},
|
||||
{
|
||||
"name": "rust-lang-nursery/lazy-static.rs",
|
||||
"version": "1.0.0",
|
||||
@@ -1791,4 +1791,4 @@
|
||||
],
|
||||
"isProd": true
|
||||
}
|
||||
]
|
||||
]
|
||||
+23
-4
@@ -32,6 +32,7 @@ VersionInfoVersion={#RawVersion}
|
||||
ShowLanguageDialog=auto
|
||||
ArchitecturesAllowed={#ArchitecturesAllowed}
|
||||
ArchitecturesInstallIn64BitMode={#ArchitecturesInstallIn64BitMode}
|
||||
SignTool=esrp
|
||||
|
||||
#if "user" == InstallTarget
|
||||
DefaultDirName={userpf}\{#DirName}
|
||||
@@ -91,7 +92,7 @@ Filename: "{app}\{#ExeBasename}.exe"; Description: "{cm:LaunchProgram,{#NameLong
|
||||
#if "user" == InstallTarget
|
||||
#define SoftwareClassesRootKey "HKCU"
|
||||
#else
|
||||
#define SoftwareClassesRootKey "HKCR"
|
||||
#define SoftwareClassesRootKey "HKLM"
|
||||
#endif
|
||||
|
||||
Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.ascx\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles
|
||||
@@ -945,7 +946,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\Drive\shell\{#RegValu
|
||||
#define Uninstall64RootKey "HKCU64"
|
||||
#define Uninstall32RootKey "HKCU32"
|
||||
#else
|
||||
#define EnvironmentRootKey "HKCR"
|
||||
#define EnvironmentRootKey "HKLM"
|
||||
#define EnvironmentKey "System\CurrentControlSet\Control\Session Manager\Environment"
|
||||
#define Uninstall64RootKey "HKLM64"
|
||||
#define Uninstall32RootKey "HKLM32"
|
||||
@@ -963,8 +964,26 @@ var
|
||||
begin
|
||||
Result := True;
|
||||
|
||||
if IsWin64 then begin
|
||||
RegKey := 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\' + copy('{#IncompatibleAppId}', 2, 38) + '_is1';
|
||||
#if "user" == InstallTarget
|
||||
#if "ia32" == Arch
|
||||
#define IncompatibleArchRootKey "HKLM32"
|
||||
#else
|
||||
#define IncompatibleArchRootKey "HKLM64"
|
||||
#endif
|
||||
|
||||
if not WizardSilent() then begin
|
||||
RegKey := 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\' + copy('{#IncompatibleTargetAppId}', 2, 38) + '_is1';
|
||||
|
||||
if RegKeyExists({#IncompatibleArchRootKey}, RegKey) then begin
|
||||
if MsgBox('{#NameShort} is already installed on this system for all users. Are you sure you want to install it for this user?', mbConfirmation, MB_YESNO) = IDNO then begin
|
||||
Result := false;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
#endif
|
||||
|
||||
if Result and IsWin64 then begin
|
||||
RegKey := 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\' + copy('{#IncompatibleArchAppId}', 2, 38) + '_is1';
|
||||
|
||||
if '{#Arch}' = 'ia32' then begin
|
||||
Result := not RegKeyExists({#Uninstall64RootKey}, RegKey);
|
||||
|
||||
Binary file not shown.
@@ -18,7 +18,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"jsonc-parser": "^1.0.0",
|
||||
"vscode-nls": "^3.2.1"
|
||||
"vscode-nls": "^3.2.4"
|
||||
},
|
||||
"contributes": {
|
||||
"languages": [
|
||||
|
||||
@@ -10,6 +10,6 @@ jsonc-parser@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-1.0.0.tgz#ddcc864ae708e60a7a6dd36daea00172fa8d9272"
|
||||
|
||||
vscode-nls@^3.2.1:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.1.tgz#b1f3e04e8a94a715d5a7bcbc8339c51e6d74ca51"
|
||||
vscode-nls@^3.2.4:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.4.tgz#2166b4183c8aea884d20727f5449e62be69fd398"
|
||||
|
||||
@@ -709,7 +709,7 @@
|
||||
"dependencies": {
|
||||
"vscode-languageclient": "^4.1.4",
|
||||
"vscode-languageserver-protocol-foldingprovider": "^2.0.1",
|
||||
"vscode-nls": "^3.2.2"
|
||||
"vscode-nls": "^3.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "7.0.43",
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"node": "*"
|
||||
},
|
||||
"dependencies": {
|
||||
"vscode-css-languageservice": "^3.0.9-next.18",
|
||||
"vscode-css-languageservice": "^3.0.9-next.20",
|
||||
"vscode-languageserver": "^4.1.3",
|
||||
"vscode-languageserver-protocol-foldingprovider": "^2.0.1"
|
||||
},
|
||||
|
||||
@@ -194,9 +194,9 @@ supports-color@5.4.0:
|
||||
dependencies:
|
||||
has-flag "^3.0.0"
|
||||
|
||||
vscode-css-languageservice@^3.0.9-next.18:
|
||||
version "3.0.9-next.18"
|
||||
resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.9-next.18.tgz#f8f25123b5a8cdc9f72fafcd2c0088f726322437"
|
||||
vscode-css-languageservice@^3.0.9-next.20:
|
||||
version "3.0.9-next.20"
|
||||
resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.9-next.20.tgz#8229aee66aa877929af5d2fd81a21731b415c92e"
|
||||
dependencies:
|
||||
vscode-languageserver-types "^3.7.2"
|
||||
vscode-nls "^3.2.2"
|
||||
|
||||
@@ -161,9 +161,9 @@ vscode-languageserver-types@^3.7.2:
|
||||
version "3.7.2"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.7.2.tgz#aad8846f8e3e27962648554de5a8417e358f34eb"
|
||||
|
||||
vscode-nls@^3.2.2:
|
||||
version "3.2.2"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.2.tgz#3817eca5b985c2393de325197cf4e15eb2aa5350"
|
||||
vscode-nls@^3.2.4:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.4.tgz#2166b4183c8aea884d20727f5449e62be69fd398"
|
||||
|
||||
wrappy@1:
|
||||
version "1.0.2"
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"vscode": "0.10.x"
|
||||
},
|
||||
"scripts": {
|
||||
"update-grammar": "node ../../build/npm/update-grammar.js atom/language-css grammars/css.cson ./syntaxes/css.tmLanguage.json"
|
||||
"update-grammar": "node ../../build/npm/update-grammar.js octref/language-css grammars/css.cson ./syntaxes/css.tmLanguage.json"
|
||||
},
|
||||
"contributes": {
|
||||
"languages": [
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"information_for_contributors": [
|
||||
"This file has been converted from https://github.com/atom/language-css/blob/master/grammars/css.cson",
|
||||
"This file has been converted from https://github.com/octref/language-css/blob/master/grammars/css.cson",
|
||||
"If you want to provide a fix or improvement, please create a pull request against the original repository.",
|
||||
"Once accepted there, we are happy to receive an update request."
|
||||
],
|
||||
"version": "https://github.com/atom/language-css/commit/17ad55bc5f65c16585e80ea1c7c19e0c0814f6d5",
|
||||
"version": "https://github.com/octref/language-css/commit/ea1d7e3619966e47c57498913a5eabea0cce7538",
|
||||
"name": "CSS",
|
||||
"scopeName": "source.css",
|
||||
"patterns": [
|
||||
@@ -604,6 +604,45 @@
|
||||
"include": "#string"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"begin": "(?i)(?=@[\\w-]+(\\s|\\(|/\\*|$))",
|
||||
"end": "(?<=})(?!\\G)",
|
||||
"patterns": [
|
||||
{
|
||||
"begin": "(?i)\\G(@)[\\w-]+",
|
||||
"beginCaptures": {
|
||||
"0": {
|
||||
"name": "keyword.control.at-rule.css"
|
||||
},
|
||||
"1": {
|
||||
"name": "punctuation.definition.keyword.css"
|
||||
}
|
||||
},
|
||||
"end": "(?=\\s*[{;])",
|
||||
"name": "meta.at-rule.header.css"
|
||||
},
|
||||
{
|
||||
"begin": "{",
|
||||
"beginCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.section.begin.bracket.curly.css"
|
||||
}
|
||||
},
|
||||
"end": "}",
|
||||
"endCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.section.end.bracket.curly.css"
|
||||
}
|
||||
},
|
||||
"name": "meta.at-rule.body.css",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "$self"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1411,7 +1450,7 @@
|
||||
"name": "invalid.illegal.colon.css"
|
||||
}
|
||||
},
|
||||
"match": "(?xi)\n(:)(:*)\n(?: active|any-link|checked|default|disabled|empty|enabled|first\n | (?:first|last|only)-(?:child|of-type)|focus|focus-within|fullscreen|host|hover\n | in-range|indeterminate|invalid|left|link|optional|out-of-range\n | read-only|read-write|required|right|root|scope|target|unresolved\n | valid|visited\n)(?![\\w-]|\\s*[;}])",
|
||||
"match": "(?xi)\n(:)(:*)\n(?: active|any-link|checked|default|defined|disabled|empty|enabled|first\n | (?:first|last|only)-(?:child|of-type)|focus|focus-visible|focus-within\n | fullscreen|host|hover|in-range|indeterminate|invalid|left|link\n | optional|out-of-range|placeholder-shown|read-only|read-write\n | required|right|root|scope|target|unresolved\n | valid|visited\n)(?![\\w-]|\\s*[;}])",
|
||||
"name": "entity.other.attribute-name.pseudo-class.css"
|
||||
},
|
||||
"pseudo-elements": {
|
||||
|
||||
@@ -449,6 +449,6 @@
|
||||
"image-size": "^0.5.2",
|
||||
"vscode-emmet-helper": "^1.2.10",
|
||||
"vscode-languageserver-types": "^3.5.0",
|
||||
"vscode-nls": "3.2.1"
|
||||
"vscode-nls": "3.2.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ function balance(out: boolean) {
|
||||
}
|
||||
|
||||
function getRangeToBalanceOut(document: vscode.TextDocument, selection: vscode.Selection, rootNode: HtmlNode): vscode.Selection {
|
||||
let nodeToBalance = getHtmlNode(document, rootNode, selection.start);
|
||||
let nodeToBalance = getHtmlNode(document, rootNode, selection.start, false);
|
||||
if (!nodeToBalance) {
|
||||
return selection;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ function getRangesToReplace(document: vscode.TextDocument, selection: vscode.Sel
|
||||
let endNodeToUpdate: Node | null;
|
||||
|
||||
if (selection.isEmpty) {
|
||||
startNodeToUpdate = endNodeToUpdate = getNode(rootNode, selection.start);
|
||||
startNodeToUpdate = endNodeToUpdate = getNode(rootNode, selection.start, true);
|
||||
} else {
|
||||
startNodeToUpdate = getNode(rootNode, selection.start, true);
|
||||
endNodeToUpdate = getNode(rootNode, selection.end, true);
|
||||
|
||||
@@ -38,7 +38,7 @@ export function removeTag() {
|
||||
|
||||
function getRangeToRemove(editor: vscode.TextEditor, rootNode: HtmlNode, selection: vscode.Selection, indentInSpaces: string): vscode.Range[] {
|
||||
|
||||
let nodeToUpdate = getHtmlNode(editor.document, rootNode, selection.start);
|
||||
let nodeToUpdate = getHtmlNode(editor.document, rootNode, selection.start, true);
|
||||
if (!nodeToUpdate) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import { getDeepestNode, findNextWord, findPrevWord, getHtmlNode } from './util'
|
||||
import { HtmlNode } from 'EmmetNode';
|
||||
|
||||
export function nextItemHTML(selectionStart: vscode.Position, selectionEnd: vscode.Position, editor: vscode.TextEditor, rootNode: HtmlNode): vscode.Selection | undefined {
|
||||
let currentNode = getHtmlNode(editor.document, rootNode, selectionEnd);
|
||||
let currentNode = getHtmlNode(editor.document, rootNode, selectionEnd, false);
|
||||
let nextNode: HtmlNode | undefined = undefined;
|
||||
|
||||
if (!currentNode) {
|
||||
@@ -31,7 +31,7 @@ export function nextItemHTML(selectionStart: vscode.Position, selectionEnd: vsco
|
||||
|
||||
// Get the first child of current node which is right after the cursor and is not a comment
|
||||
nextNode = currentNode.firstChild;
|
||||
while (nextNode && (selectionEnd.isAfterOrEqual(nextNode.start) || nextNode.type === 'comment')) {
|
||||
while (nextNode && (selectionEnd.isAfterOrEqual(nextNode.end) || nextNode.type === 'comment')) {
|
||||
nextNode = nextNode.nextSibling;
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,7 @@ export function nextItemHTML(selectionStart: vscode.Position, selectionEnd: vsco
|
||||
}
|
||||
|
||||
export function prevItemHTML(selectionStart: vscode.Position, selectionEnd: vscode.Position, editor: vscode.TextEditor, rootNode: HtmlNode): vscode.Selection | undefined {
|
||||
let currentNode = getHtmlNode(editor.document, rootNode, selectionStart);
|
||||
let currentNode = getHtmlNode(editor.document, rootNode, selectionStart, false);
|
||||
let prevNode: HtmlNode | undefined = undefined;
|
||||
|
||||
if (!currentNode) {
|
||||
@@ -63,7 +63,7 @@ export function prevItemHTML(selectionStart: vscode.Position, selectionEnd: vsco
|
||||
|
||||
if (currentNode.type !== 'comment' && selectionStart.translate(0, -1).isAfter(currentNode.open.start)) {
|
||||
|
||||
if (selectionStart.isBefore(currentNode.open.end) || !currentNode.firstChild) {
|
||||
if (selectionStart.isBefore(currentNode.open.end) || !currentNode.firstChild || selectionEnd.isBeforeOrEqual(currentNode.firstChild.start)) {
|
||||
prevNode = currentNode;
|
||||
} else {
|
||||
// Select the child that appears just before the cursor and is not a comment
|
||||
|
||||
@@ -51,7 +51,7 @@ export function nextItemStylesheet(startOffset: vscode.Position, endOffset: vsco
|
||||
}
|
||||
|
||||
export function prevItemStylesheet(startOffset: vscode.Position, endOffset: vscode.Position, editor: vscode.TextEditor, rootNode: CssNode): vscode.Selection | undefined {
|
||||
let currentNode = <CssNode>getNode(rootNode, startOffset);
|
||||
let currentNode = <CssNode>getNode(rootNode, startOffset, false);
|
||||
if (!currentNode) {
|
||||
currentNode = rootNode;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ export function splitJoinTag() {
|
||||
|
||||
return editor.edit(editBuilder => {
|
||||
editor.selections.reverse().forEach(selection => {
|
||||
let nodeToUpdate = getHtmlNode(editor.document, rootNode, selection.start);
|
||||
let nodeToUpdate = getHtmlNode(editor.document, rootNode, selection.start, true);
|
||||
if (nodeToUpdate) {
|
||||
let textEdit = getRangesToReplace(editor.document, nodeToUpdate);
|
||||
editBuilder.replace(textEdit.range, textEdit.newText);
|
||||
|
||||
@@ -113,6 +113,22 @@ suite('Tests for Next/Previous Select/Edit point and Balance actions', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('Emmet Select Next/Prev item at boundary', function(): any {
|
||||
return withRandomFileEditor(htmlContents, '.html', (editor, doc) => {
|
||||
editor.selections = [new Selection(4, 1, 4, 1)];
|
||||
|
||||
fetchSelectItem('next');
|
||||
testSelection(editor.selection, 2, 4, 6);
|
||||
|
||||
editor.selections = [new Selection(4, 1, 4, 1)];
|
||||
|
||||
fetchSelectItem('prev');
|
||||
testSelection(editor.selection, 1, 3, 5);
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('Emmet Next/Prev Item in html template', function (): any {
|
||||
const templateContents = `
|
||||
<script type="text/template">
|
||||
|
||||
@@ -56,6 +56,16 @@ suite('Tests for Emmet: Reflect CSS Value command', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('Reflect Css Value in css file, selecting entire property', function (): any {
|
||||
return withRandomFileEditor(cssContents, '.css', (editor, doc) => {
|
||||
editor.selections = [new Selection(5, 2, 5, 32)];
|
||||
return reflectCssValue().then(() => {
|
||||
assert.equal(doc.getText(), cssContents.replace(/\(50deg\)/g, '(20deg)'));
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('Reflect Css Value in html file', function (): any {
|
||||
return withRandomFileEditor(htmlContents, '.html', (editor, doc) => {
|
||||
editor.selections = [new Selection(7, 20, 7, 20)];
|
||||
@@ -66,4 +76,14 @@ suite('Tests for Emmet: Reflect CSS Value command', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('Reflect Css Value in html file, selecting entire property', function (): any {
|
||||
return withRandomFileEditor(htmlContents, '.html', (editor, doc) => {
|
||||
editor.selections = [new Selection(7, 4, 7, 34)];
|
||||
return reflectCssValue().then(() => {
|
||||
assert.equal(doc.getText(), htmlContents.replace(/\(50deg\)/g, '(20deg)'));
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
@@ -66,6 +66,31 @@ suite('Tests for Emmet actions on html tags', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// #region update tag
|
||||
test('update tag with entire node selected', () => {
|
||||
const expectedContents = `
|
||||
<div class="hello">
|
||||
<ul>
|
||||
<li><section>Hello</section></li>
|
||||
<li><span>There</span></li>
|
||||
<section><li><span>Bye</span></li></section>
|
||||
</ul>
|
||||
<span/>
|
||||
</div>
|
||||
`;
|
||||
return withRandomFileEditor(contents, 'html', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(3, 7, 3, 25),
|
||||
new Selection(5, 3, 5, 39),
|
||||
];
|
||||
|
||||
return updateTag('section')!.then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('update tag with template', () => {
|
||||
const expectedContents = `
|
||||
<script type="text/template">
|
||||
@@ -89,8 +114,9 @@ suite('Tests for Emmet actions on html tags', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
// #endregion
|
||||
|
||||
|
||||
// #region remove tag
|
||||
test('remove tag with mutliple cursors', () => {
|
||||
const expectedContents = `
|
||||
<div class="hello">
|
||||
@@ -116,6 +142,31 @@ suite('Tests for Emmet actions on html tags', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('remove tag with boundary conditions', () => {
|
||||
const expectedContents = `
|
||||
<div class="hello">
|
||||
<ul>
|
||||
<li>Hello</li>
|
||||
<li><span>There</span></li>
|
||||
<li><span>Bye</span></li>
|
||||
</ul>
|
||||
<span/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
return withRandomFileEditor(contents, 'html', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(3, 7, 3, 25),
|
||||
new Selection(5, 3, 5, 39),
|
||||
];
|
||||
|
||||
return removeTag()!.then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test('remove tag with template', () => {
|
||||
const expectedContents = `
|
||||
@@ -139,7 +190,9 @@ suite('Tests for Emmet actions on html tags', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
// #endregion
|
||||
|
||||
// #region split/join tag
|
||||
test('split/join tag with mutliple cursors', () => {
|
||||
const expectedContents = `
|
||||
<div class="hello">
|
||||
@@ -164,6 +217,30 @@ suite('Tests for Emmet actions on html tags', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('split/join tag with boundary selection', () => {
|
||||
const expectedContents = `
|
||||
<div class="hello">
|
||||
<ul>
|
||||
<li><span/></li>
|
||||
<li><span>There</span></li>
|
||||
<div><li><span>Bye</span></li></div>
|
||||
</ul>
|
||||
<span></span>
|
||||
</div>
|
||||
`;
|
||||
return withRandomFileEditor(contents, 'html', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(3, 7, 3, 25), // join tag
|
||||
new Selection(7, 2, 7, 9), // split tag
|
||||
];
|
||||
|
||||
return splitJoinTag()!.then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('split/join tag with templates', () => {
|
||||
const expectedContents = `
|
||||
<script type="text/template">
|
||||
@@ -214,7 +291,9 @@ suite('Tests for Emmet actions on html tags', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
// #endregion
|
||||
|
||||
// #region match tag
|
||||
test('match tag with mutliple cursors', () => {
|
||||
return withRandomFileEditor(contents, 'html', (editor, doc) => {
|
||||
editor.selections = [
|
||||
@@ -265,6 +344,9 @@ suite('Tests for Emmet actions on html tags', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region merge lines
|
||||
test('merge lines of tag with children when empty selection', () => {
|
||||
const expectedContents = `
|
||||
<div class="hello">
|
||||
@@ -284,6 +366,25 @@ suite('Tests for Emmet actions on html tags', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('merge lines of tag with children when full node selection', () => {
|
||||
const expectedContents = `
|
||||
<div class="hello">
|
||||
<ul><li><span>Hello</span></li><li><span>There</span></li><div><li><span>Bye</span></li></div></ul>
|
||||
<span/>
|
||||
</div>
|
||||
`;
|
||||
return withRandomFileEditor(contents, 'html', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(2, 3, 6, 7)
|
||||
];
|
||||
|
||||
return mergeLines()!.then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('merge lines is no-op when start and end nodes are on the same line', () => {
|
||||
return withRandomFileEditor(contents, 'html', (editor, doc) => {
|
||||
editor.selections = [
|
||||
@@ -298,5 +399,6 @@ suite('Tests for Emmet actions on html tags', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
// #endregion
|
||||
});
|
||||
|
||||
|
||||
@@ -73,13 +73,13 @@ function updateImageSizeHTML(editor: TextEditor, position: Position): Promise<Te
|
||||
function updateImageSizeStyleTag(editor: TextEditor, position: Position): Promise<TextEdit[] | undefined> {
|
||||
const getPropertyInsiderStyleTag = (editor: TextEditor): Property | null => {
|
||||
const rootNode = parseDocument(editor.document);
|
||||
const currentNode = <HtmlNode>getNode(rootNode, position);
|
||||
const currentNode = <HtmlNode>getNode(rootNode, position, true);
|
||||
if (currentNode && currentNode.name === 'style'
|
||||
&& currentNode.open.end.isBefore(position)
|
||||
&& currentNode.close.start.isAfter(position)) {
|
||||
let buffer = new DocumentStreamReader(editor.document, currentNode.open.end, new Range(currentNode.open.end, currentNode.close.start));
|
||||
let rootNode = parseStylesheet(buffer);
|
||||
const node = getNode(rootNode, position);
|
||||
const node = getNode(rootNode, position, true);
|
||||
return (node && node.type === 'property') ? <Property>node : null;
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -30,7 +30,7 @@ export function updateTag(tagName: string): Thenable<boolean> | undefined {
|
||||
}
|
||||
|
||||
function getRangesToUpdate(editor: vscode.TextEditor, selection: vscode.Selection, rootNode: HtmlNode): vscode.Range[] {
|
||||
let nodeToUpdate = getHtmlNode(editor.document, rootNode, selection.start);
|
||||
let nodeToUpdate = getHtmlNode(editor.document, rootNode, selection.start, true);
|
||||
if (!nodeToUpdate) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -285,7 +285,7 @@ function findClosingCommentAfterPosition(document: vscode.TextDocument, position
|
||||
/**
|
||||
* Returns node corresponding to given position in the given root node
|
||||
*/
|
||||
export function getNode(root: Node | undefined, position: vscode.Position, includeNodeBoundary: boolean = false) {
|
||||
export function getNode(root: Node | undefined, position: vscode.Position, includeNodeBoundary: boolean) {
|
||||
if (!root) {
|
||||
return null;
|
||||
}
|
||||
@@ -310,7 +310,7 @@ export function getNode(root: Node | undefined, position: vscode.Position, inclu
|
||||
return foundNode;
|
||||
}
|
||||
|
||||
export function getHtmlNode(document: vscode.TextDocument, root: Node | undefined, position: vscode.Position, includeNodeBoundary: boolean = false): HtmlNode | undefined {
|
||||
export function getHtmlNode(document: vscode.TextDocument, root: Node | undefined, position: vscode.Position, includeNodeBoundary: boolean): HtmlNode | undefined {
|
||||
let currentNode = <HtmlNode>getNode(root, position, includeNodeBoundary);
|
||||
if (!currentNode) { return; }
|
||||
|
||||
@@ -532,7 +532,7 @@ export function getCssPropertyFromRule(rule: Rule, name: string): Property | und
|
||||
*/
|
||||
export function getCssPropertyFromDocument(editor: vscode.TextEditor, position: vscode.Position): Property | null | undefined {
|
||||
const rootNode = parseDocument(editor.document);
|
||||
const node = getNode(rootNode, position);
|
||||
const node = getNode(rootNode, position, true);
|
||||
|
||||
if (isStyleSheet(editor.document.languageId)) {
|
||||
return node && node.type === 'property' ? <Property>node : null;
|
||||
@@ -545,7 +545,7 @@ export function getCssPropertyFromDocument(editor: vscode.TextEditor, position:
|
||||
&& htmlNode.close.start.isAfter(position)) {
|
||||
let buffer = new DocumentStreamReader(editor.document, htmlNode.start, new vscode.Range(htmlNode.start, htmlNode.end));
|
||||
let rootNode = parseStylesheet(buffer);
|
||||
const node = getNode(rootNode, position);
|
||||
const node = getNode(rootNode, position, true);
|
||||
return (node && node.type === 'property') ? <Property>node : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2131,9 +2131,9 @@ vscode-languageserver-types@^3.6.0-next.1:
|
||||
version "3.6.0-next.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.6.0-next.1.tgz#98e488d3f87b666b4ee1a3d89f0023e246d358f3"
|
||||
|
||||
vscode-nls@3.2.1:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.1.tgz#b1f3e04e8a94a715d5a7bcbc8339c51e6d74ca51"
|
||||
vscode-nls@3.2.4:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.4.tgz#2166b4183c8aea884d20727f5449e62be69fd398"
|
||||
|
||||
vscode@1.0.1:
|
||||
version "1.0.1"
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"jsonc-parser": "^1.0.0",
|
||||
"markdown-it": "^8.3.1",
|
||||
"parse5": "^3.0.2",
|
||||
"vscode-nls": "^3.2.1"
|
||||
"vscode-nls": "^3.2.4"
|
||||
},
|
||||
"contributes": {
|
||||
"jsonValidation": [
|
||||
|
||||
@@ -58,6 +58,6 @@ uc.micro@^1.0.1, uc.micro@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.3.tgz#7ed50d5e0f9a9fb0a573379259f2a77458d50192"
|
||||
|
||||
vscode-nls@^3.2.1:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.1.tgz#b1f3e04e8a94a715d5a7bcbc8339c51e6d74ca51"
|
||||
vscode-nls@^3.2.4:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.4.tgz#2166b4183c8aea884d20727f5449e62be69fd398"
|
||||
|
||||
@@ -1140,7 +1140,7 @@
|
||||
"iconv-lite": "0.4.19",
|
||||
"jschardet": "^1.6.0",
|
||||
"vscode-extension-telemetry": "0.0.17",
|
||||
"vscode-nls": "^3.2.1",
|
||||
"vscode-nls": "^3.2.4",
|
||||
"which": "^1.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -137,6 +137,22 @@ const ImageMimetypes = [
|
||||
'image/bmp'
|
||||
];
|
||||
|
||||
async function categorizeResourceByResolution(resources: Resource[]): Promise<{ merge: Resource[], resolved: Resource[], unresolved: Resource[] }> {
|
||||
const selection = resources.filter(s => s instanceof Resource) as Resource[];
|
||||
const merge = selection.filter(s => s.resourceGroupType === ResourceGroupType.Merge);
|
||||
const isBothAddedOrModified = (s: Resource) => s.type === Status.BOTH_MODIFIED || s.type === Status.BOTH_ADDED;
|
||||
const possibleUnresolved = merge.filter(isBothAddedOrModified);
|
||||
const promises = possibleUnresolved.map(s => grep(s.resourceUri.fsPath, /^<{7}|^={7}|^>{7}/));
|
||||
const unresolvedBothModified = await Promise.all<boolean>(promises);
|
||||
const resolved = possibleUnresolved.filter((s, i) => !unresolvedBothModified[i]);
|
||||
const unresolved = [
|
||||
...merge.filter(s => !isBothAddedOrModified(s)),
|
||||
...possibleUnresolved.filter((s, i) => unresolvedBothModified[i])
|
||||
];
|
||||
|
||||
return { merge, resolved, unresolved };
|
||||
}
|
||||
|
||||
export class CommandCenter {
|
||||
|
||||
private disposables: Disposable[];
|
||||
@@ -629,20 +645,12 @@ export class CommandCenter {
|
||||
}
|
||||
|
||||
const selection = resourceStates.filter(s => s instanceof Resource) as Resource[];
|
||||
const merge = selection.filter(s => s.resourceGroupType === ResourceGroupType.Merge);
|
||||
const bothModified = merge.filter(s => s.type === Status.BOTH_MODIFIED);
|
||||
const promises = bothModified.map(s => grep(s.resourceUri.fsPath, /^<{7}|^={7}|^>{7}/));
|
||||
const unresolvedBothModified = await Promise.all<boolean>(promises);
|
||||
const resolvedConflicts = bothModified.filter((s, i) => !unresolvedBothModified[i]);
|
||||
const unresolvedConflicts = [
|
||||
...merge.filter(s => s.type !== Status.BOTH_MODIFIED),
|
||||
...bothModified.filter((s, i) => unresolvedBothModified[i])
|
||||
];
|
||||
const { resolved, unresolved } = await categorizeResourceByResolution(selection);
|
||||
|
||||
if (unresolvedConflicts.length > 0) {
|
||||
const message = unresolvedConflicts.length > 1
|
||||
? localize('confirm stage files with merge conflicts', "Are you sure you want to stage {0} files with merge conflicts?", unresolvedConflicts.length)
|
||||
: localize('confirm stage file with merge conflicts', "Are you sure you want to stage {0} with merge conflicts?", path.basename(unresolvedConflicts[0].resourceUri.fsPath));
|
||||
if (unresolved.length > 0) {
|
||||
const message = unresolved.length > 1
|
||||
? localize('confirm stage files with merge conflicts', "Are you sure you want to stage {0} files with merge conflicts?", unresolved.length)
|
||||
: localize('confirm stage file with merge conflicts', "Are you sure you want to stage {0} with merge conflicts?", path.basename(unresolved[0].resourceUri.fsPath));
|
||||
|
||||
const yes = localize('yes', "Yes");
|
||||
const pick = await window.showWarningMessage(message, { modal: true }, yes);
|
||||
@@ -653,7 +661,7 @@ export class CommandCenter {
|
||||
}
|
||||
|
||||
const workingTree = selection.filter(s => s.resourceGroupType === ResourceGroupType.WorkingTree);
|
||||
const scmResources = [...workingTree, ...resolvedConflicts, ...unresolvedConflicts];
|
||||
const scmResources = [...workingTree, ...resolved, ...unresolved];
|
||||
|
||||
this.outputChannel.appendLine(`git.stage.scmResources ${scmResources.length}`);
|
||||
if (!scmResources.length) {
|
||||
@@ -667,12 +675,12 @@ export class CommandCenter {
|
||||
@command('git.stageAll', { repository: true })
|
||||
async stageAll(repository: Repository): Promise<void> {
|
||||
const resources = repository.mergeGroup.resourceStates.filter(s => s instanceof Resource) as Resource[];
|
||||
const mergeConflicts = resources.filter(s => s.resourceGroupType === ResourceGroupType.Merge);
|
||||
const { merge, unresolved } = await categorizeResourceByResolution(resources);
|
||||
|
||||
if (mergeConflicts.length > 0) {
|
||||
const message = mergeConflicts.length > 1
|
||||
? localize('confirm stage files with merge conflicts', "Are you sure you want to stage {0} files with merge conflicts?", mergeConflicts.length)
|
||||
: localize('confirm stage file with merge conflicts', "Are you sure you want to stage {0} with merge conflicts?", path.basename(mergeConflicts[0].resourceUri.fsPath));
|
||||
if (unresolved.length > 0) {
|
||||
const message = unresolved.length > 1
|
||||
? localize('confirm stage files with merge conflicts', "Are you sure you want to stage {0} files with merge conflicts?", merge.length)
|
||||
: localize('confirm stage file with merge conflicts', "Are you sure you want to stage {0} with merge conflicts?", path.basename(merge[0].resourceUri.fsPath));
|
||||
|
||||
const yes = localize('yes', "Yes");
|
||||
const pick = await window.showWarningMessage(message, { modal: true }, yes);
|
||||
@@ -1273,16 +1281,7 @@ export class CommandCenter {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await choice.run(repository);
|
||||
} catch (err) {
|
||||
if (err.gitErrorCode !== GitErrorCodes.Conflict) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
const message = localize('merge conflicts', "There are merge conflicts. Resolve them before committing.");
|
||||
await window.showWarningMessage(message);
|
||||
}
|
||||
await choice.run(repository);
|
||||
}
|
||||
|
||||
@command('git.createTag', { repository: true })
|
||||
@@ -1655,10 +1654,11 @@ export class CommandCenter {
|
||||
|
||||
return result.catch(async err => {
|
||||
const options: MessageOptions = {
|
||||
modal: err.gitErrorCode === GitErrorCodes.DirtyWorkTree
|
||||
modal: true
|
||||
};
|
||||
|
||||
let message: string;
|
||||
let type: 'error' | 'warning' = 'error';
|
||||
|
||||
switch (err.gitErrorCode) {
|
||||
case GitErrorCodes.DirtyWorkTree:
|
||||
@@ -1667,6 +1667,11 @@ export class CommandCenter {
|
||||
case GitErrorCodes.PushRejected:
|
||||
message = localize('cant push', "Can't push refs to remote. Try running 'Pull' first to integrate your changes.");
|
||||
break;
|
||||
case GitErrorCodes.Conflict:
|
||||
message = localize('merge conflicts', "There are merge conflicts. Resolve them before committing.");
|
||||
type = 'warning';
|
||||
options.modal = false;
|
||||
break;
|
||||
default:
|
||||
const hint = (err.stderr || err.message || String(err))
|
||||
.replace(/^error: /mi, '')
|
||||
@@ -1687,11 +1692,11 @@ export class CommandCenter {
|
||||
return;
|
||||
}
|
||||
|
||||
options.modal = true;
|
||||
|
||||
const outputChannel = this.outputChannel as OutputChannel;
|
||||
const openOutputChannelChoice = localize('open git log', "Open Git Log");
|
||||
const choice = await window.showErrorMessage(message, options, openOutputChannelChoice);
|
||||
const choice = type === 'error'
|
||||
? await window.showErrorMessage(message, options, openOutputChannelChoice)
|
||||
: await window.showWarningMessage(message, options, openOutputChannelChoice);
|
||||
|
||||
if (choice === openOutputChannelChoice) {
|
||||
outputChannel.show();
|
||||
|
||||
+31
-17
@@ -336,7 +336,7 @@ export const GitErrorCodes = {
|
||||
NoStashFound: 'NoStashFound',
|
||||
LocalChangesOverwritten: 'LocalChangesOverwritten',
|
||||
NoUpstreamBranch: 'NoUpstreamBranch',
|
||||
IsInSubmodule: 'IsInSubmodule'
|
||||
IsInSubmodule: 'IsInSubmodule',
|
||||
};
|
||||
|
||||
function getGitErrorCode(stderr: string): string | undefined {
|
||||
@@ -876,27 +876,41 @@ export class Repository {
|
||||
try {
|
||||
await this.run(args, { input: message || '' });
|
||||
} catch (commitErr) {
|
||||
if (/not possible because you have unmerged files/.test(commitErr.stderr || '')) {
|
||||
commitErr.gitErrorCode = GitErrorCodes.UnmergedChanges;
|
||||
throw commitErr;
|
||||
}
|
||||
await this.handleCommitError(commitErr);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await this.run(['config', '--get-all', 'user.name']);
|
||||
} catch (err) {
|
||||
err.gitErrorCode = GitErrorCodes.NoUserNameConfigured;
|
||||
throw err;
|
||||
}
|
||||
async rebaseContinue(): Promise<void> {
|
||||
const args = ['rebase', '--continue'];
|
||||
|
||||
try {
|
||||
await this.run(['config', '--get-all', 'user.email']);
|
||||
} catch (err) {
|
||||
err.gitErrorCode = GitErrorCodes.NoUserEmailConfigured;
|
||||
throw err;
|
||||
}
|
||||
try {
|
||||
await this.run(args);
|
||||
} catch (commitErr) {
|
||||
await this.handleCommitError(commitErr);
|
||||
}
|
||||
}
|
||||
|
||||
private async handleCommitError(commitErr: any): Promise<void> {
|
||||
if (/not possible because you have unmerged files/.test(commitErr.stderr || '')) {
|
||||
commitErr.gitErrorCode = GitErrorCodes.UnmergedChanges;
|
||||
throw commitErr;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.run(['config', '--get-all', 'user.name']);
|
||||
} catch (err) {
|
||||
err.gitErrorCode = GitErrorCodes.NoUserNameConfigured;
|
||||
throw err;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.run(['config', '--get-all', 'user.email']);
|
||||
} catch (err) {
|
||||
err.gitErrorCode = GitErrorCodes.NoUserEmailConfigured;
|
||||
throw err;
|
||||
}
|
||||
|
||||
throw commitErr;
|
||||
}
|
||||
|
||||
async branch(name: string, checkout: boolean): Promise<void> {
|
||||
|
||||
@@ -299,7 +299,8 @@ export enum Operation {
|
||||
Stash = 'Stash',
|
||||
CheckIgnore = 'CheckIgnore',
|
||||
LSTree = 'LSTree',
|
||||
SubmoduleUpdate = 'SubmoduleUpdate'
|
||||
SubmoduleUpdate = 'SubmoduleUpdate',
|
||||
RebaseContinue = 'RebaseContinue',
|
||||
}
|
||||
|
||||
function isReadOnly(operation: Operation): boolean {
|
||||
@@ -479,6 +480,22 @@ export class Repository implements Disposable {
|
||||
return this._submodules;
|
||||
}
|
||||
|
||||
private _rebaseCommit: Commit | undefined = undefined;
|
||||
|
||||
set rebaseCommit(rebaseCommit: Commit | undefined) {
|
||||
if (this._rebaseCommit && !rebaseCommit) {
|
||||
this.inputBox.value = '';
|
||||
} else if (rebaseCommit && (!this._rebaseCommit || this._rebaseCommit.hash !== rebaseCommit.hash)) {
|
||||
this.inputBox.value = rebaseCommit.message;
|
||||
}
|
||||
|
||||
this._rebaseCommit = rebaseCommit;
|
||||
}
|
||||
|
||||
get rebaseCommit(): Commit | undefined {
|
||||
return this._rebaseCommit;
|
||||
}
|
||||
|
||||
private _operations = new OperationsImpl();
|
||||
get operations(): Operations { return this._operations; }
|
||||
|
||||
@@ -553,6 +570,15 @@ export class Repository implements Disposable {
|
||||
}
|
||||
|
||||
validateInput(text: string, position: number): SourceControlInputBoxValidation | undefined {
|
||||
if (this.rebaseCommit) {
|
||||
if (this.rebaseCommit.message !== text) {
|
||||
return {
|
||||
message: localize('commit in rebase', "It's not possible to change the commit message in the middle of a rebase. Please complete the rebase operation and use interactive rebase instead."),
|
||||
type: SourceControlInputBoxValidationType.Warning
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const config = workspace.getConfiguration('git');
|
||||
const setting = config.get<'always' | 'warn' | 'off'>('inputValidation');
|
||||
|
||||
@@ -636,13 +662,23 @@ export class Repository implements Disposable {
|
||||
}
|
||||
|
||||
async commit(message: string, opts: CommitOptions = Object.create(null)): Promise<void> {
|
||||
await this.run(Operation.Commit, async () => {
|
||||
if (opts.all) {
|
||||
await this.repository.add([]);
|
||||
}
|
||||
if (this.rebaseCommit) {
|
||||
await this.run(Operation.RebaseContinue, async () => {
|
||||
if (opts.all) {
|
||||
await this.repository.add([]);
|
||||
}
|
||||
|
||||
await this.repository.commit(message, opts);
|
||||
});
|
||||
await this.repository.rebaseContinue();
|
||||
});
|
||||
} else {
|
||||
await this.run(Operation.Commit, async () => {
|
||||
if (opts.all) {
|
||||
await this.repository.add([]);
|
||||
}
|
||||
|
||||
await this.repository.commit(message, opts);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async clean(resources: Uri[]): Promise<void> {
|
||||
@@ -1025,12 +1061,13 @@ export class Repository implements Disposable {
|
||||
// noop
|
||||
}
|
||||
|
||||
const [refs, remotes, submodules] = await Promise.all([this.repository.getRefs(), this.repository.getRemotes(), this.repository.getSubmodules()]);
|
||||
const [refs, remotes, submodules, rebaseCommit] = await Promise.all([this.repository.getRefs(), this.repository.getRemotes(), this.repository.getSubmodules(), this.getRebaseCommit()]);
|
||||
|
||||
this._HEAD = HEAD;
|
||||
this._refs = refs;
|
||||
this._remotes = remotes;
|
||||
this._submodules = submodules;
|
||||
this.rebaseCommit = rebaseCommit;
|
||||
|
||||
const index: Resource[] = [];
|
||||
const workingTree: Resource[] = [];
|
||||
@@ -1089,6 +1126,17 @@ export class Repository implements Disposable {
|
||||
this._onDidChangeStatus.fire();
|
||||
}
|
||||
|
||||
private async getRebaseCommit(): Promise<Commit | undefined> {
|
||||
const rebaseHeadPath = path.join(this.repository.root, '.git', 'REBASE_HEAD');
|
||||
|
||||
try {
|
||||
const rebaseHead = await new Promise<string>((c, e) => fs.readFile(rebaseHeadPath, 'utf8', (err, result) => err ? e(err) : c(result)));
|
||||
return await this.getCommit(rebaseHead.trim());
|
||||
} catch (err) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private onFSChange(uri: Uri): void {
|
||||
const config = workspace.getConfiguration('git');
|
||||
const autorefresh = config.get<boolean>('autorefresh');
|
||||
|
||||
@@ -24,7 +24,8 @@ class CheckoutStatusBar {
|
||||
}
|
||||
|
||||
get command(): Command | undefined {
|
||||
const title = `$(git-branch) ${this.repository.headLabel}`;
|
||||
const rebasing = !!this.repository.rebaseCommit;
|
||||
const title = `$(git-branch) ${this.repository.headLabel}${rebasing ? ` (${localize('rebasing', 'Rebasing')})` : ''}`;
|
||||
|
||||
return {
|
||||
command: 'git.checkout',
|
||||
|
||||
@@ -263,9 +263,9 @@ vscode-extension-telemetry@0.0.17:
|
||||
dependencies:
|
||||
applicationinsights "1.0.1"
|
||||
|
||||
vscode-nls@^3.2.1:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.1.tgz#b1f3e04e8a94a715d5a7bcbc8339c51e6d74ca51"
|
||||
vscode-nls@^3.2.4:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.4.tgz#2166b4183c8aea884d20727f5449e62be69fd398"
|
||||
|
||||
which@^1.3.0:
|
||||
version "1.3.0"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "grunt",
|
||||
"publisher": "vscode",
|
||||
"description": "Extension to add Grunt capabilities to VSCode.",
|
||||
"displayName": "Grunt support for VSCode",
|
||||
"description": "Extension to add Grunt capabilities to VS Code.",
|
||||
"displayName": "Grunt support for VS Code",
|
||||
"version": "1.0.0",
|
||||
"icon": "images/grunt.png",
|
||||
"engines": {
|
||||
@@ -16,7 +16,7 @@
|
||||
"watch": "gulp watch-extension:grunt"
|
||||
},
|
||||
"dependencies": {
|
||||
"vscode-nls": "^3.2.1"
|
||||
"vscode-nls": "^3.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "7.0.43"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"description": "Extension to add Grunt capabilities to VSCode.",
|
||||
"displayName": "Grunt support for VSCode",
|
||||
"description": "Extension to add Grunt capabilities to VS Code.",
|
||||
"displayName": "Grunt support for VS Code",
|
||||
"config.grunt.autoDetect": "Controls whether auto detection of Grunt tasks is on or off. Default is on.",
|
||||
"grunt.taskDefinition.type.description": "The Grunt task to customize.",
|
||||
"grunt.taskDefinition.file.description": "The Grunt file that provides the task. Can be omitted."
|
||||
|
||||
@@ -6,6 +6,6 @@
|
||||
version "7.0.43"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.43.tgz#a187e08495a075f200ca946079c914e1a5fe962c"
|
||||
|
||||
vscode-nls@^3.2.1:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.1.tgz#b1f3e04e8a94a715d5a7bcbc8339c51e6d74ca51"
|
||||
vscode-nls@^3.2.4:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.4.tgz#2166b4183c8aea884d20727f5449e62be69fd398"
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
"watch": "gulp watch-extension:gulp"
|
||||
},
|
||||
"dependencies": {
|
||||
"vscode-nls": "^3.2.1"
|
||||
"vscode-nls": "^3.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "7.0.43"
|
||||
|
||||
@@ -6,6 +6,6 @@
|
||||
version "7.0.43"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.43.tgz#a187e08495a075f200ca946079c914e1a5fe962c"
|
||||
|
||||
vscode-nls@^3.2.1:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.1.tgz#b1f3e04e8a94a715d5a7bcbc8339c51e6d74ca51"
|
||||
vscode-nls@^3.2.4:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.4.tgz#2166b4183c8aea884d20727f5449e62be69fd398"
|
||||
|
||||
@@ -176,7 +176,7 @@
|
||||
"vscode-extension-telemetry": "0.0.17",
|
||||
"vscode-languageclient": "^4.1.4",
|
||||
"vscode-languageserver-protocol-foldingprovider": "^2.0.1",
|
||||
"vscode-nls": "^3.2.2"
|
||||
"vscode-nls": "^3.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "7.0.43"
|
||||
|
||||
@@ -8,12 +8,12 @@
|
||||
"node": "*"
|
||||
},
|
||||
"dependencies": {
|
||||
"vscode-css-languageservice": "^3.0.9-next.18",
|
||||
"vscode-css-languageservice": "^3.0.9-next.20",
|
||||
"vscode-html-languageservice": "^2.1.3-next.5",
|
||||
"vscode-languageserver": "^4.1.3",
|
||||
"vscode-languageserver-protocol-foldingprovider": "^2.0.1",
|
||||
"vscode-languageserver-types": "^3.7.2",
|
||||
"vscode-nls": "^3.2.2",
|
||||
"vscode-nls": "^3.2.4",
|
||||
"vscode-uri": "^1.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -194,9 +194,9 @@ supports-color@5.4.0:
|
||||
dependencies:
|
||||
has-flag "^3.0.0"
|
||||
|
||||
vscode-css-languageservice@^3.0.9-next.18:
|
||||
version "3.0.9-next.18"
|
||||
resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.9-next.18.tgz#f8f25123b5a8cdc9f72fafcd2c0088f726322437"
|
||||
vscode-css-languageservice@^3.0.9-next.20:
|
||||
version "3.0.9-next.20"
|
||||
resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.9-next.20.tgz#8229aee66aa877929af5d2fd81a21731b415c92e"
|
||||
dependencies:
|
||||
vscode-languageserver-types "^3.7.2"
|
||||
vscode-nls "^3.2.2"
|
||||
@@ -242,6 +242,10 @@ vscode-nls@^3.2.2:
|
||||
version "3.2.2"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.2.tgz#3817eca5b985c2393de325197cf4e15eb2aa5350"
|
||||
|
||||
vscode-nls@^3.2.4:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.4.tgz#2166b4183c8aea884d20727f5449e62be69fd398"
|
||||
|
||||
vscode-uri@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-1.0.1.tgz#11a86befeac3c4aa3ec08623651a3c81a6d0bbc8"
|
||||
|
||||
@@ -62,9 +62,9 @@ vscode-languageserver-types@^3.7.2:
|
||||
version "3.7.2"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.7.2.tgz#aad8846f8e3e27962648554de5a8417e358f34eb"
|
||||
|
||||
vscode-nls@^3.2.2:
|
||||
version "3.2.2"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.2.tgz#3817eca5b985c2393de325197cf4e15eb2aa5350"
|
||||
vscode-nls@^3.2.4:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.4.tgz#2166b4183c8aea884d20727f5449e62be69fd398"
|
||||
|
||||
zone.js@0.7.6:
|
||||
version "0.7.6"
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
"watch": "gulp watch-extension:jake"
|
||||
},
|
||||
"dependencies": {
|
||||
"vscode-nls": "^3.2.1"
|
||||
"vscode-nls": "^3.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "7.0.43"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"description": "Extension to add Jake capabilities to VSCode.",
|
||||
"displayName": "Jake support for VSCode",
|
||||
"description": "Extension to add Jake capabilities to VS Code.",
|
||||
"displayName": "Jake support for VS Code",
|
||||
"jake.taskDefinition.type.description": "The Jake task to customize.",
|
||||
"jake.taskDefinition.file.description": "The Jake file that provides the task. Can be omitted.",
|
||||
"config.jake.autoDetect": "Controls whether auto detection of Jake tasks is on or off. Default is on."
|
||||
|
||||
@@ -6,6 +6,6 @@
|
||||
version "7.0.43"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.43.tgz#a187e08495a075f200ca946079c914e1a5fe962c"
|
||||
|
||||
vscode-nls@^3.2.1:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.1.tgz#b1f3e04e8a94a715d5a7bcbc8339c51e6d74ca51"
|
||||
vscode-nls@^3.2.4:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.4.tgz#2166b4183c8aea884d20727f5449e62be69fd398"
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"If you want to provide a fix or improvement, please create a pull request against the original repository.",
|
||||
"Once accepted there, we are happy to receive an update request."
|
||||
],
|
||||
"version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/7bf8960f7042474b10b519f39339fc527907ce16",
|
||||
"version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/88217b1c7d36ed5d35adc3099ba7978aabe2531b",
|
||||
"name": "JavaScript (with React support)",
|
||||
"scopeName": "source.js",
|
||||
"patterns": [
|
||||
@@ -4115,7 +4115,7 @@
|
||||
},
|
||||
"directives": {
|
||||
"name": "comment.line.triple-slash.directive.js",
|
||||
"begin": "^(///)\\s*(?=<(reference|amd-dependency|amd-module)(\\s+(path|types|no-default-lib|name)\\s*=\\s*((\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")))+\\s*/>\\s*$)",
|
||||
"begin": "^(///)\\s*(?=<(reference|amd-dependency|amd-module)(\\s+(path|types|no-default-lib|lib|name)\\s*=\\s*((\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")))+\\s*/>\\s*$)",
|
||||
"beginCaptures": {
|
||||
"1": {
|
||||
"name": "punctuation.definition.comment.js"
|
||||
@@ -4143,7 +4143,7 @@
|
||||
"patterns": [
|
||||
{
|
||||
"name": "entity.other.attribute-name.directive.js",
|
||||
"match": "path|types|no-default-lib|name"
|
||||
"match": "path|types|no-default-lib|lib|name"
|
||||
},
|
||||
{
|
||||
"name": "keyword.operator.assignment.js",
|
||||
@@ -4664,8 +4664,8 @@
|
||||
]
|
||||
},
|
||||
"jsx-tag-in-expression": {
|
||||
"begin": "(?x)\n (?<!\\+\\+|--)(?<=[({\\[,?=>:*]|&&|\\|\\||\\?|^return|[^\\._$[:alnum:]]return|^default|[^\\._$[:alnum:]]default|^)\\s*\n (?!<\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s+[^=>])|,)) # look ahead is not type parameter of arrow\n (?=(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$a-zA-Z][-$\\w.]*))(?<!\\.|-))(?=((<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)|(\\s+))(?!\\?)|\\/?>))",
|
||||
"end": "(?!(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$a-zA-Z][-$\\w.]*))(?<!\\.|-))(?=((<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)|(\\s+))(?!\\?)|\\/?>))",
|
||||
"begin": "(?x)\n (?<!\\+\\+|--)(?<=[({\\[,?=>:*]|&&|\\|\\||\\?|^return|[^\\._$[:alnum:]]return|^default|[^\\._$[:alnum:]]default|^)\\s*\n (?!<\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s+[^=>])|,)) # look ahead is not type parameter of arrow\n (?=(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$a-zA-Z][-$\\w.]*))(?<!\\.|-))(?=((<\\s*)|(\\s+))(?!\\?)|\\/?>))",
|
||||
"end": "(?!(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$a-zA-Z][-$\\w.]*))(?<!\\.|-))(?=((<\\s*)|(\\s+))(?!\\?)|\\/?>))",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#jsx-tag"
|
||||
@@ -4674,7 +4674,7 @@
|
||||
},
|
||||
"jsx-tag": {
|
||||
"name": "meta.tag.js",
|
||||
"begin": "(?=(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$a-zA-Z][-$\\w.]*))(?<!\\.|-))(?=((<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)|(\\s+))(?!\\?)|\\/?>))",
|
||||
"begin": "(?=(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$a-zA-Z][-$\\w.]*))(?<!\\.|-))(?=((<\\s*)|(\\s+))(?!\\?)|\\/?>))",
|
||||
"end": "(/>)|(?:(</)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$a-zA-Z][-$\\w.]*))(?<!\\.|-))?\\s*(>))",
|
||||
"endCaptures": {
|
||||
"1": {
|
||||
@@ -4701,7 +4701,7 @@
|
||||
},
|
||||
"patterns": [
|
||||
{
|
||||
"begin": "(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$a-zA-Z][-$\\w.]*))(?<!\\.|-))(?=((<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)|(\\s+))(?!\\?)|\\/?>)",
|
||||
"begin": "(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$a-zA-Z][-$\\w.]*))(?<!\\.|-))(?=((<\\s*)|(\\s+))(?!\\?)|\\/?>)",
|
||||
"beginCaptures": {
|
||||
"1": {
|
||||
"name": "punctuation.definition.tag.begin.js"
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"If you want to provide a fix or improvement, please create a pull request against the original repository.",
|
||||
"Once accepted there, we are happy to receive an update request."
|
||||
],
|
||||
"version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/7bf8960f7042474b10b519f39339fc527907ce16",
|
||||
"version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/88217b1c7d36ed5d35adc3099ba7978aabe2531b",
|
||||
"name": "JavaScript (with React support)",
|
||||
"scopeName": "source.js.jsx",
|
||||
"patterns": [
|
||||
@@ -4115,7 +4115,7 @@
|
||||
},
|
||||
"directives": {
|
||||
"name": "comment.line.triple-slash.directive.js.jsx",
|
||||
"begin": "^(///)\\s*(?=<(reference|amd-dependency|amd-module)(\\s+(path|types|no-default-lib|name)\\s*=\\s*((\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")))+\\s*/>\\s*$)",
|
||||
"begin": "^(///)\\s*(?=<(reference|amd-dependency|amd-module)(\\s+(path|types|no-default-lib|lib|name)\\s*=\\s*((\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")))+\\s*/>\\s*$)",
|
||||
"beginCaptures": {
|
||||
"1": {
|
||||
"name": "punctuation.definition.comment.js.jsx"
|
||||
@@ -4143,7 +4143,7 @@
|
||||
"patterns": [
|
||||
{
|
||||
"name": "entity.other.attribute-name.directive.js.jsx",
|
||||
"match": "path|types|no-default-lib|name"
|
||||
"match": "path|types|no-default-lib|lib|name"
|
||||
},
|
||||
{
|
||||
"name": "keyword.operator.assignment.js.jsx",
|
||||
@@ -4664,8 +4664,8 @@
|
||||
]
|
||||
},
|
||||
"jsx-tag-in-expression": {
|
||||
"begin": "(?x)\n (?<!\\+\\+|--)(?<=[({\\[,?=>:*]|&&|\\|\\||\\?|^return|[^\\._$[:alnum:]]return|^default|[^\\._$[:alnum:]]default|^)\\s*\n (?!<\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s+[^=>])|,)) # look ahead is not type parameter of arrow\n (?=(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$a-zA-Z][-$\\w.]*))(?<!\\.|-))(?=((<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)|(\\s+))(?!\\?)|\\/?>))",
|
||||
"end": "(?!(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$a-zA-Z][-$\\w.]*))(?<!\\.|-))(?=((<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)|(\\s+))(?!\\?)|\\/?>))",
|
||||
"begin": "(?x)\n (?<!\\+\\+|--)(?<=[({\\[,?=>:*]|&&|\\|\\||\\?|^return|[^\\._$[:alnum:]]return|^default|[^\\._$[:alnum:]]default|^)\\s*\n (?!<\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s+[^=>])|,)) # look ahead is not type parameter of arrow\n (?=(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$a-zA-Z][-$\\w.]*))(?<!\\.|-))(?=((<\\s*)|(\\s+))(?!\\?)|\\/?>))",
|
||||
"end": "(?!(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$a-zA-Z][-$\\w.]*))(?<!\\.|-))(?=((<\\s*)|(\\s+))(?!\\?)|\\/?>))",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#jsx-tag"
|
||||
@@ -4674,7 +4674,7 @@
|
||||
},
|
||||
"jsx-tag": {
|
||||
"name": "meta.tag.js.jsx",
|
||||
"begin": "(?=(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$a-zA-Z][-$\\w.]*))(?<!\\.|-))(?=((<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)|(\\s+))(?!\\?)|\\/?>))",
|
||||
"begin": "(?=(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$a-zA-Z][-$\\w.]*))(?<!\\.|-))(?=((<\\s*)|(\\s+))(?!\\?)|\\/?>))",
|
||||
"end": "(/>)|(?:(</)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$a-zA-Z][-$\\w.]*))(?<!\\.|-))?\\s*(>))",
|
||||
"endCaptures": {
|
||||
"1": {
|
||||
@@ -4701,7 +4701,7 @@
|
||||
},
|
||||
"patterns": [
|
||||
{
|
||||
"begin": "(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$a-zA-Z][-$\\w.]*))(?<!\\.|-))(?=((<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)|(\\s+))(?!\\?)|\\/?>)",
|
||||
"begin": "(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$a-zA-Z][-$\\w.]*))(?<!\\.|-))(?=((<\\s*)|(\\s+))(?!\\?)|\\/?>)",
|
||||
"beginCaptures": {
|
||||
"1": {
|
||||
"name": "punctuation.definition.tag.begin.js.jsx"
|
||||
|
||||
@@ -251,11 +251,22 @@ function getSettings(): Settings {
|
||||
settings.json!.schemas!.push(schemaSetting);
|
||||
}
|
||||
let fileMatches = setting.fileMatch;
|
||||
let resultingFileMatches = schemaSetting.fileMatch!;
|
||||
if (Array.isArray(fileMatches)) {
|
||||
if (fileMatchPrefix) {
|
||||
fileMatches = fileMatches.map(m => fileMatchPrefix + m);
|
||||
for (let fileMatch of fileMatches) {
|
||||
if (fileMatch[0] === '/') {
|
||||
resultingFileMatches.push(fileMatchPrefix + fileMatch);
|
||||
resultingFileMatches.push(fileMatchPrefix + '/*' + fileMatch);
|
||||
} else {
|
||||
resultingFileMatches.push(fileMatchPrefix + '/' + fileMatch);
|
||||
resultingFileMatches.push(fileMatchPrefix + '/*/' + fileMatch);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
resultingFileMatches.push(...fileMatches);
|
||||
}
|
||||
schemaSetting.fileMatch!.push(...fileMatches);
|
||||
|
||||
}
|
||||
if (setting.schema) {
|
||||
schemaSetting.schema = setting.schema;
|
||||
@@ -276,10 +287,10 @@ function getSettings(): Settings {
|
||||
let folderSchemas = schemaConfigInfo!.workspaceFolderValue;
|
||||
if (Array.isArray(folderSchemas)) {
|
||||
let folderPath = folderUri.toString();
|
||||
if (folderPath[folderPath.length - 1] !== '/') {
|
||||
folderPath = folderPath + '/';
|
||||
if (folderPath[folderPath.length - 1] === '/') {
|
||||
folderPath = folderPath.substr(0, folderPath.length - 1);
|
||||
}
|
||||
collectSchemaSettings(folderSchemas, folderUri.fsPath, folderPath + '*');
|
||||
collectSchemaSettings(folderSchemas, folderUri.fsPath, folderPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@
|
||||
"vscode-extension-telemetry": "0.0.17",
|
||||
"vscode-languageclient": "^4.1.4",
|
||||
"vscode-languageserver-protocol-foldingprovider": "^2.0.1",
|
||||
"vscode-nls": "^3.2.2"
|
||||
"vscode-nls": "^3.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "7.0.43"
|
||||
|
||||
@@ -13,10 +13,10 @@
|
||||
"dependencies": {
|
||||
"jsonc-parser": "^2.0.0-next.1",
|
||||
"request-light": "^0.2.2",
|
||||
"vscode-json-languageservice": "^3.1.2-next.3",
|
||||
"vscode-json-languageservice": "^3.1.2",
|
||||
"vscode-languageserver": "^4.1.3",
|
||||
"vscode-languageserver-protocol-foldingprovider": "^2.0.1",
|
||||
"vscode-nls": "^3.2.2",
|
||||
"vscode-nls": "^3.2.4",
|
||||
"vscode-uri": "^1.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -52,14 +52,14 @@ https-proxy-agent@2.1.1:
|
||||
agent-base "^4.1.0"
|
||||
debug "^3.1.0"
|
||||
|
||||
jsonc-parser@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.0.0.tgz#62ff087a7e753875febf3c55f1fc0cd737c36b5a"
|
||||
|
||||
jsonc-parser@^2.0.0-next.1:
|
||||
version "2.0.0-next.1"
|
||||
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.0.0-next.1.tgz#445a824f765a96abfbb286d759a9b1d226b18088"
|
||||
|
||||
jsonc-parser@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.0.1.tgz#9d23cd2709714fff508a1a6679d82135bee1ae60"
|
||||
|
||||
ms@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||
@@ -72,11 +72,11 @@ request-light@^0.2.2:
|
||||
https-proxy-agent "2.1.1"
|
||||
vscode-nls "^2.0.2"
|
||||
|
||||
vscode-json-languageservice@^3.1.2-next.3:
|
||||
version "3.1.2-next.3"
|
||||
resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.1.2-next.3.tgz#cc0902148f898b413987fb1b4c4a9e7fc1a79c78"
|
||||
vscode-json-languageservice@^3.1.2:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.1.2.tgz#5c70fc32ad389e6da48452e7b0187ea5e70f68bf"
|
||||
dependencies:
|
||||
jsonc-parser "^2.0.0"
|
||||
jsonc-parser "^2.0.1"
|
||||
vscode-languageserver-types "^3.7.2"
|
||||
vscode-nls "^3.2.2"
|
||||
vscode-uri "^1.0.3"
|
||||
@@ -118,6 +118,10 @@ vscode-nls@^3.2.2:
|
||||
version "3.2.2"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.2.tgz#3817eca5b985c2393de325197cf4e15eb2aa5350"
|
||||
|
||||
vscode-nls@^3.2.4:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.4.tgz#2166b4183c8aea884d20727f5449e62be69fd398"
|
||||
|
||||
vscode-uri@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-1.0.1.tgz#11a86befeac3c4aa3ec08623651a3c81a6d0bbc8"
|
||||
|
||||
@@ -62,9 +62,9 @@ vscode-languageserver-types@^3.7.2:
|
||||
version "3.7.2"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.7.2.tgz#aad8846f8e3e27962648554de5a8417e358f34eb"
|
||||
|
||||
vscode-nls@^3.2.2:
|
||||
version "3.2.2"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.2.tgz#3817eca5b985c2393de325197cf4e15eb2aa5350"
|
||||
vscode-nls@^3.2.4:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.4.tgz#2166b4183c8aea884d20727f5449e62be69fd398"
|
||||
|
||||
zone.js@0.7.6:
|
||||
version "0.7.6"
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
var updateGrammar = require('../../../build/npm/update-grammar');
|
||||
|
||||
function adaptJSON(grammar, replacementScope) {
|
||||
grammar.name = 'JSON with comments';
|
||||
grammar.scopeName = `source${replacementScope}`;
|
||||
|
||||
var fixScopeNames = function(rule) {
|
||||
if (typeof rule.name === 'string') {
|
||||
rule.name = rule.name.replace(/\.json/g, replacementScope);
|
||||
}
|
||||
if (typeof rule.contentName === 'string') {
|
||||
rule.contentName = rule.contentName.replace(/\.json/g, replacementScope);
|
||||
}
|
||||
for (var property in rule) {
|
||||
var value = rule[property];
|
||||
if (typeof value === 'object') {
|
||||
fixScopeNames(value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var repository = grammar.repository;
|
||||
for (var key in repository) {
|
||||
fixScopeNames(repository[key]);
|
||||
}
|
||||
}
|
||||
|
||||
var tsGrammarRepo = 'Microsoft/vscode-JSON.tmLanguage';
|
||||
updateGrammar.update(tsGrammarRepo, 'JSON.tmLanguage', './syntaxes/JSON.tmLanguage.json');
|
||||
updateGrammar.update(tsGrammarRepo, 'JSON.tmLanguage', './syntaxes/JSONC.tmLanguage.json', grammar => adaptJSON(grammar, '.json.comments'));
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"vscode": "0.10.x"
|
||||
},
|
||||
"scripts": {
|
||||
"update-grammar": "node ../../build/npm/update-grammar.js Microsoft/vscode-JSON.tmLanguage JSON.tmLanguage ./syntaxes/JSON.tmLanguage.json"
|
||||
"update-grammar": "node ./build/update-grammars.js"
|
||||
},
|
||||
"contributes": {
|
||||
"languages": [
|
||||
@@ -58,8 +58,8 @@
|
||||
},
|
||||
{
|
||||
"language": "jsonc",
|
||||
"scopeName": "source.json",
|
||||
"path": "./syntaxes/JSON.tmLanguage.json"
|
||||
"scopeName": "source.json.comments",
|
||||
"path": "./syntaxes/JSONC.tmLanguage.json"
|
||||
}
|
||||
],
|
||||
"jsonValidation": [
|
||||
|
||||
@@ -0,0 +1,213 @@
|
||||
{
|
||||
"information_for_contributors": [
|
||||
"This file has been converted from https://github.com/Microsoft/vscode-JSON.tmLanguage/blob/master/JSON.tmLanguage",
|
||||
"If you want to provide a fix or improvement, please create a pull request against the original repository.",
|
||||
"Once accepted there, we are happy to receive an update request."
|
||||
],
|
||||
"version": "https://github.com/Microsoft/vscode-JSON.tmLanguage/commit/9bd83f1c252b375e957203f21793316203f61f70",
|
||||
"name": "JSON with comments",
|
||||
"scopeName": "source.json.comments",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#value"
|
||||
}
|
||||
],
|
||||
"repository": {
|
||||
"array": {
|
||||
"begin": "\\[",
|
||||
"beginCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.definition.array.begin.json.comments"
|
||||
}
|
||||
},
|
||||
"end": "\\]",
|
||||
"endCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.definition.array.end.json.comments"
|
||||
}
|
||||
},
|
||||
"name": "meta.structure.array.json.comments",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#value"
|
||||
},
|
||||
{
|
||||
"match": ",",
|
||||
"name": "punctuation.separator.array.json.comments"
|
||||
},
|
||||
{
|
||||
"match": "[^\\s\\]]",
|
||||
"name": "invalid.illegal.expected-array-separator.json.comments"
|
||||
}
|
||||
]
|
||||
},
|
||||
"comments": {
|
||||
"patterns": [
|
||||
{
|
||||
"begin": "/\\*\\*(?!/)",
|
||||
"captures": {
|
||||
"0": {
|
||||
"name": "punctuation.definition.comment.json.comments"
|
||||
}
|
||||
},
|
||||
"end": "\\*/",
|
||||
"name": "comment.block.documentation.json.comments"
|
||||
},
|
||||
{
|
||||
"begin": "/\\*",
|
||||
"captures": {
|
||||
"0": {
|
||||
"name": "punctuation.definition.comment.json.comments"
|
||||
}
|
||||
},
|
||||
"end": "\\*/",
|
||||
"name": "comment.block.json.comments"
|
||||
},
|
||||
{
|
||||
"captures": {
|
||||
"1": {
|
||||
"name": "punctuation.definition.comment.json.comments"
|
||||
}
|
||||
},
|
||||
"match": "(//).*$\\n?",
|
||||
"name": "comment.line.double-slash.js"
|
||||
}
|
||||
]
|
||||
},
|
||||
"constant": {
|
||||
"match": "\\b(?:true|false|null)\\b",
|
||||
"name": "constant.language.json.comments"
|
||||
},
|
||||
"number": {
|
||||
"match": "(?x) # turn on extended mode\n -? # an optional minus\n (?:\n 0 # a zero\n | # ...or...\n [1-9] # a 1-9 character\n \\d* # followed by zero or more digits\n )\n (?:\n (?:\n \\. # a period\n \\d+ # followed by one or more digits\n )?\n (?:\n [eE] # an e character\n [+-]? # followed by an option +/-\n \\d+ # followed by one or more digits\n )? # make exponent optional\n )? # make decimal portion optional",
|
||||
"name": "constant.numeric.json.comments"
|
||||
},
|
||||
"object": {
|
||||
"begin": "\\{",
|
||||
"beginCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.definition.dictionary.begin.json.comments"
|
||||
}
|
||||
},
|
||||
"end": "\\}",
|
||||
"endCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.definition.dictionary.end.json.comments"
|
||||
}
|
||||
},
|
||||
"name": "meta.structure.dictionary.json.comments",
|
||||
"patterns": [
|
||||
{
|
||||
"comment": "the JSON object key",
|
||||
"include": "#objectkey"
|
||||
},
|
||||
{
|
||||
"include": "#comments"
|
||||
},
|
||||
{
|
||||
"begin": ":",
|
||||
"beginCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.separator.dictionary.key-value.json.comments"
|
||||
}
|
||||
},
|
||||
"end": "(,)|(?=\\})",
|
||||
"endCaptures": {
|
||||
"1": {
|
||||
"name": "punctuation.separator.dictionary.pair.json.comments"
|
||||
}
|
||||
},
|
||||
"name": "meta.structure.dictionary.value.json.comments",
|
||||
"patterns": [
|
||||
{
|
||||
"comment": "the JSON object value",
|
||||
"include": "#value"
|
||||
},
|
||||
{
|
||||
"match": "[^\\s,]",
|
||||
"name": "invalid.illegal.expected-dictionary-separator.json.comments"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"match": "[^\\s\\}]",
|
||||
"name": "invalid.illegal.expected-dictionary-separator.json.comments"
|
||||
}
|
||||
]
|
||||
},
|
||||
"string": {
|
||||
"begin": "\"",
|
||||
"beginCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.definition.string.begin.json.comments"
|
||||
}
|
||||
},
|
||||
"end": "\"",
|
||||
"endCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.definition.string.end.json.comments"
|
||||
}
|
||||
},
|
||||
"name": "string.quoted.double.json.comments",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#stringcontent"
|
||||
}
|
||||
]
|
||||
},
|
||||
"objectkey": {
|
||||
"begin": "\"",
|
||||
"beginCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.support.type.property-name.begin.json.comments"
|
||||
}
|
||||
},
|
||||
"end": "\"",
|
||||
"endCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.support.type.property-name.end.json.comments"
|
||||
}
|
||||
},
|
||||
"name": "string.json.comments support.type.property-name.json.comments",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#stringcontent"
|
||||
}
|
||||
]
|
||||
},
|
||||
"stringcontent": {
|
||||
"patterns": [
|
||||
{
|
||||
"match": "(?x) # turn on extended mode\n \\\\ # a literal backslash\n (?: # ...followed by...\n [\"\\\\/bfnrt] # one of these characters\n | # ...or...\n u # a u\n [0-9a-fA-F]{4}) # and four hex digits",
|
||||
"name": "constant.character.escape.json.comments"
|
||||
},
|
||||
{
|
||||
"match": "\\\\.",
|
||||
"name": "invalid.illegal.unrecognized-string-escape.json.comments"
|
||||
}
|
||||
]
|
||||
},
|
||||
"value": {
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#constant"
|
||||
},
|
||||
{
|
||||
"include": "#number"
|
||||
},
|
||||
{
|
||||
"include": "#string"
|
||||
},
|
||||
{
|
||||
"include": "#array"
|
||||
},
|
||||
{
|
||||
"include": "#object"
|
||||
},
|
||||
{
|
||||
"include": "#comments"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -120,6 +120,11 @@ Visual Studio-like style based on original C# coloring by Jason Diamond <jason@d
|
||||
|
||||
*/
|
||||
|
||||
.vscode-light .hljs-function,
|
||||
.vscode-light .hljs-params {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.vscode-light .hljs-comment,
|
||||
.vscode-light .hljs-quote,
|
||||
.vscode-light .hljs-variable {
|
||||
|
||||
@@ -196,18 +196,15 @@ pre code {
|
||||
}
|
||||
|
||||
|
||||
.vscode-light pre:not(.hljs),
|
||||
.vscode-light code > div {
|
||||
.vscode-light pre {
|
||||
background-color: rgba(220, 220, 220, 0.4);
|
||||
}
|
||||
|
||||
.vscode-dark pre:not(.hljs),
|
||||
.vscode-dark code > div {
|
||||
.vscode-dark pre {
|
||||
background-color: rgba(10, 10, 10, 0.4);
|
||||
}
|
||||
|
||||
.vscode-high-contrast pre:not(.hljs),
|
||||
.vscode-high-contrast code > div {
|
||||
.vscode-high-contrast pre {
|
||||
background-color: rgb(0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -296,7 +296,7 @@
|
||||
"markdown-it": "^8.4.1",
|
||||
"markdown-it-named-headers": "0.0.4",
|
||||
"vscode-extension-telemetry": "0.0.17",
|
||||
"vscode-nls": "^3.2.1"
|
||||
"vscode-nls": "^3.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/highlight.js": "9.1.10",
|
||||
|
||||
@@ -5,8 +5,13 @@
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { MarkdownEngine } from '../markdownEngine';
|
||||
import { TableOfContentsProvider, SkinnyTextDocument } from '../tableOfContentsProvider';
|
||||
import { TableOfContentsProvider, SkinnyTextDocument, TocEntry } from '../tableOfContentsProvider';
|
||||
|
||||
interface MarkdownSymbol {
|
||||
readonly level: number;
|
||||
readonly parent: MarkdownSymbol | undefined;
|
||||
readonly children: vscode.DocumentSymbol[];
|
||||
}
|
||||
|
||||
export default class MDDocumentSymbolProvider implements vscode.DocumentSymbolProvider {
|
||||
|
||||
@@ -14,10 +19,57 @@ export default class MDDocumentSymbolProvider implements vscode.DocumentSymbolPr
|
||||
private readonly engine: MarkdownEngine
|
||||
) { }
|
||||
|
||||
public async provideDocumentSymbols(document: SkinnyTextDocument): Promise<vscode.SymbolInformation[]> {
|
||||
public async provideDocumentSymbolInformation(document: SkinnyTextDocument): Promise<vscode.SymbolInformation[]> {
|
||||
const toc = await new TableOfContentsProvider(this.engine, document).getToc();
|
||||
return toc.map(entry => {
|
||||
return new vscode.SymbolInformation('#'.repeat(entry.level) + ' ' + entry.text, vscode.SymbolKind.String, '', entry.location);
|
||||
});
|
||||
return toc.map(entry => this.toSymbolInformation(entry));
|
||||
}
|
||||
|
||||
public async provideDocumentSymbols(document: SkinnyTextDocument): Promise<vscode.DocumentSymbol[]> {
|
||||
const toc = await new TableOfContentsProvider(this.engine, document).getToc();
|
||||
const root: MarkdownSymbol = {
|
||||
level: -Infinity,
|
||||
children: [],
|
||||
parent: undefined
|
||||
};
|
||||
this.buildTree(root, toc);
|
||||
return root.children;
|
||||
}
|
||||
|
||||
private buildTree(parent: MarkdownSymbol, entries: TocEntry[]) {
|
||||
if (!entries.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const entry = entries[0];
|
||||
const symbol = this.toDocumentSymbol(entry);
|
||||
symbol.children = [];
|
||||
|
||||
while (parent && entry.level <= parent.level) {
|
||||
parent = parent.parent!;
|
||||
}
|
||||
parent.children.push(symbol);
|
||||
this.buildTree({ level: entry.level, children: symbol.children, parent }, entries.slice(1));
|
||||
}
|
||||
|
||||
|
||||
private toSymbolInformation(entry: TocEntry): vscode.SymbolInformation {
|
||||
return new vscode.SymbolInformation(
|
||||
this.getSymbolName(entry),
|
||||
vscode.SymbolKind.String,
|
||||
'',
|
||||
entry.location);
|
||||
}
|
||||
|
||||
private toDocumentSymbol(entry: TocEntry) {
|
||||
return new vscode.DocumentSymbol(
|
||||
this.getSymbolName(entry),
|
||||
'',
|
||||
vscode.SymbolKind.String,
|
||||
entry.location.range,
|
||||
entry.location.range);
|
||||
}
|
||||
|
||||
private getSymbolName(entry: TocEntry): string {
|
||||
return '#'.repeat(entry.level) + ' ' + entry.text;
|
||||
}
|
||||
}
|
||||
@@ -136,7 +136,7 @@ export default class MarkdownWorkspaceSymbolProvider implements vscode.Workspace
|
||||
|
||||
private getSymbols(document: SkinnyTextDocument): Lazy<Thenable<vscode.SymbolInformation[]>> {
|
||||
return lazy(async () => {
|
||||
return this._symbolProvider.provideDocumentSymbols(document);
|
||||
return this._symbolProvider.provideDocumentSymbolInformation(document);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import 'mocha';
|
||||
import * as vscode from 'vscode';
|
||||
import SymbolProvider from '../features/documentSymbolProvider';
|
||||
import { InMemoryDocument } from './inMemoryDocument';
|
||||
import { createNewMarkdownEngine } from './engine';
|
||||
|
||||
|
||||
const testFileName = vscode.Uri.parse('test.md');
|
||||
|
||||
|
||||
function getSymbolsForFile(fileContents: string) {
|
||||
const doc = new InMemoryDocument(testFileName, fileContents);
|
||||
const provider = new SymbolProvider(createNewMarkdownEngine());
|
||||
return provider.provideDocumentSymbols(doc);
|
||||
}
|
||||
|
||||
|
||||
suite('markdown.DocumentSymbolProvider', () => {
|
||||
test('Should not return anything for empty document', async () => {
|
||||
const symbols = await getSymbolsForFile('');
|
||||
assert.strictEqual(symbols.length, 0);
|
||||
});
|
||||
|
||||
test('Should not return anything for document with no headers', async () => {
|
||||
const symbols = await getSymbolsForFile('a\na');
|
||||
assert.strictEqual(symbols.length, 0);
|
||||
});
|
||||
|
||||
test('Should not return anything for document with # but no real headers', async () => {
|
||||
const symbols = await getSymbolsForFile('a#a\na#');
|
||||
assert.strictEqual(symbols.length, 0);
|
||||
});
|
||||
|
||||
test('Should return single symbol for single header', async () => {
|
||||
const symbols = await getSymbolsForFile('# h');
|
||||
assert.strictEqual(symbols.length, 1);
|
||||
assert.strictEqual(symbols[0].name, '# h');
|
||||
});
|
||||
|
||||
test('Should not care about symbol level for single header', async () => {
|
||||
const symbols = await getSymbolsForFile('### h');
|
||||
assert.strictEqual(symbols.length, 1);
|
||||
assert.strictEqual(symbols[0].name, '### h');
|
||||
});
|
||||
|
||||
test('Should put symbols of same level in flat list', async () => {
|
||||
const symbols = await getSymbolsForFile('## h\n## h2');
|
||||
assert.strictEqual(symbols.length, 2);
|
||||
assert.strictEqual(symbols[0].name, '## h');
|
||||
assert.strictEqual(symbols[1].name, '## h2');
|
||||
});
|
||||
|
||||
test('Should nest symbol of level - 1 under parent', async () => {
|
||||
|
||||
const symbols = await getSymbolsForFile('# h\n## h2\n## h3');
|
||||
assert.strictEqual(symbols.length, 1);
|
||||
assert.strictEqual(symbols[0].name, '# h');
|
||||
assert.strictEqual(symbols[0].children.length, 2);
|
||||
assert.strictEqual(symbols[0].children[0].name, '## h2');
|
||||
assert.strictEqual(symbols[0].children[1].name, '## h3');
|
||||
});
|
||||
|
||||
test('Should nest symbol of level - n under parent', async () => {
|
||||
const symbols = await getSymbolsForFile('# h\n#### h2');
|
||||
assert.strictEqual(symbols.length, 1);
|
||||
assert.strictEqual(symbols[0].name, '# h');
|
||||
assert.strictEqual(symbols[0].children.length, 1);
|
||||
assert.strictEqual(symbols[0].children[0].name, '#### h2');
|
||||
});
|
||||
|
||||
test('Should flatten children where lower level occurs first', async () => {
|
||||
const symbols = await getSymbolsForFile('# h\n### h2\n## h3');
|
||||
assert.strictEqual(symbols.length, 1);
|
||||
assert.strictEqual(symbols[0].name, '# h');
|
||||
assert.strictEqual(symbols[0].children.length, 2);
|
||||
assert.strictEqual(symbols[0].children[0].name, '### h2');
|
||||
assert.strictEqual(symbols[0].children[1].name, '## h3');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5462,9 +5462,9 @@ vscode-extension-telemetry@0.0.17:
|
||||
dependencies:
|
||||
applicationinsights "1.0.1"
|
||||
|
||||
vscode-nls@^3.2.1:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.1.tgz#b1f3e04e8a94a715d5a7bcbc8339c51e6d74ca51"
|
||||
vscode-nls@^3.2.4:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.4.tgz#2166b4183c8aea884d20727f5449e62be69fd398"
|
||||
|
||||
vscode@^1.1.10:
|
||||
version "1.1.10"
|
||||
|
||||
@@ -100,7 +100,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"vscode-nls": "^3.2.1"
|
||||
"vscode-nls": "^3.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "8.0.33"
|
||||
|
||||
@@ -6,6 +6,6 @@
|
||||
version "8.0.33"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.33.tgz#1126e94374014e54478092830704f6ea89df04cd"
|
||||
|
||||
vscode-nls@^3.2.1:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.1.tgz#b1f3e04e8a94a715d5a7bcbc8339c51e6d74ca51"
|
||||
vscode-nls@^3.2.4:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.4.tgz#2166b4183c8aea884d20727f5449e62be69fd398"
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"jsonc-parser": "^1.0.0",
|
||||
"minimatch": "^3.0.4",
|
||||
"request-light": "^0.2.2",
|
||||
"vscode-nls": "^3.2.1"
|
||||
"vscode-nls": "^3.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/minimatch": "^3.0.3",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"description": "Extension to add task support for npm scripts.",
|
||||
"displayName": "Npm support for VSCode",
|
||||
"displayName": "Npm support for VS Code",
|
||||
"config.npm.autoDetect": "Controls whether auto detection of npm scripts is on or off. Default is on.",
|
||||
"config.npm.runSilent": "Run npm commands with the `--silent` option.",
|
||||
"config.npm.packageManager": "The package manager used to run scripts.",
|
||||
|
||||
@@ -164,10 +164,14 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider<TreeItem> {
|
||||
private extractDebugArg(scripts: any, task: Task): [string, number] | undefined {
|
||||
let script: string = scripts[task.name];
|
||||
|
||||
let match = script.match(/--(inspect|debug)(-brk)?(=(\d*))?/);
|
||||
// matches --debug, --debug=1234, --debug-brk, debug-brk=1234, --inspect,
|
||||
// --inspect=1234, --inspect-brk, --inspect-brk=1234,
|
||||
// --inspect=localhost:1245, --inspect=127.0.0.1:1234, --inspect=[aa:1:0:0:0]:1234, --inspect=:1234
|
||||
let match = script.match(/--(inspect|debug)(-brk)?(=((\[[0-9a-fA-F:]*\]|[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+|[a-zA-Z0-9\.]*):)?(\d+))?/);
|
||||
|
||||
if (match) {
|
||||
if (match[4]) {
|
||||
return [match[1], parseInt(match[4])];
|
||||
if (match[6]) {
|
||||
return [match[1], parseInt(match[6])];
|
||||
}
|
||||
if (match[1] === 'inspect') {
|
||||
return [match[1], 9229];
|
||||
|
||||
@@ -93,6 +93,6 @@ vscode-nls@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-2.0.2.tgz#808522380844b8ad153499af5c3b03921aea02da"
|
||||
|
||||
vscode-nls@^3.2.1:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.1.tgz#b1f3e04e8a94a715d5a7bcbc8339c51e6d74ca51"
|
||||
vscode-nls@^3.2.4:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.4.tgz#2166b4183c8aea884d20727f5449e62be69fd398"
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
"watch": "gulp watch-extension:php"
|
||||
},
|
||||
"dependencies": {
|
||||
"vscode-nls": "^3.2.1"
|
||||
"vscode-nls": "^3.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "7.0.43"
|
||||
|
||||
@@ -6,6 +6,6 @@
|
||||
version "7.0.43"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.43.tgz#a187e08495a075f200ca946079c914e1a5fe962c"
|
||||
|
||||
vscode-nls@^3.2.1:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.1.tgz#b1f3e04e8a94a715d5a7bcbc8339c51e6d74ca51"
|
||||
vscode-nls@^3.2.4:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.4.tgz#2166b4183c8aea884d20727f5449e62be69fd398"
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"categories": [],
|
||||
"dependencies": {
|
||||
"vscode-extension-telemetry": "0.0.15",
|
||||
"vscode-nls": "^3.2.1",
|
||||
"vscode-nls": "^3.2.4",
|
||||
"vscode-ripgrep": "^1.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
import * as arrays from './common/arrays';
|
||||
import { compareItemsByScore, IItemAccessor, prepareQuery, ScorerCache } from './common/fileSearchScorer';
|
||||
import * as strings from './common/strings';
|
||||
import { joinPath } from './utils';
|
||||
|
||||
interface IProviderArgs {
|
||||
query: vscode.FileSearchQuery;
|
||||
options: vscode.FileSearchOptions;
|
||||
progress: vscode.Progress<vscode.Uri>;
|
||||
token: vscode.CancellationToken;
|
||||
}
|
||||
|
||||
export interface IInternalFileSearchProvider {
|
||||
provideFileSearchResults(options: vscode.FileSearchOptions, progress: vscode.Progress<string>, token: vscode.CancellationToken): Thenable<void>;
|
||||
}
|
||||
|
||||
export class CachedSearchProvider {
|
||||
|
||||
private static readonly BATCH_SIZE = 512;
|
||||
|
||||
private caches: { [cacheKey: string]: Cache; } = Object.create(null);
|
||||
|
||||
provideFileSearchResults(provider: IInternalFileSearchProvider, query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, progress: vscode.Progress<vscode.Uri>, token: vscode.CancellationToken): Thenable<void> {
|
||||
const onResult = (result: IInternalFileMatch) => {
|
||||
progress.report(joinPath(options.folder, result.relativePath));
|
||||
};
|
||||
|
||||
const providerArgs: IProviderArgs = {
|
||||
query, options, progress, token
|
||||
};
|
||||
|
||||
let sortedSearch = this.trySortedSearchFromCache(providerArgs, onResult);
|
||||
if (!sortedSearch) {
|
||||
const engineOpts = options.maxResults ?
|
||||
{
|
||||
...options,
|
||||
...{ maxResults: 1e9 }
|
||||
} :
|
||||
options;
|
||||
providerArgs.options = engineOpts;
|
||||
|
||||
sortedSearch = this.doSortedSearch(providerArgs, provider);
|
||||
}
|
||||
|
||||
return sortedSearch.then(rawMatches => {
|
||||
rawMatches.forEach(onResult);
|
||||
});
|
||||
}
|
||||
|
||||
private doSortedSearch(args: IProviderArgs, provider: IInternalFileSearchProvider): Promise<IInternalFileMatch[]> {
|
||||
const allResultsPromise = new Promise<IInternalFileMatch[]>((c, e) => {
|
||||
const results: IInternalFileMatch[] = [];
|
||||
const onResult = (progress: IInternalFileMatch[]) => results.push(...progress);
|
||||
|
||||
// TODO@roblou set maxResult = null
|
||||
this.doSearch(args, provider, onResult, CachedSearchProvider.BATCH_SIZE)
|
||||
.then(() => c(results), e);
|
||||
});
|
||||
|
||||
let cache: Cache;
|
||||
if (args.query.cacheKey) {
|
||||
cache = this.getOrCreateCache(args.query.cacheKey); // TODO include folder in cache key
|
||||
cache.resultsToSearchCache[args.query.pattern] = { finished: allResultsPromise };
|
||||
allResultsPromise.then(null, err => {
|
||||
delete cache.resultsToSearchCache[args.query.pattern];
|
||||
});
|
||||
}
|
||||
|
||||
return allResultsPromise.then(results => {
|
||||
const scorerCache: ScorerCache = cache ? cache.scorerCache : Object.create(null);
|
||||
return this.sortResults(args, results, scorerCache);
|
||||
});
|
||||
}
|
||||
|
||||
private getOrCreateCache(cacheKey: string): Cache {
|
||||
const existing = this.caches[cacheKey];
|
||||
if (existing) {
|
||||
return existing;
|
||||
}
|
||||
return this.caches[cacheKey] = new Cache();
|
||||
}
|
||||
|
||||
private trySortedSearchFromCache(args: IProviderArgs, onResult: (result: IInternalFileMatch) => void): Promise<IInternalFileMatch[]> {
|
||||
const cache = args.query.cacheKey && this.caches[args.query.cacheKey];
|
||||
if (!cache) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const cached = this.getResultsFromCache(cache, args.query.pattern, onResult);
|
||||
if (cached) {
|
||||
return cached.then((results) => this.sortResults(args, results, cache.scorerCache));
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private sortResults(args: IProviderArgs, results: IInternalFileMatch[], scorerCache: ScorerCache): Promise<IInternalFileMatch[]> {
|
||||
// we use the same compare function that is used later when showing the results using fuzzy scoring
|
||||
// this is very important because we are also limiting the number of results by config.maxResults
|
||||
// and as such we want the top items to be included in this result set if the number of items
|
||||
// exceeds config.maxResults.
|
||||
const preparedQuery = prepareQuery(args.query.pattern);
|
||||
const compare = (matchA: IInternalFileMatch, matchB: IInternalFileMatch) => compareItemsByScore(matchA, matchB, preparedQuery, true, FileMatchItemAccessor, scorerCache);
|
||||
|
||||
return arrays.topAsync(results, compare, args.options.maxResults || 0, 10000);
|
||||
}
|
||||
|
||||
private getResultsFromCache(cache: Cache, searchValue: string, onResult: (results: IInternalFileMatch) => void): Promise<IInternalFileMatch[]> {
|
||||
// Find cache entries by prefix of search value
|
||||
const hasPathSep = searchValue.indexOf(path.sep) >= 0;
|
||||
let cached: CacheEntry<IInternalFileMatch>;
|
||||
let wasResolved: boolean;
|
||||
for (let previousSearch in cache.resultsToSearchCache) {
|
||||
// If we narrow down, we might be able to reuse the cached results
|
||||
if (searchValue.startsWith(previousSearch)) {
|
||||
if (hasPathSep && previousSearch.indexOf(path.sep) < 0) {
|
||||
continue; // since a path character widens the search for potential more matches, require it in previous search too
|
||||
}
|
||||
|
||||
const c = cache.resultsToSearchCache[previousSearch];
|
||||
c.finished.then(() => { wasResolved = false; });
|
||||
cached = c;
|
||||
wasResolved = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!cached) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Promise((c, e) => {
|
||||
cached.finished.then(cachedEntries => {
|
||||
const cacheFilterStartTime = Date.now();
|
||||
|
||||
// Pattern match on results
|
||||
let results: IInternalFileMatch[] = [];
|
||||
const normalizedSearchValueLowercase = strings.stripWildcards(searchValue).toLowerCase();
|
||||
for (let i = 0; i < cachedEntries.length; i++) {
|
||||
let entry = cachedEntries[i];
|
||||
|
||||
// Check if this entry is a match for the search value
|
||||
if (!strings.fuzzyContains(entry.relativePath, normalizedSearchValueLowercase)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
results.push(entry);
|
||||
}
|
||||
|
||||
c(results);
|
||||
}, e);
|
||||
});
|
||||
}
|
||||
|
||||
private doSearch(args: IProviderArgs, provider: IInternalFileSearchProvider, onResult: (result: IInternalFileMatch[]) => void, batchSize: number): Promise<void> {
|
||||
return new Promise<void>((c, e) => {
|
||||
let batch: IInternalFileMatch[] = [];
|
||||
const onProviderResult = (match: string) => {
|
||||
if (match) {
|
||||
const internalMatch: IInternalFileMatch = {
|
||||
relativePath: match,
|
||||
basename: path.basename(match)
|
||||
};
|
||||
|
||||
batch.push(internalMatch);
|
||||
if (batchSize > 0 && batch.length >= batchSize) {
|
||||
onResult(batch);
|
||||
batch = [];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
provider.provideFileSearchResults(args.options, { report: onProviderResult }, args.token).then(() => {
|
||||
if (batch.length) {
|
||||
onResult(batch);
|
||||
}
|
||||
|
||||
c();
|
||||
}, error => {
|
||||
if (batch.length) {
|
||||
onResult(batch);
|
||||
}
|
||||
|
||||
e(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public clearCache(cacheKey: string): Promise<void> {
|
||||
delete this.caches[cacheKey];
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
interface IInternalFileMatch {
|
||||
relativePath?: string; // Not present for extraFiles or absolute path matches
|
||||
basename: string;
|
||||
}
|
||||
|
||||
interface CacheEntry<T> {
|
||||
finished: Promise<T[]>;
|
||||
}
|
||||
|
||||
class Cache {
|
||||
public resultsToSearchCache: { [searchValue: string]: CacheEntry<IInternalFileMatch> } = Object.create(null);
|
||||
public scorerCache: ScorerCache = Object.create(null);
|
||||
}
|
||||
|
||||
const FileMatchItemAccessor = new class implements IItemAccessor<IInternalFileMatch> {
|
||||
|
||||
public getItemLabel(match: IInternalFileMatch): string {
|
||||
return match.basename; // e.g. myFile.txt
|
||||
}
|
||||
|
||||
public getItemDescription(match: IInternalFileMatch): string {
|
||||
return match.relativePath.substr(0, match.relativePath.length - match.basename.length - 1); // e.g. some/path/to/file
|
||||
}
|
||||
|
||||
public getItemPath(match: IInternalFileMatch): string {
|
||||
return match.relativePath; // e.g. some/path/to/file/myFile.txt
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,75 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Asynchronous variant of `top()` allowing for splitting up work in batches between which the event loop can run.
|
||||
*
|
||||
* Returns the top N elements from the array.
|
||||
*
|
||||
* Faster than sorting the entire array when the array is a lot larger than N.
|
||||
*
|
||||
* @param array The unsorted array.
|
||||
* @param compare A sort function for the elements.
|
||||
* @param n The number of elements to return.
|
||||
* @param batch The number of elements to examine before yielding to the event loop.
|
||||
* @return The first n elemnts from array when sorted with compare.
|
||||
*/
|
||||
export function topAsync<T>(array: T[], compare: (a: T, b: T) => number, n: number, batch: number): Promise<T[]> {
|
||||
// TODO@roblou cancellation
|
||||
|
||||
if (n === 0) {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
let canceled = false;
|
||||
return new Promise((resolve, reject) => {
|
||||
(async () => {
|
||||
const o = array.length;
|
||||
const result = array.slice(0, n).sort(compare);
|
||||
for (let i = n, m = Math.min(n + batch, o); i < o; i = m, m = Math.min(m + batch, o)) {
|
||||
if (i > n) {
|
||||
await new Promise(resolve => setTimeout(resolve, 0)); // nextTick() would starve I/O.
|
||||
}
|
||||
if (canceled) {
|
||||
throw new Error('canceled');
|
||||
}
|
||||
topStep(array, compare, result, i, m);
|
||||
}
|
||||
return result;
|
||||
})()
|
||||
.then(resolve, reject);
|
||||
});
|
||||
}
|
||||
|
||||
function topStep<T>(array: T[], compare: (a: T, b: T) => number, result: T[], i: number, m: number): void {
|
||||
for (const n = result.length; i < m; i++) {
|
||||
const element = array[i];
|
||||
if (compare(element, result[n - 1]) < 0) {
|
||||
result.pop();
|
||||
const j = findFirstInSorted(result, e => compare(element, e) < 0);
|
||||
result.splice(j, 0, element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a sorted array and a function p. The array is sorted in such a way that all elements where p(x) is false
|
||||
* are located before all elements where p(x) is true.
|
||||
* @returns the least x for which p(x) is true or array.length if no element fullfills the given function.
|
||||
*/
|
||||
export function findFirstInSorted<T>(array: T[], p: (x: T) => boolean): number {
|
||||
let low = 0, high = array.length;
|
||||
if (high === 0) {
|
||||
return 0; // no children
|
||||
}
|
||||
while (low < high) {
|
||||
let mid = Math.floor((low + high) / 2);
|
||||
if (p(array[mid])) {
|
||||
high = mid;
|
||||
} else {
|
||||
low = mid + 1;
|
||||
}
|
||||
}
|
||||
return low;
|
||||
}
|
||||
@@ -0,0 +1,422 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
// Names from https://blog.codinghorror.com/ascii-pronunciation-rules-for-programmers/
|
||||
|
||||
/**
|
||||
* An inlined enum containing useful character codes (to be used with String.charCodeAt).
|
||||
* Please leave the const keyword such that it gets inlined when compiled to JavaScript!
|
||||
*/
|
||||
export const enum CharCode {
|
||||
Null = 0,
|
||||
/**
|
||||
* The `\t` character.
|
||||
*/
|
||||
Tab = 9,
|
||||
/**
|
||||
* The `\n` character.
|
||||
*/
|
||||
LineFeed = 10,
|
||||
/**
|
||||
* The `\r` character.
|
||||
*/
|
||||
CarriageReturn = 13,
|
||||
Space = 32,
|
||||
/**
|
||||
* The `!` character.
|
||||
*/
|
||||
ExclamationMark = 33,
|
||||
/**
|
||||
* The `"` character.
|
||||
*/
|
||||
DoubleQuote = 34,
|
||||
/**
|
||||
* The `#` character.
|
||||
*/
|
||||
Hash = 35,
|
||||
/**
|
||||
* The `$` character.
|
||||
*/
|
||||
DollarSign = 36,
|
||||
/**
|
||||
* The `%` character.
|
||||
*/
|
||||
PercentSign = 37,
|
||||
/**
|
||||
* The `&` character.
|
||||
*/
|
||||
Ampersand = 38,
|
||||
/**
|
||||
* The `'` character.
|
||||
*/
|
||||
SingleQuote = 39,
|
||||
/**
|
||||
* The `(` character.
|
||||
*/
|
||||
OpenParen = 40,
|
||||
/**
|
||||
* The `)` character.
|
||||
*/
|
||||
CloseParen = 41,
|
||||
/**
|
||||
* The `*` character.
|
||||
*/
|
||||
Asterisk = 42,
|
||||
/**
|
||||
* The `+` character.
|
||||
*/
|
||||
Plus = 43,
|
||||
/**
|
||||
* The `,` character.
|
||||
*/
|
||||
Comma = 44,
|
||||
/**
|
||||
* The `-` character.
|
||||
*/
|
||||
Dash = 45,
|
||||
/**
|
||||
* The `.` character.
|
||||
*/
|
||||
Period = 46,
|
||||
/**
|
||||
* The `/` character.
|
||||
*/
|
||||
Slash = 47,
|
||||
|
||||
Digit0 = 48,
|
||||
Digit1 = 49,
|
||||
Digit2 = 50,
|
||||
Digit3 = 51,
|
||||
Digit4 = 52,
|
||||
Digit5 = 53,
|
||||
Digit6 = 54,
|
||||
Digit7 = 55,
|
||||
Digit8 = 56,
|
||||
Digit9 = 57,
|
||||
|
||||
/**
|
||||
* The `:` character.
|
||||
*/
|
||||
Colon = 58,
|
||||
/**
|
||||
* The `;` character.
|
||||
*/
|
||||
Semicolon = 59,
|
||||
/**
|
||||
* The `<` character.
|
||||
*/
|
||||
LessThan = 60,
|
||||
/**
|
||||
* The `=` character.
|
||||
*/
|
||||
Equals = 61,
|
||||
/**
|
||||
* The `>` character.
|
||||
*/
|
||||
GreaterThan = 62,
|
||||
/**
|
||||
* The `?` character.
|
||||
*/
|
||||
QuestionMark = 63,
|
||||
/**
|
||||
* The `@` character.
|
||||
*/
|
||||
AtSign = 64,
|
||||
|
||||
A = 65,
|
||||
B = 66,
|
||||
C = 67,
|
||||
D = 68,
|
||||
E = 69,
|
||||
F = 70,
|
||||
G = 71,
|
||||
H = 72,
|
||||
I = 73,
|
||||
J = 74,
|
||||
K = 75,
|
||||
L = 76,
|
||||
M = 77,
|
||||
N = 78,
|
||||
O = 79,
|
||||
P = 80,
|
||||
Q = 81,
|
||||
R = 82,
|
||||
S = 83,
|
||||
T = 84,
|
||||
U = 85,
|
||||
V = 86,
|
||||
W = 87,
|
||||
X = 88,
|
||||
Y = 89,
|
||||
Z = 90,
|
||||
|
||||
/**
|
||||
* The `[` character.
|
||||
*/
|
||||
OpenSquareBracket = 91,
|
||||
/**
|
||||
* The `\` character.
|
||||
*/
|
||||
Backslash = 92,
|
||||
/**
|
||||
* The `]` character.
|
||||
*/
|
||||
CloseSquareBracket = 93,
|
||||
/**
|
||||
* The `^` character.
|
||||
*/
|
||||
Caret = 94,
|
||||
/**
|
||||
* The `_` character.
|
||||
*/
|
||||
Underline = 95,
|
||||
/**
|
||||
* The ``(`)`` character.
|
||||
*/
|
||||
BackTick = 96,
|
||||
|
||||
a = 97,
|
||||
b = 98,
|
||||
c = 99,
|
||||
d = 100,
|
||||
e = 101,
|
||||
f = 102,
|
||||
g = 103,
|
||||
h = 104,
|
||||
i = 105,
|
||||
j = 106,
|
||||
k = 107,
|
||||
l = 108,
|
||||
m = 109,
|
||||
n = 110,
|
||||
o = 111,
|
||||
p = 112,
|
||||
q = 113,
|
||||
r = 114,
|
||||
s = 115,
|
||||
t = 116,
|
||||
u = 117,
|
||||
v = 118,
|
||||
w = 119,
|
||||
x = 120,
|
||||
y = 121,
|
||||
z = 122,
|
||||
|
||||
/**
|
||||
* The `{` character.
|
||||
*/
|
||||
OpenCurlyBrace = 123,
|
||||
/**
|
||||
* The `|` character.
|
||||
*/
|
||||
Pipe = 124,
|
||||
/**
|
||||
* The `}` character.
|
||||
*/
|
||||
CloseCurlyBrace = 125,
|
||||
/**
|
||||
* The `~` character.
|
||||
*/
|
||||
Tilde = 126,
|
||||
|
||||
U_Combining_Grave_Accent = 0x0300, // U+0300 Combining Grave Accent
|
||||
U_Combining_Acute_Accent = 0x0301, // U+0301 Combining Acute Accent
|
||||
U_Combining_Circumflex_Accent = 0x0302, // U+0302 Combining Circumflex Accent
|
||||
U_Combining_Tilde = 0x0303, // U+0303 Combining Tilde
|
||||
U_Combining_Macron = 0x0304, // U+0304 Combining Macron
|
||||
U_Combining_Overline = 0x0305, // U+0305 Combining Overline
|
||||
U_Combining_Breve = 0x0306, // U+0306 Combining Breve
|
||||
U_Combining_Dot_Above = 0x0307, // U+0307 Combining Dot Above
|
||||
U_Combining_Diaeresis = 0x0308, // U+0308 Combining Diaeresis
|
||||
U_Combining_Hook_Above = 0x0309, // U+0309 Combining Hook Above
|
||||
U_Combining_Ring_Above = 0x030A, // U+030A Combining Ring Above
|
||||
U_Combining_Double_Acute_Accent = 0x030B, // U+030B Combining Double Acute Accent
|
||||
U_Combining_Caron = 0x030C, // U+030C Combining Caron
|
||||
U_Combining_Vertical_Line_Above = 0x030D, // U+030D Combining Vertical Line Above
|
||||
U_Combining_Double_Vertical_Line_Above = 0x030E, // U+030E Combining Double Vertical Line Above
|
||||
U_Combining_Double_Grave_Accent = 0x030F, // U+030F Combining Double Grave Accent
|
||||
U_Combining_Candrabindu = 0x0310, // U+0310 Combining Candrabindu
|
||||
U_Combining_Inverted_Breve = 0x0311, // U+0311 Combining Inverted Breve
|
||||
U_Combining_Turned_Comma_Above = 0x0312, // U+0312 Combining Turned Comma Above
|
||||
U_Combining_Comma_Above = 0x0313, // U+0313 Combining Comma Above
|
||||
U_Combining_Reversed_Comma_Above = 0x0314, // U+0314 Combining Reversed Comma Above
|
||||
U_Combining_Comma_Above_Right = 0x0315, // U+0315 Combining Comma Above Right
|
||||
U_Combining_Grave_Accent_Below = 0x0316, // U+0316 Combining Grave Accent Below
|
||||
U_Combining_Acute_Accent_Below = 0x0317, // U+0317 Combining Acute Accent Below
|
||||
U_Combining_Left_Tack_Below = 0x0318, // U+0318 Combining Left Tack Below
|
||||
U_Combining_Right_Tack_Below = 0x0319, // U+0319 Combining Right Tack Below
|
||||
U_Combining_Left_Angle_Above = 0x031A, // U+031A Combining Left Angle Above
|
||||
U_Combining_Horn = 0x031B, // U+031B Combining Horn
|
||||
U_Combining_Left_Half_Ring_Below = 0x031C, // U+031C Combining Left Half Ring Below
|
||||
U_Combining_Up_Tack_Below = 0x031D, // U+031D Combining Up Tack Below
|
||||
U_Combining_Down_Tack_Below = 0x031E, // U+031E Combining Down Tack Below
|
||||
U_Combining_Plus_Sign_Below = 0x031F, // U+031F Combining Plus Sign Below
|
||||
U_Combining_Minus_Sign_Below = 0x0320, // U+0320 Combining Minus Sign Below
|
||||
U_Combining_Palatalized_Hook_Below = 0x0321, // U+0321 Combining Palatalized Hook Below
|
||||
U_Combining_Retroflex_Hook_Below = 0x0322, // U+0322 Combining Retroflex Hook Below
|
||||
U_Combining_Dot_Below = 0x0323, // U+0323 Combining Dot Below
|
||||
U_Combining_Diaeresis_Below = 0x0324, // U+0324 Combining Diaeresis Below
|
||||
U_Combining_Ring_Below = 0x0325, // U+0325 Combining Ring Below
|
||||
U_Combining_Comma_Below = 0x0326, // U+0326 Combining Comma Below
|
||||
U_Combining_Cedilla = 0x0327, // U+0327 Combining Cedilla
|
||||
U_Combining_Ogonek = 0x0328, // U+0328 Combining Ogonek
|
||||
U_Combining_Vertical_Line_Below = 0x0329, // U+0329 Combining Vertical Line Below
|
||||
U_Combining_Bridge_Below = 0x032A, // U+032A Combining Bridge Below
|
||||
U_Combining_Inverted_Double_Arch_Below = 0x032B, // U+032B Combining Inverted Double Arch Below
|
||||
U_Combining_Caron_Below = 0x032C, // U+032C Combining Caron Below
|
||||
U_Combining_Circumflex_Accent_Below = 0x032D, // U+032D Combining Circumflex Accent Below
|
||||
U_Combining_Breve_Below = 0x032E, // U+032E Combining Breve Below
|
||||
U_Combining_Inverted_Breve_Below = 0x032F, // U+032F Combining Inverted Breve Below
|
||||
U_Combining_Tilde_Below = 0x0330, // U+0330 Combining Tilde Below
|
||||
U_Combining_Macron_Below = 0x0331, // U+0331 Combining Macron Below
|
||||
U_Combining_Low_Line = 0x0332, // U+0332 Combining Low Line
|
||||
U_Combining_Double_Low_Line = 0x0333, // U+0333 Combining Double Low Line
|
||||
U_Combining_Tilde_Overlay = 0x0334, // U+0334 Combining Tilde Overlay
|
||||
U_Combining_Short_Stroke_Overlay = 0x0335, // U+0335 Combining Short Stroke Overlay
|
||||
U_Combining_Long_Stroke_Overlay = 0x0336, // U+0336 Combining Long Stroke Overlay
|
||||
U_Combining_Short_Solidus_Overlay = 0x0337, // U+0337 Combining Short Solidus Overlay
|
||||
U_Combining_Long_Solidus_Overlay = 0x0338, // U+0338 Combining Long Solidus Overlay
|
||||
U_Combining_Right_Half_Ring_Below = 0x0339, // U+0339 Combining Right Half Ring Below
|
||||
U_Combining_Inverted_Bridge_Below = 0x033A, // U+033A Combining Inverted Bridge Below
|
||||
U_Combining_Square_Below = 0x033B, // U+033B Combining Square Below
|
||||
U_Combining_Seagull_Below = 0x033C, // U+033C Combining Seagull Below
|
||||
U_Combining_X_Above = 0x033D, // U+033D Combining X Above
|
||||
U_Combining_Vertical_Tilde = 0x033E, // U+033E Combining Vertical Tilde
|
||||
U_Combining_Double_Overline = 0x033F, // U+033F Combining Double Overline
|
||||
U_Combining_Grave_Tone_Mark = 0x0340, // U+0340 Combining Grave Tone Mark
|
||||
U_Combining_Acute_Tone_Mark = 0x0341, // U+0341 Combining Acute Tone Mark
|
||||
U_Combining_Greek_Perispomeni = 0x0342, // U+0342 Combining Greek Perispomeni
|
||||
U_Combining_Greek_Koronis = 0x0343, // U+0343 Combining Greek Koronis
|
||||
U_Combining_Greek_Dialytika_Tonos = 0x0344, // U+0344 Combining Greek Dialytika Tonos
|
||||
U_Combining_Greek_Ypogegrammeni = 0x0345, // U+0345 Combining Greek Ypogegrammeni
|
||||
U_Combining_Bridge_Above = 0x0346, // U+0346 Combining Bridge Above
|
||||
U_Combining_Equals_Sign_Below = 0x0347, // U+0347 Combining Equals Sign Below
|
||||
U_Combining_Double_Vertical_Line_Below = 0x0348, // U+0348 Combining Double Vertical Line Below
|
||||
U_Combining_Left_Angle_Below = 0x0349, // U+0349 Combining Left Angle Below
|
||||
U_Combining_Not_Tilde_Above = 0x034A, // U+034A Combining Not Tilde Above
|
||||
U_Combining_Homothetic_Above = 0x034B, // U+034B Combining Homothetic Above
|
||||
U_Combining_Almost_Equal_To_Above = 0x034C, // U+034C Combining Almost Equal To Above
|
||||
U_Combining_Left_Right_Arrow_Below = 0x034D, // U+034D Combining Left Right Arrow Below
|
||||
U_Combining_Upwards_Arrow_Below = 0x034E, // U+034E Combining Upwards Arrow Below
|
||||
U_Combining_Grapheme_Joiner = 0x034F, // U+034F Combining Grapheme Joiner
|
||||
U_Combining_Right_Arrowhead_Above = 0x0350, // U+0350 Combining Right Arrowhead Above
|
||||
U_Combining_Left_Half_Ring_Above = 0x0351, // U+0351 Combining Left Half Ring Above
|
||||
U_Combining_Fermata = 0x0352, // U+0352 Combining Fermata
|
||||
U_Combining_X_Below = 0x0353, // U+0353 Combining X Below
|
||||
U_Combining_Left_Arrowhead_Below = 0x0354, // U+0354 Combining Left Arrowhead Below
|
||||
U_Combining_Right_Arrowhead_Below = 0x0355, // U+0355 Combining Right Arrowhead Below
|
||||
U_Combining_Right_Arrowhead_And_Up_Arrowhead_Below = 0x0356, // U+0356 Combining Right Arrowhead And Up Arrowhead Below
|
||||
U_Combining_Right_Half_Ring_Above = 0x0357, // U+0357 Combining Right Half Ring Above
|
||||
U_Combining_Dot_Above_Right = 0x0358, // U+0358 Combining Dot Above Right
|
||||
U_Combining_Asterisk_Below = 0x0359, // U+0359 Combining Asterisk Below
|
||||
U_Combining_Double_Ring_Below = 0x035A, // U+035A Combining Double Ring Below
|
||||
U_Combining_Zigzag_Above = 0x035B, // U+035B Combining Zigzag Above
|
||||
U_Combining_Double_Breve_Below = 0x035C, // U+035C Combining Double Breve Below
|
||||
U_Combining_Double_Breve = 0x035D, // U+035D Combining Double Breve
|
||||
U_Combining_Double_Macron = 0x035E, // U+035E Combining Double Macron
|
||||
U_Combining_Double_Macron_Below = 0x035F, // U+035F Combining Double Macron Below
|
||||
U_Combining_Double_Tilde = 0x0360, // U+0360 Combining Double Tilde
|
||||
U_Combining_Double_Inverted_Breve = 0x0361, // U+0361 Combining Double Inverted Breve
|
||||
U_Combining_Double_Rightwards_Arrow_Below = 0x0362, // U+0362 Combining Double Rightwards Arrow Below
|
||||
U_Combining_Latin_Small_Letter_A = 0x0363, // U+0363 Combining Latin Small Letter A
|
||||
U_Combining_Latin_Small_Letter_E = 0x0364, // U+0364 Combining Latin Small Letter E
|
||||
U_Combining_Latin_Small_Letter_I = 0x0365, // U+0365 Combining Latin Small Letter I
|
||||
U_Combining_Latin_Small_Letter_O = 0x0366, // U+0366 Combining Latin Small Letter O
|
||||
U_Combining_Latin_Small_Letter_U = 0x0367, // U+0367 Combining Latin Small Letter U
|
||||
U_Combining_Latin_Small_Letter_C = 0x0368, // U+0368 Combining Latin Small Letter C
|
||||
U_Combining_Latin_Small_Letter_D = 0x0369, // U+0369 Combining Latin Small Letter D
|
||||
U_Combining_Latin_Small_Letter_H = 0x036A, // U+036A Combining Latin Small Letter H
|
||||
U_Combining_Latin_Small_Letter_M = 0x036B, // U+036B Combining Latin Small Letter M
|
||||
U_Combining_Latin_Small_Letter_R = 0x036C, // U+036C Combining Latin Small Letter R
|
||||
U_Combining_Latin_Small_Letter_T = 0x036D, // U+036D Combining Latin Small Letter T
|
||||
U_Combining_Latin_Small_Letter_V = 0x036E, // U+036E Combining Latin Small Letter V
|
||||
U_Combining_Latin_Small_Letter_X = 0x036F, // U+036F Combining Latin Small Letter X
|
||||
|
||||
/**
|
||||
* Unicode Character 'LINE SEPARATOR' (U+2028)
|
||||
* http://www.fileformat.info/info/unicode/char/2028/index.htm
|
||||
*/
|
||||
LINE_SEPARATOR_2028 = 8232,
|
||||
|
||||
// http://www.fileformat.info/info/unicode/category/Sk/list.htm
|
||||
U_CIRCUMFLEX = 0x005E, // U+005E CIRCUMFLEX
|
||||
U_GRAVE_ACCENT = 0x0060, // U+0060 GRAVE ACCENT
|
||||
U_DIAERESIS = 0x00A8, // U+00A8 DIAERESIS
|
||||
U_MACRON = 0x00AF, // U+00AF MACRON
|
||||
U_ACUTE_ACCENT = 0x00B4, // U+00B4 ACUTE ACCENT
|
||||
U_CEDILLA = 0x00B8, // U+00B8 CEDILLA
|
||||
U_MODIFIER_LETTER_LEFT_ARROWHEAD = 0x02C2, // U+02C2 MODIFIER LETTER LEFT ARROWHEAD
|
||||
U_MODIFIER_LETTER_RIGHT_ARROWHEAD = 0x02C3, // U+02C3 MODIFIER LETTER RIGHT ARROWHEAD
|
||||
U_MODIFIER_LETTER_UP_ARROWHEAD = 0x02C4, // U+02C4 MODIFIER LETTER UP ARROWHEAD
|
||||
U_MODIFIER_LETTER_DOWN_ARROWHEAD = 0x02C5, // U+02C5 MODIFIER LETTER DOWN ARROWHEAD
|
||||
U_MODIFIER_LETTER_CENTRED_RIGHT_HALF_RING = 0x02D2, // U+02D2 MODIFIER LETTER CENTRED RIGHT HALF RING
|
||||
U_MODIFIER_LETTER_CENTRED_LEFT_HALF_RING = 0x02D3, // U+02D3 MODIFIER LETTER CENTRED LEFT HALF RING
|
||||
U_MODIFIER_LETTER_UP_TACK = 0x02D4, // U+02D4 MODIFIER LETTER UP TACK
|
||||
U_MODIFIER_LETTER_DOWN_TACK = 0x02D5, // U+02D5 MODIFIER LETTER DOWN TACK
|
||||
U_MODIFIER_LETTER_PLUS_SIGN = 0x02D6, // U+02D6 MODIFIER LETTER PLUS SIGN
|
||||
U_MODIFIER_LETTER_MINUS_SIGN = 0x02D7, // U+02D7 MODIFIER LETTER MINUS SIGN
|
||||
U_BREVE = 0x02D8, // U+02D8 BREVE
|
||||
U_DOT_ABOVE = 0x02D9, // U+02D9 DOT ABOVE
|
||||
U_RING_ABOVE = 0x02DA, // U+02DA RING ABOVE
|
||||
U_OGONEK = 0x02DB, // U+02DB OGONEK
|
||||
U_SMALL_TILDE = 0x02DC, // U+02DC SMALL TILDE
|
||||
U_DOUBLE_ACUTE_ACCENT = 0x02DD, // U+02DD DOUBLE ACUTE ACCENT
|
||||
U_MODIFIER_LETTER_RHOTIC_HOOK = 0x02DE, // U+02DE MODIFIER LETTER RHOTIC HOOK
|
||||
U_MODIFIER_LETTER_CROSS_ACCENT = 0x02DF, // U+02DF MODIFIER LETTER CROSS ACCENT
|
||||
U_MODIFIER_LETTER_EXTRA_HIGH_TONE_BAR = 0x02E5, // U+02E5 MODIFIER LETTER EXTRA-HIGH TONE BAR
|
||||
U_MODIFIER_LETTER_HIGH_TONE_BAR = 0x02E6, // U+02E6 MODIFIER LETTER HIGH TONE BAR
|
||||
U_MODIFIER_LETTER_MID_TONE_BAR = 0x02E7, // U+02E7 MODIFIER LETTER MID TONE BAR
|
||||
U_MODIFIER_LETTER_LOW_TONE_BAR = 0x02E8, // U+02E8 MODIFIER LETTER LOW TONE BAR
|
||||
U_MODIFIER_LETTER_EXTRA_LOW_TONE_BAR = 0x02E9, // U+02E9 MODIFIER LETTER EXTRA-LOW TONE BAR
|
||||
U_MODIFIER_LETTER_YIN_DEPARTING_TONE_MARK = 0x02EA, // U+02EA MODIFIER LETTER YIN DEPARTING TONE MARK
|
||||
U_MODIFIER_LETTER_YANG_DEPARTING_TONE_MARK = 0x02EB, // U+02EB MODIFIER LETTER YANG DEPARTING TONE MARK
|
||||
U_MODIFIER_LETTER_UNASPIRATED = 0x02ED, // U+02ED MODIFIER LETTER UNASPIRATED
|
||||
U_MODIFIER_LETTER_LOW_DOWN_ARROWHEAD = 0x02EF, // U+02EF MODIFIER LETTER LOW DOWN ARROWHEAD
|
||||
U_MODIFIER_LETTER_LOW_UP_ARROWHEAD = 0x02F0, // U+02F0 MODIFIER LETTER LOW UP ARROWHEAD
|
||||
U_MODIFIER_LETTER_LOW_LEFT_ARROWHEAD = 0x02F1, // U+02F1 MODIFIER LETTER LOW LEFT ARROWHEAD
|
||||
U_MODIFIER_LETTER_LOW_RIGHT_ARROWHEAD = 0x02F2, // U+02F2 MODIFIER LETTER LOW RIGHT ARROWHEAD
|
||||
U_MODIFIER_LETTER_LOW_RING = 0x02F3, // U+02F3 MODIFIER LETTER LOW RING
|
||||
U_MODIFIER_LETTER_MIDDLE_GRAVE_ACCENT = 0x02F4, // U+02F4 MODIFIER LETTER MIDDLE GRAVE ACCENT
|
||||
U_MODIFIER_LETTER_MIDDLE_DOUBLE_GRAVE_ACCENT = 0x02F5, // U+02F5 MODIFIER LETTER MIDDLE DOUBLE GRAVE ACCENT
|
||||
U_MODIFIER_LETTER_MIDDLE_DOUBLE_ACUTE_ACCENT = 0x02F6, // U+02F6 MODIFIER LETTER MIDDLE DOUBLE ACUTE ACCENT
|
||||
U_MODIFIER_LETTER_LOW_TILDE = 0x02F7, // U+02F7 MODIFIER LETTER LOW TILDE
|
||||
U_MODIFIER_LETTER_RAISED_COLON = 0x02F8, // U+02F8 MODIFIER LETTER RAISED COLON
|
||||
U_MODIFIER_LETTER_BEGIN_HIGH_TONE = 0x02F9, // U+02F9 MODIFIER LETTER BEGIN HIGH TONE
|
||||
U_MODIFIER_LETTER_END_HIGH_TONE = 0x02FA, // U+02FA MODIFIER LETTER END HIGH TONE
|
||||
U_MODIFIER_LETTER_BEGIN_LOW_TONE = 0x02FB, // U+02FB MODIFIER LETTER BEGIN LOW TONE
|
||||
U_MODIFIER_LETTER_END_LOW_TONE = 0x02FC, // U+02FC MODIFIER LETTER END LOW TONE
|
||||
U_MODIFIER_LETTER_SHELF = 0x02FD, // U+02FD MODIFIER LETTER SHELF
|
||||
U_MODIFIER_LETTER_OPEN_SHELF = 0x02FE, // U+02FE MODIFIER LETTER OPEN SHELF
|
||||
U_MODIFIER_LETTER_LOW_LEFT_ARROW = 0x02FF, // U+02FF MODIFIER LETTER LOW LEFT ARROW
|
||||
U_GREEK_LOWER_NUMERAL_SIGN = 0x0375, // U+0375 GREEK LOWER NUMERAL SIGN
|
||||
U_GREEK_TONOS = 0x0384, // U+0384 GREEK TONOS
|
||||
U_GREEK_DIALYTIKA_TONOS = 0x0385, // U+0385 GREEK DIALYTIKA TONOS
|
||||
U_GREEK_KORONIS = 0x1FBD, // U+1FBD GREEK KORONIS
|
||||
U_GREEK_PSILI = 0x1FBF, // U+1FBF GREEK PSILI
|
||||
U_GREEK_PERISPOMENI = 0x1FC0, // U+1FC0 GREEK PERISPOMENI
|
||||
U_GREEK_DIALYTIKA_AND_PERISPOMENI = 0x1FC1, // U+1FC1 GREEK DIALYTIKA AND PERISPOMENI
|
||||
U_GREEK_PSILI_AND_VARIA = 0x1FCD, // U+1FCD GREEK PSILI AND VARIA
|
||||
U_GREEK_PSILI_AND_OXIA = 0x1FCE, // U+1FCE GREEK PSILI AND OXIA
|
||||
U_GREEK_PSILI_AND_PERISPOMENI = 0x1FCF, // U+1FCF GREEK PSILI AND PERISPOMENI
|
||||
U_GREEK_DASIA_AND_VARIA = 0x1FDD, // U+1FDD GREEK DASIA AND VARIA
|
||||
U_GREEK_DASIA_AND_OXIA = 0x1FDE, // U+1FDE GREEK DASIA AND OXIA
|
||||
U_GREEK_DASIA_AND_PERISPOMENI = 0x1FDF, // U+1FDF GREEK DASIA AND PERISPOMENI
|
||||
U_GREEK_DIALYTIKA_AND_VARIA = 0x1FED, // U+1FED GREEK DIALYTIKA AND VARIA
|
||||
U_GREEK_DIALYTIKA_AND_OXIA = 0x1FEE, // U+1FEE GREEK DIALYTIKA AND OXIA
|
||||
U_GREEK_VARIA = 0x1FEF, // U+1FEF GREEK VARIA
|
||||
U_GREEK_OXIA = 0x1FFD, // U+1FFD GREEK OXIA
|
||||
U_GREEK_DASIA = 0x1FFE, // U+1FFE GREEK DASIA
|
||||
|
||||
|
||||
U_OVERLINE = 0x203E, // Unicode Character 'OVERLINE'
|
||||
|
||||
/**
|
||||
* UTF-8 BOM
|
||||
* Unicode Character 'ZERO WIDTH NO-BREAK SPACE' (U+FEFF)
|
||||
* http://www.fileformat.info/info/unicode/char/feff/index.htm
|
||||
*/
|
||||
UTF8_BOM = 65279
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import * as strings from './strings';
|
||||
|
||||
let intlFileNameCollator: Intl.Collator;
|
||||
let intlFileNameCollatorIsNumeric: boolean;
|
||||
|
||||
setFileNameComparer(new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }));
|
||||
|
||||
export function setFileNameComparer(collator: Intl.Collator): void {
|
||||
intlFileNameCollator = collator;
|
||||
intlFileNameCollatorIsNumeric = collator.resolvedOptions().numeric;
|
||||
}
|
||||
|
||||
export function compareFileNames(one: string, other: string, caseSensitive = false): number {
|
||||
if (intlFileNameCollator) {
|
||||
const a = one || '';
|
||||
const b = other || '';
|
||||
const result = intlFileNameCollator.compare(a, b);
|
||||
|
||||
// Using the numeric option in the collator will
|
||||
// make compare(`foo1`, `foo01`) === 0. We must disambiguate.
|
||||
if (intlFileNameCollatorIsNumeric && result === 0 && a !== b) {
|
||||
return a < b ? -1 : 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return noIntlCompareFileNames(one, other, caseSensitive);
|
||||
}
|
||||
|
||||
const FileNameMatch = /^(.*?)(\.([^.]*))?$/;
|
||||
|
||||
export function noIntlCompareFileNames(one: string, other: string, caseSensitive = false): number {
|
||||
if (!caseSensitive) {
|
||||
one = one && one.toLowerCase();
|
||||
other = other && other.toLowerCase();
|
||||
}
|
||||
|
||||
const [oneName, oneExtension] = extractNameAndExtension(one);
|
||||
const [otherName, otherExtension] = extractNameAndExtension(other);
|
||||
|
||||
if (oneName !== otherName) {
|
||||
return oneName < otherName ? -1 : 1;
|
||||
}
|
||||
|
||||
if (oneExtension === otherExtension) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return oneExtension < otherExtension ? -1 : 1;
|
||||
}
|
||||
|
||||
function extractNameAndExtension(str?: string): [string, string] {
|
||||
const match = str ? FileNameMatch.exec(str) : [] as RegExpExecArray;
|
||||
|
||||
return [(match && match[1]) || '', (match && match[3]) || ''];
|
||||
}
|
||||
|
||||
export function compareAnything(one: string, other: string, lookFor: string): number {
|
||||
let elementAName = one.toLowerCase();
|
||||
let elementBName = other.toLowerCase();
|
||||
|
||||
// Sort prefix matches over non prefix matches
|
||||
const prefixCompare = compareByPrefix(one, other, lookFor);
|
||||
if (prefixCompare) {
|
||||
return prefixCompare;
|
||||
}
|
||||
|
||||
// Sort suffix matches over non suffix matches
|
||||
let elementASuffixMatch = strings.endsWith(elementAName, lookFor);
|
||||
let elementBSuffixMatch = strings.endsWith(elementBName, lookFor);
|
||||
if (elementASuffixMatch !== elementBSuffixMatch) {
|
||||
return elementASuffixMatch ? -1 : 1;
|
||||
}
|
||||
|
||||
// Understand file names
|
||||
let r = compareFileNames(elementAName, elementBName);
|
||||
if (r !== 0) {
|
||||
return r;
|
||||
}
|
||||
|
||||
// Compare by name
|
||||
return elementAName.localeCompare(elementBName);
|
||||
}
|
||||
|
||||
export function compareByPrefix(one: string, other: string, lookFor: string): number {
|
||||
let elementAName = one.toLowerCase();
|
||||
let elementBName = other.toLowerCase();
|
||||
|
||||
// Sort prefix matches over non prefix matches
|
||||
let elementAPrefixMatch = strings.startsWith(elementAName, lookFor);
|
||||
let elementBPrefixMatch = strings.startsWith(elementBName, lookFor);
|
||||
if (elementAPrefixMatch !== elementBPrefixMatch) {
|
||||
return elementAPrefixMatch ? -1 : 1;
|
||||
}
|
||||
|
||||
// Same prefix: Sort shorter matches to the top to have those on top that match more precisely
|
||||
else if (elementAPrefixMatch && elementBPrefixMatch) {
|
||||
if (elementAName.length < elementBName.length) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (elementAName.length > elementBName.length) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,619 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { stripWildcards, equalsIgnoreCase } from './strings';
|
||||
import { matchesPrefix, matchesCamelCase, createMatches, IMatch, isUpper } from './filters';
|
||||
import { compareAnything } from './comparers';
|
||||
import { CharCode } from './charCode';
|
||||
|
||||
const isWindows = process.platform === 'win32';
|
||||
const isMacintosh = (process.platform === 'darwin');
|
||||
const isLinux = (process.platform === 'linux');
|
||||
|
||||
const nativeSep = isWindows ? '\\' : '/';
|
||||
|
||||
export type Score = [number /* score */, number[] /* match positions */];
|
||||
export type ScorerCache = { [key: string]: IItemScore };
|
||||
|
||||
const NO_MATCH = 0;
|
||||
const NO_SCORE: Score = [NO_MATCH, []];
|
||||
|
||||
// const DEBUG = false;
|
||||
// const DEBUG_MATRIX = false;
|
||||
|
||||
export function score(target: string, query: string, queryLower: string, fuzzy: boolean): Score {
|
||||
if (!target || !query) {
|
||||
return NO_SCORE; // return early if target or query are undefined
|
||||
}
|
||||
|
||||
const targetLength = target.length;
|
||||
const queryLength = query.length;
|
||||
|
||||
if (targetLength < queryLength) {
|
||||
return NO_SCORE; // impossible for query to be contained in target
|
||||
}
|
||||
|
||||
// if (DEBUG) {
|
||||
// console.group(`Target: ${target}, Query: ${query}`);
|
||||
// }
|
||||
|
||||
const targetLower = target.toLowerCase();
|
||||
|
||||
// When not searching fuzzy, we require the query to be contained fully
|
||||
// in the target string contiguously.
|
||||
if (!fuzzy) {
|
||||
const indexOfQueryInTarget = targetLower.indexOf(queryLower);
|
||||
if (indexOfQueryInTarget === -1) {
|
||||
// if (DEBUG) {
|
||||
// console.log(`Characters not matching consecutively ${queryLower} within ${targetLower}`);
|
||||
// }
|
||||
|
||||
return NO_SCORE;
|
||||
}
|
||||
}
|
||||
|
||||
const res = doScore(query, queryLower, queryLength, target, targetLower, targetLength);
|
||||
|
||||
// if (DEBUG) {
|
||||
// console.log(`%cFinal Score: ${res[0]}`, 'font-weight: bold');
|
||||
// console.groupEnd();
|
||||
// }
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
function doScore(query: string, queryLower: string, queryLength: number, target: string, targetLower: string, targetLength: number): [number, number[]] {
|
||||
const scores = [];
|
||||
const matches = [];
|
||||
|
||||
//
|
||||
// Build Scorer Matrix:
|
||||
//
|
||||
// The matrix is composed of query q and target t. For each index we score
|
||||
// q[i] with t[i] and compare that with the previous score. If the score is
|
||||
// equal or larger, we keep the match. In addition to the score, we also keep
|
||||
// the length of the consecutive matches to use as boost for the score.
|
||||
//
|
||||
// t a r g e t
|
||||
// q
|
||||
// u
|
||||
// e
|
||||
// r
|
||||
// y
|
||||
//
|
||||
for (let queryIndex = 0; queryIndex < queryLength; queryIndex++) {
|
||||
for (let targetIndex = 0; targetIndex < targetLength; targetIndex++) {
|
||||
const currentIndex = queryIndex * targetLength + targetIndex;
|
||||
const leftIndex = currentIndex - 1;
|
||||
const diagIndex = (queryIndex - 1) * targetLength + targetIndex - 1;
|
||||
|
||||
const leftScore: number = targetIndex > 0 ? scores[leftIndex] : 0;
|
||||
const diagScore: number = queryIndex > 0 && targetIndex > 0 ? scores[diagIndex] : 0;
|
||||
|
||||
const matchesSequenceLength: number = queryIndex > 0 && targetIndex > 0 ? matches[diagIndex] : 0;
|
||||
|
||||
// If we are not matching on the first query character any more, we only produce a
|
||||
// score if we had a score previously for the last query index (by looking at the diagScore).
|
||||
// This makes sure that the query always matches in sequence on the target. For example
|
||||
// given a target of "ede" and a query of "de", we would otherwise produce a wrong high score
|
||||
// for query[1] ("e") matching on target[0] ("e") because of the "beginning of word" boost.
|
||||
let score: number;
|
||||
if (!diagScore && queryIndex > 0) {
|
||||
score = 0;
|
||||
} else {
|
||||
score = computeCharScore(query, queryLower, queryIndex, target, targetLower, targetIndex, matchesSequenceLength);
|
||||
}
|
||||
|
||||
// We have a score and its equal or larger than the left score
|
||||
// Match: sequence continues growing from previous diag value
|
||||
// Score: increases by diag score value
|
||||
if (score && diagScore + score >= leftScore) {
|
||||
matches[currentIndex] = matchesSequenceLength + 1;
|
||||
scores[currentIndex] = diagScore + score;
|
||||
}
|
||||
|
||||
// We either have no score or the score is lower than the left score
|
||||
// Match: reset to 0
|
||||
// Score: pick up from left hand side
|
||||
else {
|
||||
matches[currentIndex] = NO_MATCH;
|
||||
scores[currentIndex] = leftScore;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Restore Positions (starting from bottom right of matrix)
|
||||
const positions = [];
|
||||
let queryIndex = queryLength - 1;
|
||||
let targetIndex = targetLength - 1;
|
||||
while (queryIndex >= 0 && targetIndex >= 0) {
|
||||
const currentIndex = queryIndex * targetLength + targetIndex;
|
||||
const match = matches[currentIndex];
|
||||
if (match === NO_MATCH) {
|
||||
targetIndex--; // go left
|
||||
} else {
|
||||
positions.push(targetIndex);
|
||||
|
||||
// go up and left
|
||||
queryIndex--;
|
||||
targetIndex--;
|
||||
}
|
||||
}
|
||||
|
||||
// Print matrix
|
||||
// if (DEBUG_MATRIX) {
|
||||
// printMatrix(query, target, matches, scores);
|
||||
// }
|
||||
|
||||
return [scores[queryLength * targetLength - 1], positions.reverse()];
|
||||
}
|
||||
|
||||
function computeCharScore(query: string, queryLower: string, queryIndex: number, target: string, targetLower: string, targetIndex: number, matchesSequenceLength: number): number {
|
||||
let score = 0;
|
||||
|
||||
if (queryLower[queryIndex] !== targetLower[targetIndex]) {
|
||||
return score; // no match of characters
|
||||
}
|
||||
|
||||
// Character match bonus
|
||||
score += 1;
|
||||
|
||||
// if (DEBUG) {
|
||||
// console.groupCollapsed(`%cCharacter match bonus: +1 (char: ${queryLower[queryIndex]} at index ${targetIndex}, total score: ${score})`, 'font-weight: normal');
|
||||
// }
|
||||
|
||||
// Consecutive match bonus
|
||||
if (matchesSequenceLength > 0) {
|
||||
score += (matchesSequenceLength * 5);
|
||||
|
||||
// if (DEBUG) {
|
||||
// console.log('Consecutive match bonus: ' + (matchesSequenceLength * 5));
|
||||
// }
|
||||
}
|
||||
|
||||
// Same case bonus
|
||||
if (query[queryIndex] === target[targetIndex]) {
|
||||
score += 1;
|
||||
|
||||
// if (DEBUG) {
|
||||
// console.log('Same case bonus: +1');
|
||||
// }
|
||||
}
|
||||
|
||||
// Start of word bonus
|
||||
if (targetIndex === 0) {
|
||||
score += 8;
|
||||
|
||||
// if (DEBUG) {
|
||||
// console.log('Start of word bonus: +8');
|
||||
// }
|
||||
}
|
||||
|
||||
else {
|
||||
|
||||
// After separator bonus
|
||||
const separatorBonus = scoreSeparatorAtPos(target.charCodeAt(targetIndex - 1));
|
||||
if (separatorBonus) {
|
||||
score += separatorBonus;
|
||||
|
||||
// if (DEBUG) {
|
||||
// console.log('After separtor bonus: +4');
|
||||
// }
|
||||
}
|
||||
|
||||
// Inside word upper case bonus (camel case)
|
||||
else if (isUpper(target.charCodeAt(targetIndex))) {
|
||||
score += 1;
|
||||
|
||||
// if (DEBUG) {
|
||||
// console.log('Inside word upper case bonus: +1');
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
// if (DEBUG) {
|
||||
// console.groupEnd();
|
||||
// }
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
function scoreSeparatorAtPos(charCode: number): number {
|
||||
switch (charCode) {
|
||||
case CharCode.Slash:
|
||||
case CharCode.Backslash:
|
||||
return 5; // prefer path separators...
|
||||
case CharCode.Underline:
|
||||
case CharCode.Dash:
|
||||
case CharCode.Period:
|
||||
case CharCode.Space:
|
||||
case CharCode.SingleQuote:
|
||||
case CharCode.DoubleQuote:
|
||||
case CharCode.Colon:
|
||||
return 4; // ...over other separators
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// function printMatrix(query: string, target: string, matches: number[], scores: number[]): void {
|
||||
// console.log('\t' + target.split('').join('\t'));
|
||||
// for (let queryIndex = 0; queryIndex < query.length; queryIndex++) {
|
||||
// let line = query[queryIndex] + '\t';
|
||||
// for (let targetIndex = 0; targetIndex < target.length; targetIndex++) {
|
||||
// const currentIndex = queryIndex * target.length + targetIndex;
|
||||
// line = line + 'M' + matches[currentIndex] + '/' + 'S' + scores[currentIndex] + '\t';
|
||||
// }
|
||||
|
||||
// console.log(line);
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* Scoring on structural items that have a label and optional description.
|
||||
*/
|
||||
export interface IItemScore {
|
||||
|
||||
/**
|
||||
* Overall score.
|
||||
*/
|
||||
score: number;
|
||||
|
||||
/**
|
||||
* Matches within the label.
|
||||
*/
|
||||
labelMatch?: IMatch[];
|
||||
|
||||
/**
|
||||
* Matches within the description.
|
||||
*/
|
||||
descriptionMatch?: IMatch[];
|
||||
}
|
||||
|
||||
const NO_ITEM_SCORE: IItemScore = Object.freeze({ score: 0 });
|
||||
|
||||
export interface IItemAccessor<T> {
|
||||
|
||||
/**
|
||||
* Just the label of the item to score on.
|
||||
*/
|
||||
getItemLabel(item: T): string;
|
||||
|
||||
/**
|
||||
* The optional description of the item to score on. Can be null.
|
||||
*/
|
||||
getItemDescription(item: T): string;
|
||||
|
||||
/**
|
||||
* If the item is a file, the path of the file to score on. Can be null.
|
||||
*/
|
||||
getItemPath(file: T): string;
|
||||
}
|
||||
|
||||
const PATH_IDENTITY_SCORE = 1 << 18;
|
||||
const LABEL_PREFIX_SCORE = 1 << 17;
|
||||
const LABEL_CAMELCASE_SCORE = 1 << 16;
|
||||
const LABEL_SCORE_THRESHOLD = 1 << 15;
|
||||
|
||||
export interface IPreparedQuery {
|
||||
original: string;
|
||||
value: string;
|
||||
lowercase: string;
|
||||
containsPathSeparator: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to prepare a search value for scoring in quick open by removing unwanted characters.
|
||||
*/
|
||||
export function prepareQuery(original: string): IPreparedQuery {
|
||||
let lowercase: string;
|
||||
let containsPathSeparator: boolean;
|
||||
let value: string;
|
||||
|
||||
if (original) {
|
||||
value = stripWildcards(original).replace(/\s/g, ''); // get rid of all wildcards and whitespace
|
||||
if (isWindows) {
|
||||
value = value.replace(/\//g, nativeSep); // Help Windows users to search for paths when using slash
|
||||
}
|
||||
|
||||
lowercase = value.toLowerCase();
|
||||
containsPathSeparator = value.indexOf(nativeSep) >= 0;
|
||||
}
|
||||
|
||||
return { original, value, lowercase, containsPathSeparator };
|
||||
}
|
||||
|
||||
export function scoreItem<T>(item: T, query: IPreparedQuery, fuzzy: boolean, accessor: IItemAccessor<T>, cache: ScorerCache): IItemScore {
|
||||
if (!item || !query.value) {
|
||||
return NO_ITEM_SCORE; // we need an item and query to score on at least
|
||||
}
|
||||
|
||||
const label = accessor.getItemLabel(item);
|
||||
if (!label) {
|
||||
return NO_ITEM_SCORE; // we need a label at least
|
||||
}
|
||||
|
||||
const description = accessor.getItemDescription(item);
|
||||
|
||||
let cacheHash: string;
|
||||
if (description) {
|
||||
cacheHash = `${label}${description}${query.value}${fuzzy}`;
|
||||
} else {
|
||||
cacheHash = `${label}${query.value}${fuzzy}`;
|
||||
}
|
||||
|
||||
const cached = cache[cacheHash];
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
const itemScore = doScoreItem(label, description, accessor.getItemPath(item), query, fuzzy);
|
||||
cache[cacheHash] = itemScore;
|
||||
|
||||
return itemScore;
|
||||
}
|
||||
|
||||
function doScoreItem(label: string, description: string, path: string, query: IPreparedQuery, fuzzy: boolean): IItemScore {
|
||||
|
||||
// 1.) treat identity matches on full path highest
|
||||
if (path && isLinux ? query.original === path : equalsIgnoreCase(query.original, path)) {
|
||||
return { score: PATH_IDENTITY_SCORE, labelMatch: [{ start: 0, end: label.length }], descriptionMatch: description ? [{ start: 0, end: description.length }] : void 0 };
|
||||
}
|
||||
|
||||
// We only consider label matches if the query is not including file path separators
|
||||
const preferLabelMatches = !path || !query.containsPathSeparator;
|
||||
if (preferLabelMatches) {
|
||||
|
||||
// 2.) treat prefix matches on the label second highest
|
||||
const prefixLabelMatch = matchesPrefix(query.value, label);
|
||||
if (prefixLabelMatch) {
|
||||
return { score: LABEL_PREFIX_SCORE, labelMatch: prefixLabelMatch };
|
||||
}
|
||||
|
||||
// 3.) treat camelcase matches on the label third highest
|
||||
const camelcaseLabelMatch = matchesCamelCase(query.value, label);
|
||||
if (camelcaseLabelMatch) {
|
||||
return { score: LABEL_CAMELCASE_SCORE, labelMatch: camelcaseLabelMatch };
|
||||
}
|
||||
|
||||
// 4.) prefer scores on the label if any
|
||||
const [labelScore, labelPositions] = score(label, query.value, query.lowercase, fuzzy);
|
||||
if (labelScore) {
|
||||
return { score: labelScore + LABEL_SCORE_THRESHOLD, labelMatch: createMatches(labelPositions) };
|
||||
}
|
||||
}
|
||||
|
||||
// 5.) finally compute description + label scores if we have a description
|
||||
if (description) {
|
||||
let descriptionPrefix = description;
|
||||
if (!!path) {
|
||||
descriptionPrefix = `${description}${nativeSep}`; // assume this is a file path
|
||||
}
|
||||
|
||||
const descriptionPrefixLength = descriptionPrefix.length;
|
||||
const descriptionAndLabel = `${descriptionPrefix}${label}`;
|
||||
|
||||
const [labelDescriptionScore, labelDescriptionPositions] = score(descriptionAndLabel, query.value, query.lowercase, fuzzy);
|
||||
if (labelDescriptionScore) {
|
||||
const labelDescriptionMatches = createMatches(labelDescriptionPositions);
|
||||
const labelMatch: IMatch[] = [];
|
||||
const descriptionMatch: IMatch[] = [];
|
||||
|
||||
// We have to split the matches back onto the label and description portions
|
||||
labelDescriptionMatches.forEach(h => {
|
||||
|
||||
// Match overlaps label and description part, we need to split it up
|
||||
if (h.start < descriptionPrefixLength && h.end > descriptionPrefixLength) {
|
||||
labelMatch.push({ start: 0, end: h.end - descriptionPrefixLength });
|
||||
descriptionMatch.push({ start: h.start, end: descriptionPrefixLength });
|
||||
}
|
||||
|
||||
// Match on label part
|
||||
else if (h.start >= descriptionPrefixLength) {
|
||||
labelMatch.push({ start: h.start - descriptionPrefixLength, end: h.end - descriptionPrefixLength });
|
||||
}
|
||||
|
||||
// Match on description part
|
||||
else {
|
||||
descriptionMatch.push(h);
|
||||
}
|
||||
});
|
||||
|
||||
return { score: labelDescriptionScore, labelMatch, descriptionMatch };
|
||||
}
|
||||
}
|
||||
|
||||
return NO_ITEM_SCORE;
|
||||
}
|
||||
|
||||
export function compareItemsByScore<T>(itemA: T, itemB: T, query: IPreparedQuery, fuzzy: boolean, accessor: IItemAccessor<T>, cache: ScorerCache, fallbackComparer = fallbackCompare): number {
|
||||
const itemScoreA = scoreItem(itemA, query, fuzzy, accessor, cache);
|
||||
const itemScoreB = scoreItem(itemB, query, fuzzy, accessor, cache);
|
||||
|
||||
const scoreA = itemScoreA.score;
|
||||
const scoreB = itemScoreB.score;
|
||||
|
||||
// 1.) prefer identity matches
|
||||
if (scoreA === PATH_IDENTITY_SCORE || scoreB === PATH_IDENTITY_SCORE) {
|
||||
if (scoreA !== scoreB) {
|
||||
return scoreA === PATH_IDENTITY_SCORE ? -1 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 2.) prefer label prefix matches
|
||||
if (scoreA === LABEL_PREFIX_SCORE || scoreB === LABEL_PREFIX_SCORE) {
|
||||
if (scoreA !== scoreB) {
|
||||
return scoreA === LABEL_PREFIX_SCORE ? -1 : 1;
|
||||
}
|
||||
|
||||
const labelA = accessor.getItemLabel(itemA);
|
||||
const labelB = accessor.getItemLabel(itemB);
|
||||
|
||||
// prefer shorter names when both match on label prefix
|
||||
if (labelA.length !== labelB.length) {
|
||||
return labelA.length - labelB.length;
|
||||
}
|
||||
}
|
||||
|
||||
// 3.) prefer camelcase matches
|
||||
if (scoreA === LABEL_CAMELCASE_SCORE || scoreB === LABEL_CAMELCASE_SCORE) {
|
||||
if (scoreA !== scoreB) {
|
||||
return scoreA === LABEL_CAMELCASE_SCORE ? -1 : 1;
|
||||
}
|
||||
|
||||
const labelA = accessor.getItemLabel(itemA);
|
||||
const labelB = accessor.getItemLabel(itemB);
|
||||
|
||||
// prefer more compact camel case matches over longer
|
||||
const comparedByMatchLength = compareByMatchLength(itemScoreA.labelMatch, itemScoreB.labelMatch);
|
||||
if (comparedByMatchLength !== 0) {
|
||||
return comparedByMatchLength;
|
||||
}
|
||||
|
||||
// prefer shorter names when both match on label camelcase
|
||||
if (labelA.length !== labelB.length) {
|
||||
return labelA.length - labelB.length;
|
||||
}
|
||||
}
|
||||
|
||||
// 4.) prefer label scores
|
||||
if (scoreA > LABEL_SCORE_THRESHOLD || scoreB > LABEL_SCORE_THRESHOLD) {
|
||||
if (scoreB < LABEL_SCORE_THRESHOLD) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (scoreA < LABEL_SCORE_THRESHOLD) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 5.) compare by score
|
||||
if (scoreA !== scoreB) {
|
||||
return scoreA > scoreB ? -1 : 1;
|
||||
}
|
||||
|
||||
// 6.) scores are identical, prefer more compact matches (label and description)
|
||||
const itemAMatchDistance = computeLabelAndDescriptionMatchDistance(itemA, itemScoreA, accessor);
|
||||
const itemBMatchDistance = computeLabelAndDescriptionMatchDistance(itemB, itemScoreB, accessor);
|
||||
if (itemAMatchDistance && itemBMatchDistance && itemAMatchDistance !== itemBMatchDistance) {
|
||||
return itemBMatchDistance > itemAMatchDistance ? -1 : 1;
|
||||
}
|
||||
|
||||
// 7.) at this point, scores are identical and match compactness as well
|
||||
// for both items so we start to use the fallback compare
|
||||
return fallbackComparer(itemA, itemB, query, accessor);
|
||||
}
|
||||
|
||||
function computeLabelAndDescriptionMatchDistance<T>(item: T, score: IItemScore, accessor: IItemAccessor<T>): number {
|
||||
const hasLabelMatches = (score.labelMatch && score.labelMatch.length);
|
||||
const hasDescriptionMatches = (score.descriptionMatch && score.descriptionMatch.length);
|
||||
|
||||
let matchStart: number = -1;
|
||||
let matchEnd: number = -1;
|
||||
|
||||
// If we have description matches, the start is first of description match
|
||||
if (hasDescriptionMatches) {
|
||||
matchStart = score.descriptionMatch[0].start;
|
||||
}
|
||||
|
||||
// Otherwise, the start is the first label match
|
||||
else if (hasLabelMatches) {
|
||||
matchStart = score.labelMatch[0].start;
|
||||
}
|
||||
|
||||
// If we have label match, the end is the last label match
|
||||
// If we had a description match, we add the length of the description
|
||||
// as offset to the end to indicate this.
|
||||
if (hasLabelMatches) {
|
||||
matchEnd = score.labelMatch[score.labelMatch.length - 1].end;
|
||||
if (hasDescriptionMatches) {
|
||||
const itemDescription = accessor.getItemDescription(item);
|
||||
if (itemDescription) {
|
||||
matchEnd += itemDescription.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we have just a description match, the end is the last description match
|
||||
else if (hasDescriptionMatches) {
|
||||
matchEnd = score.descriptionMatch[score.descriptionMatch.length - 1].end;
|
||||
}
|
||||
|
||||
return matchEnd - matchStart;
|
||||
}
|
||||
|
||||
function compareByMatchLength(matchesA?: IMatch[], matchesB?: IMatch[]): number {
|
||||
if ((!matchesA && !matchesB) || (!matchesA.length && !matchesB.length)) {
|
||||
return 0; // make sure to not cause bad comparing when matches are not provided
|
||||
}
|
||||
|
||||
if (!matchesB || !matchesB.length) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!matchesA || !matchesA.length) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Compute match length of A (first to last match)
|
||||
const matchStartA = matchesA[0].start;
|
||||
const matchEndA = matchesA[matchesA.length - 1].end;
|
||||
const matchLengthA = matchEndA - matchStartA;
|
||||
|
||||
// Compute match length of B (first to last match)
|
||||
const matchStartB = matchesB[0].start;
|
||||
const matchEndB = matchesB[matchesB.length - 1].end;
|
||||
const matchLengthB = matchEndB - matchStartB;
|
||||
|
||||
// Prefer shorter match length
|
||||
return matchLengthA === matchLengthB ? 0 : matchLengthB < matchLengthA ? 1 : -1;
|
||||
}
|
||||
|
||||
export function fallbackCompare<T>(itemA: T, itemB: T, query: IPreparedQuery, accessor: IItemAccessor<T>): number {
|
||||
|
||||
// check for label + description length and prefer shorter
|
||||
const labelA = accessor.getItemLabel(itemA);
|
||||
const labelB = accessor.getItemLabel(itemB);
|
||||
|
||||
const descriptionA = accessor.getItemDescription(itemA);
|
||||
const descriptionB = accessor.getItemDescription(itemB);
|
||||
|
||||
const labelDescriptionALength = labelA.length + (descriptionA ? descriptionA.length : 0);
|
||||
const labelDescriptionBLength = labelB.length + (descriptionB ? descriptionB.length : 0);
|
||||
|
||||
if (labelDescriptionALength !== labelDescriptionBLength) {
|
||||
return labelDescriptionALength - labelDescriptionBLength;
|
||||
}
|
||||
|
||||
// check for path length and prefer shorter
|
||||
const pathA = accessor.getItemPath(itemA);
|
||||
const pathB = accessor.getItemPath(itemB);
|
||||
|
||||
if (pathA && pathB && pathA.length !== pathB.length) {
|
||||
return pathA.length - pathB.length;
|
||||
}
|
||||
|
||||
// 7.) finally we have equal scores and equal length, we fallback to comparer
|
||||
|
||||
// compare by label
|
||||
if (labelA !== labelB) {
|
||||
return compareAnything(labelA, labelB, query.value);
|
||||
}
|
||||
|
||||
// compare by description
|
||||
if (descriptionA && descriptionB && descriptionA !== descriptionB) {
|
||||
return compareAnything(descriptionA, descriptionB, query.value);
|
||||
}
|
||||
|
||||
// compare by path
|
||||
if (pathA && pathB && pathA !== pathB) {
|
||||
return compareAnything(pathA, pathB, query.value);
|
||||
}
|
||||
|
||||
// equal
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import * as strings from './strings';
|
||||
import { CharCode } from './charCode';
|
||||
|
||||
export interface IFilter {
|
||||
// Returns null if word doesn't match.
|
||||
(word: string, wordToMatchAgainst: string): IMatch[];
|
||||
}
|
||||
|
||||
export interface IMatch {
|
||||
start: number;
|
||||
end: number;
|
||||
}
|
||||
|
||||
// Prefix
|
||||
|
||||
export const matchesPrefix: IFilter = _matchesPrefix.bind(undefined, true);
|
||||
|
||||
function _matchesPrefix(ignoreCase: boolean, word: string, wordToMatchAgainst: string): IMatch[] {
|
||||
if (!wordToMatchAgainst || wordToMatchAgainst.length < word.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let matches: boolean;
|
||||
if (ignoreCase) {
|
||||
matches = strings.startsWithIgnoreCase(wordToMatchAgainst, word);
|
||||
} else {
|
||||
matches = wordToMatchAgainst.indexOf(word) === 0;
|
||||
}
|
||||
|
||||
if (!matches) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return word.length > 0 ? [{ start: 0, end: word.length }] : [];
|
||||
}
|
||||
|
||||
// CamelCase
|
||||
|
||||
function isLower(code: number): boolean {
|
||||
return CharCode.a <= code && code <= CharCode.z;
|
||||
}
|
||||
|
||||
export function isUpper(code: number): boolean {
|
||||
return CharCode.A <= code && code <= CharCode.Z;
|
||||
}
|
||||
|
||||
function isNumber(code: number): boolean {
|
||||
return CharCode.Digit0 <= code && code <= CharCode.Digit9;
|
||||
}
|
||||
|
||||
function isWhitespace(code: number): boolean {
|
||||
return (
|
||||
code === CharCode.Space
|
||||
|| code === CharCode.Tab
|
||||
|| code === CharCode.LineFeed
|
||||
|| code === CharCode.CarriageReturn
|
||||
);
|
||||
}
|
||||
|
||||
function isAlphanumeric(code: number): boolean {
|
||||
return isLower(code) || isUpper(code) || isNumber(code);
|
||||
}
|
||||
|
||||
function join(head: IMatch, tail: IMatch[]): IMatch[] {
|
||||
if (tail.length === 0) {
|
||||
tail = [head];
|
||||
} else if (head.end === tail[0].start) {
|
||||
tail[0].start = head.start;
|
||||
} else {
|
||||
tail.unshift(head);
|
||||
}
|
||||
return tail;
|
||||
}
|
||||
|
||||
function nextAnchor(camelCaseWord: string, start: number): number {
|
||||
for (let i = start; i < camelCaseWord.length; i++) {
|
||||
let c = camelCaseWord.charCodeAt(i);
|
||||
if (isUpper(c) || isNumber(c) || (i > 0 && !isAlphanumeric(camelCaseWord.charCodeAt(i - 1)))) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return camelCaseWord.length;
|
||||
}
|
||||
|
||||
function _matchesCamelCase(word: string, camelCaseWord: string, i: number, j: number): IMatch[] {
|
||||
if (i === word.length) {
|
||||
return [];
|
||||
} else if (j === camelCaseWord.length) {
|
||||
return null;
|
||||
} else if (word[i] !== camelCaseWord[j].toLowerCase()) {
|
||||
return null;
|
||||
} else {
|
||||
let result: IMatch[] = null;
|
||||
let nextUpperIndex = j + 1;
|
||||
result = _matchesCamelCase(word, camelCaseWord, i + 1, j + 1);
|
||||
while (!result && (nextUpperIndex = nextAnchor(camelCaseWord, nextUpperIndex)) < camelCaseWord.length) {
|
||||
result = _matchesCamelCase(word, camelCaseWord, i + 1, nextUpperIndex);
|
||||
nextUpperIndex++;
|
||||
}
|
||||
return result === null ? null : join({ start: j, end: j + 1 }, result);
|
||||
}
|
||||
}
|
||||
|
||||
interface ICamelCaseAnalysis {
|
||||
upperPercent: number;
|
||||
lowerPercent: number;
|
||||
alphaPercent: number;
|
||||
numericPercent: number;
|
||||
}
|
||||
|
||||
// Heuristic to avoid computing camel case matcher for words that don't
|
||||
// look like camelCaseWords.
|
||||
function analyzeCamelCaseWord(word: string): ICamelCaseAnalysis {
|
||||
let upper = 0, lower = 0, alpha = 0, numeric = 0, code = 0;
|
||||
|
||||
for (let i = 0; i < word.length; i++) {
|
||||
code = word.charCodeAt(i);
|
||||
|
||||
if (isUpper(code)) { upper++; }
|
||||
if (isLower(code)) { lower++; }
|
||||
if (isAlphanumeric(code)) { alpha++; }
|
||||
if (isNumber(code)) { numeric++; }
|
||||
}
|
||||
|
||||
let upperPercent = upper / word.length;
|
||||
let lowerPercent = lower / word.length;
|
||||
let alphaPercent = alpha / word.length;
|
||||
let numericPercent = numeric / word.length;
|
||||
|
||||
return { upperPercent, lowerPercent, alphaPercent, numericPercent };
|
||||
}
|
||||
|
||||
function isUpperCaseWord(analysis: ICamelCaseAnalysis): boolean {
|
||||
const { upperPercent, lowerPercent } = analysis;
|
||||
return lowerPercent === 0 && upperPercent > 0.6;
|
||||
}
|
||||
|
||||
function isCamelCaseWord(analysis: ICamelCaseAnalysis): boolean {
|
||||
const { upperPercent, lowerPercent, alphaPercent, numericPercent } = analysis;
|
||||
return lowerPercent > 0.2 && upperPercent < 0.8 && alphaPercent > 0.6 && numericPercent < 0.2;
|
||||
}
|
||||
|
||||
// Heuristic to avoid computing camel case matcher for words that don't
|
||||
// look like camel case patterns.
|
||||
function isCamelCasePattern(word: string): boolean {
|
||||
let upper = 0, lower = 0, code = 0, whitespace = 0;
|
||||
|
||||
for (let i = 0; i < word.length; i++) {
|
||||
code = word.charCodeAt(i);
|
||||
|
||||
if (isUpper(code)) { upper++; }
|
||||
if (isLower(code)) { lower++; }
|
||||
if (isWhitespace(code)) { whitespace++; }
|
||||
}
|
||||
|
||||
if ((upper === 0 || lower === 0) && whitespace === 0) {
|
||||
return word.length <= 30;
|
||||
} else {
|
||||
return upper <= 5;
|
||||
}
|
||||
}
|
||||
|
||||
export function matchesCamelCase(word: string, camelCaseWord: string): IMatch[] {
|
||||
if (!camelCaseWord) {
|
||||
return null;
|
||||
}
|
||||
|
||||
camelCaseWord = camelCaseWord.trim();
|
||||
|
||||
if (camelCaseWord.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!isCamelCasePattern(word)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (camelCaseWord.length > 60) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const analysis = analyzeCamelCaseWord(camelCaseWord);
|
||||
|
||||
if (!isCamelCaseWord(analysis)) {
|
||||
if (!isUpperCaseWord(analysis)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
camelCaseWord = camelCaseWord.toLowerCase();
|
||||
}
|
||||
|
||||
let result: IMatch[] = null;
|
||||
let i = 0;
|
||||
|
||||
word = word.toLowerCase();
|
||||
while (i < camelCaseWord.length && (result = _matchesCamelCase(word, camelCaseWord, 0, i)) === null) {
|
||||
i = nextAnchor(camelCaseWord, i + 1);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function createMatches(position: number[]): IMatch[] {
|
||||
let ret: IMatch[] = [];
|
||||
if (!position) {
|
||||
return ret;
|
||||
}
|
||||
let last: IMatch;
|
||||
for (const pos of position) {
|
||||
if (last && last.end === pos) {
|
||||
last.end += 1;
|
||||
} else {
|
||||
last = { start: pos, end: pos + 1 };
|
||||
ret.push(last);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { CharCode } from './charCode';
|
||||
|
||||
export function stripWildcards(pattern: string): string {
|
||||
return pattern.replace(/\*/g, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if haystack starts with needle.
|
||||
*/
|
||||
export function startsWith(haystack: string, needle: string): boolean {
|
||||
if (haystack.length < needle.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (haystack === needle) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (let i = 0; i < needle.length; i++) {
|
||||
if (haystack[i] !== needle[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export function startsWithIgnoreCase(str: string, candidate: string): boolean {
|
||||
const candidateLength = candidate.length;
|
||||
if (candidate.length > str.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return doEqualsIgnoreCase(str, candidate, candidateLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if haystack ends with needle.
|
||||
*/
|
||||
export function endsWith(haystack: string, needle: string): boolean {
|
||||
let diff = haystack.length - needle.length;
|
||||
if (diff > 0) {
|
||||
return haystack.indexOf(needle, diff) === diff;
|
||||
} else if (diff === 0) {
|
||||
return haystack === needle;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function isLowerAsciiLetter(code: number): boolean {
|
||||
return code >= CharCode.a && code <= CharCode.z;
|
||||
}
|
||||
|
||||
function isUpperAsciiLetter(code: number): boolean {
|
||||
return code >= CharCode.A && code <= CharCode.Z;
|
||||
}
|
||||
|
||||
function isAsciiLetter(code: number): boolean {
|
||||
return isLowerAsciiLetter(code) || isUpperAsciiLetter(code);
|
||||
}
|
||||
|
||||
export function equalsIgnoreCase(a: string, b: string): boolean {
|
||||
const len1 = a ? a.length : 0;
|
||||
const len2 = b ? b.length : 0;
|
||||
|
||||
if (len1 !== len2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return doEqualsIgnoreCase(a, b);
|
||||
}
|
||||
|
||||
function doEqualsIgnoreCase(a: string, b: string, stopAt = a.length): boolean {
|
||||
if (typeof a !== 'string' || typeof b !== 'string') {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < stopAt; i++) {
|
||||
const codeA = a.charCodeAt(i);
|
||||
const codeB = b.charCodeAt(i);
|
||||
|
||||
if (codeA === codeB) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// a-z A-Z
|
||||
if (isAsciiLetter(codeA) && isAsciiLetter(codeB)) {
|
||||
let diff = Math.abs(codeA - codeB);
|
||||
if (diff !== 0 && diff !== 32) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Any other charcode
|
||||
else {
|
||||
if (String.fromCharCode(codeA).toLowerCase() !== String.fromCharCode(codeB).toLowerCase()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the characters of the provided query string are included in the
|
||||
* target string. The characters do not have to be contiguous within the string.
|
||||
*/
|
||||
export function fuzzyContains(target: string, query: string): boolean {
|
||||
if (!target || !query) {
|
||||
return false; // return early if target or query are undefined
|
||||
}
|
||||
|
||||
if (target.length < query.length) {
|
||||
return false; // impossible for query to be contained in target
|
||||
}
|
||||
|
||||
const queryLen = query.length;
|
||||
const targetLower = target.toLowerCase();
|
||||
|
||||
let index = 0;
|
||||
let lastIndexOf = -1;
|
||||
while (index < queryLen) {
|
||||
let indexOf = targetLower.indexOf(query[index], lastIndexOf + 1);
|
||||
if (indexOf < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
lastIndexOf = indexOf;
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { RipgrepTextSearchEngine } from './ripgrepTextSearch';
|
||||
import { RipgrepFileSearchEngine } from './ripgrepFileSearch';
|
||||
import { CachedSearchProvider } from './cachedSearchProvider';
|
||||
|
||||
export function activate(): void {
|
||||
if (vscode.workspace.getConfiguration('searchRipgrep').get('enable')) {
|
||||
@@ -15,17 +16,39 @@ export function activate(): void {
|
||||
}
|
||||
}
|
||||
|
||||
type SearchEngine = RipgrepFileSearchEngine | RipgrepTextSearchEngine;
|
||||
|
||||
class RipgrepSearchProvider implements vscode.SearchProvider {
|
||||
private cachedProvider: CachedSearchProvider;
|
||||
private inProgress: Set<SearchEngine> = new Set();
|
||||
|
||||
constructor(private outputChannel: vscode.OutputChannel) {
|
||||
this.cachedProvider = new CachedSearchProvider();
|
||||
process.once('exit', () => this.dispose());
|
||||
}
|
||||
|
||||
provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress<vscode.TextSearchResult>, token: vscode.CancellationToken): Thenable<void> {
|
||||
const engine = new RipgrepTextSearchEngine(this.outputChannel);
|
||||
return engine.provideTextSearchResults(query, options, progress, token);
|
||||
return this.withEngine(engine, () => engine.provideTextSearchResults(query, options, progress, token));
|
||||
}
|
||||
|
||||
provideFileSearchResults(options: vscode.SearchOptions, progress: vscode.Progress<string>, token: vscode.CancellationToken): Thenable<void> {
|
||||
provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.SearchOptions, progress: vscode.Progress<vscode.Uri>, token: vscode.CancellationToken): Thenable<void> {
|
||||
const engine = new RipgrepFileSearchEngine(this.outputChannel);
|
||||
return engine.provideFileSearchResults(options, progress, token);
|
||||
return this.withEngine(engine, () => this.cachedProvider.provideFileSearchResults(engine, query, options, progress, token));
|
||||
}
|
||||
|
||||
clearCache(cacheKey: string): void {
|
||||
this.cachedProvider.clearCache(cacheKey);
|
||||
}
|
||||
|
||||
private withEngine(engine: SearchEngine, fn: () => Thenable<void>): Thenable<void> {
|
||||
this.inProgress.add(engine);
|
||||
return fn().then(() => {
|
||||
this.inProgress.delete(engine);
|
||||
});
|
||||
}
|
||||
|
||||
private dispose() {
|
||||
this.inProgress.forEach(engine => engine.cancel());
|
||||
}
|
||||
}
|
||||
@@ -7,30 +7,31 @@ import * as cp from 'child_process';
|
||||
import { Readable } from 'stream';
|
||||
import { NodeStringDecoder, StringDecoder } from 'string_decoder';
|
||||
import * as vscode from 'vscode';
|
||||
import { normalizeNFC, normalizeNFD } from './normalization';
|
||||
import { normalizeNFC, normalizeNFD } from './common/normalization';
|
||||
import { rgPath } from './ripgrep';
|
||||
import { anchorGlob } from './ripgrepHelpers';
|
||||
import { anchorGlob } from './utils';
|
||||
import { rgErrorMsgForDisplay } from './ripgrepTextSearch';
|
||||
import { IInternalFileSearchProvider } from './cachedSearchProvider';
|
||||
|
||||
const isMac = process.platform === 'darwin';
|
||||
|
||||
// If vscode-ripgrep is in an .asar file, then the binary is unpacked.
|
||||
const rgDiskPath = rgPath.replace(/\bnode_modules\.asar\b/, 'node_modules.asar.unpacked');
|
||||
|
||||
export class RipgrepFileSearchEngine {
|
||||
export class RipgrepFileSearchEngine implements IInternalFileSearchProvider {
|
||||
private rgProc: cp.ChildProcess;
|
||||
private killRgProcFn: (code?: number) => void;
|
||||
private isDone: boolean;
|
||||
|
||||
constructor(private outputChannel: vscode.OutputChannel) {
|
||||
this.killRgProcFn = () => this.rgProc && this.rgProc.kill();
|
||||
process.once('exit', this.killRgProcFn);
|
||||
constructor(private outputChannel: vscode.OutputChannel) { }
|
||||
|
||||
cancel() {
|
||||
this.isDone = true;
|
||||
if (this.rgProc) {
|
||||
this.rgProc.kill();
|
||||
}
|
||||
}
|
||||
|
||||
private dispose() {
|
||||
process.removeListener('exit', this.killRgProcFn);
|
||||
}
|
||||
|
||||
provideFileSearchResults(options: vscode.SearchOptions, progress: vscode.Progress<string>, token: vscode.CancellationToken): Thenable<void> {
|
||||
provideFileSearchResults(options: vscode.FileSearchOptions, progress: vscode.Progress<string>, token: vscode.CancellationToken): Thenable<void> {
|
||||
this.outputChannel.appendLine(`provideFileSearchResults ${JSON.stringify({
|
||||
...options,
|
||||
...{
|
||||
@@ -39,12 +40,7 @@ export class RipgrepFileSearchEngine {
|
||||
})}`);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let isDone = false;
|
||||
const cancel = () => {
|
||||
isDone = true;
|
||||
this.rgProc.kill();
|
||||
};
|
||||
token.onCancellationRequested(cancel);
|
||||
token.onCancellationRequested(() => this.cancel());
|
||||
|
||||
const rgArgs = getRgArgs(options);
|
||||
|
||||
@@ -93,7 +89,7 @@ export class RipgrepFileSearchEngine {
|
||||
});
|
||||
|
||||
if (last) {
|
||||
if (isDone) {
|
||||
if (this.isDone) {
|
||||
resolve();
|
||||
} else {
|
||||
// Trigger last result
|
||||
@@ -106,12 +102,7 @@ export class RipgrepFileSearchEngine {
|
||||
}
|
||||
}
|
||||
});
|
||||
}).then(
|
||||
() => this.dispose(),
|
||||
err => {
|
||||
this.dispose();
|
||||
return Promise.reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private collectStdout(cmd: cp.ChildProcess, cb: (err: Error, stdout?: string, last?: boolean) => void): void {
|
||||
|
||||
@@ -7,10 +7,11 @@
|
||||
|
||||
import * as cp from 'child_process';
|
||||
import { EventEmitter } from 'events';
|
||||
import * as path from 'path';
|
||||
import { NodeStringDecoder, StringDecoder } from 'string_decoder';
|
||||
import * as vscode from 'vscode';
|
||||
import { rgPath } from './ripgrep';
|
||||
import { anchorGlob } from './ripgrepHelpers';
|
||||
import { anchorGlob } from './utils';
|
||||
|
||||
// If vscode-ripgrep is in an .asar file, then the binary is unpacked.
|
||||
const rgDiskPath = rgPath.replace(/\bnode_modules\.asar\b/, 'node_modules.asar.unpacked');
|
||||
@@ -21,12 +22,21 @@ const MAX_TEXT_RESULTS = 10000;
|
||||
export class RipgrepTextSearchEngine {
|
||||
private isDone = false;
|
||||
private rgProc: cp.ChildProcess;
|
||||
private killRgProcFn: (code?: number) => void;
|
||||
|
||||
private ripgrepParser: RipgrepParser;
|
||||
|
||||
constructor(private outputChannel: vscode.OutputChannel) {
|
||||
this.killRgProcFn = () => this.rgProc && this.rgProc.kill();
|
||||
constructor(private outputChannel: vscode.OutputChannel) { }
|
||||
|
||||
cancel() {
|
||||
this.isDone = true;
|
||||
|
||||
if (this.rgProc) {
|
||||
this.rgProc.kill();
|
||||
}
|
||||
|
||||
if (this.ripgrepParser) {
|
||||
this.ripgrepParser.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress<vscode.TextSearchResult>, token: vscode.CancellationToken): Thenable<void> {
|
||||
@@ -38,12 +48,7 @@ export class RipgrepTextSearchEngine {
|
||||
})}`);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const cancel = () => {
|
||||
this.isDone = true;
|
||||
this.ripgrepParser.cancel();
|
||||
this.rgProc.kill();
|
||||
};
|
||||
token.onCancellationRequested(cancel);
|
||||
token.onCancellationRequested(() => this.cancel());
|
||||
|
||||
const rgArgs = getRgArgs(query, options);
|
||||
|
||||
@@ -55,7 +60,6 @@ export class RipgrepTextSearchEngine {
|
||||
this.outputChannel.appendLine(`rg ${escapedArgs}\n - cwd: ${cwd}`);
|
||||
|
||||
this.rgProc = cp.spawn(rgDiskPath, rgArgs, { cwd });
|
||||
process.once('exit', this.killRgProcFn);
|
||||
this.rgProc.on('error', e => {
|
||||
console.error(e);
|
||||
this.outputChannel.append('Error: ' + (e && e.message));
|
||||
@@ -70,7 +74,7 @@ export class RipgrepTextSearchEngine {
|
||||
});
|
||||
|
||||
this.ripgrepParser.on('hitLimit', () => {
|
||||
cancel();
|
||||
this.cancel();
|
||||
});
|
||||
|
||||
this.rgProc.stdout.on('data', data => {
|
||||
@@ -91,7 +95,6 @@ export class RipgrepTextSearchEngine {
|
||||
this.outputChannel.appendLine(gotData ? 'Got data from stdout' : 'No data from stdout');
|
||||
this.outputChannel.appendLine(gotResult ? 'Got result from parser' : 'No result from parser');
|
||||
this.outputChannel.appendLine('');
|
||||
process.removeListener('exit', this.killRgProcFn);
|
||||
if (this.isDone) {
|
||||
resolve();
|
||||
} else {
|
||||
@@ -291,7 +294,7 @@ export class RipgrepParser extends EventEmitter {
|
||||
lineMatches
|
||||
.map(range => {
|
||||
return <vscode.TextSearchResult>{
|
||||
path: this.currentFile,
|
||||
uri: vscode.Uri.file(path.join(this.rootFolder, this.currentFile)),
|
||||
range,
|
||||
preview: {
|
||||
text: preview,
|
||||
|
||||
@@ -5,9 +5,8 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
export function fixDriveC(_path: string): string {
|
||||
const root = path.parse(_path).root;
|
||||
@@ -19,3 +18,10 @@ export function fixDriveC(_path: string): string {
|
||||
export function anchorGlob(glob: string): string {
|
||||
return glob.startsWith('**') || glob.startsWith('/') ? glob : `/${glob}`;
|
||||
}
|
||||
|
||||
export function joinPath(resource: vscode.Uri, pathFragment: string): vscode.Uri {
|
||||
const joinedPath = path.join(resource.path || '/', pathFragment);
|
||||
return resource.with({
|
||||
path: joinedPath
|
||||
});
|
||||
}
|
||||
@@ -1545,9 +1545,9 @@ vscode-extension-telemetry@0.0.15:
|
||||
dependencies:
|
||||
applicationinsights "1.0.1"
|
||||
|
||||
vscode-nls@^3.2.1:
|
||||
version "3.2.2"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.2.tgz#3817eca5b985c2393de325197cf4e15eb2aa5350"
|
||||
vscode-nls@^3.2.4:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.4.tgz#2166b4183c8aea884d20727f5449e62be69fd398"
|
||||
|
||||
vscode-ripgrep@^1.0.1:
|
||||
version "1.0.1"
|
||||
|
||||
@@ -290,7 +290,9 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "support.type.property-name.json",
|
||||
"scope": [
|
||||
"support.type.property-name.json"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#0451a5"
|
||||
}
|
||||
|
||||
@@ -40,7 +40,8 @@
|
||||
"name": "Comments: Documentation",
|
||||
"scope": [
|
||||
"comment.documentation",
|
||||
"comment.block.documentation"
|
||||
"comment.block.documentation",
|
||||
"comment.block.documentation punctuation.definition.comment "
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#448C27"
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"If you want to provide a fix or improvement, please create a pull request against the original repository.",
|
||||
"Once accepted there, we are happy to receive an update request."
|
||||
],
|
||||
"version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/7bf8960f7042474b10b519f39339fc527907ce16",
|
||||
"version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/40288b872220e5c0b844b1de507f1749ed14589b",
|
||||
"name": "TypeScript",
|
||||
"scopeName": "source.ts",
|
||||
"patterns": [
|
||||
@@ -4149,7 +4149,7 @@
|
||||
},
|
||||
"directives": {
|
||||
"name": "comment.line.triple-slash.directive.ts",
|
||||
"begin": "^(///)\\s*(?=<(reference|amd-dependency|amd-module)(\\s+(path|types|no-default-lib|name)\\s*=\\s*((\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")))+\\s*/>\\s*$)",
|
||||
"begin": "^(///)\\s*(?=<(reference|amd-dependency|amd-module)(\\s+(path|types|no-default-lib|lib|name)\\s*=\\s*((\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")))+\\s*/>\\s*$)",
|
||||
"beginCaptures": {
|
||||
"1": {
|
||||
"name": "punctuation.definition.comment.ts"
|
||||
@@ -4177,7 +4177,7 @@
|
||||
"patterns": [
|
||||
{
|
||||
"name": "entity.other.attribute-name.directive.ts",
|
||||
"match": "path|types|no-default-lib|name"
|
||||
"match": "path|types|no-default-lib|lib|name"
|
||||
},
|
||||
{
|
||||
"name": "keyword.operator.assignment.ts",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"If you want to provide a fix or improvement, please create a pull request against the original repository.",
|
||||
"Once accepted there, we are happy to receive an update request."
|
||||
],
|
||||
"version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/7bf8960f7042474b10b519f39339fc527907ce16",
|
||||
"version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/88217b1c7d36ed5d35adc3099ba7978aabe2531b",
|
||||
"name": "TypeScriptReact",
|
||||
"scopeName": "source.tsx",
|
||||
"patterns": [
|
||||
@@ -4115,7 +4115,7 @@
|
||||
},
|
||||
"directives": {
|
||||
"name": "comment.line.triple-slash.directive.tsx",
|
||||
"begin": "^(///)\\s*(?=<(reference|amd-dependency|amd-module)(\\s+(path|types|no-default-lib|name)\\s*=\\s*((\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")))+\\s*/>\\s*$)",
|
||||
"begin": "^(///)\\s*(?=<(reference|amd-dependency|amd-module)(\\s+(path|types|no-default-lib|lib|name)\\s*=\\s*((\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")))+\\s*/>\\s*$)",
|
||||
"beginCaptures": {
|
||||
"1": {
|
||||
"name": "punctuation.definition.comment.tsx"
|
||||
@@ -4143,7 +4143,7 @@
|
||||
"patterns": [
|
||||
{
|
||||
"name": "entity.other.attribute-name.directive.tsx",
|
||||
"match": "path|types|no-default-lib|name"
|
||||
"match": "path|types|no-default-lib|lib|name"
|
||||
},
|
||||
{
|
||||
"name": "keyword.operator.assignment.tsx",
|
||||
@@ -4664,8 +4664,8 @@
|
||||
]
|
||||
},
|
||||
"jsx-tag-in-expression": {
|
||||
"begin": "(?x)\n (?<!\\+\\+|--)(?<=[({\\[,?=>:*]|&&|\\|\\||\\?|^return|[^\\._$[:alnum:]]return|^default|[^\\._$[:alnum:]]default|^)\\s*\n (?!<\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s+[^=>])|,)) # look ahead is not type parameter of arrow\n (?=(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$a-zA-Z][-$\\w.]*))(?<!\\.|-))(?=((<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)|(\\s+))(?!\\?)|\\/?>))",
|
||||
"end": "(?!(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$a-zA-Z][-$\\w.]*))(?<!\\.|-))(?=((<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)|(\\s+))(?!\\?)|\\/?>))",
|
||||
"begin": "(?x)\n (?<!\\+\\+|--)(?<=[({\\[,?=>:*]|&&|\\|\\||\\?|^return|[^\\._$[:alnum:]]return|^default|[^\\._$[:alnum:]]default|^)\\s*\n (?!<\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s+[^=>])|,)) # look ahead is not type parameter of arrow\n (?=(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$a-zA-Z][-$\\w.]*))(?<!\\.|-))(?=((<\\s*)|(\\s+))(?!\\?)|\\/?>))",
|
||||
"end": "(?!(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$a-zA-Z][-$\\w.]*))(?<!\\.|-))(?=((<\\s*)|(\\s+))(?!\\?)|\\/?>))",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#jsx-tag"
|
||||
@@ -4674,7 +4674,7 @@
|
||||
},
|
||||
"jsx-tag": {
|
||||
"name": "meta.tag.tsx",
|
||||
"begin": "(?=(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$a-zA-Z][-$\\w.]*))(?<!\\.|-))(?=((<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)|(\\s+))(?!\\?)|\\/?>))",
|
||||
"begin": "(?=(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$a-zA-Z][-$\\w.]*))(?<!\\.|-))(?=((<\\s*)|(\\s+))(?!\\?)|\\/?>))",
|
||||
"end": "(/>)|(?:(</)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$a-zA-Z][-$\\w.]*))(?<!\\.|-))?\\s*(>))",
|
||||
"endCaptures": {
|
||||
"1": {
|
||||
@@ -4701,7 +4701,7 @@
|
||||
},
|
||||
"patterns": [
|
||||
{
|
||||
"begin": "(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$a-zA-Z][-$\\w.]*))(?<!\\.|-))(?=((<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)|(\\s+))(?!\\?)|\\/?>)",
|
||||
"begin": "(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$a-zA-Z][-$\\w.]*))(?<!\\.|-))(?=((<\\s*)|(\\s+))(?!\\?)|\\/?>)",
|
||||
"beginCaptures": {
|
||||
"1": {
|
||||
"name": "punctuation.definition.tag.begin.tsx"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user