Merge remote-tracking branch 'origin/main' into alex/main-process-extension-host
This commit is contained in:
commit
18777d92ea
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
@ -86,6 +86,7 @@
|
|||
"splitview",
|
||||
"table",
|
||||
"list",
|
||||
"git"
|
||||
"git",
|
||||
"sash"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -233,7 +233,7 @@ steps:
|
|||
APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH)
|
||||
APP_NAME="`ls $APP_ROOT | head -n 1`"
|
||||
VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin" \
|
||||
yarn smoketest-no-compile --build "$APP_ROOT/$APP_NAME" --remote --screenshots $(Build.SourcesDirectory)/.build/logs/smoke-tests
|
||||
yarn smoketest-no-compile --build "$APP_ROOT/$APP_NAME" --remote --screenshots $(Build.SourcesDirectory)/.build/logs/smoke-tests-remote
|
||||
timeoutInMinutes: 5
|
||||
displayName: Run smoke tests (Remote)
|
||||
condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
||||
|
|
|
@ -220,7 +220,7 @@ steps:
|
|||
set -e
|
||||
APP_PATH=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH)
|
||||
VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH)" \
|
||||
yarn smoketest-no-compile --build "$APP_PATH" --remote --electronArgs="--disable-dev-shm-usage --use-gl=swiftshader" --screenshots $(Build.SourcesDirectory)/.build/logs/smoke-tests
|
||||
yarn smoketest-no-compile --build "$APP_PATH" --remote --electronArgs="--disable-dev-shm-usage --use-gl=swiftshader" --screenshots $(Build.SourcesDirectory)/.build/logs/smoke-tests-remote
|
||||
timeoutInMinutes: 5
|
||||
displayName: Run smoke tests (Remote)
|
||||
condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
||||
|
|
|
@ -216,7 +216,7 @@ steps:
|
|||
$ErrorActionPreference = "Stop"
|
||||
$AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)"
|
||||
$env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH)"
|
||||
exec { yarn smoketest-no-compile --build "$AppRoot" --remote }
|
||||
exec { yarn smoketest-no-compile --build "$AppRoot" --remote --screenshots $(Build.SourcesDirectory)\.build\logs\smoke-tests-remote }
|
||||
displayName: Run smoke tests (Remote)
|
||||
timeoutInMinutes: 5
|
||||
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64'))
|
||||
|
|
|
@ -7,7 +7,7 @@ module.exports = new class ApiEventNaming {
|
|||
constructor() {
|
||||
this.meta = {
|
||||
messages: {
|
||||
comment: 'region comments should start with the GH issue link, e.g #region https://github.com/microsoft/vscode/issues/<number>',
|
||||
comment: 'region comments should start with a camel case identifier, `:`, then either a GH issue link or owner, e.g #region myProposalName: https://github.com/microsoft/vscode/issues/<number>',
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -15,14 +15,14 @@ module.exports = new class ApiEventNaming {
|
|||
const sourceCode = context.getSourceCode();
|
||||
return {
|
||||
['Program']: (_node) => {
|
||||
for (let comment of sourceCode.getAllComments()) {
|
||||
for (const comment of sourceCode.getAllComments()) {
|
||||
if (comment.type !== 'Line') {
|
||||
continue;
|
||||
}
|
||||
if (!comment.value.match(/^\s*#region /)) {
|
||||
if (!/^\s*#region /.test(comment.value)) {
|
||||
continue;
|
||||
}
|
||||
if (!comment.value.match(/https:\/\/github.com\/microsoft\/vscode\/issues\/\d+/i)) {
|
||||
if (!/^\s*#region ([a-z]+): (@[a-z]+|https:\/\/github.com\/microsoft\/vscode\/issues\/\d+)/i.test(comment.value)) {
|
||||
context.report({
|
||||
node: comment,
|
||||
messageId: 'comment',
|
||||
|
|
|
@ -9,7 +9,7 @@ export = new class ApiEventNaming implements eslint.Rule.RuleModule {
|
|||
|
||||
readonly meta: eslint.Rule.RuleMetaData = {
|
||||
messages: {
|
||||
comment: 'region comments should start with the GH issue link, e.g #region https://github.com/microsoft/vscode/issues/<number>',
|
||||
comment: 'region comments should start with a camel case identifier, `:`, then either a GH issue link or owner, e.g #region myProposalName: https://github.com/microsoft/vscode/issues/<number>',
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -17,18 +17,16 @@ export = new class ApiEventNaming implements eslint.Rule.RuleModule {
|
|||
|
||||
const sourceCode = context.getSourceCode();
|
||||
|
||||
|
||||
return {
|
||||
['Program']: (_node: any) => {
|
||||
|
||||
for (let comment of sourceCode.getAllComments()) {
|
||||
for (const comment of sourceCode.getAllComments()) {
|
||||
if (comment.type !== 'Line') {
|
||||
continue;
|
||||
}
|
||||
if (!comment.value.match(/^\s*#region /)) {
|
||||
if (!/^\s*#region /.test(comment.value)) {
|
||||
continue;
|
||||
}
|
||||
if (!comment.value.match(/https:\/\/github.com\/microsoft\/vscode\/issues\/\d+/i)) {
|
||||
if (!/^\s*#region ([a-z]+): (@[a-z]+|https:\/\/github.com\/microsoft\/vscode\/issues\/\d+)/i.test(comment.value)) {
|
||||
context.report({
|
||||
node: <any>comment,
|
||||
messageId: 'comment',
|
||||
|
|
|
@ -8,7 +8,7 @@ let err = false;
|
|||
const majorNodeVersion = parseInt(/^(\d+)\./.exec(process.versions.node)[1]);
|
||||
|
||||
if (majorNodeVersion < 14 || majorNodeVersion >= 17) {
|
||||
console.error('\033[1;31m*** Please use node.js versions >=14 and <=17.\033[0;0m');
|
||||
console.error('\033[1;31m*** Please use node.js versions >=14 and <17.\033[0;0m');
|
||||
err = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ export async function wrapWithAbbreviation(args: any): Promise<boolean> {
|
|||
|
||||
const helper = getEmmetHelper();
|
||||
|
||||
const operationRanges = editor.selections.sort((a, b) => a.start.compareTo(b.start)).map(selection => {
|
||||
const operationRanges = Array.from(editor.selections).sort((a, b) => a.start.compareTo(b.start)).map(selection => {
|
||||
let rangeToReplace: vscode.Range = selection;
|
||||
// wrap around the node if the selection falls inside its open or close tag
|
||||
{
|
||||
|
|
|
@ -8,8 +8,8 @@ import { getHtmlFlatNode, offsetRangeToSelection, validate } from './util';
|
|||
import { getRootNode } from './parseDocument';
|
||||
import { HtmlNode as HtmlFlatNode } from 'EmmetFlatNode';
|
||||
|
||||
let balanceOutStack: Array<vscode.Selection[]> = [];
|
||||
let lastBalancedSelections: vscode.Selection[] = [];
|
||||
let balanceOutStack: Array<readonly vscode.Selection[]> = [];
|
||||
let lastBalancedSelections: readonly vscode.Selection[] = [];
|
||||
|
||||
export function balanceOut() {
|
||||
balance(true);
|
||||
|
@ -31,10 +31,8 @@ function balance(out: boolean) {
|
|||
}
|
||||
|
||||
const rangeFn = out ? getRangeToBalanceOut : getRangeToBalanceIn;
|
||||
let newSelections: vscode.Selection[] = [];
|
||||
editor.selections.forEach(selection => {
|
||||
const range = rangeFn(document, rootNode, selection);
|
||||
newSelections.push(range);
|
||||
let newSelections: readonly vscode.Selection[] = editor.selections.map(selection => {
|
||||
return rangeFn(document, rootNode, selection);
|
||||
});
|
||||
|
||||
// check whether we are starting a balance elsewhere
|
||||
|
@ -122,7 +120,7 @@ function getRangeToBalanceIn(document: vscode.TextDocument, rootNode: HtmlFlatNo
|
|||
return offsetRangeToSelection(document, firstChild.start, firstChild.end);
|
||||
}
|
||||
|
||||
function areSameSelections(a: vscode.Selection[], b: vscode.Selection[]): boolean {
|
||||
function areSameSelections(a: readonly vscode.Selection[], b: readonly vscode.Selection[]): boolean {
|
||||
if (a.length !== b.length) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ export function mergeLines() {
|
|||
}
|
||||
|
||||
return editor.edit(editBuilder => {
|
||||
editor.selections.reverse().forEach(selection => {
|
||||
Array.from(editor.selections).reverse().forEach(selection => {
|
||||
const textEdit = getRangesToReplace(editor.document, selection, rootNode);
|
||||
if (textEdit) {
|
||||
editBuilder.replace(textEdit.range, textEdit.newText);
|
||||
|
|
|
@ -19,7 +19,7 @@ export function removeTag() {
|
|||
return;
|
||||
}
|
||||
|
||||
let finalRangesToRemove = editor.selections.reverse()
|
||||
let finalRangesToRemove = Array.from(editor.selections).reverse()
|
||||
.reduce<vscode.Range[]>((prev, selection) =>
|
||||
prev.concat(getRangesToRemove(editor.document, rootNode, selection)), []);
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ export function splitJoinTag() {
|
|||
}
|
||||
|
||||
return editor.edit(editBuilder => {
|
||||
editor.selections.reverse().forEach(selection => {
|
||||
Array.from(editor.selections).reverse().forEach(selection => {
|
||||
const documentText = document.getText();
|
||||
const offset = document.offsetAt(selection.start);
|
||||
const nodeToUpdate = getHtmlFlatNode(documentText, rootNode, offset, true);
|
||||
|
|
|
@ -28,7 +28,7 @@ export function toggleComment(): Thenable<boolean> | undefined {
|
|||
|
||||
return editor.edit(editBuilder => {
|
||||
let allEdits: vscode.TextEdit[][] = [];
|
||||
editor.selections.reverse().forEach(selection => {
|
||||
Array.from(editor.selections).reverse().forEach(selection => {
|
||||
const edits = isStyleSheet(editor.document.languageId) ? toggleCommentStylesheet(editor.document, selection, <Stylesheet>rootNode) : toggleCommentHTML(editor.document, selection, rootNode!);
|
||||
if (edits.length > 0) {
|
||||
allEdits.push(edits);
|
||||
|
|
|
@ -23,7 +23,7 @@ export function updateImageSize(): Promise<boolean> | undefined {
|
|||
}
|
||||
const editor = window.activeTextEditor;
|
||||
|
||||
const allUpdatesPromise = editor.selections.reverse().map(selection => {
|
||||
const allUpdatesPromise = Array.from(editor.selections).reverse().map(selection => {
|
||||
const position = selection.isReversed ? selection.active : selection.anchor;
|
||||
if (!isStyleSheet(editor.document.languageId)) {
|
||||
return updateImageSizeHTML(editor, position);
|
||||
|
|
|
@ -25,7 +25,7 @@ export async function updateTag(tagName: string | undefined): Promise<boolean |
|
|||
return;
|
||||
}
|
||||
|
||||
const rangesToUpdate = editor.selections.reverse()
|
||||
const rangesToUpdate = Array.from(editor.selections).reverse()
|
||||
.reduce<TagRange[]>((prev, selection) =>
|
||||
prev.concat(getRangesToUpdate(document, selection, rootNode)), []);
|
||||
if (!rangesToUpdate.length) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/bin/sh
|
||||
VSCODE_GIT_ASKPASS_PIPE=`mktemp`
|
||||
ELECTRON_RUN_AS_NODE="1" VSCODE_GIT_ASKPASS_PIPE="$VSCODE_GIT_ASKPASS_PIPE" "$VSCODE_GIT_ASKPASS_NODE" "$VSCODE_GIT_ASKPASS_EXTRA_ARGS" "$VSCODE_GIT_ASKPASS_MAIN" $*
|
||||
ELECTRON_RUN_AS_NODE="1" VSCODE_GIT_ASKPASS_PIPE="$VSCODE_GIT_ASKPASS_PIPE" "$VSCODE_GIT_ASKPASS_NODE" "$VSCODE_GIT_ASKPASS_MAIN" $VSCODE_GIT_ASKPASS_EXTRA_ARGS $*
|
||||
cat $VSCODE_GIT_ASKPASS_PIPE
|
||||
rm $VSCODE_GIT_ASKPASS_PIPE
|
||||
|
|
|
@ -49,7 +49,7 @@ export function applyLineChanges(original: TextDocument, modified: TextDocument,
|
|||
return result.join('');
|
||||
}
|
||||
|
||||
export function toLineRanges(selections: Selection[], textDocument: TextDocument): Range[] {
|
||||
export function toLineRanges(selections: readonly Selection[], textDocument: TextDocument): Range[] {
|
||||
const lineRanges = selections.map(s => {
|
||||
const startLine = textDocument.lineAt(s.start.line);
|
||||
const endLine = textDocument.lineAt(s.end.line);
|
||||
|
|
|
@ -56,7 +56,9 @@ suite('git smoke test', function () {
|
|||
git = ext!.exports.getAPI(1);
|
||||
|
||||
if (git.repositories.length === 0) {
|
||||
await eventToPromise(git.onDidOpenRepository);
|
||||
const onDidOpenRepository = eventToPromise(git.onDidOpenRepository);
|
||||
await commands.executeCommand('git.openRepository', cwd);
|
||||
await onDidOpenRepository;
|
||||
}
|
||||
|
||||
assert.strictEqual(git.repositories.length, 1);
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
"html.format.wrapAttributes.preservealigned": "Preserve wrapping of attributes but align.",
|
||||
"html.format.templating.desc": "Honor django, erb, handlebars and php templating language tags.",
|
||||
"html.format.unformattedContentDelimiter.desc": "Keep text content together between this string.",
|
||||
"html.format.wrapAttributesIndentSize.desc": "Alignment size when using 'force aligned' and 'aligned multiple' in `#html.format.wrapAttributes#` or `null` to use the default indent size.",
|
||||
"html.format.wrapAttributesIndentSize.desc": "Indent wrapped attributes to after N characters. Use `null` to use the default indent size. Ignored if `#html.format.wrapAttributes#` is set to 'aligned'.",
|
||||
"html.suggest.html5.desc": "Controls whether the built-in HTML language support suggests HTML5 tags, properties and values.",
|
||||
"html.trace.server.desc": "Traces the communication between VS Code and the HTML language server.",
|
||||
"html.validate.scripts": "Controls whether the built-in HTML language support validates embedded scripts.",
|
||||
|
|
|
@ -161,7 +161,6 @@ export const activate: ActivationFunction<void> = (ctx) => {
|
|||
|
||||
pre code {
|
||||
font-family: var(--vscode-editor-font-family);
|
||||
font-size: var(--vscode-editor-font-size);
|
||||
|
||||
line-height: 1.357em;
|
||||
white-space: pre-wrap;
|
||||
|
|
|
@ -134,6 +134,7 @@ window.addEventListener('message', async event => {
|
|||
root.replaceWith(newContent.querySelector('.markdown-body')!);
|
||||
documentResource = event.data.source;
|
||||
} else {
|
||||
// Compare two elements but skip `data-line`
|
||||
const areEqual = (a: Element, b: Element): boolean => {
|
||||
if (a.isEqualNode(b)) {
|
||||
return true;
|
||||
|
@ -143,6 +144,23 @@ window.addEventListener('message', async event => {
|
|||
return false;
|
||||
}
|
||||
|
||||
const aAttrs = a.attributes;
|
||||
const bAttrs = b.attributes;
|
||||
if (aAttrs.length !== bAttrs.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < aAttrs.length; ++i) {
|
||||
const aAttr = aAttrs[i];
|
||||
const bAttr = bAttrs[i];
|
||||
if (aAttr.name !== bAttr.name) {
|
||||
return false;
|
||||
}
|
||||
if (aAttr.value !== bAttr.value && aAttr.name !== 'data-line') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const aChildren = Array.from(a.children);
|
||||
const bChildren = Array.from(b.children);
|
||||
|
||||
|
|
|
@ -120,6 +120,7 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider {
|
|||
private imageInfo: { readonly id: string, readonly width: number, readonly height: number; }[] = [];
|
||||
|
||||
private readonly _fileWatchersBySrc = new Map</* src: */ string, vscode.FileSystemWatcher>();
|
||||
|
||||
private readonly _onScrollEmitter = this._register(new vscode.EventEmitter<LastScrollLocation>());
|
||||
public readonly onScroll = this._onScrollEmitter.event;
|
||||
|
||||
|
@ -262,13 +263,13 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider {
|
|||
* The first call immediately refreshes the preview,
|
||||
* calls happening shortly thereafter are debounced.
|
||||
*/
|
||||
public refresh() {
|
||||
public refresh(forceUpdate: boolean = false) {
|
||||
// Schedule update if none is pending
|
||||
if (!this.throttleTimer) {
|
||||
if (this.firstUpdate) {
|
||||
this.updatePreview(true);
|
||||
} else {
|
||||
this.throttleTimer = setTimeout(() => this.updatePreview(true), this.delay);
|
||||
this.throttleTimer = setTimeout(() => this.updatePreview(forceUpdate), this.delay);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -333,7 +334,7 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider {
|
|||
return;
|
||||
}
|
||||
|
||||
const shouldReloadPage = !this.currentVersion || this.currentVersion.resource.toString() !== pendingVersion.resource.toString();
|
||||
const shouldReloadPage = forceUpdate || !this.currentVersion || this.currentVersion.resource.toString() !== pendingVersion.resource.toString();
|
||||
this.currentVersion = pendingVersion;
|
||||
|
||||
const content = await (shouldReloadPage
|
||||
|
@ -429,7 +430,7 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider {
|
|||
if (uri && uri.scheme === 'file' && !this._fileWatchersBySrc.has(src)) {
|
||||
const watcher = vscode.workspace.createFileSystemWatcher(uri.fsPath);
|
||||
watcher.onDidChange(() => {
|
||||
this.refresh();
|
||||
this.refresh(true);
|
||||
});
|
||||
this._fileWatchersBySrc.set(src, watcher);
|
||||
}
|
||||
|
|
|
@ -15,10 +15,13 @@ function workspaceFile(...segments: string[]) {
|
|||
}
|
||||
|
||||
async function getLinksForFile(file: vscode.Uri): Promise<vscode.DocumentLink[]> {
|
||||
return (await vscode.commands.executeCommand<vscode.DocumentLink[]>('vscode.executeLinkProvider', file))!;
|
||||
console.log('getting links', file.toString(), Date.now());
|
||||
const r = (await vscode.commands.executeCommand<vscode.DocumentLink[]>('vscode.executeLinkProvider', file))!;
|
||||
console.log('got links', file.toString(), Date.now());
|
||||
return r;
|
||||
}
|
||||
|
||||
suite('Markdown Document links', () => {
|
||||
suite.skip('Markdown Document links', () => {
|
||||
|
||||
setup(async () => {
|
||||
// the tests make the assumption that link providers are already registered
|
||||
|
@ -94,7 +97,6 @@ suite('Markdown Document links', () => {
|
|||
assert.strictEqual(vscode.window.activeTextEditor!.selection.start.line, 1);
|
||||
});
|
||||
|
||||
|
||||
test('Should navigate to line number within non-md file', async () => {
|
||||
await withFileContents(testFileA, '[b](sub/foo.txt#L3)');
|
||||
|
||||
|
@ -147,15 +149,21 @@ function assertActiveDocumentUri(expectedUri: vscode.Uri) {
|
|||
}
|
||||
|
||||
async function withFileContents(file: vscode.Uri, contents: string): Promise<void> {
|
||||
console.log('openTextDocument', file.toString(), Date.now());
|
||||
const document = await vscode.workspace.openTextDocument(file);
|
||||
console.log('showTextDocument', file.toString(), Date.now());
|
||||
const editor = await vscode.window.showTextDocument(document);
|
||||
console.log('editTextDocument', file.toString(), Date.now());
|
||||
await editor.edit(edit => {
|
||||
edit.replace(new vscode.Range(0, 0, 1000, 0), contents);
|
||||
});
|
||||
console.log('opened done', vscode.window.activeTextEditor?.document.toString(), Date.now());
|
||||
}
|
||||
|
||||
async function executeLink(link: vscode.DocumentLink) {
|
||||
console.log('executeingLink', link.target?.toString(), Date.now());
|
||||
|
||||
const args = JSON.parse(decodeURIComponent(link.target!.query));
|
||||
await vscode.commands.executeCommand(link.target!.path, args);
|
||||
console.log('executedLink', vscode.window.activeTextEditor?.document.toString(), Date.now());
|
||||
}
|
||||
|
||||
|
|
|
@ -240,6 +240,7 @@ export class AzureActiveDirectoryService {
|
|||
}
|
||||
|
||||
if (added.length || removed.length) {
|
||||
Logger.info(`Sending change event with ${added.length} added and ${removed.length} removed`);
|
||||
onDidChangeSessions.fire({ added: added, removed: removed, changed: [] });
|
||||
}
|
||||
}
|
||||
|
@ -380,7 +381,7 @@ export class AzureActiveDirectoryService {
|
|||
throw codeRes.err;
|
||||
}
|
||||
token = await this.exchangeCodeForToken(codeRes.code, codeVerifier, scope);
|
||||
this.setToken(token, scope);
|
||||
await this.setToken(token, scope);
|
||||
Logger.info(`Login successful for scopes: ${scope}`);
|
||||
res.writeHead(302, { Location: '/' });
|
||||
const session = await this.convertToSession(token);
|
||||
|
@ -491,7 +492,7 @@ export class AzureActiveDirectoryService {
|
|||
}
|
||||
|
||||
const token = await this.exchangeCodeForToken(code, verifier, scope);
|
||||
this.setToken(token, scope);
|
||||
await this.setToken(token, scope);
|
||||
|
||||
const session = await this.convertToSession(token);
|
||||
resolve(session);
|
||||
|
@ -509,6 +510,7 @@ export class AzureActiveDirectoryService {
|
|||
}
|
||||
|
||||
private async setToken(token: IToken, scope: string): Promise<void> {
|
||||
Logger.info(`Setting token for scopes: ${scope}`);
|
||||
const existingTokenIndex = this._tokens.findIndex(t => t.sessionId === token.sessionId);
|
||||
if (existingTokenIndex > -1) {
|
||||
this._tokens.splice(existingTokenIndex, 1, token);
|
||||
|
@ -522,6 +524,7 @@ export class AzureActiveDirectoryService {
|
|||
this._refreshTimeouts.set(token.sessionId, setTimeout(async () => {
|
||||
try {
|
||||
const refreshedToken = await this.refreshToken(token.refreshToken, scope, token.sessionId);
|
||||
Logger.info('Triggering change session event...');
|
||||
onDidChangeSessions.fire({ added: [], removed: [], changed: [this.convertToSessionSync(refreshedToken)] });
|
||||
} catch (e) {
|
||||
if (e.message === REFRESH_NETWORK_FAILURE) {
|
||||
|
@ -537,7 +540,7 @@ export class AzureActiveDirectoryService {
|
|||
}, 1000 * (token.expiresIn - 30)));
|
||||
}
|
||||
|
||||
this.storeTokenData();
|
||||
await this.storeTokenData();
|
||||
}
|
||||
|
||||
private getTokenFromResponse(json: ITokenResponse, scope: string, existingId?: string): IToken {
|
||||
|
@ -649,7 +652,7 @@ export class AzureActiveDirectoryService {
|
|||
if (result.ok) {
|
||||
const json = await result.json();
|
||||
const token = this.getTokenFromResponse(json, scope, sessionId);
|
||||
this.setToken(token, scope);
|
||||
await this.setToken(token, scope);
|
||||
Logger.info(`Token refresh success for scopes: ${token.scope}`);
|
||||
return token;
|
||||
} else {
|
||||
|
|
|
@ -83,6 +83,10 @@ export function activate(context: vscode.ExtensionContext) {
|
|||
const commandArgs = ['--port=0', '--disable-telemetry'];
|
||||
const env = getNewEnv();
|
||||
const remoteDataDir = process.env['TESTRESOLVER_DATA_FOLDER'] || path.join(os.homedir(), serverDataFolderName || `${dataFolderName}-testresolver`);
|
||||
const logsDir = process.env['TESTRESOLVER_LOGS_FOLDER'];
|
||||
if (logsDir) {
|
||||
commandArgs.push('--logsPath', logsDir);
|
||||
}
|
||||
|
||||
env['VSCODE_AGENT_FOLDER'] = remoteDataDir;
|
||||
outputChannel.appendLine(`Using data folder at ${remoteDataDir}`);
|
||||
|
|
|
@ -59,7 +59,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@microsoft/applicationinsights-web": "^2.6.4",
|
||||
"@parcel/watcher": "2.0.0",
|
||||
"@parcel/watcher": "2.0.1",
|
||||
"@vscode/sqlite3": "4.0.12",
|
||||
"@vscode/vscode-languagedetection": "1.0.21",
|
||||
"applicationinsights": "1.0.8",
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"private": true,
|
||||
"dependencies": {
|
||||
"@microsoft/applicationinsights-web": "^2.6.4",
|
||||
"@parcel/watcher": "2.0.0",
|
||||
"@parcel/watcher": "2.0.1",
|
||||
"@vscode/vscode-languagedetection": "1.0.21",
|
||||
"applicationinsights": "1.0.8",
|
||||
"cookie": "^0.4.0",
|
||||
|
|
|
@ -83,10 +83,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.4.tgz#40e1c0ad20743fcee1604a7df2c57faf0aa1af87"
|
||||
integrity sha512-Ot53G927ykMF8cQ3/zq4foZtdk+Tt1YpX7aUTHxBU7UHNdkEiBvBfZSq+rnlUmKCJ19VatwPG4mNzvcGpBj4og==
|
||||
|
||||
"@parcel/watcher@2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.0.0.tgz#ebe992a4838b35c3da9a568eb95a71cb26ddf551"
|
||||
integrity sha512-ByalKmRRXNNAhwZ0X1r0XeIhh1jG8zgdlvjgHk9ZV3YxiersEGNQkwew+RfqJbIL4gOJfvC2ey6lg5kaeRainw==
|
||||
"@parcel/watcher@2.0.1":
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.0.1.tgz#ec4bb6c43d9588a1ffd3d2abe6df5b501463c62d"
|
||||
integrity sha512-XegFF4L8sFn1RzU5KKOZxXUuzgOSwd6+X2ez3Cy6MVhYMbiLZ1moceMTqDhuT3N8DNbdumK3zP1wojsIsnX40w==
|
||||
dependencies:
|
||||
node-addon-api "^3.2.1"
|
||||
node-gyp-build "^4.3.0"
|
||||
|
|
|
@ -21,8 +21,9 @@ IF "%VSCODEUSERDATADIR%" == "" (
|
|||
|
||||
set REMOTE_VSCODE=%AUTHORITY%%EXT_PATH%
|
||||
set VSCODECRASHDIR=%~dp0\..\..\..\.build\crashes
|
||||
set VSCODELOGSDIR=%~dp0\..\..\..\.build\logs\remote-integration-tests
|
||||
set VSCODELOGSDIR=%~dp0\..\..\..\.build\logs\integration-tests-remote
|
||||
set TESTRESOLVER_DATA_FOLDER=%TMP%\testresolverdatafolder-%RANDOM%-%TIME:~6,5%
|
||||
set TESTRESOLVER_LOGS_FOLDER=%VSCODELOGSDIR%\server
|
||||
|
||||
if "%VSCODE_REMOTE_SERVER_PATH%"=="" (
|
||||
echo "Using remote server out of sources for integration tests"
|
||||
|
|
|
@ -29,7 +29,7 @@ fi
|
|||
|
||||
export REMOTE_VSCODE=$AUTHORITY$EXT_PATH
|
||||
VSCODECRASHDIR=$ROOT/.build/crashes
|
||||
VSCODELOGSDIR=$ROOT/.build/logs/remote-integration-tests
|
||||
VSCODELOGSDIR=$ROOT/.build/logs/integration-tests-remote
|
||||
|
||||
# Figure out which Electron to use for running tests
|
||||
if [ -z "$INTEGRATION_TEST_ELECTRON_PATH" ]
|
||||
|
@ -74,6 +74,7 @@ else
|
|||
fi
|
||||
|
||||
export TESTRESOLVER_DATA_FOLDER=$TESTRESOLVER_DATA_FOLDER
|
||||
export TESTRESOLVER_LOGS_FOLDER=$VSCODELOGSDIR/server
|
||||
|
||||
# Figure out which remote server to use for running tests
|
||||
if [ -z "$VSCODE_REMOTE_SERVER_PATH" ]
|
||||
|
|
|
@ -26,6 +26,7 @@ if "%INTEGRATION_TEST_ELECTRON_PATH%"=="" (
|
|||
compile-extension:markdown-language-features^
|
||||
compile-extension:typescript-language-features^
|
||||
compile-extension:vscode-custom-editor-tests^
|
||||
compile-extension:vscode-notebook-tests^
|
||||
compile-extension:emmet^
|
||||
compile-extension:css-language-features-server^
|
||||
compile-extension:html-language-features-server^
|
||||
|
|
|
@ -32,6 +32,7 @@ else
|
|||
yarn gulp compile-extension:vscode-api-tests \
|
||||
compile-extension:vscode-colorize-tests \
|
||||
compile-extension:vscode-custom-editor-tests \
|
||||
compile-extension:vscode-notebook-tests \
|
||||
compile-extension:markdown-language-features \
|
||||
compile-extension:typescript-language-features \
|
||||
compile-extension:emmet \
|
||||
|
|
24
src/bootstrap-window.js
vendored
24
src/bootstrap-window.js
vendored
|
@ -45,11 +45,13 @@
|
|||
async function load(modulePaths, resultCallback, options) {
|
||||
const isDev = !!safeProcess.env['VSCODE_DEV'];
|
||||
|
||||
// Error handler (TODO@sandbox non-sandboxed only)
|
||||
// Error handler (node.js enabled renderers only)
|
||||
let showDevtoolsOnError = isDev;
|
||||
safeProcess.on('uncaughtException', function (/** @type {string | Error} */ error) {
|
||||
onUnexpectedError(error, showDevtoolsOnError);
|
||||
});
|
||||
if (!safeProcess.sandboxed) {
|
||||
safeProcess.on('uncaughtException', function (/** @type {string | Error} */ error) {
|
||||
onUnexpectedError(error, showDevtoolsOnError);
|
||||
});
|
||||
}
|
||||
|
||||
// Await window configuration from preload
|
||||
const timeout = setTimeout(() => { console.error(`[resolve window config] Could not resolve window configuration within 10 seconds, but will continue to wait...`); }, 10000);
|
||||
|
@ -83,7 +85,7 @@
|
|||
developerDeveloperKeybindingsDisposable = registerDeveloperKeybindings(disallowReloadKeybinding);
|
||||
}
|
||||
|
||||
// Enable ASAR support (TODO@sandbox non-sandboxed only)
|
||||
// Enable ASAR support (node.js enabled renderers only)
|
||||
if (!safeProcess.sandboxed) {
|
||||
globalThis.MonacoBootstrap.enableASARSupport(configuration.appRoot);
|
||||
}
|
||||
|
@ -100,9 +102,12 @@
|
|||
|
||||
window.document.documentElement.setAttribute('lang', locale);
|
||||
|
||||
// Replace the patched electron fs with the original node fs for all AMD code (TODO@sandbox non-sandboxed only)
|
||||
// Define `fs` as `original-fs` to disable ASAR support
|
||||
// in fs-operations (node.js enabled renderers only)
|
||||
if (!safeProcess.sandboxed) {
|
||||
require.define('fs', [], function () { return require.__$__nodeRequire('original-fs'); });
|
||||
require.define('fs', [], function () {
|
||||
return require.__$__nodeRequire('original-fs');
|
||||
});
|
||||
}
|
||||
|
||||
window['MonacoEnvironment'] = {};
|
||||
|
@ -140,8 +145,9 @@
|
|||
'tas-client-umd': `${baseNodeModulesPath}/tas-client-umd/lib/tas-client-umd.js`
|
||||
};
|
||||
|
||||
// For priviledged renderers, allow to load built-in and other node.js
|
||||
// modules via AMD which has a fallback to using node.js `require`
|
||||
// Allow to load built-in and other node.js modules via AMD
|
||||
// which has a fallback to using node.js `require`
|
||||
// (node.js enabled renderers only)
|
||||
if (!safeProcess.sandboxed) {
|
||||
loaderConfig.amdModulesPattern = /(^vs\/)|(^vscode-textmate$)|(^vscode-oniguruma$)|(^xterm$)|(^xterm-addon-search$)|(^xterm-addon-unicode11$)|(^xterm-addon-webgl$)|(^iconv-lite-umd$)|(^jschardet$)|(^@vscode\/vscode-languagedetection$)|(^tas-client-umd$)/;
|
||||
}
|
||||
|
|
5
src/bootstrap.js
vendored
5
src/bootstrap.js
vendored
|
@ -42,9 +42,6 @@
|
|||
//#region Add support for using node_modules.asar
|
||||
|
||||
/**
|
||||
* TODO@sandbox remove the support for passing in `appRoot` once
|
||||
* sandbox is fully enabled
|
||||
*
|
||||
* @param {string=} appRoot
|
||||
*/
|
||||
function enableASARSupport(appRoot) {
|
||||
|
@ -100,7 +97,7 @@
|
|||
}
|
||||
if (!asarPathAdded && appRoot) {
|
||||
// Assuming that adding just `NODE_MODULES_ASAR_PATH` is sufficient
|
||||
// because nodejs should find it even if it has a different driver letter case
|
||||
// because nodejs should find it even if it has a different drive letter case
|
||||
paths.push(NODE_MODULES_ASAR_PATH);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1382,5 +1382,3 @@ define(function () { return purify; });
|
|||
// export const removeHooks = purify.removeHooks;
|
||||
// export const removeAllHooks = purify.removeAllHooks;
|
||||
// ESM-uncomment-end
|
||||
|
||||
//# sourceMappingURL=purify.es.js.map
|
||||
|
|
|
@ -9,6 +9,9 @@ import { Event } from 'vs/base/common/event';
|
|||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import 'vs/css!./gridview';
|
||||
import { Box, GridView, IBoundarySashes, IGridViewOptions, IGridViewStyles, IView as IGridViewView, IViewSize, orthogonal, Sizing as GridViewSizing } from './gridview';
|
||||
import type { GridLocation } from 'vs/base/browser/ui/grid/gridview';
|
||||
///@ts-ignore
|
||||
import type { SplitView } from 'vs/base/browser/ui/splitview/splitview';
|
||||
|
||||
export { IViewSize, LayoutPriority, Orientation, orthogonal } from './gridview';
|
||||
|
||||
|
@ -28,9 +31,22 @@ function oppositeDirection(direction: Direction): Direction {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The interface to implement for views within a {@link Grid}.
|
||||
*/
|
||||
export interface IView extends IGridViewView {
|
||||
readonly preferredHeight?: number;
|
||||
|
||||
/**
|
||||
* The preferred width for when the user double clicks a sash
|
||||
* adjacent to this view.
|
||||
*/
|
||||
readonly preferredWidth?: number;
|
||||
|
||||
/**
|
||||
* The preferred height for when the user double clicks a sash
|
||||
* adjacent to this view.
|
||||
*/
|
||||
readonly preferredHeight?: number;
|
||||
}
|
||||
|
||||
export interface GridLeafNode<T extends IView> {
|
||||
|
@ -50,7 +66,7 @@ export function isGridBranchNode<T extends IView>(node: GridNode<T>): node is Gr
|
|||
return !!(node as any).children;
|
||||
}
|
||||
|
||||
function getGridNode<T extends IView>(node: GridNode<T>, location: number[]): GridNode<T> {
|
||||
function getGridNode<T extends IView>(node: GridNode<T>, location: GridLocation): GridNode<T> {
|
||||
if (location.length === 0) {
|
||||
return node;
|
||||
}
|
||||
|
@ -113,7 +129,7 @@ function findAdjacentBoxLeafNodes<T extends IView>(boxNode: GridNode<T>, directi
|
|||
return result;
|
||||
}
|
||||
|
||||
function getLocationOrientation(rootOrientation: Orientation, location: number[]): Orientation {
|
||||
function getLocationOrientation(rootOrientation: Orientation, location: GridLocation): Orientation {
|
||||
return location.length % 2 === 0 ? orthogonal(rootOrientation) : rootOrientation;
|
||||
}
|
||||
|
||||
|
@ -121,7 +137,7 @@ function getDirectionOrientation(direction: Direction): Orientation {
|
|||
return direction === Direction.Up || direction === Direction.Down ? Orientation.VERTICAL : Orientation.HORIZONTAL;
|
||||
}
|
||||
|
||||
export function getRelativeLocation(rootOrientation: Orientation, location: number[], direction: Direction): number[] {
|
||||
export function getRelativeLocation(rootOrientation: Orientation, location: GridLocation, direction: Direction): GridLocation {
|
||||
const orientation = getLocationOrientation(rootOrientation, location);
|
||||
const directionOrientation = getDirectionOrientation(direction);
|
||||
|
||||
|
@ -163,7 +179,7 @@ function indexInParent(element: HTMLElement): number {
|
|||
*
|
||||
* This will break as soon as DOM structures of the Splitview or Gridview change.
|
||||
*/
|
||||
function getGridLocation(element: HTMLElement): number[] {
|
||||
function getGridLocation(element: HTMLElement): GridLocation {
|
||||
const parentElement = element.parentElement;
|
||||
|
||||
if (!parentElement) {
|
||||
|
@ -191,40 +207,93 @@ export namespace Sizing {
|
|||
}
|
||||
|
||||
export interface IGridStyles extends IGridViewStyles { }
|
||||
export interface IGridOptions extends IGridViewOptions { }
|
||||
|
||||
export interface IGridOptions extends IGridViewOptions {
|
||||
readonly firstViewVisibleCachedSize?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link Grid} exposes a Grid widget in a friendlier API than the underlying
|
||||
* {@link GridView} widget. Namely, all mutation operations are addressed by the
|
||||
* model elements, rather than indexes.
|
||||
*
|
||||
* It support the same features as the {@link GridView}.
|
||||
*/
|
||||
export class Grid<T extends IView = IView> extends Disposable {
|
||||
|
||||
protected gridview: GridView;
|
||||
private views = new Map<T, HTMLElement>();
|
||||
|
||||
/**
|
||||
* The orientation of the grid. Matches the orientation of the root
|
||||
* {@link SplitView} in the grid's {@link GridLocation} model.
|
||||
*/
|
||||
get orientation(): Orientation { return this.gridview.orientation; }
|
||||
set orientation(orientation: Orientation) { this.gridview.orientation = orientation; }
|
||||
|
||||
/**
|
||||
* The width of the grid.
|
||||
*/
|
||||
get width(): number { return this.gridview.width; }
|
||||
|
||||
/**
|
||||
* The height of the grid.
|
||||
*/
|
||||
get height(): number { return this.gridview.height; }
|
||||
|
||||
/**
|
||||
* The minimum width of the grid.
|
||||
*/
|
||||
get minimumWidth(): number { return this.gridview.minimumWidth; }
|
||||
|
||||
/**
|
||||
* The minimum height of the grid.
|
||||
*/
|
||||
get minimumHeight(): number { return this.gridview.minimumHeight; }
|
||||
|
||||
/**
|
||||
* The maximum width of the grid.
|
||||
*/
|
||||
get maximumWidth(): number { return this.gridview.maximumWidth; }
|
||||
|
||||
/**
|
||||
* The maximum height of the grid.
|
||||
*/
|
||||
get maximumHeight(): number { return this.gridview.maximumHeight; }
|
||||
|
||||
/**
|
||||
* Fires whenever a view within the grid changes its size constraints.
|
||||
*/
|
||||
readonly onDidChange: Event<{ width: number; height: number; } | undefined>;
|
||||
|
||||
/**
|
||||
* Fires whenever the user scrolls a {@link SplitView} within
|
||||
* the grid.
|
||||
*/
|
||||
readonly onDidScroll: Event<void>;
|
||||
|
||||
/**
|
||||
* A collection of sashes perpendicular to each edge of the grid.
|
||||
* Corner sashes will be created for each intersection.
|
||||
*/
|
||||
get boundarySashes(): IBoundarySashes { return this.gridview.boundarySashes; }
|
||||
set boundarySashes(boundarySashes: IBoundarySashes) { this.gridview.boundarySashes = boundarySashes; }
|
||||
|
||||
/**
|
||||
* Enable/disable edge snapping across all grid views.
|
||||
*/
|
||||
set edgeSnapping(edgeSnapping: boolean) { this.gridview.edgeSnapping = edgeSnapping; }
|
||||
|
||||
/**
|
||||
* The DOM element for this view.
|
||||
*/
|
||||
get element(): HTMLElement { return this.gridview.element; }
|
||||
|
||||
private didLayout = false;
|
||||
|
||||
constructor(gridview: GridView, options?: IGridOptions);
|
||||
constructor(view: T, options?: IGridOptions);
|
||||
/**
|
||||
* Create a new {@link Grid}. A grid must *always* have a view
|
||||
* inside.
|
||||
*
|
||||
* @param view An initial view for this Grid.
|
||||
*/
|
||||
constructor(view: T | GridView, options: IGridOptions = {}) {
|
||||
super();
|
||||
|
||||
|
@ -238,12 +307,8 @@ export class Grid<T extends IView = IView> extends Disposable {
|
|||
this._register(this.gridview);
|
||||
this._register(this.gridview.onDidSashReset(this.onDidSashReset, this));
|
||||
|
||||
const size: number | GridViewSizing = typeof options.firstViewVisibleCachedSize === 'number'
|
||||
? GridViewSizing.Invisible(options.firstViewVisibleCachedSize)
|
||||
: 0;
|
||||
|
||||
if (!(view instanceof GridView)) {
|
||||
this._addView(view, size, [0]);
|
||||
this._addView(view, 0, [0]);
|
||||
}
|
||||
|
||||
this.onDidChange = this.gridview.onDidChange;
|
||||
|
@ -254,15 +319,67 @@ export class Grid<T extends IView = IView> extends Disposable {
|
|||
this.gridview.style(styles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Layout the {@link Grid}.
|
||||
*
|
||||
* Optionally provide a `top` and `left` positions, those will propagate
|
||||
* as an origin for positions passed to {@link IView.layout}.
|
||||
*
|
||||
* @param width The width of the {@link Grid}.
|
||||
* @param height The height of the {@link Grid}.
|
||||
* @param top Optional, the top location of the {@link Grid}.
|
||||
* @param left Optional, the left location of the {@link Grid}.
|
||||
*/
|
||||
layout(width: number, height: number, top: number = 0, left: number = 0): void {
|
||||
this.gridview.layout(width, height, top, left);
|
||||
this.didLayout = true;
|
||||
}
|
||||
|
||||
hasView(view: T): boolean {
|
||||
return this.views.has(view);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a {@link IView view} to this {@link Grid}, based on another reference view.
|
||||
*
|
||||
* Take this grid as an example:
|
||||
*
|
||||
* ```
|
||||
* +-----+---------------+
|
||||
* | A | B |
|
||||
* +-----+---------+-----+
|
||||
* | C | |
|
||||
* +---------------+ D |
|
||||
* | E | |
|
||||
* +---------------+-----+
|
||||
* ```
|
||||
*
|
||||
* Calling `addView(X, Sizing.Distribute, C, Direction.Right)` will make the following
|
||||
* changes:
|
||||
*
|
||||
* ```
|
||||
* +-----+---------------+
|
||||
* | A | B |
|
||||
* +-----+-+-------+-----+
|
||||
* | C | X | |
|
||||
* +-------+-------+ D |
|
||||
* | E | |
|
||||
* +---------------+-----+
|
||||
* ```
|
||||
*
|
||||
* Or `addView(X, Sizing.Distribute, D, Direction.Down)`:
|
||||
*
|
||||
* ```
|
||||
* +-----+---------------+
|
||||
* | A | B |
|
||||
* +-----+---------+-----+
|
||||
* | C | D |
|
||||
* +---------------+-----+
|
||||
* | E | X |
|
||||
* +---------------+-----+
|
||||
* ```
|
||||
*
|
||||
* @param newView The view to add.
|
||||
* @param size Either a fixed size, or a dynamic {@link Sizing} strategy.
|
||||
* @param referenceView Another view to place this new view next to.
|
||||
* @param direction The direction the new view should be placed next to the reference view.
|
||||
*/
|
||||
addView(newView: T, size: number | Sizing, referenceView: T, direction: Direction): void {
|
||||
if (this.views.has(newView)) {
|
||||
throw new Error('Can\'t add same view twice');
|
||||
|
@ -293,7 +410,7 @@ export class Grid<T extends IView = IView> extends Disposable {
|
|||
this._addView(newView, viewSize, location);
|
||||
}
|
||||
|
||||
addViewAt(newView: T, size: number | DistributeSizing | InvisibleSizing, location: number[]): void {
|
||||
private addViewAt(newView: T, size: number | DistributeSizing | InvisibleSizing, location: GridLocation): void {
|
||||
if (this.views.has(newView)) {
|
||||
throw new Error('Can\'t add same view twice');
|
||||
}
|
||||
|
@ -311,11 +428,17 @@ export class Grid<T extends IView = IView> extends Disposable {
|
|||
this._addView(newView, viewSize, location);
|
||||
}
|
||||
|
||||
protected _addView(newView: T, size: number | GridViewSizing, location: number[]): void {
|
||||
protected _addView(newView: T, size: number | GridViewSizing, location: GridLocation): void {
|
||||
this.views.set(newView, newView.element);
|
||||
this.gridview.addView(newView, size, location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a {@link IView view} from this {@link Grid}.
|
||||
*
|
||||
* @param view The {@link IView view} to remove.
|
||||
* @param sizing Whether to distribute other {@link IView view}'s sizes.
|
||||
*/
|
||||
removeView(view: T, sizing?: Sizing): void {
|
||||
if (this.views.size === 1) {
|
||||
throw new Error('Can\'t remove last view');
|
||||
|
@ -326,6 +449,16 @@ export class Grid<T extends IView = IView> extends Disposable {
|
|||
this.views.delete(view);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move a {@link IView view} to another location in the grid.
|
||||
*
|
||||
* @remarks See {@link Grid.addView}.
|
||||
*
|
||||
* @param view The {@link IView view} to move.
|
||||
* @param sizing Either a fixed size, or a dynamic {@link Sizing} strategy.
|
||||
* @param referenceView Another view to place the view next to.
|
||||
* @param direction The direction the view should be placed next to the reference view.
|
||||
*/
|
||||
moveView(view: T, sizing: number | Sizing, referenceView: T, direction: Direction): void {
|
||||
const sourceLocation = this.getViewLocation(view);
|
||||
const [sourceParentLocation, from] = tail(sourceLocation);
|
||||
|
@ -342,7 +475,16 @@ export class Grid<T extends IView = IView> extends Disposable {
|
|||
}
|
||||
}
|
||||
|
||||
moveViewTo(view: T, location: number[]): void {
|
||||
/**
|
||||
* Move a {@link IView view} to another location in the grid.
|
||||
*
|
||||
* @remarks Internal method, do not use without knowing what you're doing.
|
||||
* @remarks See {@link GridView.moveView}.
|
||||
*
|
||||
* @param view The {@link IView view} to move.
|
||||
* @param location The {@link GridLocation location} to insert the view on.
|
||||
*/
|
||||
moveViewTo(view: T, location: GridLocation): void {
|
||||
const sourceLocation = this.getViewLocation(view);
|
||||
const [sourceParentLocation, from] = tail(sourceLocation);
|
||||
const [targetParentLocation, to] = tail(location);
|
||||
|
@ -362,17 +504,35 @@ export class Grid<T extends IView = IView> extends Disposable {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Swap two {@link IView views} within the {@link Grid}.
|
||||
*
|
||||
* @param from One {@link IView view}.
|
||||
* @param to Another {@link IView view}.
|
||||
*/
|
||||
swapViews(from: T, to: T): void {
|
||||
const fromLocation = this.getViewLocation(from);
|
||||
const toLocation = this.getViewLocation(to);
|
||||
return this.gridview.swapViews(fromLocation, toLocation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resize a {@link IView view}.
|
||||
*
|
||||
* @param view The {@link IView view} to resize.
|
||||
* @param size The size the view should be.
|
||||
*/
|
||||
resizeView(view: T, size: IViewSize): void {
|
||||
const location = this.getViewLocation(view);
|
||||
return this.gridview.resizeView(location, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size of a {@link IView view}.
|
||||
*
|
||||
* @param view The {@link IView view}. Provide `undefined` to get the size
|
||||
* of the grid itself.
|
||||
*/
|
||||
getViewSize(view?: T): IViewSize {
|
||||
if (!view) {
|
||||
return this.gridview.getViewSize();
|
||||
|
@ -382,34 +542,71 @@ export class Grid<T extends IView = IView> extends Disposable {
|
|||
return this.gridview.getViewSize(location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cached visible size of a {@link IView view}. This was the size
|
||||
* of the view at the moment it last became hidden.
|
||||
*
|
||||
* @param view The {@link IView view}.
|
||||
*/
|
||||
getViewCachedVisibleSize(view: T): number | undefined {
|
||||
const location = this.getViewLocation(view);
|
||||
return this.gridview.getViewCachedVisibleSize(location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maximize the size of a {@link IView view} by collapsing all other views
|
||||
* to their minimum sizes.
|
||||
*
|
||||
* @param view The {@link IView view}.
|
||||
*/
|
||||
maximizeViewSize(view: T): void {
|
||||
const location = this.getViewLocation(view);
|
||||
this.gridview.maximizeViewSize(location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Distribute the size among all {@link IView views} within the entire
|
||||
* grid or within a single {@link SplitView}.
|
||||
*/
|
||||
distributeViewSizes(): void {
|
||||
this.gridview.distributeViewSizes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether a {@link IView view} is visible.
|
||||
*
|
||||
* @param view The {@link IView view}.
|
||||
*/
|
||||
isViewVisible(view: T): boolean {
|
||||
const location = this.getViewLocation(view);
|
||||
return this.gridview.isViewVisible(location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the visibility state of a {@link IView view}.
|
||||
*
|
||||
* @param view The {@link IView view}.
|
||||
*/
|
||||
setViewVisible(view: T, visible: boolean): void {
|
||||
const location = this.getViewLocation(view);
|
||||
this.gridview.setViewVisible(location, visible);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a descriptor for the entire grid.
|
||||
*/
|
||||
getViews(): GridBranchNode<T> {
|
||||
return this.gridview.getView() as GridBranchNode<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to return the collection all views which intersect
|
||||
* a view's edge.
|
||||
*
|
||||
* @param view The {@link IView view}.
|
||||
* @param direction Which direction edge to be considered.
|
||||
* @param wrap Whether the grid wraps around (from right to left, from bottom to top).
|
||||
*/
|
||||
getNeighborViews(view: T, direction: Direction, wrap: boolean = false): T[] {
|
||||
if (!this.didLayout) {
|
||||
throw new Error('Can\'t call getNeighborViews before first layout');
|
||||
|
@ -436,7 +633,7 @@ export class Grid<T extends IView = IView> extends Disposable {
|
|||
.map(node => node.view);
|
||||
}
|
||||
|
||||
getViewLocation(view: T): number[] {
|
||||
private getViewLocation(view: T): GridLocation {
|
||||
const element = this.views.get(view);
|
||||
|
||||
if (!element) {
|
||||
|
@ -446,8 +643,8 @@ export class Grid<T extends IView = IView> extends Disposable {
|
|||
return getGridLocation(element);
|
||||
}
|
||||
|
||||
private onDidSashReset(location: number[]): void {
|
||||
const resizeToPreferredSize = (location: number[]): boolean => {
|
||||
private onDidSashReset(location: GridLocation): void {
|
||||
const resizeToPreferredSize = (location: GridLocation): boolean => {
|
||||
const node = this.gridview.getView(location) as GridNode<T>;
|
||||
|
||||
if (isGridBranchNode(node)) {
|
||||
|
@ -510,6 +707,9 @@ export interface ISerializedGrid {
|
|||
height: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link Grid} which can serialize itself.
|
||||
*/
|
||||
export class SerializableGrid<T extends ISerializableView> extends Grid<T> {
|
||||
|
||||
private static serializeNode<T extends ISerializableView>(node: GridNode<T>, orientation: Orientation): ISerializedNode {
|
||||
|
@ -526,6 +726,13 @@ export class SerializableGrid<T extends ISerializableView> extends Grid<T> {
|
|||
return { type: 'branch', data: node.children.map(c => SerializableGrid.serializeNode(c, orthogonal(orientation))), size };
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new {@link SerializableGrid} from a JSON object.
|
||||
*
|
||||
* @param json The JSON object.
|
||||
* @param deserializer A deserializer which can revive each view.
|
||||
* @returns A new {@link SerializableGrid} instance.
|
||||
*/
|
||||
static deserialize<T extends ISerializableView>(json: ISerializedGrid, deserializer: IViewDeserializer<T>, options: IGridOptions = {}): SerializableGrid<T> {
|
||||
if (typeof json.orientation !== 'number') {
|
||||
throw new Error('Invalid JSON: \'orientation\' property must be a number.');
|
||||
|
@ -547,6 +754,9 @@ export class SerializableGrid<T extends ISerializableView> extends Grid<T> {
|
|||
*/
|
||||
private initialLayoutContext: boolean = true;
|
||||
|
||||
/**
|
||||
* Serialize this grid into a JSON object.
|
||||
*/
|
||||
serialize(): ISerializedGrid {
|
||||
return {
|
||||
root: SerializableGrid.serializeNode(this.getViews(), this.orientation),
|
||||
|
@ -629,6 +839,10 @@ function getDimensions(node: ISerializedNode, orientation: Orientation): { width
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new JSON object from a {@link GridDescriptor}, which can
|
||||
* be deserialized by {@link SerializableGrid.deserialize}.
|
||||
*/
|
||||
export function createSerializedGrid(gridDescriptor: GridDescriptor): ISerializedGrid {
|
||||
sanitizeGridNodeDescriptor(gridDescriptor, true);
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import { $ } from 'vs/base/browser/dom';
|
||||
import { Orientation, Sash } from 'vs/base/browser/ui/sash/sash';
|
||||
import { ISplitViewStyles, IView as ISplitView, LayoutPriority, Sizing, SplitView } from 'vs/base/browser/ui/splitview/splitview';
|
||||
import { DistributeSizing, ISplitViewStyles, IView as ISplitView, LayoutPriority, Sizing, SplitView } from 'vs/base/browser/ui/splitview/splitview';
|
||||
import { equals as arrayEquals, tail2 as tail } from 'vs/base/common/arrays';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { Emitter, Event, Relay } from 'vs/base/common/event';
|
||||
|
@ -17,6 +17,12 @@ import 'vs/css!./gridview';
|
|||
export { Orientation } from 'vs/base/browser/ui/sash/sash';
|
||||
export { LayoutPriority, Sizing } from 'vs/base/browser/ui/splitview/splitview';
|
||||
|
||||
export interface IGridViewStyles extends ISplitViewStyles { }
|
||||
|
||||
const defaultStyles: IGridViewStyles = {
|
||||
separatorBorder: Color.transparent
|
||||
};
|
||||
|
||||
export interface IViewSize {
|
||||
readonly width: number;
|
||||
readonly height: number;
|
||||
|
@ -36,17 +42,95 @@ export interface IBoundarySashes {
|
|||
readonly left?: Sash;
|
||||
}
|
||||
|
||||
/**
|
||||
* The interface to implement for views within a {@link GridView}.
|
||||
*/
|
||||
export interface IView {
|
||||
|
||||
/**
|
||||
* The DOM element for this view.
|
||||
*/
|
||||
readonly element: HTMLElement;
|
||||
|
||||
/**
|
||||
* A minimum width for this view.
|
||||
*
|
||||
* @remarks If none, set it to `0`.
|
||||
*/
|
||||
readonly minimumWidth: number;
|
||||
|
||||
/**
|
||||
* A minimum width for this view.
|
||||
*
|
||||
* @remarks If none, set it to `Number.POSITIVE_INFINITY`.
|
||||
*/
|
||||
readonly maximumWidth: number;
|
||||
|
||||
/**
|
||||
* A minimum height for this view.
|
||||
*
|
||||
* @remarks If none, set it to `0`.
|
||||
*/
|
||||
readonly minimumHeight: number;
|
||||
|
||||
/**
|
||||
* A minimum height for this view.
|
||||
*
|
||||
* @remarks If none, set it to `Number.POSITIVE_INFINITY`.
|
||||
*/
|
||||
readonly maximumHeight: number;
|
||||
readonly onDidChange: Event<IViewSize | undefined>;
|
||||
|
||||
/**
|
||||
* The priority of the view when the {@link GridView} layout algorithm
|
||||
* runs. Views with higher priority will be resized first.
|
||||
*
|
||||
* @remarks Only used when `proportionalLayout` is false.
|
||||
*/
|
||||
readonly priority?: LayoutPriority;
|
||||
|
||||
/**
|
||||
* Whether the view will snap whenever the user reaches its minimum size or
|
||||
* attempts to grow it beyond the minimum size.
|
||||
*
|
||||
* @defaultValue `false`
|
||||
*/
|
||||
readonly snap?: boolean;
|
||||
|
||||
/**
|
||||
* View instances are supposed to fire this event whenever any of the constraint
|
||||
* properties have changed:
|
||||
*
|
||||
* - {@link IView.minimumWidth}
|
||||
* - {@link IView.maximumWidth}
|
||||
* - {@link IView.minimumHeight}
|
||||
* - {@link IView.maximumHeight}
|
||||
* - {@link IView.priority}
|
||||
* - {@link IView.snap}
|
||||
*
|
||||
* The {@link GridView} will relayout whenever that happens. The event can
|
||||
* optionally emit the view's preferred size for that relayout.
|
||||
*/
|
||||
readonly onDidChange: Event<IViewSize | undefined>;
|
||||
|
||||
/**
|
||||
* This will be called by the {@link GridView} during layout. A view meant to
|
||||
* pass along the layout information down to its descendants.
|
||||
*/
|
||||
layout(width: number, height: number, top: number, left: number): void;
|
||||
|
||||
/**
|
||||
* This will be called by the {@link GridView} whenever this view is made
|
||||
* visible or hidden.
|
||||
*
|
||||
* @param visible Whether the view becomes visible.
|
||||
*/
|
||||
setVisible?(visible: boolean): void;
|
||||
|
||||
/**
|
||||
* This will be called by the {@link GridView} whenever this view is on
|
||||
* an edge of the grid and the grid's
|
||||
* {@link GridView.boundarySashes boundary sashes} change.
|
||||
*/
|
||||
setBoundarySashes?(sashes: IBoundarySashes): void;
|
||||
}
|
||||
|
||||
|
@ -108,29 +192,23 @@ export function isGridBranchNode(node: GridNode): node is GridBranchNode {
|
|||
return !!(node as any).children;
|
||||
}
|
||||
|
||||
export interface IGridViewStyles extends ISplitViewStyles { }
|
||||
|
||||
const defaultStyles: IGridViewStyles = {
|
||||
separatorBorder: Color.transparent
|
||||
};
|
||||
|
||||
export interface ILayoutController {
|
||||
readonly isLayoutEnabled: boolean;
|
||||
}
|
||||
|
||||
export class LayoutController implements ILayoutController {
|
||||
class LayoutController {
|
||||
constructor(public isLayoutEnabled: boolean) { }
|
||||
}
|
||||
|
||||
export class MultiplexLayoutController implements ILayoutController {
|
||||
get isLayoutEnabled(): boolean { return this.layoutControllers.every(l => l.isLayoutEnabled); }
|
||||
constructor(private layoutControllers: ILayoutController[]) { }
|
||||
}
|
||||
|
||||
export interface IGridViewOptions {
|
||||
|
||||
/**
|
||||
* Styles overriding the {@link defaultStyles default ones}.
|
||||
*/
|
||||
readonly styles?: IGridViewStyles;
|
||||
|
||||
/**
|
||||
* Resize each view proportionally when resizing the {@link GridView}.
|
||||
*
|
||||
* @defaultValue `true`
|
||||
*/
|
||||
readonly proportionalLayout?: boolean; // default true
|
||||
readonly layoutController?: ILayoutController;
|
||||
}
|
||||
|
||||
interface ILayoutContext {
|
||||
|
@ -249,8 +327,8 @@ class BranchNode implements ISplitView<ILayoutContext>, IDisposable {
|
|||
|
||||
private childrenChangeDisposable: IDisposable = Disposable.None;
|
||||
|
||||
private readonly _onDidSashReset = new Emitter<number[]>();
|
||||
readonly onDidSashReset: Event<number[]> = this._onDidSashReset.event;
|
||||
private readonly _onDidSashReset = new Emitter<GridLocation>();
|
||||
readonly onDidSashReset: Event<GridLocation> = this._onDidSashReset.event;
|
||||
private splitviewSashResetDisposable: IDisposable = Disposable.None;
|
||||
private childrenSashResetDisposable: IDisposable = Disposable.None;
|
||||
|
||||
|
@ -296,7 +374,7 @@ class BranchNode implements ISplitView<ILayoutContext>, IDisposable {
|
|||
|
||||
constructor(
|
||||
readonly orientation: Orientation,
|
||||
readonly layoutController: ILayoutController,
|
||||
readonly layoutController: LayoutController,
|
||||
styles: IGridViewStyles,
|
||||
readonly proportionalLayout: boolean,
|
||||
size: number = 0,
|
||||
|
@ -685,7 +763,7 @@ class LeafNode implements ISplitView<ILayoutContext>, IDisposable {
|
|||
private absoluteOrthogonalOffset: number = 0;
|
||||
|
||||
readonly onDidScroll: Event<void> = Event.None;
|
||||
readonly onDidSashReset: Event<number[]> = Event.None;
|
||||
readonly onDidSashReset: Event<GridLocation> = Event.None;
|
||||
|
||||
private _onDidLinkedWidthNodeChange = new Relay<number | undefined>();
|
||||
private _linkedWidthNode: LeafNode | undefined = undefined;
|
||||
|
@ -712,7 +790,7 @@ class LeafNode implements ISplitView<ILayoutContext>, IDisposable {
|
|||
constructor(
|
||||
readonly view: IView,
|
||||
readonly orientation: Orientation,
|
||||
readonly layoutController: ILayoutController,
|
||||
readonly layoutController: LayoutController,
|
||||
orthogonalSize: number,
|
||||
size: number = 0
|
||||
) {
|
||||
|
@ -871,21 +949,88 @@ function flipNode<T extends Node>(node: T, size: number, orthogonalSize: number)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The location of a {@link IView view} within a {@link GridView}.
|
||||
*
|
||||
* A GridView is a tree composition of multiple {@link SplitView} instances, orthogonal
|
||||
* between one another. Here's an example:
|
||||
*
|
||||
* ```
|
||||
* +-----+---------------+
|
||||
* | A | B |
|
||||
* +-----+---------+-----+
|
||||
* | C | |
|
||||
* +---------------+ D |
|
||||
* | E | |
|
||||
* +---------------+-----+
|
||||
* ```
|
||||
*
|
||||
* The above grid's tree structure is:
|
||||
*
|
||||
* ```
|
||||
* Vertical SplitView
|
||||
* +-Horizontal SplitView
|
||||
* | +-A
|
||||
* | +-B
|
||||
* +- Horizontal SplitView
|
||||
* +-Vertical SplitView
|
||||
* | +-C
|
||||
* | +-E
|
||||
* +-D
|
||||
* ```
|
||||
*
|
||||
* So, {@link IView views} within a {@link GridView} can be referenced by
|
||||
* a sequence of indexes, each index referencing each SplitView. Here are
|
||||
* each view's locations, from the example above:
|
||||
*
|
||||
* - `A`: `[0,0]`
|
||||
* - `B`: `[0,1]`
|
||||
* - `C`: `[1,0,0]`
|
||||
* - `D`: `[1,1]`
|
||||
* - `E`: `[1,0,1]`
|
||||
*/
|
||||
export type GridLocation = number[];
|
||||
|
||||
/**
|
||||
* The {@link GridView} is the UI component which implements a two dimensional
|
||||
* flex-like layout algorithm for a collection of {@link IView} instances, which
|
||||
* are mostly HTMLElement instances with size constraints. A {@link GridView} is a
|
||||
* tree composition of multiple {@link SplitView} instances, orthogonal between
|
||||
* one another. It will respect view's size contraints, just like the SplitView.
|
||||
*
|
||||
* It has a low-level index based API, allowing for fine grain performant operations.
|
||||
* Look into the {@link Grid} widget for a higher-level API.
|
||||
*
|
||||
* Features:
|
||||
* - flex-like layout algorithm
|
||||
* - snap support
|
||||
* - corner sash support
|
||||
* - Alt key modifier behavior, macOS style
|
||||
* - layout (de)serialization
|
||||
*/
|
||||
export class GridView implements IDisposable {
|
||||
|
||||
/**
|
||||
* The DOM element for this view.
|
||||
*/
|
||||
readonly element: HTMLElement;
|
||||
|
||||
private styles: IGridViewStyles;
|
||||
private proportionalLayout: boolean;
|
||||
|
||||
private _root!: BranchNode;
|
||||
private onDidSashResetRelay = new Relay<number[]>();
|
||||
readonly onDidSashReset: Event<number[]> = this.onDidSashResetRelay.event;
|
||||
private onDidSashResetRelay = new Relay<GridLocation>();
|
||||
private _onDidScroll = new Relay<void>();
|
||||
private _onDidChange = new Relay<IViewSize | undefined>();
|
||||
private _boundarySashes: IBoundarySashes = {};
|
||||
|
||||
/**
|
||||
* The layout controller makes sure layout only propagates
|
||||
* to the views after the very first call to {@link GridView.layout}.
|
||||
*/
|
||||
private layoutController: LayoutController;
|
||||
private disposable2x2: IDisposable = Disposable.None;
|
||||
|
||||
private get root(): BranchNode {
|
||||
return this._root;
|
||||
}
|
||||
private get root(): BranchNode { return this._root; }
|
||||
|
||||
private set root(root: BranchNode) {
|
||||
const oldRoot = this._root;
|
||||
|
@ -902,10 +1047,59 @@ export class GridView implements IDisposable {
|
|||
this._onDidScroll.input = root.onDidScroll;
|
||||
}
|
||||
|
||||
get orientation(): Orientation {
|
||||
return this._root.orientation;
|
||||
}
|
||||
/**
|
||||
* Fires whenever the user double clicks a {@link Sash sash}.
|
||||
*/
|
||||
readonly onDidSashReset = this.onDidSashResetRelay.event;
|
||||
|
||||
/**
|
||||
* Fires whenever the user scrolls a {@link SplitView} within
|
||||
* the grid.
|
||||
*/
|
||||
readonly onDidScroll = this._onDidScroll.event;
|
||||
|
||||
/**
|
||||
* Fires whenever a view within the grid changes its size constraints.
|
||||
*/
|
||||
readonly onDidChange = this._onDidChange.event;
|
||||
|
||||
/**
|
||||
* The width of the grid.
|
||||
*/
|
||||
get width(): number { return this.root.width; }
|
||||
|
||||
/**
|
||||
* The height of the grid.
|
||||
*/
|
||||
get height(): number { return this.root.height; }
|
||||
|
||||
/**
|
||||
* The minimum width of the grid.
|
||||
*/
|
||||
get minimumWidth(): number { return this.root.minimumWidth; }
|
||||
|
||||
/**
|
||||
* The minimum height of the grid.
|
||||
*/
|
||||
get minimumHeight(): number { return this.root.minimumHeight; }
|
||||
|
||||
/**
|
||||
* The maximum width of the grid.
|
||||
*/
|
||||
get maximumWidth(): number { return this.root.maximumHeight; }
|
||||
|
||||
/**
|
||||
* The maximum height of the grid.
|
||||
*/
|
||||
get maximumHeight(): number { return this.root.maximumHeight; }
|
||||
|
||||
get orientation(): Orientation { return this._root.orientation; }
|
||||
get boundarySashes(): IBoundarySashes { return this._boundarySashes; }
|
||||
|
||||
/**
|
||||
* The orientation of the grid. Matches the orientation of the root
|
||||
* {@link SplitView} in the grid's tree model.
|
||||
*/
|
||||
set orientation(orientation: Orientation) {
|
||||
if (this._root.orientation === orientation) {
|
||||
return;
|
||||
|
@ -917,77 +1111,67 @@ export class GridView implements IDisposable {
|
|||
this.boundarySashes = this.boundarySashes;
|
||||
}
|
||||
|
||||
get width(): number { return this.root.width; }
|
||||
get height(): number { return this.root.height; }
|
||||
|
||||
get minimumWidth(): number { return this.root.minimumWidth; }
|
||||
get minimumHeight(): number { return this.root.minimumHeight; }
|
||||
get maximumWidth(): number { return this.root.maximumHeight; }
|
||||
get maximumHeight(): number { return this.root.maximumHeight; }
|
||||
|
||||
private _onDidScroll = new Relay<void>();
|
||||
readonly onDidScroll = this._onDidScroll.event;
|
||||
|
||||
private _onDidChange = new Relay<IViewSize | undefined>();
|
||||
readonly onDidChange = this._onDidChange.event;
|
||||
|
||||
private _boundarySashes: IBoundarySashes = {};
|
||||
get boundarySashes(): IBoundarySashes { return this._boundarySashes; }
|
||||
/**
|
||||
* A collection of sashes perpendicular to each edge of the grid.
|
||||
* Corner sashes will be created for each intersection.
|
||||
*/
|
||||
set boundarySashes(boundarySashes: IBoundarySashes) {
|
||||
this._boundarySashes = boundarySashes;
|
||||
this.root.boundarySashes = fromAbsoluteBoundarySashes(boundarySashes, this.orientation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/disable edge snapping across all grid views.
|
||||
*/
|
||||
set edgeSnapping(edgeSnapping: boolean) {
|
||||
this.root.edgeSnapping = edgeSnapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* The first layout controller makes sure layout only propagates
|
||||
* to the views after the very first call to gridview.layout()
|
||||
* Create a new {@link GridView} instance.
|
||||
*
|
||||
* @remarks It's the caller's responsibility to append the
|
||||
* {@link GridView.element} to the page's DOM.
|
||||
*/
|
||||
private firstLayoutController: LayoutController;
|
||||
private layoutController: LayoutController;
|
||||
|
||||
constructor(options: IGridViewOptions = {}) {
|
||||
this.element = $('.monaco-grid-view');
|
||||
this.styles = options.styles || defaultStyles;
|
||||
this.proportionalLayout = typeof options.proportionalLayout !== 'undefined' ? !!options.proportionalLayout : true;
|
||||
|
||||
this.firstLayoutController = new LayoutController(false);
|
||||
this.layoutController = new MultiplexLayoutController([
|
||||
this.firstLayoutController,
|
||||
...(options.layoutController ? [options.layoutController] : [])
|
||||
]);
|
||||
|
||||
this.layoutController = new LayoutController(false);
|
||||
this.root = new BranchNode(Orientation.VERTICAL, this.layoutController, this.styles, this.proportionalLayout);
|
||||
}
|
||||
|
||||
getViewMap(map: Map<IView, HTMLElement>, node?: Node): void {
|
||||
if (!node) {
|
||||
node = this.root;
|
||||
}
|
||||
|
||||
if (node instanceof BranchNode) {
|
||||
node.children.forEach(child => this.getViewMap(map, child));
|
||||
} else {
|
||||
map.set(node.view, node.element);
|
||||
}
|
||||
}
|
||||
|
||||
style(styles: IGridViewStyles): void {
|
||||
this.styles = styles;
|
||||
this.root.style(styles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Layout the {@link GridView}.
|
||||
*
|
||||
* Optionally provide a `top` and `left` positions, those will propagate
|
||||
* as an origin for positions passed to {@link IView.layout}.
|
||||
*
|
||||
* @param width The width of the {@link GridView}.
|
||||
* @param height The height of the {@link GridView}.
|
||||
* @param top Optional, the top location of the {@link GridView}.
|
||||
* @param left Optional, the left location of the {@link GridView}.
|
||||
*/
|
||||
layout(width: number, height: number, top: number = 0, left: number = 0): void {
|
||||
this.firstLayoutController.isLayoutEnabled = true;
|
||||
this.layoutController.isLayoutEnabled = true;
|
||||
|
||||
const [size, orthogonalSize, offset, orthogonalOffset] = this.root.orientation === Orientation.HORIZONTAL ? [height, width, top, left] : [width, height, left, top];
|
||||
this.root.layout(size, offset, { orthogonalSize, absoluteOffset: offset, absoluteOrthogonalOffset: orthogonalOffset, absoluteSize: size, absoluteOrthogonalSize: orthogonalSize });
|
||||
}
|
||||
|
||||
addView(view: IView, size: number | Sizing, location: number[]): void {
|
||||
/**
|
||||
* Add a {@link IView view} to this {@link GridView}.
|
||||
*
|
||||
* @param view The view to add.
|
||||
* @param size Either a fixed size, or a dynamic {@link Sizing} strategy.
|
||||
* @param location The {@link GridLocation location} to insert the view on.
|
||||
*/
|
||||
addView(view: IView, size: number | Sizing, location: GridLocation): void {
|
||||
this.disposable2x2.dispose();
|
||||
this.disposable2x2 = Disposable.None;
|
||||
|
||||
|
@ -1028,7 +1212,13 @@ export class GridView implements IDisposable {
|
|||
this.trySet2x2();
|
||||
}
|
||||
|
||||
removeView(location: number[], sizing?: Sizing): IView {
|
||||
/**
|
||||
* Remove a {@link IView view} from this {@link GridView}.
|
||||
*
|
||||
* @param location The {@link GridLocation location} of the {@link IView view}.
|
||||
* @param sizing Whether to distribute other {@link IView view}'s sizes.
|
||||
*/
|
||||
removeView(location: GridLocation, sizing?: DistributeSizing): IView {
|
||||
this.disposable2x2.dispose();
|
||||
this.disposable2x2 = Disposable.None;
|
||||
|
||||
|
@ -1102,7 +1292,14 @@ export class GridView implements IDisposable {
|
|||
return node.view;
|
||||
}
|
||||
|
||||
moveView(parentLocation: number[], from: number, to: number): void {
|
||||
/**
|
||||
* Move a {@link IView view} within its parent.
|
||||
*
|
||||
* @param parentLocation The {@link GridLocation location} of the {@link IView view}'s parent.
|
||||
* @param from The index of the {@link IView view} to move.
|
||||
* @param to The index where the {@link IView view} should move to.
|
||||
*/
|
||||
moveView(parentLocation: GridLocation, from: number, to: number): void {
|
||||
const [, parent] = this.getNode(parentLocation);
|
||||
|
||||
if (!(parent instanceof BranchNode)) {
|
||||
|
@ -1114,7 +1311,13 @@ export class GridView implements IDisposable {
|
|||
this.trySet2x2();
|
||||
}
|
||||
|
||||
swapViews(from: number[], to: number[]): void {
|
||||
/**
|
||||
* Swap two {@link IView views} within the {@link GridView}.
|
||||
*
|
||||
* @param from The {@link GridLocation location} of one view.
|
||||
* @param to The {@link GridLocation location} of another view.
|
||||
*/
|
||||
swapViews(from: GridLocation, to: GridLocation): void {
|
||||
const [fromRest, fromIndex] = tail(from);
|
||||
const [, fromParent] = this.getNode(fromRest);
|
||||
|
||||
|
@ -1156,7 +1359,13 @@ export class GridView implements IDisposable {
|
|||
this.trySet2x2();
|
||||
}
|
||||
|
||||
resizeView(location: number[], { width, height }: Partial<IViewSize>): void {
|
||||
/**
|
||||
* Resize a {@link IView view}.
|
||||
*
|
||||
* @param location The {@link GridLocation location} of the view.
|
||||
* @param size The size the view should be. Optionally provide a single dimension.
|
||||
*/
|
||||
resizeView(location: GridLocation, size: Partial<IViewSize>): void {
|
||||
const [rest, index] = tail(location);
|
||||
const [pathToParent, parent] = this.getNode(rest);
|
||||
|
||||
|
@ -1164,11 +1373,11 @@ export class GridView implements IDisposable {
|
|||
throw new Error('Invalid location');
|
||||
}
|
||||
|
||||
if (!width && !height) {
|
||||
if (!size.width && !size.height) {
|
||||
return;
|
||||
}
|
||||
|
||||
const [parentSize, grandParentSize] = parent.orientation === Orientation.HORIZONTAL ? [width, height] : [height, width];
|
||||
const [parentSize, grandParentSize] = parent.orientation === Orientation.HORIZONTAL ? [size.width, size.height] : [size.height, size.width];
|
||||
|
||||
if (typeof grandParentSize === 'number' && pathToParent.length > 0) {
|
||||
const [, grandParent] = tail(pathToParent);
|
||||
|
@ -1184,7 +1393,13 @@ export class GridView implements IDisposable {
|
|||
this.trySet2x2();
|
||||
}
|
||||
|
||||
getViewSize(location?: number[]): IViewSize {
|
||||
/**
|
||||
* Get the size of a {@link IView view}.
|
||||
*
|
||||
* @param location The {@link GridLocation location} of the view. Provide `undefined` to get
|
||||
* the size of the grid itself.
|
||||
*/
|
||||
getViewSize(location?: GridLocation): IViewSize {
|
||||
if (!location) {
|
||||
return { width: this.root.width, height: this.root.height };
|
||||
}
|
||||
|
@ -1193,7 +1408,13 @@ export class GridView implements IDisposable {
|
|||
return { width: node.width, height: node.height };
|
||||
}
|
||||
|
||||
getViewCachedVisibleSize(location: number[]): number | undefined {
|
||||
/**
|
||||
* Get the cached visible size of a {@link IView view}. This was the size
|
||||
* of the view at the moment it last became hidden.
|
||||
*
|
||||
* @param location The {@link GridLocation location} of the view.
|
||||
*/
|
||||
getViewCachedVisibleSize(location: GridLocation): number | undefined {
|
||||
const [rest, index] = tail(location);
|
||||
const [, parent] = this.getNode(rest);
|
||||
|
||||
|
@ -1204,7 +1425,13 @@ export class GridView implements IDisposable {
|
|||
return parent.getChildCachedVisibleSize(index);
|
||||
}
|
||||
|
||||
maximizeViewSize(location: number[]): void {
|
||||
/**
|
||||
* Maximize the size of a {@link IView view} by collapsing all other views
|
||||
* to their minimum sizes.
|
||||
*
|
||||
* @param location The {@link GridLocation location} of the view.
|
||||
*/
|
||||
maximizeViewSize(location: GridLocation): void {
|
||||
const [ancestors, node] = this.getNode(location);
|
||||
|
||||
if (!(node instanceof LeafNode)) {
|
||||
|
@ -1216,7 +1443,16 @@ export class GridView implements IDisposable {
|
|||
}
|
||||
}
|
||||
|
||||
distributeViewSizes(location?: number[]): void {
|
||||
/**
|
||||
* Distribute the size among all {@link IView views} within the entire
|
||||
* grid or within a single {@link SplitView}.
|
||||
*
|
||||
* @param location The {@link GridLocation location} of a view containing
|
||||
* children views, which will have their sizes distributed within the parent
|
||||
* view's size. Provide `undefined` to recursively distribute all views' sizes
|
||||
* in the entire grid.
|
||||
*/
|
||||
distributeViewSizes(location?: GridLocation): void {
|
||||
if (!location) {
|
||||
this.root.distributeViewSizes(true);
|
||||
return;
|
||||
|
@ -1232,7 +1468,12 @@ export class GridView implements IDisposable {
|
|||
this.trySet2x2();
|
||||
}
|
||||
|
||||
isViewVisible(location: number[]): boolean {
|
||||
/**
|
||||
* Returns whether a {@link IView view} is visible.
|
||||
*
|
||||
* @param location The {@link GridLocation location} of the view.
|
||||
*/
|
||||
isViewVisible(location: GridLocation): boolean {
|
||||
const [rest, index] = tail(location);
|
||||
const [, parent] = this.getNode(rest);
|
||||
|
||||
|
@ -1243,7 +1484,12 @@ export class GridView implements IDisposable {
|
|||
return parent.isChildVisible(index);
|
||||
}
|
||||
|
||||
setViewVisible(location: number[], visible: boolean): void {
|
||||
/**
|
||||
* Set the visibility state of a {@link IView view}.
|
||||
*
|
||||
* @param location The {@link GridLocation location} of the view.
|
||||
*/
|
||||
setViewVisible(location: GridLocation, visible: boolean): void {
|
||||
const [rest, index] = tail(location);
|
||||
const [, parent] = this.getNode(rest);
|
||||
|
||||
|
@ -1254,13 +1500,31 @@ export class GridView implements IDisposable {
|
|||
parent.setChildVisible(index, visible);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a descriptor for the entire grid.
|
||||
*/
|
||||
getView(): GridBranchNode;
|
||||
getView(location?: number[]): GridNode;
|
||||
getView(location?: number[]): GridNode {
|
||||
|
||||
/**
|
||||
* Returns a descriptor for a {@link GridLocation subtree} within the
|
||||
* {@link GridView}.
|
||||
*
|
||||
* @param location The {@link GridLocation location} of the root of
|
||||
* the {@link GridLocation subtree}.
|
||||
*/
|
||||
getView(location: GridLocation): GridNode;
|
||||
getView(location?: GridLocation): GridNode {
|
||||
const node = location ? this.getNode(location)[1] : this._root;
|
||||
return this._getViews(node, this.orientation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new {@link GridView} from a JSON object.
|
||||
*
|
||||
* @param json The JSON object.
|
||||
* @param deserializer A deserializer which can revive each view.
|
||||
* @returns A new {@link GridView} instance.
|
||||
*/
|
||||
static deserialize<T extends ISerializableView>(json: ISerializedGridView, deserializer: IViewDeserializer<T>, options: IGridViewOptions = {}): GridView {
|
||||
if (typeof json.orientation !== 'number') {
|
||||
throw new Error('Invalid JSON: \'orientation\' property must be a number.');
|
||||
|
@ -1323,7 +1587,7 @@ export class GridView implements IDisposable {
|
|||
return { children, box };
|
||||
}
|
||||
|
||||
private getNode(location: number[], node: Node = this.root, path: BranchNode[] = []): [BranchNode[], Node] {
|
||||
private getNode(location: GridLocation, node: Node = this.root, path: BranchNode[] = []): [BranchNode[], Node] {
|
||||
if (location.length === 0) {
|
||||
return [path, node];
|
||||
}
|
||||
|
@ -1344,6 +1608,13 @@ export class GridView implements IDisposable {
|
|||
return this.getNode(rest, child, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to lock the {@link Sash sashes} in this {@link GridView} so
|
||||
* the grid behaves as a 2x2 matrix, with a corner sash in the middle.
|
||||
*
|
||||
* In case the grid isn't a 2x2 grid _and_ all sashes are not aligned,
|
||||
* this method is a no-op.
|
||||
*/
|
||||
trySet2x2(): void {
|
||||
this.disposable2x2.dispose();
|
||||
this.disposable2x2 = Disposable.None;
|
||||
|
@ -1361,6 +1632,22 @@ export class GridView implements IDisposable {
|
|||
this.disposable2x2 = first.trySet2x2(second);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate a map with views to DOM nodes.
|
||||
* @remarks To be used internally only.
|
||||
*/
|
||||
getViewMap(map: Map<IView, HTMLElement>, node?: Node): void {
|
||||
if (!node) {
|
||||
node = this.root;
|
||||
}
|
||||
|
||||
if (node instanceof BranchNode) {
|
||||
node.children.forEach(child => this.getViewMap(map, child));
|
||||
} else {
|
||||
map.set(node.view, node.element);
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.onDidSashResetRelay.dispose();
|
||||
this.root.dispose();
|
||||
|
|
|
@ -34,6 +34,7 @@ export interface IMenuBarOptions {
|
|||
getKeybinding?: (action: IAction) => ResolvedKeybinding | undefined;
|
||||
alwaysOnMnemonics?: boolean;
|
||||
compactMode?: Direction;
|
||||
actionRunner?: IActionRunner;
|
||||
getCompactMenuActions?: () => IAction[]
|
||||
}
|
||||
|
||||
|
@ -109,7 +110,7 @@ export class MenuBar extends Disposable {
|
|||
|
||||
this.menuUpdater = this._register(new RunOnceScheduler(() => this.update(), 200));
|
||||
|
||||
this.actionRunner = this._register(new ActionRunner());
|
||||
this.actionRunner = this.options.actionRunner ?? this._register(new ActionRunner());
|
||||
this._register(this.actionRunner.onBeforeRun(() => {
|
||||
this.setUnfocusedState();
|
||||
}));
|
||||
|
|
|
@ -13,29 +13,39 @@ import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecy
|
|||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import 'vs/css!./sash';
|
||||
|
||||
/**
|
||||
* Allow the sashes to be visible at runtime.
|
||||
* @remark Use for development purposes only.
|
||||
*/
|
||||
let DEBUG = false;
|
||||
// DEBUG = Boolean("true"); // done "weirdly" so that a lint warning prevents you from pushing this
|
||||
|
||||
export interface ISashLayoutProvider { }
|
||||
|
||||
export interface IVerticalSashLayoutProvider extends ISashLayoutProvider {
|
||||
/**
|
||||
* A vertical sash layout provider provides position and height for a sash.
|
||||
*/
|
||||
export interface IVerticalSashLayoutProvider {
|
||||
getVerticalSashLeft(sash: Sash): number;
|
||||
getVerticalSashTop?(sash: Sash): number;
|
||||
getVerticalSashHeight?(sash: Sash): number;
|
||||
}
|
||||
|
||||
export interface IHorizontalSashLayoutProvider extends ISashLayoutProvider {
|
||||
/**
|
||||
* A vertical sash layout provider provides position and width for a sash.
|
||||
*/
|
||||
export interface IHorizontalSashLayoutProvider {
|
||||
getHorizontalSashTop(sash: Sash): number;
|
||||
getHorizontalSashLeft?(sash: Sash): number;
|
||||
getHorizontalSashWidth?(sash: Sash): number;
|
||||
}
|
||||
|
||||
type ISashLayoutProvider = IVerticalSashLayoutProvider | IHorizontalSashLayoutProvider;
|
||||
|
||||
export interface ISashEvent {
|
||||
startX: number;
|
||||
currentX: number;
|
||||
startY: number;
|
||||
currentY: number;
|
||||
altKey: boolean;
|
||||
readonly startX: number;
|
||||
readonly currentX: number;
|
||||
readonly startY: number;
|
||||
readonly currentY: number;
|
||||
readonly altKey: boolean;
|
||||
}
|
||||
|
||||
export enum OrthogonalEdge {
|
||||
|
@ -46,10 +56,41 @@ export enum OrthogonalEdge {
|
|||
}
|
||||
|
||||
export interface ISashOptions {
|
||||
|
||||
/**
|
||||
* Whether a sash is horizontal or vertical.
|
||||
*/
|
||||
readonly orientation: Orientation;
|
||||
readonly orthogonalStartSash?: Sash;
|
||||
readonly orthogonalEndSash?: Sash;
|
||||
|
||||
/**
|
||||
* The width or height of a vertical or horizontal sash, respectively.
|
||||
*/
|
||||
readonly size?: number;
|
||||
|
||||
/**
|
||||
* A reference to another sash, perpendicular to this one, which
|
||||
* aligns at the start of this one. A corner sash will be created
|
||||
* automatically at that location.
|
||||
*
|
||||
* The start of a horizontal sash is its left-most position.
|
||||
* The start of a vertical sash is its top-most position.
|
||||
*/
|
||||
readonly orthogonalStartSash?: Sash;
|
||||
|
||||
/**
|
||||
* A reference to another sash, perpendicular to this one, which
|
||||
* aligns at the end of this one. A corner sash will be created
|
||||
* automatically at that location.
|
||||
*
|
||||
* The end of a horizontal sash is its right-most position.
|
||||
* The end of a vertical sash is its bottom-most position.
|
||||
*/
|
||||
readonly orthogonalEndSash?: Sash;
|
||||
|
||||
/**
|
||||
* Provides a hint as to what mouse cursor to use whenever the user
|
||||
* hovers over a corner sash provided by this and an orthogonal sash.
|
||||
*/
|
||||
readonly orthogonalEdge?: OrthogonalEdge;
|
||||
}
|
||||
|
||||
|
@ -67,9 +108,31 @@ export const enum Orientation {
|
|||
}
|
||||
|
||||
export const enum SashState {
|
||||
|
||||
/**
|
||||
* Disable any UI interaction.
|
||||
*/
|
||||
Disabled,
|
||||
Minimum,
|
||||
Maximum,
|
||||
|
||||
/**
|
||||
* Allow dragging down or to the right, depending on the sash orientation.
|
||||
*
|
||||
* Some OSs allow customizing the mouse cursor differently whenever
|
||||
* some resizable component can't be any smaller, but can be larger.
|
||||
*/
|
||||
AtMinimum,
|
||||
|
||||
/**
|
||||
* Allow dragging up or to the left, depending on the sash orientation.
|
||||
*
|
||||
* Some OSs allow customizing the mouse cursor differently whenever
|
||||
* some resizable component can't be any larger, but can be smaller.
|
||||
*/
|
||||
AtMaximum,
|
||||
|
||||
/**
|
||||
* Enable dragging.
|
||||
*/
|
||||
Enabled
|
||||
}
|
||||
|
||||
|
@ -159,52 +222,101 @@ class OrthogonalPointerEventFactory implements IPointerEventFactory {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link Sash} is the UI component which allows the user to resize other
|
||||
* components. It's usually an invisible horizontal or vertical line which, when
|
||||
* hovered, becomes highlighted and can be dragged along the perpendicular dimension
|
||||
* to its direction.
|
||||
*
|
||||
* Features:
|
||||
* - Touch event handling
|
||||
* - Corner sash support
|
||||
* - Hover with different mouse cursor support
|
||||
* - Configurable hover size
|
||||
* - Linked sash support, for 2x2 corner sashes
|
||||
*/
|
||||
export class Sash extends Disposable {
|
||||
|
||||
private el: HTMLElement;
|
||||
private layoutProvider: ISashLayoutProvider;
|
||||
private orientation!: Orientation;
|
||||
private orientation: Orientation;
|
||||
private size: number;
|
||||
private hoverDelay = globalHoverDelay;
|
||||
private hoverDelayer = this._register(new Delayer(this.hoverDelay));
|
||||
|
||||
private _state: SashState = SashState.Enabled;
|
||||
private readonly onDidEnablementChange = this._register(new Emitter<SashState>());
|
||||
private readonly _onDidStart = this._register(new Emitter<ISashEvent>());
|
||||
private readonly _onDidChange = this._register(new Emitter<ISashEvent>());
|
||||
private readonly _onDidReset = this._register(new Emitter<void>());
|
||||
private readonly _onDidEnd = this._register(new Emitter<void>());
|
||||
private readonly orthogonalStartSashDisposables = this._register(new DisposableStore());
|
||||
private _orthogonalStartSash: Sash | undefined;
|
||||
private readonly orthogonalStartDragHandleDisposables = this._register(new DisposableStore());
|
||||
private _orthogonalStartDragHandle: HTMLElement | undefined;
|
||||
private readonly orthogonalEndSashDisposables = this._register(new DisposableStore());
|
||||
private _orthogonalEndSash: Sash | undefined;
|
||||
private readonly orthogonalEndDragHandleDisposables = this._register(new DisposableStore());
|
||||
private _orthogonalEndDragHandle: HTMLElement | undefined;
|
||||
|
||||
get state(): SashState { return this._state; }
|
||||
get orthogonalStartSash(): Sash | undefined { return this._orthogonalStartSash; }
|
||||
get orthogonalEndSash(): Sash | undefined { return this._orthogonalEndSash; }
|
||||
|
||||
/**
|
||||
* The state of a sash defines whether it can be interacted with by the user
|
||||
* as well as what mouse cursor to use, when hovered.
|
||||
*/
|
||||
set state(state: SashState) {
|
||||
if (this._state === state) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.el.classList.toggle('disabled', state === SashState.Disabled);
|
||||
this.el.classList.toggle('minimum', state === SashState.Minimum);
|
||||
this.el.classList.toggle('maximum', state === SashState.Maximum);
|
||||
this.el.classList.toggle('minimum', state === SashState.AtMinimum);
|
||||
this.el.classList.toggle('maximum', state === SashState.AtMaximum);
|
||||
|
||||
this._state = state;
|
||||
this._onDidEnablementChange.fire(state);
|
||||
this.onDidEnablementChange.fire(state);
|
||||
}
|
||||
|
||||
private readonly _onDidEnablementChange = this._register(new Emitter<SashState>());
|
||||
readonly onDidEnablementChange: Event<SashState> = this._onDidEnablementChange.event;
|
||||
|
||||
private readonly _onDidStart = this._register(new Emitter<ISashEvent>());
|
||||
/**
|
||||
* An event which fires whenever the user starts dragging this sash.
|
||||
*/
|
||||
readonly onDidStart: Event<ISashEvent> = this._onDidStart.event;
|
||||
|
||||
private readonly _onDidChange = this._register(new Emitter<ISashEvent>());
|
||||
/**
|
||||
* An event which fires whenever the user moves the mouse while
|
||||
* dragging this sash.
|
||||
*/
|
||||
readonly onDidChange: Event<ISashEvent> = this._onDidChange.event;
|
||||
|
||||
private readonly _onDidReset = this._register(new Emitter<void>());
|
||||
/**
|
||||
* An event which fires whenever the user double clicks this sash.
|
||||
*/
|
||||
readonly onDidReset: Event<void> = this._onDidReset.event;
|
||||
|
||||
private readonly _onDidEnd = this._register(new Emitter<void>());
|
||||
/**
|
||||
* An event which fires whenever the user stops dragging this sash.
|
||||
*/
|
||||
readonly onDidEnd: Event<void> = this._onDidEnd.event;
|
||||
|
||||
/**
|
||||
* A linked sash will be forwarded the same user interactions and events
|
||||
* so it moves exactly the same way as this sash.
|
||||
*
|
||||
* Useful in 2x2 grids. Not meant for widespread usage.
|
||||
*/
|
||||
linkedSash: Sash | undefined = undefined;
|
||||
|
||||
private readonly orthogonalStartSashDisposables = this._register(new DisposableStore());
|
||||
private _orthogonalStartSash: Sash | undefined;
|
||||
private readonly orthogonalStartDragHandleDisposables = this._register(new DisposableStore());
|
||||
private _orthogonalStartDragHandle: HTMLElement | undefined;
|
||||
get orthogonalStartSash(): Sash | undefined { return this._orthogonalStartSash; }
|
||||
/**
|
||||
* A reference to another sash, perpendicular to this one, which
|
||||
* aligns at the start of this one. A corner sash will be created
|
||||
* automatically at that location.
|
||||
*
|
||||
* The start of a horizontal sash is its left-most position.
|
||||
* The start of a vertical sash is its top-most position.
|
||||
*/
|
||||
set orthogonalStartSash(sash: Sash | undefined) {
|
||||
this.orthogonalStartDragHandleDisposables.clear();
|
||||
this.orthogonalStartSashDisposables.clear();
|
||||
|
@ -223,18 +335,22 @@ export class Sash extends Disposable {
|
|||
}
|
||||
};
|
||||
|
||||
this.orthogonalStartSashDisposables.add(sash.onDidEnablementChange(onChange, this));
|
||||
this.orthogonalStartSashDisposables.add(sash.onDidEnablementChange.event(onChange, this));
|
||||
onChange(sash.state);
|
||||
}
|
||||
|
||||
this._orthogonalStartSash = sash;
|
||||
}
|
||||
|
||||
private readonly orthogonalEndSashDisposables = this._register(new DisposableStore());
|
||||
private _orthogonalEndSash: Sash | undefined;
|
||||
private readonly orthogonalEndDragHandleDisposables = this._register(new DisposableStore());
|
||||
private _orthogonalEndDragHandle: HTMLElement | undefined;
|
||||
get orthogonalEndSash(): Sash | undefined { return this._orthogonalEndSash; }
|
||||
/**
|
||||
* A reference to another sash, perpendicular to this one, which
|
||||
* aligns at the end of this one. A corner sash will be created
|
||||
* automatically at that location.
|
||||
*
|
||||
* The end of a horizontal sash is its right-most position.
|
||||
* The end of a vertical sash is its bottom-most position.
|
||||
*/
|
||||
|
||||
set orthogonalEndSash(sash: Sash | undefined) {
|
||||
this.orthogonalEndDragHandleDisposables.clear();
|
||||
this.orthogonalEndSashDisposables.clear();
|
||||
|
@ -253,15 +369,30 @@ export class Sash extends Disposable {
|
|||
}
|
||||
};
|
||||
|
||||
this.orthogonalEndSashDisposables.add(sash.onDidEnablementChange(onChange, this));
|
||||
this.orthogonalEndSashDisposables.add(sash.onDidEnablementChange.event(onChange, this));
|
||||
onChange(sash.state);
|
||||
}
|
||||
|
||||
this._orthogonalEndSash = sash;
|
||||
}
|
||||
|
||||
constructor(container: HTMLElement, layoutProvider: IVerticalSashLayoutProvider, options: ISashOptions);
|
||||
constructor(container: HTMLElement, layoutProvider: IHorizontalSashLayoutProvider, options: ISashOptions);
|
||||
/**
|
||||
* Create a new vertical sash.
|
||||
*
|
||||
* @param container A DOM node to append the sash to.
|
||||
* @param verticalLayoutProvider A vertical layout provider.
|
||||
* @param options The options.
|
||||
*/
|
||||
constructor(container: HTMLElement, verticalLayoutProvider: IVerticalSashLayoutProvider, options: IVerticalSashOptions);
|
||||
|
||||
/**
|
||||
* Create a new horizontal sash.
|
||||
*
|
||||
* @param container A DOM node to append the sash to.
|
||||
* @param horizontalLayoutProvider A horizontal layout provider.
|
||||
* @param options The options.
|
||||
*/
|
||||
constructor(container: HTMLElement, horizontalLayoutProvider: IHorizontalSashLayoutProvider, options: IHorizontalSashOptions);
|
||||
constructor(container: HTMLElement, layoutProvider: ISashLayoutProvider, options: ISashOptions) {
|
||||
super();
|
||||
|
||||
|
@ -381,17 +512,17 @@ export class Sash extends Disposable {
|
|||
if (isMultisashResize) {
|
||||
cursor = 'all-scroll';
|
||||
} else if (this.orientation === Orientation.HORIZONTAL) {
|
||||
if (this.state === SashState.Minimum) {
|
||||
if (this.state === SashState.AtMinimum) {
|
||||
cursor = 's-resize';
|
||||
} else if (this.state === SashState.Maximum) {
|
||||
} else if (this.state === SashState.AtMaximum) {
|
||||
cursor = 'n-resize';
|
||||
} else {
|
||||
cursor = isMacintosh ? 'row-resize' : 'ns-resize';
|
||||
}
|
||||
} else {
|
||||
if (this.state === SashState.Minimum) {
|
||||
if (this.state === SashState.AtMinimum) {
|
||||
cursor = 'e-resize';
|
||||
} else if (this.state === SashState.Maximum) {
|
||||
} else if (this.state === SashState.AtMaximum) {
|
||||
cursor = 'w-resize';
|
||||
} else {
|
||||
cursor = isMacintosh ? 'col-resize' : 'ew-resize';
|
||||
|
@ -406,7 +537,7 @@ export class Sash extends Disposable {
|
|||
updateStyle();
|
||||
|
||||
if (!isMultisashResize) {
|
||||
this.onDidEnablementChange(updateStyle, null, disposables);
|
||||
this.onDidEnablementChange.event(updateStyle, null, disposables);
|
||||
}
|
||||
|
||||
const onPointerMove = (e: PointerEvent) => {
|
||||
|
@ -472,10 +603,19 @@ export class Sash extends Disposable {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Forcefully stop any user interactions with this sash.
|
||||
* Useful when hiding a parent component, while the user is still
|
||||
* interacting with the sash.
|
||||
*/
|
||||
clearSashHoverState(): void {
|
||||
Sash.onMouseLeave(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Layout the sash. The sash will size and position itself
|
||||
* based on its provided {@link ISashLayoutProvider layout provider}.
|
||||
*/
|
||||
layout(): void {
|
||||
if (this.orientation === Orientation.VERTICAL) {
|
||||
const verticalProvider = (<IVerticalSashLayoutProvider>this.layoutProvider);
|
||||
|
|
|
@ -17,45 +17,178 @@ import 'vs/css!./splitview';
|
|||
export { Orientation } from 'vs/base/browser/ui/sash/sash';
|
||||
|
||||
export interface ISplitViewStyles {
|
||||
separatorBorder: Color;
|
||||
readonly separatorBorder: Color;
|
||||
}
|
||||
|
||||
const defaultStyles: ISplitViewStyles = {
|
||||
separatorBorder: Color.transparent
|
||||
};
|
||||
|
||||
export interface ISplitViewOptions<TLayoutContext = undefined> {
|
||||
readonly orientation?: Orientation; // default Orientation.VERTICAL
|
||||
readonly styles?: ISplitViewStyles;
|
||||
readonly orthogonalStartSash?: Sash;
|
||||
readonly orthogonalEndSash?: Sash;
|
||||
readonly inverseAltBehavior?: boolean;
|
||||
readonly proportionalLayout?: boolean; // default true,
|
||||
readonly descriptor?: ISplitViewDescriptor<TLayoutContext>;
|
||||
readonly scrollbarVisibility?: ScrollbarVisibility;
|
||||
readonly getSashOrthogonalSize?: () => number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only used when `proportionalLayout` is false.
|
||||
*/
|
||||
export const enum LayoutPriority {
|
||||
Normal,
|
||||
Low,
|
||||
High
|
||||
}
|
||||
|
||||
/**
|
||||
* The interface to implement for views within a {@link SplitView}.
|
||||
*
|
||||
* An optional {@link TLayoutContext layout context type} may be used in order to
|
||||
* pass along layout contextual data from the {@link SplitView.layout} method down
|
||||
* to each view's {@link IView.layout} calls.
|
||||
*/
|
||||
export interface IView<TLayoutContext = undefined> {
|
||||
|
||||
/**
|
||||
* The DOM element for this view.
|
||||
*/
|
||||
readonly element: HTMLElement;
|
||||
|
||||
/**
|
||||
* A minimum size for this view.
|
||||
*
|
||||
* @remarks If none, set it to `0`.
|
||||
*/
|
||||
readonly minimumSize: number;
|
||||
|
||||
/**
|
||||
* A minimum size for this view.
|
||||
*
|
||||
* @remarks If none, set it to `Number.POSITIVE_INFINITY`.
|
||||
*/
|
||||
readonly maximumSize: number;
|
||||
readonly onDidChange: Event<number | undefined>;
|
||||
|
||||
/**
|
||||
* The priority of the view when the {@link SplitView.resize layout} algorithm
|
||||
* runs. Views with higher priority will be resized first.
|
||||
*
|
||||
* @remarks Only used when `proportionalLayout` is false.
|
||||
*/
|
||||
readonly priority?: LayoutPriority;
|
||||
|
||||
/**
|
||||
* Whether the view will snap whenever the user reaches its minimum size or
|
||||
* attempts to grow it beyond the minimum size.
|
||||
*
|
||||
* @defaultValue `false`
|
||||
*/
|
||||
readonly snap?: boolean;
|
||||
|
||||
/**
|
||||
* View instances are supposed to fire the {@link IView.onDidChange} event whenever
|
||||
* any of the constraint properties have changed:
|
||||
*
|
||||
* - {@link IView.minimumSize}
|
||||
* - {@link IView.maximumSize}
|
||||
* - {@link IView.priority}
|
||||
* - {@link IView.snap}
|
||||
*
|
||||
* The SplitView will relayout whenever that happens. The event can optionally emit
|
||||
* the view's preferred size for that relayout.
|
||||
*/
|
||||
readonly onDidChange: Event<number | undefined>;
|
||||
|
||||
/**
|
||||
* This will be called by the {@link SplitView} during layout. A view meant to
|
||||
* pass along the layout information down to its descendants.
|
||||
*
|
||||
* @param size The size of this view, in pixels.
|
||||
* @param offset The offset of this view, relative to the start of the {@link SplitView}.
|
||||
* @param context The optional {@link IView layout context} passed to {@link SplitView.layout}.
|
||||
*/
|
||||
layout(size: number, offset: number, context: TLayoutContext | undefined): void;
|
||||
|
||||
/**
|
||||
* This will be called by the {@link SplitView} whenever this view is made
|
||||
* visible or hidden.
|
||||
*
|
||||
* @param visible Whether the view becomes visible.
|
||||
*/
|
||||
setVisible?(visible: boolean): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* A descriptor for a {@link SplitView} instance.
|
||||
*/
|
||||
export interface ISplitViewDescriptor<TLayoutContext = undefined> {
|
||||
|
||||
/**
|
||||
* The layout size of the {@link SplitView}.
|
||||
*/
|
||||
readonly size: number;
|
||||
|
||||
/**
|
||||
* Descriptors for each {@link IView view}.
|
||||
*/
|
||||
readonly views: {
|
||||
|
||||
/**
|
||||
* Whether the {@link IView view} is visible.
|
||||
*
|
||||
* @defaultValue `true`
|
||||
*/
|
||||
readonly visible?: boolean;
|
||||
|
||||
/**
|
||||
* The size of the {@link IView view}.
|
||||
*
|
||||
* @defaultValue `true`
|
||||
*/
|
||||
readonly size: number;
|
||||
|
||||
/**
|
||||
* The size of the {@link IView view}.
|
||||
*
|
||||
* @defaultValue `true`
|
||||
*/
|
||||
readonly view: IView<TLayoutContext>;
|
||||
}[];
|
||||
}
|
||||
|
||||
export interface ISplitViewOptions<TLayoutContext = undefined> {
|
||||
|
||||
/**
|
||||
* Which axis the views align on.
|
||||
*
|
||||
* @defaultValue `Orientation.VERTICAL`
|
||||
*/
|
||||
readonly orientation?: Orientation;
|
||||
|
||||
/**
|
||||
* Styles overriding the {@link defaultStyles default ones}.
|
||||
*/
|
||||
readonly styles?: ISplitViewStyles;
|
||||
|
||||
/**
|
||||
* Make Alt-drag the default drag operation.
|
||||
*/
|
||||
readonly inverseAltBehavior?: boolean;
|
||||
|
||||
/**
|
||||
* Resize each view proportionally when resizing the SplitView.
|
||||
*
|
||||
* @defaultValue `true`
|
||||
*/
|
||||
readonly proportionalLayout?: boolean;
|
||||
|
||||
/**
|
||||
* An initial description of this {@link SplitView} instance, allowing
|
||||
* to initialze all views within the ctor.
|
||||
*/
|
||||
readonly descriptor?: ISplitViewDescriptor<TLayoutContext>;
|
||||
|
||||
/**
|
||||
* The scrollbar visibility setting for whenever the views within
|
||||
* the {@link SplitView} overflow.
|
||||
*/
|
||||
readonly scrollbarVisibility?: ScrollbarVisibility;
|
||||
|
||||
/**
|
||||
* Override the orthogonal size of sashes.
|
||||
*/
|
||||
readonly getSashOrthogonalSize?: () => number;
|
||||
}
|
||||
|
||||
interface ISashEvent {
|
||||
readonly sash: Sash;
|
||||
readonly start: number;
|
||||
|
@ -190,30 +323,89 @@ enum State {
|
|||
Busy
|
||||
}
|
||||
|
||||
/**
|
||||
* When adding or removing views, distribute the delta space among
|
||||
* all other views.
|
||||
*/
|
||||
export type DistributeSizing = { type: 'distribute' };
|
||||
|
||||
/**
|
||||
* When adding or removing views, split the delta space with another
|
||||
* specific view, indexed by the provided `index`.
|
||||
*/
|
||||
export type SplitSizing = { type: 'split', index: number };
|
||||
|
||||
/**
|
||||
* When adding or removing views, assume the view is invisible.
|
||||
*/
|
||||
export type InvisibleSizing = { type: 'invisible', cachedVisibleSize: number };
|
||||
|
||||
/**
|
||||
* When adding or removing views, the sizing provides fine grained
|
||||
* control over how other views get resized.
|
||||
*/
|
||||
export type Sizing = DistributeSizing | SplitSizing | InvisibleSizing;
|
||||
|
||||
export namespace Sizing {
|
||||
|
||||
/**
|
||||
* When adding or removing views, distribute the delta space among
|
||||
* all other views.
|
||||
*/
|
||||
export const Distribute: DistributeSizing = { type: 'distribute' };
|
||||
|
||||
/**
|
||||
* When adding or removing views, split the delta space with another
|
||||
* specific view, indexed by the provided `index`.
|
||||
*/
|
||||
export function Split(index: number): SplitSizing { return { type: 'split', index }; }
|
||||
|
||||
/**
|
||||
* When adding or removing views, assume the view is invisible.
|
||||
*/
|
||||
export function Invisible(cachedVisibleSize: number): InvisibleSizing { return { type: 'invisible', cachedVisibleSize }; }
|
||||
}
|
||||
|
||||
export interface ISplitViewDescriptor<TLayoutContext = undefined> {
|
||||
size: number;
|
||||
views: {
|
||||
visible?: boolean;
|
||||
size: number;
|
||||
view: IView<TLayoutContext>;
|
||||
}[];
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link SplitView} is the UI component which implements a one dimensional
|
||||
* flex-like layout algorithm for a collection of {@link IView} instances, which
|
||||
* are essentially HTMLElement instances with the following size constraints:
|
||||
*
|
||||
* - {@link IView.minimumSize}
|
||||
* - {@link IView.maximumSize}
|
||||
* - {@link IView.priority}
|
||||
* - {@link IView.snap}
|
||||
*
|
||||
* In case the SplitView doesn't have enough size to fit all views, it will overflow
|
||||
* its content with a scrollbar.
|
||||
*
|
||||
* In between each pair of views there will be a {@link Sash} allowing the user
|
||||
* to resize the views, making sure the constraints are respected.
|
||||
*
|
||||
* An optional {@link TLayoutContext layout context type} may be used in order to
|
||||
* pass along layout contextual data from the {@link SplitView.layout} method down
|
||||
* to each view's {@link IView.layout} calls.
|
||||
*
|
||||
* Features:
|
||||
* - Flex-like layout algorithm
|
||||
* - Snap support
|
||||
* - Orthogonal sash support, for corner sashes
|
||||
* - View hide/show support
|
||||
* - View swap/move support
|
||||
* - Alt key modifier behavior, macOS style
|
||||
*/
|
||||
export class SplitView<TLayoutContext = undefined> extends Disposable {
|
||||
|
||||
/**
|
||||
* This {@link SplitView}'s orientation.
|
||||
*/
|
||||
readonly orientation: Orientation;
|
||||
|
||||
/**
|
||||
* The DOM element representing this {@link SplitView}.
|
||||
*/
|
||||
readonly el: HTMLElement;
|
||||
|
||||
private sashContainer: HTMLElement;
|
||||
private viewContainer: HTMLElement;
|
||||
private scrollable: Scrollable;
|
||||
|
@ -231,27 +423,58 @@ export class SplitView<TLayoutContext = undefined> extends Disposable {
|
|||
private readonly getSashOrthogonalSize: { (): number } | undefined;
|
||||
|
||||
private _onDidSashChange = this._register(new Emitter<number>());
|
||||
private _onDidSashReset = this._register(new Emitter<number>());
|
||||
private _orthogonalStartSash: Sash | undefined;
|
||||
private _orthogonalEndSash: Sash | undefined;
|
||||
private _startSnappingEnabled = true;
|
||||
private _endSnappingEnabled = true;
|
||||
|
||||
/**
|
||||
* Fires whenever the user resizes a {@link Sash sash}.
|
||||
*/
|
||||
readonly onDidSashChange = this._onDidSashChange.event;
|
||||
|
||||
private _onDidSashReset = this._register(new Emitter<number>());
|
||||
/**
|
||||
* Fires whenever the user double clicks a {@link Sash sash}.
|
||||
*/
|
||||
readonly onDidSashReset = this._onDidSashReset.event;
|
||||
|
||||
/**
|
||||
* Fires whenever the split view is scrolled.
|
||||
*/
|
||||
readonly onDidScroll: Event<ScrollEvent>;
|
||||
|
||||
/**
|
||||
* The amount of views in this {@link SplitView}.
|
||||
*/
|
||||
get length(): number {
|
||||
return this.viewItems.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* The minimum size of this {@link SplitView}.
|
||||
*/
|
||||
get minimumSize(): number {
|
||||
return this.viewItems.reduce((r, item) => r + item.minimumSize, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum size of this {@link SplitView}.
|
||||
*/
|
||||
get maximumSize(): number {
|
||||
return this.length === 0 ? Number.POSITIVE_INFINITY : this.viewItems.reduce((r, item) => r + item.maximumSize, 0);
|
||||
}
|
||||
|
||||
private _orthogonalStartSash: Sash | undefined;
|
||||
get orthogonalStartSash(): Sash | undefined { return this._orthogonalStartSash; }
|
||||
get orthogonalEndSash(): Sash | undefined { return this._orthogonalEndSash; }
|
||||
get startSnappingEnabled(): boolean { return this._startSnappingEnabled; }
|
||||
get endSnappingEnabled(): boolean { return this._endSnappingEnabled; }
|
||||
|
||||
/**
|
||||
* A reference to a sash, perpendicular to all sashes in this {@link SplitView},
|
||||
* located at the left- or top-most side of the SplitView.
|
||||
* Corner sashes will be created automatically at the intersections.
|
||||
*/
|
||||
set orthogonalStartSash(sash: Sash | undefined) {
|
||||
for (const sashItem of this.sashItems) {
|
||||
sashItem.sash.orthogonalStartSash = sash;
|
||||
|
@ -260,8 +483,11 @@ export class SplitView<TLayoutContext = undefined> extends Disposable {
|
|||
this._orthogonalStartSash = sash;
|
||||
}
|
||||
|
||||
private _orthogonalEndSash: Sash | undefined;
|
||||
get orthogonalEndSash(): Sash | undefined { return this._orthogonalEndSash; }
|
||||
/**
|
||||
* A reference to a sash, perpendicular to all sashes in this {@link SplitView},
|
||||
* located at the right- or bottom-most side of the SplitView.
|
||||
* Corner sashes will be created automatically at the intersections.
|
||||
*/
|
||||
set orthogonalEndSash(sash: Sash | undefined) {
|
||||
for (const sashItem of this.sashItems) {
|
||||
sashItem.sash.orthogonalEndSash = sash;
|
||||
|
@ -270,12 +496,16 @@ export class SplitView<TLayoutContext = undefined> extends Disposable {
|
|||
this._orthogonalEndSash = sash;
|
||||
}
|
||||
|
||||
get sashes(): Sash[] {
|
||||
/**
|
||||
* The internal sashes within this {@link SplitView}.
|
||||
*/
|
||||
get sashes(): readonly Sash[] {
|
||||
return this.sashItems.map(s => s.sash);
|
||||
}
|
||||
|
||||
private _startSnappingEnabled = true;
|
||||
get startSnappingEnabled(): boolean { return this._startSnappingEnabled; }
|
||||
/**
|
||||
* Enable/disable snapping at the beginning of this {@link SplitView}.
|
||||
*/
|
||||
set startSnappingEnabled(startSnappingEnabled: boolean) {
|
||||
if (this._startSnappingEnabled === startSnappingEnabled) {
|
||||
return;
|
||||
|
@ -285,8 +515,9 @@ export class SplitView<TLayoutContext = undefined> extends Disposable {
|
|||
this.updateSashEnablement();
|
||||
}
|
||||
|
||||
private _endSnappingEnabled = true;
|
||||
get endSnappingEnabled(): boolean { return this._endSnappingEnabled; }
|
||||
/**
|
||||
* Enable/disable snapping at the end of this {@link SplitView}.
|
||||
*/
|
||||
set endSnappingEnabled(endSnappingEnabled: boolean) {
|
||||
if (this._endSnappingEnabled === endSnappingEnabled) {
|
||||
return;
|
||||
|
@ -296,12 +527,15 @@ export class SplitView<TLayoutContext = undefined> extends Disposable {
|
|||
this.updateSashEnablement();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link SplitView} instance.
|
||||
*/
|
||||
constructor(container: HTMLElement, options: ISplitViewOptions<TLayoutContext> = {}) {
|
||||
super();
|
||||
|
||||
this.orientation = types.isUndefined(options.orientation) ? Orientation.VERTICAL : options.orientation;
|
||||
this.inverseAltBehavior = !!options.inverseAltBehavior;
|
||||
this.proportionalLayout = types.isUndefined(options.proportionalLayout) ? true : !!options.proportionalLayout;
|
||||
this.orientation = options.orientation ?? Orientation.VERTICAL;
|
||||
this.inverseAltBehavior = options.inverseAltBehavior ?? false;
|
||||
this.proportionalLayout = options.proportionalLayout ?? true;
|
||||
this.getSashOrthogonalSize = options.getSashOrthogonalSize;
|
||||
|
||||
this.el = document.createElement('div');
|
||||
|
@ -354,10 +588,24 @@ export class SplitView<TLayoutContext = undefined> extends Disposable {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a {@link IView view} to this {@link SplitView}.
|
||||
*
|
||||
* @param view The view to add.
|
||||
* @param size Either a fixed size, or a dynamic {@link Sizing} strategy.
|
||||
* @param index The index to insert the view on.
|
||||
* @param skipLayout Whether layout should be skipped.
|
||||
*/
|
||||
addView(view: IView<TLayoutContext>, size: number | Sizing, index = this.viewItems.length, skipLayout?: boolean): void {
|
||||
this.doAddView(view, size, index, skipLayout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a {@link IView view} from this {@link SplitView}.
|
||||
*
|
||||
* @param index The index where the {@link IView view} is located.
|
||||
* @param sizing Whether to distribute other {@link IView view}'s sizes.
|
||||
*/
|
||||
removeView(index: number, sizing?: Sizing): IView<TLayoutContext> {
|
||||
if (this.state !== State.Idle) {
|
||||
throw new Error('Cant modify splitview');
|
||||
|
@ -383,13 +631,19 @@ export class SplitView<TLayoutContext = undefined> extends Disposable {
|
|||
this.relayout();
|
||||
this.state = State.Idle;
|
||||
|
||||
if (sizing && sizing.type === 'distribute') {
|
||||
if (sizing?.type === 'distribute') {
|
||||
this.distributeViewSizes();
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move a {@link IView view} to a different index.
|
||||
*
|
||||
* @param from The source index.
|
||||
* @param to The target index.
|
||||
*/
|
||||
moveView(from: number, to: number): void {
|
||||
if (this.state !== State.Idle) {
|
||||
throw new Error('Cant modify splitview');
|
||||
|
@ -401,6 +655,13 @@ export class SplitView<TLayoutContext = undefined> extends Disposable {
|
|||
this.addView(view, sizing, to);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Swap two {@link IView views}.
|
||||
*
|
||||
* @param from The source index.
|
||||
* @param to The target index.
|
||||
*/
|
||||
swapViews(from: number, to: number): void {
|
||||
if (this.state !== State.Idle) {
|
||||
throw new Error('Cant modify splitview');
|
||||
|
@ -419,6 +680,11 @@ export class SplitView<TLayoutContext = undefined> extends Disposable {
|
|||
this.addView(fromView, toSize, to);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the {@link IView view} is visible.
|
||||
*
|
||||
* @param index The {@link IView view} index.
|
||||
*/
|
||||
isViewVisible(index: number): boolean {
|
||||
if (index < 0 || index >= this.viewItems.length) {
|
||||
throw new Error('Index out of bounds');
|
||||
|
@ -428,6 +694,12 @@ export class SplitView<TLayoutContext = undefined> extends Disposable {
|
|||
return viewItem.visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a {@link IView view}'s visibility.
|
||||
*
|
||||
* @param index The {@link IView view} index.
|
||||
* @param visible Whether the {@link IView view} should be visible.
|
||||
*/
|
||||
setViewVisible(index: number, visible: boolean): void {
|
||||
if (index < 0 || index >= this.viewItems.length) {
|
||||
throw new Error('Index out of bounds');
|
||||
|
@ -441,6 +713,11 @@ export class SplitView<TLayoutContext = undefined> extends Disposable {
|
|||
this.saveProportions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link IView view}'s size previously to being hidden.
|
||||
*
|
||||
* @param index The {@link IView view} index.
|
||||
*/
|
||||
getViewCachedVisibleSize(index: number): number | undefined {
|
||||
if (index < 0 || index >= this.viewItems.length) {
|
||||
throw new Error('Index out of bounds');
|
||||
|
@ -450,6 +727,12 @@ export class SplitView<TLayoutContext = undefined> extends Disposable {
|
|||
return viewItem.cachedVisibleSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Layout the {@link SplitView}.
|
||||
*
|
||||
* @param size The entire size of the {@link SplitView}.
|
||||
* @param layoutContext An optional layout context to pass along to {@link IView views}.
|
||||
*/
|
||||
layout(size: number, layoutContext?: TLayoutContext): void {
|
||||
const previousSize = Math.max(this.size, this.contentSize);
|
||||
this.size = size;
|
||||
|
@ -616,6 +899,12 @@ export class SplitView<TLayoutContext = undefined> extends Disposable {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resize a {@link IView view} within the {@link SplitView}.
|
||||
*
|
||||
* @param index The {@link IView view} index.
|
||||
* @param size The {@link IView view} size.
|
||||
*/
|
||||
resizeView(index: number, size: number): void {
|
||||
if (this.state !== State.Idle) {
|
||||
throw new Error('Cant modify splitview');
|
||||
|
@ -640,6 +929,9 @@ export class SplitView<TLayoutContext = undefined> extends Disposable {
|
|||
this.state = State.Idle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Distribute the entire {@link SplitView} size among all {@link IView views}.
|
||||
*/
|
||||
distributeViewSizes(): void {
|
||||
const flexibleViewItems: ViewItem<TLayoutContext>[] = [];
|
||||
let flexibleSize = 0;
|
||||
|
@ -664,6 +956,9 @@ export class SplitView<TLayoutContext = undefined> extends Disposable {
|
|||
this.relayout(lowPriorityIndexes, highPriorityIndexes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of a {@link IView view}.
|
||||
*/
|
||||
getViewSize(index: number): number {
|
||||
if (index < 0 || index >= this.viewItems.length) {
|
||||
return -1;
|
||||
|
@ -964,16 +1259,16 @@ export class SplitView<TLayoutContext = undefined> extends Disposable {
|
|||
const snappedAfter = typeof snapAfterIndex === 'number' && !this.viewItems[snapAfterIndex].visible;
|
||||
|
||||
if (snappedBefore && collapsesUp[index] && (position > 0 || this.startSnappingEnabled)) {
|
||||
sash.state = SashState.Minimum;
|
||||
sash.state = SashState.AtMinimum;
|
||||
} else if (snappedAfter && collapsesDown[index] && (position < this.contentSize || this.endSnappingEnabled)) {
|
||||
sash.state = SashState.Maximum;
|
||||
sash.state = SashState.AtMaximum;
|
||||
} else {
|
||||
sash.state = SashState.Disabled;
|
||||
}
|
||||
} else if (min && !max) {
|
||||
sash.state = SashState.Minimum;
|
||||
sash.state = SashState.AtMinimum;
|
||||
} else if (!min && max) {
|
||||
sash.state = SashState.Maximum;
|
||||
sash.state = SashState.AtMaximum;
|
||||
} else {
|
||||
sash.state = SashState.Enabled;
|
||||
}
|
||||
|
|
|
@ -229,9 +229,9 @@ export function getCaseInsensitive(target: obj, key: string): any {
|
|||
|
||||
export function filter(obj: obj, predicate: (key: string, value: any) => boolean): obj {
|
||||
const result = Object.create(null);
|
||||
for (const key of Object.keys(obj)) {
|
||||
if (predicate(key, obj[key])) {
|
||||
result[key] = obj[key];
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
if (predicate(key, value)) {
|
||||
result[key] = value;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
|
|
@ -63,6 +63,90 @@ function doFindFreePort(startPort: number, giveUpAfter: number, stride: number,
|
|||
client.connect(startPort, '127.0.0.1');
|
||||
}
|
||||
|
||||
// Reference: https://chromium.googlesource.com/chromium/src.git/+/refs/heads/main/net/base/port_util.cc#56
|
||||
export const BROWSER_RESTRICTED_PORTS: any = {
|
||||
1: true, // tcpmux
|
||||
7: true, // echo
|
||||
9: true, // discard
|
||||
11: true, // systat
|
||||
13: true, // daytime
|
||||
15: true, // netstat
|
||||
17: true, // qotd
|
||||
19: true, // chargen
|
||||
20: true, // ftp data
|
||||
21: true, // ftp access
|
||||
22: true, // ssh
|
||||
23: true, // telnet
|
||||
25: true, // smtp
|
||||
37: true, // time
|
||||
42: true, // name
|
||||
43: true, // nicname
|
||||
53: true, // domain
|
||||
69: true, // tftp
|
||||
77: true, // priv-rjs
|
||||
79: true, // finger
|
||||
87: true, // ttylink
|
||||
95: true, // supdup
|
||||
101: true, // hostriame
|
||||
102: true, // iso-tsap
|
||||
103: true, // gppitnp
|
||||
104: true, // acr-nema
|
||||
109: true, // pop2
|
||||
110: true, // pop3
|
||||
111: true, // sunrpc
|
||||
113: true, // auth
|
||||
115: true, // sftp
|
||||
117: true, // uucp-path
|
||||
119: true, // nntp
|
||||
123: true, // NTP
|
||||
135: true, // loc-srv /epmap
|
||||
137: true, // netbios
|
||||
139: true, // netbios
|
||||
143: true, // imap2
|
||||
161: true, // snmp
|
||||
179: true, // BGP
|
||||
389: true, // ldap
|
||||
427: true, // SLP (Also used by Apple Filing Protocol)
|
||||
465: true, // smtp+ssl
|
||||
512: true, // print / exec
|
||||
513: true, // login
|
||||
514: true, // shell
|
||||
515: true, // printer
|
||||
526: true, // tempo
|
||||
530: true, // courier
|
||||
531: true, // chat
|
||||
532: true, // netnews
|
||||
540: true, // uucp
|
||||
548: true, // AFP (Apple Filing Protocol)
|
||||
554: true, // rtsp
|
||||
556: true, // remotefs
|
||||
563: true, // nntp+ssl
|
||||
587: true, // smtp (rfc6409)
|
||||
601: true, // syslog-conn (rfc3195)
|
||||
636: true, // ldap+ssl
|
||||
989: true, // ftps-data
|
||||
990: true, // ftps
|
||||
993: true, // ldap+ssl
|
||||
995: true, // pop3+ssl
|
||||
1719: true, // h323gatestat
|
||||
1720: true, // h323hostcall
|
||||
1723: true, // pptp
|
||||
2049: true, // nfs
|
||||
3659: true, // apple-sasl / PasswordServer
|
||||
4045: true, // lockd
|
||||
5060: true, // sip
|
||||
5061: true, // sips
|
||||
6000: true, // X11
|
||||
6566: true, // sane-port
|
||||
6665: true, // Alternate IRC [Apple addition]
|
||||
6666: true, // Alternate IRC [Apple addition]
|
||||
6667: true, // Standard IRC [Apple addition]
|
||||
6668: true, // Alternate IRC [Apple addition]
|
||||
6669: true, // Alternate IRC [Apple addition]
|
||||
6697: true, // IRC + TLS
|
||||
10080: true // Amanda
|
||||
};
|
||||
|
||||
/**
|
||||
* Uses listen instead of connect. Is faster, but if there is another listener on 0.0.0.0 then this will take 127.0.0.1 from that listener.
|
||||
*/
|
||||
|
|
|
@ -297,12 +297,12 @@ suite('Splitview', () => {
|
|||
assert.strictEqual(sashes[1].state, SashState.Disabled, 'second sash is disabled');
|
||||
|
||||
view1.maximumSize = 300;
|
||||
assert.strictEqual(sashes[0].state, SashState.Minimum, 'first sash is enabled');
|
||||
assert.strictEqual(sashes[1].state, SashState.Minimum, 'second sash is enabled');
|
||||
assert.strictEqual(sashes[0].state, SashState.AtMinimum, 'first sash is enabled');
|
||||
assert.strictEqual(sashes[1].state, SashState.AtMinimum, 'second sash is enabled');
|
||||
|
||||
view2.maximumSize = 200;
|
||||
assert.strictEqual(sashes[0].state, SashState.Minimum, 'first sash is enabled');
|
||||
assert.strictEqual(sashes[1].state, SashState.Minimum, 'second sash is enabled');
|
||||
assert.strictEqual(sashes[0].state, SashState.AtMinimum, 'first sash is enabled');
|
||||
assert.strictEqual(sashes[1].state, SashState.AtMinimum, 'second sash is enabled');
|
||||
|
||||
splitview.resizeView(0, 40);
|
||||
assert.strictEqual(sashes[0].state, SashState.Enabled, 'first sash is enabled');
|
||||
|
|
|
@ -118,7 +118,7 @@ class CodeMain {
|
|||
});
|
||||
|
||||
// Delay creation of spdlog for perf reasons (https://github.com/microsoft/vscode/issues/72906)
|
||||
bufferLogService.logger = new SpdLogLogger('main', join(environmentMainService.logsPath, 'main.log'), true, bufferLogService.getLevel());
|
||||
bufferLogService.logger = new SpdLogLogger('main', join(environmentMainService.logsPath, 'main.log'), true, false, bufferLogService.getLevel());
|
||||
|
||||
// Lifecycle
|
||||
once(lifecycleMainService.onWillShutdown)(evt => {
|
||||
|
|
|
@ -110,7 +110,7 @@ class CliMain extends Disposable {
|
|||
// Log
|
||||
const logLevel = getLogLevel(environmentService);
|
||||
const loggers: ILogger[] = [];
|
||||
loggers.push(new SpdLogLogger('cli', join(environmentService.logsPath, 'cli.log'), true, logLevel));
|
||||
loggers.push(new SpdLogLogger('cli', join(environmentService.logsPath, 'cli.log'), true, false, logLevel));
|
||||
if (logLevel === LogLevel.Trace) {
|
||||
loggers.push(new ConsoleLogger(logLevel));
|
||||
}
|
||||
|
|
|
@ -202,9 +202,12 @@ export class DecorationsOverlay extends DynamicViewOverlay {
|
|||
|
||||
if (showIfCollapsed && lineVisibleRanges.ranges.length === 1) {
|
||||
const singleVisibleRange = lineVisibleRanges.ranges[0];
|
||||
if (singleVisibleRange.width === 0) {
|
||||
// collapsed range case => make the decoration visible by faking its width
|
||||
lineVisibleRanges.ranges[0] = new HorizontalRange(singleVisibleRange.left, this._typicalHalfwidthCharacterWidth);
|
||||
if (singleVisibleRange.width < this._typicalHalfwidthCharacterWidth) {
|
||||
// collapsed/very small range case => make the decoration visible by expanding its width
|
||||
// expand its size on both sides (both to the left and to the right, keeping it centered)
|
||||
const center = Math.round(singleVisibleRange.left + singleVisibleRange.width / 2);
|
||||
const left = Math.max(0, Math.round(center - this._typicalHalfwidthCharacterWidth / 2));
|
||||
lineVisibleRanges.ranges[0] = new HorizontalRange(left, this._typicalHalfwidthCharacterWidth);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4373,7 +4373,7 @@ export const EditorOptions = {
|
|||
default: 0,
|
||||
minimum: 0,
|
||||
maximum: 100,
|
||||
markdownDescription: nls.localize('codeLensFontSize', "Controls the font size in pixels for CodeLens. When set to `0`, the 90% of `#editor.fontSize#` is used.")
|
||||
markdownDescription: nls.localize('codeLensFontSize', "Controls the font size in pixels for CodeLens. When set to `0`, 90% of `#editor.fontSize#` is used.")
|
||||
})),
|
||||
colorDecorators: register(new EditorBooleanOption(
|
||||
EditorOption.colorDecorators, 'colorDecorators', true,
|
||||
|
|
|
@ -27,6 +27,7 @@ export class DocumentSemanticTokensResult {
|
|||
constructor(
|
||||
public readonly provider: DocumentSemanticTokensProvider,
|
||||
public readonly tokens: SemanticTokens | SemanticTokensEdits | null,
|
||||
public readonly error: any
|
||||
) { }
|
||||
}
|
||||
|
||||
|
@ -45,10 +46,11 @@ export async function getDocumentSemanticTokens(model: ITextModel, lastProvider:
|
|||
// Get tokens from all providers at the same time.
|
||||
const results = await Promise.all(providers.map(async (provider) => {
|
||||
let result: SemanticTokens | SemanticTokensEdits | null | undefined;
|
||||
let error: any = null;
|
||||
try {
|
||||
result = await provider.provideDocumentSemanticTokens(model, (provider === lastProvider ? lastResultId : null), token);
|
||||
} catch (err) {
|
||||
onUnexpectedExternalError(err);
|
||||
error = err;
|
||||
result = null;
|
||||
}
|
||||
|
||||
|
@ -56,11 +58,15 @@ export async function getDocumentSemanticTokens(model: ITextModel, lastProvider:
|
|||
result = null;
|
||||
}
|
||||
|
||||
return new DocumentSemanticTokensResult(provider, result);
|
||||
return new DocumentSemanticTokensResult(provider, result, error);
|
||||
}));
|
||||
|
||||
// Try to return the first result with actual tokens
|
||||
// Try to return the first result with actual tokens or
|
||||
// the first result which threw an error (!!)
|
||||
for (const result of results) {
|
||||
if (result.error) {
|
||||
throw result.error;
|
||||
}
|
||||
if (result.tokens) {
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -474,6 +474,11 @@ function resolveRenderLineInput(input: RenderLineInput): ResolvedRenderLineInput
|
|||
}
|
||||
|
||||
let tokens = transformAndRemoveOverflowing(input.lineTokens, input.fauxIndentLength, len);
|
||||
if (input.renderControlCharacters && !input.isBasicASCII) {
|
||||
// Calling `extractControlCharacters` before adding (possibly empty) line parts
|
||||
// for inline decorations. `extractControlCharacters` removes empty line parts.
|
||||
tokens = extractControlCharacters(lineContent, tokens);
|
||||
}
|
||||
if (input.renderWhitespace === RenderWhitespace.All ||
|
||||
input.renderWhitespace === RenderWhitespace.Boundary ||
|
||||
(input.renderWhitespace === RenderWhitespace.Selection && !!input.selectionsOnLine) ||
|
||||
|
@ -500,9 +505,6 @@ function resolveRenderLineInput(input: RenderLineInput): ResolvedRenderLineInput
|
|||
// We can never split RTL text, as it ruins the rendering
|
||||
tokens = splitLargeTokens(lineContent, tokens, !input.isBasicASCII || input.fontLigatures);
|
||||
}
|
||||
if (input.renderControlCharacters && !input.isBasicASCII) {
|
||||
tokens = extractControlCharacters(lineContent, tokens);
|
||||
}
|
||||
|
||||
return new ResolvedRenderLineInput(
|
||||
input.useMonospaceOptimizations,
|
||||
|
|
|
@ -21,6 +21,7 @@ import { IRange } from 'vs/editor/common/core/range';
|
|||
import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { IModelContentChangedEvent } from 'vs/editor/common/model/textModelEvents';
|
||||
import { FoldingRangeKind, FoldingRangeProviderRegistry } from 'vs/editor/common/modes';
|
||||
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
|
||||
import { CollapseMemento, FoldingModel, getNextFoldLine, getParentFoldLine as getParentFoldLine, getPreviousFoldLine, setCollapseStateAtLevel, setCollapseStateForMatchingLines, setCollapseStateForRest, setCollapseStateForType, setCollapseStateLevelsDown, setCollapseStateLevelsUp, setCollapseStateUp, toggleCollapseState } from 'vs/editor/contrib/folding/foldingModel';
|
||||
|
@ -130,7 +131,7 @@ export class FoldingController extends Disposable implements IEditorContribution
|
|||
const options = this.editor.getOptions();
|
||||
this.foldingDecorationProvider.autoHideFoldingControls = options.get(EditorOption.showFoldingControls) === 'mouseover';
|
||||
this.foldingDecorationProvider.showFoldingHighlights = options.get(EditorOption.foldingHighlight);
|
||||
this.onModelContentChanged();
|
||||
this.triggerFoldingModelChanged();
|
||||
}
|
||||
if (e.hasChanged(EditorOption.foldingStrategy)) {
|
||||
this._useFoldingProviders = this.editor.getOptions().get(EditorOption.foldingStrategy) !== 'indentation';
|
||||
|
@ -225,7 +226,7 @@ export class FoldingController extends Disposable implements IEditorContribution
|
|||
this.localToDispose.add(this.cursorChangedScheduler);
|
||||
this.localToDispose.add(FoldingRangeProviderRegistry.onDidChange(() => this.onFoldingStrategyChanged()));
|
||||
this.localToDispose.add(this.editor.onDidChangeModelLanguageConfiguration(() => this.onFoldingStrategyChanged())); // covers model language changes as well
|
||||
this.localToDispose.add(this.editor.onDidChangeModelContent(() => this.onModelContentChanged()));
|
||||
this.localToDispose.add(this.editor.onDidChangeModelContent(e => this.onDidChangeModelContent(e)));
|
||||
this.localToDispose.add(this.editor.onDidChangeCursorPosition(() => this.onCursorPositionChanged()));
|
||||
this.localToDispose.add(this.editor.onMouseDown(e => this.onEditorMouseDown(e)));
|
||||
this.localToDispose.add(this.editor.onMouseUp(e => this.onEditorMouseUp(e)));
|
||||
|
@ -250,7 +251,7 @@ export class FoldingController extends Disposable implements IEditorContribution
|
|||
this.rangeProvider = null;
|
||||
}
|
||||
});
|
||||
this.onModelContentChanged();
|
||||
this.triggerFoldingModelChanged();
|
||||
}
|
||||
|
||||
private onFoldingStrategyChanged() {
|
||||
|
@ -258,7 +259,7 @@ export class FoldingController extends Disposable implements IEditorContribution
|
|||
this.rangeProvider.dispose();
|
||||
}
|
||||
this.rangeProvider = null;
|
||||
this.onModelContentChanged();
|
||||
this.triggerFoldingModelChanged();
|
||||
}
|
||||
|
||||
private getRangeProvider(editorModel: ITextModel): RangeProvider {
|
||||
|
@ -278,7 +279,7 @@ export class FoldingController extends Disposable implements IEditorContribution
|
|||
}, 30000);
|
||||
return rangeProvider; // keep memento in case there are still no foldingProviders on the next request.
|
||||
} else if (foldingProviders.length > 0) {
|
||||
this.rangeProvider = new SyntaxRangeProvider(editorModel, foldingProviders, () => this.onModelContentChanged());
|
||||
this.rangeProvider = new SyntaxRangeProvider(editorModel, foldingProviders, () => this.triggerFoldingModelChanged());
|
||||
}
|
||||
}
|
||||
this.foldingStateMemento = null;
|
||||
|
@ -289,7 +290,12 @@ export class FoldingController extends Disposable implements IEditorContribution
|
|||
return this.foldingModelPromise;
|
||||
}
|
||||
|
||||
private onModelContentChanged() {
|
||||
private onDidChangeModelContent(e: IModelContentChangedEvent) {
|
||||
this.hiddenRangeModel?.notifyChangeModelContent(e);
|
||||
this.triggerFoldingModelChanged();
|
||||
}
|
||||
|
||||
private triggerFoldingModelChanged() {
|
||||
if (this.updateScheduler) {
|
||||
if (this.foldingRegionPromise) {
|
||||
this.foldingRegionPromise.cancel();
|
||||
|
|
|
@ -4,17 +4,22 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { findFirstInSorted } from 'vs/base/common/arrays';
|
||||
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IRange, Range } from 'vs/editor/common/core/range';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { IModelContentChangedEvent } from 'vs/editor/common/model/textModelEvents';
|
||||
import { countEOL } from 'vs/editor/common/model/tokensStore';
|
||||
import { CollapseMemento, FoldingModel } from 'vs/editor/contrib/folding/foldingModel';
|
||||
|
||||
export class HiddenRangeModel {
|
||||
|
||||
private readonly _foldingModel: FoldingModel;
|
||||
private _hiddenRanges: IRange[];
|
||||
private _foldingModelListener: IDisposable | null;
|
||||
private readonly _updateEventEmitter = new Emitter<IRange[]>();
|
||||
private _hasLineChanges: boolean = false;
|
||||
|
||||
public get onDidChange(): Event<IRange[]> { return this._updateEventEmitter.event; }
|
||||
public get hiddenRanges() { return this._hiddenRanges; }
|
||||
|
@ -28,6 +33,14 @@ export class HiddenRangeModel {
|
|||
}
|
||||
}
|
||||
|
||||
public notifyChangeModelContent(e: IModelContentChangedEvent) {
|
||||
if (this._hiddenRanges.length && !this._hasLineChanges) {
|
||||
this._hasLineChanges = e.changes.some(change => {
|
||||
return change.range.endLineNumber !== change.range.startLineNumber || countEOL(change.text)[0] !== 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private updateHiddenRanges(): void {
|
||||
let updateHiddenAreas = false;
|
||||
let newHiddenAreas: IRange[] = [];
|
||||
|
@ -61,7 +74,7 @@ export class HiddenRangeModel {
|
|||
lastCollapsedStart = startLineNumber;
|
||||
lastCollapsedEnd = endLineNumber;
|
||||
}
|
||||
if (updateHiddenAreas || k < this._hiddenRanges.length) {
|
||||
if (this._hasLineChanges || updateHiddenAreas || k < this._hiddenRanges.length) {
|
||||
this.applyHiddenRanges(newHiddenAreas);
|
||||
}
|
||||
}
|
||||
|
@ -90,6 +103,7 @@ export class HiddenRangeModel {
|
|||
|
||||
private applyHiddenRanges(newHiddenAreas: IRange[]) {
|
||||
this._hiddenRanges = newHiddenAreas;
|
||||
this._hasLineChanges = false;
|
||||
this._updateEventEmitter.fire(newHiddenAreas);
|
||||
}
|
||||
|
||||
|
|
|
@ -105,9 +105,17 @@ class ModesContentComputer implements IHoverComputer<IHoverPart[]> {
|
|||
|
||||
const startColumn = (d.range.startLineNumber === lineNumber) ? d.range.startColumn : 1;
|
||||
const endColumn = (d.range.endLineNumber === lineNumber) ? d.range.endColumn : maxColumn;
|
||||
if (startColumn > anchor.range.startColumn || anchor.range.endColumn > endColumn) {
|
||||
return false;
|
||||
if (d.options.showIfCollapsed) {
|
||||
// Relax check around `showIfCollapsed` decorations to also include +/- 1 character
|
||||
if (startColumn > anchor.range.startColumn + 1 || anchor.range.endColumn - 1 > endColumn) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (startColumn > anchor.range.startColumn || anchor.range.endColumn > endColumn) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
|
44
src/vs/editor/test/common/services/getSemanticTokens.test.ts
Normal file
44
src/vs/editor/test/common/services/getSemanticTokens.test.ts
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { canceled } from 'vs/base/common/errors';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { DocumentSemanticTokensProvider, DocumentSemanticTokensProviderRegistry, ProviderResult, SemanticTokens, SemanticTokensEdits, SemanticTokensLegend } from 'vs/editor/common/modes';
|
||||
import { getDocumentSemanticTokens } from 'vs/editor/common/services/getSemanticTokens';
|
||||
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
|
||||
|
||||
suite('getSemanticTokens', () => {
|
||||
|
||||
test('issue #136540: semantic highlighting flickers', async () => {
|
||||
const disposables = new DisposableStore();
|
||||
|
||||
const provider = new class implements DocumentSemanticTokensProvider {
|
||||
getLegend(): SemanticTokensLegend {
|
||||
return { tokenTypes: ['test'], tokenModifiers: [] };
|
||||
}
|
||||
provideDocumentSemanticTokens(model: ITextModel, lastResultId: string | null, token: CancellationToken): ProviderResult<SemanticTokens | SemanticTokensEdits> {
|
||||
throw canceled();
|
||||
}
|
||||
releaseDocumentSemanticTokens(resultId: string | undefined): void {
|
||||
}
|
||||
};
|
||||
|
||||
disposables.add(DocumentSemanticTokensProviderRegistry.register('testLang', provider));
|
||||
|
||||
const textModel = disposables.add(createTextModel('example', undefined, 'testLang'));
|
||||
|
||||
await getDocumentSemanticTokens(textModel, null, null, CancellationToken.None).then((res) => {
|
||||
assert.fail();
|
||||
}, (err) => {
|
||||
assert.ok(!!err);
|
||||
});
|
||||
|
||||
disposables.dispose();
|
||||
});
|
||||
|
||||
});
|
|
@ -1722,6 +1722,46 @@ suite('viewLineRenderer.renderLine 2', () => {
|
|||
assert.deepStrictEqual(actual.html, expected);
|
||||
});
|
||||
|
||||
test('issue #136622: Inline decorations are not rendering on non-ASCII lines when renderControlCharacters is on', () => {
|
||||
|
||||
let actual = renderViewLine(new RenderLineInput(
|
||||
true,
|
||||
true,
|
||||
'some text £',
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
0,
|
||||
createViewLineTokens([createPart(11, 3)]),
|
||||
[
|
||||
new LineDecoration(5, 5, 'inlineDec1', InlineDecorationType.After),
|
||||
new LineDecoration(6, 6, 'inlineDec2', InlineDecorationType.Before),
|
||||
],
|
||||
4,
|
||||
0,
|
||||
10,
|
||||
10,
|
||||
10,
|
||||
10000,
|
||||
'none',
|
||||
true,
|
||||
false,
|
||||
null
|
||||
));
|
||||
|
||||
let expected = [
|
||||
'<span>',
|
||||
'<span class="mtk3">some</span>',
|
||||
'<span class="mtk3 inlineDec1"></span>',
|
||||
'<span class="mtk3">\u00a0</span>',
|
||||
'<span class="mtk3 inlineDec2"></span>',
|
||||
'<span class="mtk3">text\u00a0£</span>',
|
||||
'</span>'
|
||||
].join('');
|
||||
|
||||
assert.deepStrictEqual(actual.html, expected);
|
||||
});
|
||||
|
||||
test('issue #22832: Consider fullwidth characters when rendering tabs', () => {
|
||||
|
||||
let actual = renderViewLine(new RenderLineInput(
|
||||
|
|
|
@ -101,6 +101,7 @@ export class MenuId {
|
|||
static readonly ExplorerContext = new MenuId('ExplorerContext');
|
||||
static readonly ExtensionContext = new MenuId('ExtensionContext');
|
||||
static readonly GlobalActivity = new MenuId('GlobalActivity');
|
||||
static readonly LayoutControlMenu = new MenuId('LayoutControlMenu');
|
||||
static readonly MenubarMainMenu = new MenuId('MenubarMainMenu');
|
||||
static readonly MenubarAppearanceMenu = new MenuId('MenubarAppearanceMenu');
|
||||
static readonly MenubarDebugMenu = new MenuId('MenubarDebugMenu');
|
||||
|
|
|
@ -9,40 +9,6 @@ import { language, locale } from 'vs/base/common/platform';
|
|||
import { IElement, ILocaleInfo, ILocalizedStrings, IWindowDriver } from 'vs/platform/driver/common/driver';
|
||||
import localizedStrings from 'vs/platform/localizations/common/localizedStrings';
|
||||
|
||||
function serializeElement(element: Element, recursive: boolean): IElement {
|
||||
const attributes = Object.create(null);
|
||||
|
||||
for (let j = 0; j < element.attributes.length; j++) {
|
||||
const attr = element.attributes.item(j);
|
||||
if (attr) {
|
||||
attributes[attr.name] = attr.value;
|
||||
}
|
||||
}
|
||||
|
||||
const children: IElement[] = [];
|
||||
|
||||
if (recursive) {
|
||||
for (let i = 0; i < element.children.length; i++) {
|
||||
const child = element.children.item(i);
|
||||
if (child) {
|
||||
children.push(serializeElement(child, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const { left, top } = getTopLeftOffset(element as HTMLElement);
|
||||
|
||||
return {
|
||||
tagName: element.tagName,
|
||||
className: element.className,
|
||||
textContent: element.textContent || '',
|
||||
attributes,
|
||||
children,
|
||||
left,
|
||||
top
|
||||
};
|
||||
}
|
||||
|
||||
export abstract class BaseWindowDriver implements IWindowDriver {
|
||||
|
||||
abstract click(selector: string, xoffset?: number, yoffset?: number): Promise<void>;
|
||||
|
@ -94,12 +60,46 @@ export abstract class BaseWindowDriver implements IWindowDriver {
|
|||
|
||||
for (let i = 0; i < query.length; i++) {
|
||||
const element = query.item(i);
|
||||
result.push(serializeElement(element, recursive));
|
||||
result.push(this.serializeElement(element, recursive));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private serializeElement(element: Element, recursive: boolean): IElement {
|
||||
const attributes = Object.create(null);
|
||||
|
||||
for (let j = 0; j < element.attributes.length; j++) {
|
||||
const attr = element.attributes.item(j);
|
||||
if (attr) {
|
||||
attributes[attr.name] = attr.value;
|
||||
}
|
||||
}
|
||||
|
||||
const children: IElement[] = [];
|
||||
|
||||
if (recursive) {
|
||||
for (let i = 0; i < element.children.length; i++) {
|
||||
const child = element.children.item(i);
|
||||
if (child) {
|
||||
children.push(this.serializeElement(child, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const { left, top } = getTopLeftOffset(element as HTMLElement);
|
||||
|
||||
return {
|
||||
tagName: element.tagName,
|
||||
className: element.className,
|
||||
textContent: element.textContent || '',
|
||||
attributes,
|
||||
children,
|
||||
left,
|
||||
top
|
||||
};
|
||||
}
|
||||
|
||||
async getElementXY(selector: string, xoffset?: number, yoffset?: number): Promise<{ x: number; y: number; }> {
|
||||
const offset = typeof xoffset === 'number' && typeof yoffset === 'number' ? { x: xoffset, y: yoffset } : undefined;
|
||||
return this._getElementXY(selector, offset);
|
||||
|
|
|
@ -84,42 +84,13 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
|
|||
}
|
||||
|
||||
async installFromGallery(extension: IGalleryExtension, options: InstallOptions = {}): Promise<ILocalExtension> {
|
||||
if (!this.galleryService.isEnabled()) {
|
||||
throw new ExtensionManagementError(nls.localize('MarketPlaceDisabled', "Marketplace is not enabled"), ExtensionManagementErrorCode.Internal);
|
||||
}
|
||||
|
||||
if (!await this.canInstall(extension)) {
|
||||
const targetPlatform = await this.getTargetPlatform();
|
||||
const error = new ExtensionManagementError(nls.localize('incompatible platform', "The '{0}' extension is not available in {1} for {2}.", extension.identifier.id, this.productService.nameLong, TargetPlatformToString(targetPlatform)), ExtensionManagementErrorCode.Incompatible);
|
||||
this.logService.error(`Cannot install extension.`, extension.identifier.id, error.message);
|
||||
reportTelemetry(this.telemetryService, 'extensionGallery:install', getGalleryExtensionTelemetryData(extension), undefined, error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
try {
|
||||
extension = await this.checkAndGetCompatibleVersion(extension, !options.installGivenVersion);
|
||||
return await this.doInstallFromGallery(extension, options);
|
||||
} catch (error) {
|
||||
this.logService.error(getErrorMessage(error));
|
||||
reportTelemetry(this.telemetryService, 'extensionGallery:install', getGalleryExtensionTelemetryData(extension), undefined, error);
|
||||
throw error;
|
||||
this.logService.error(`Failed to install extension.`, extension.identifier.id);
|
||||
this.logService.error(error);
|
||||
throw toExtensionManagementError(error);
|
||||
}
|
||||
|
||||
const manifest = await this.galleryService.getManifest(extension, CancellationToken.None);
|
||||
if (manifest === null) {
|
||||
const error = new ExtensionManagementError(`Missing manifest for extension ${extension.identifier.id}`, ExtensionManagementErrorCode.Invalid);
|
||||
this.logService.error(`Failed to install extension:`, extension.identifier.id, error.message);
|
||||
reportTelemetry(this.telemetryService, 'extensionGallery:install', getGalleryExtensionTelemetryData(extension), undefined, error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (manifest.version !== extension.version) {
|
||||
const error = new ExtensionManagementError(`Cannot install '${extension.identifier.id}' extension because of version mismatch in Marketplace`, ExtensionManagementErrorCode.Invalid);
|
||||
this.logService.error(error.message);
|
||||
reportTelemetry(this.telemetryService, 'extensionGallery:install', getGalleryExtensionTelemetryData(extension), undefined, error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
return this.installExtension(manifest, extension, options);
|
||||
}
|
||||
|
||||
async uninstall(extension: ILocalExtension, options: UninstallOptions = {}): Promise<void> {
|
||||
|
@ -157,6 +128,41 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
|
|||
this.participants.push(participant);
|
||||
}
|
||||
|
||||
private async doInstallFromGallery(extension: IGalleryExtension, options: InstallOptions = {}): Promise<ILocalExtension> {
|
||||
if (!this.galleryService.isEnabled()) {
|
||||
throw new ExtensionManagementError(nls.localize('MarketPlaceDisabled', "Marketplace is not enabled"), ExtensionManagementErrorCode.Internal);
|
||||
}
|
||||
|
||||
if (!await this.canInstall(extension)) {
|
||||
const targetPlatform = await this.getTargetPlatform();
|
||||
const error = new ExtensionManagementError(nls.localize('incompatible platform', "The '{0}' extension is not available in {1} for {2}.", extension.identifier.id, this.productService.nameLong, TargetPlatformToString(targetPlatform)), ExtensionManagementErrorCode.Incompatible);
|
||||
reportTelemetry(this.telemetryService, 'extensionGallery:install', getGalleryExtensionTelemetryData(extension), undefined, error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
try {
|
||||
extension = await this.checkAndGetCompatibleVersion(extension, !options.installGivenVersion);
|
||||
} catch (error) {
|
||||
reportTelemetry(this.telemetryService, 'extensionGallery:install', getGalleryExtensionTelemetryData(extension), undefined, error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
const manifest = await this.galleryService.getManifest(extension, CancellationToken.None);
|
||||
if (manifest === null) {
|
||||
const error = new ExtensionManagementError(`Missing manifest for extension ${extension.identifier.id}`, ExtensionManagementErrorCode.Invalid);
|
||||
reportTelemetry(this.telemetryService, 'extensionGallery:install', getGalleryExtensionTelemetryData(extension), undefined, error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (manifest.version !== extension.version) {
|
||||
const error = new ExtensionManagementError(`Cannot install '${extension.identifier.id}' extension because of version mismatch in Marketplace`, ExtensionManagementErrorCode.Invalid);
|
||||
reportTelemetry(this.telemetryService, 'extensionGallery:install', getGalleryExtensionTelemetryData(extension), undefined, error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
return this.installExtension(manifest, extension, options);
|
||||
}
|
||||
|
||||
protected async installExtension(manifest: IExtensionManifest, extension: URI | IGalleryExtension, options: InstallOptions & InstallVSIXOptions): Promise<ILocalExtension> {
|
||||
// only cache gallery extensions tasks
|
||||
if (!URI.isUri(extension)) {
|
||||
|
@ -209,7 +215,6 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
|
|||
}
|
||||
} else {
|
||||
this.logService.error('Error while preparing to install dependencies and extension packs of the extension:', installExtensionTask.identifier.id);
|
||||
this.logService.error(error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
@ -253,7 +258,6 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
|
|||
reportTelemetry(this.telemetryService, task.operation === InstallOperation.Update ? 'extensionGallery:update' : 'extensionGallery:install', getGalleryExtensionTelemetryData(task.source), new Date().getTime() - startTime, error);
|
||||
}
|
||||
this.logService.error('Error while installing the extension:', task.identifier.id);
|
||||
this.logService.error(error);
|
||||
throw error;
|
||||
} finally { extensionsToInstallMap.delete(task.identifier.id.toLowerCase()); }
|
||||
}));
|
||||
|
@ -287,12 +291,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
|
|||
}
|
||||
}
|
||||
|
||||
this.logService.error(`Failed to install extension:`, installExtensionTask.identifier.id, getErrorMessage(error));
|
||||
this._onDidInstallExtensions.fire(allInstallExtensionTasks.map(({ task }) => ({ identifier: task.identifier, operation: InstallOperation.Install, source: task.source })));
|
||||
|
||||
if (error instanceof Error) {
|
||||
error.name = error && (<ExtensionManagementError>error).code ? (<ExtensionManagementError>error).code : ExtensionManagementErrorCode.Internal;
|
||||
}
|
||||
throw error;
|
||||
} finally {
|
||||
/* Remove the gallery tasks from the cache */
|
||||
|
@ -619,6 +618,15 @@ export function joinErrors(errorOrErrors: (Error | string) | (Array<Error | stri
|
|||
}, new Error(''));
|
||||
}
|
||||
|
||||
function toExtensionManagementError(error: Error): ExtensionManagementError {
|
||||
if (error instanceof ExtensionManagementError) {
|
||||
return error;
|
||||
}
|
||||
const e = new ExtensionManagementError(error.message, ExtensionManagementErrorCode.Internal);
|
||||
e.stack = error.stack;
|
||||
return e;
|
||||
}
|
||||
|
||||
export function reportTelemetry(telemetryService: ITelemetryService, eventName: string, extensionData: any, duration?: number, error?: Error): void {
|
||||
const errorcode = error ? error instanceof ExtensionManagementError ? error.code : ExtensionManagementErrorCode.Internal : undefined;
|
||||
/* __GDPR__
|
||||
|
|
|
@ -10,7 +10,7 @@ export interface IKeyboardMapper {
|
|||
dumpDebugInfo(): string;
|
||||
resolveKeybinding(keybinding: Keybinding): ResolvedKeybinding[];
|
||||
resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): ResolvedKeybinding;
|
||||
resolveUserBinding(firstPart: (SimpleKeybinding | ScanCodeBinding)[]): ResolvedKeybinding[];
|
||||
resolveUserBinding(parts: (SimpleKeybinding | ScanCodeBinding)[]): ResolvedKeybinding[];
|
||||
}
|
||||
|
||||
export class CachedKeyboardMapper implements IKeyboardMapper {
|
||||
|
|
|
@ -154,7 +154,7 @@ export class FileLoggerService extends AbstractLoggerService implements ILoggerS
|
|||
|
||||
protected doCreateLogger(resource: URI, logLevel: LogLevel, options?: ILoggerOptions): ILogger {
|
||||
const logger = new BufferLogService(logLevel);
|
||||
whenProviderRegistered(resource, this.fileService).then(() => (<BufferLogService>logger).logger = this.instantiationService.createInstance(FileLogger, basename(resource), resource, logger.getLevel(), !!options?.donotUseFormatters));
|
||||
whenProviderRegistered(resource, this.fileService).then(() => (<BufferLogService>logger).logger = this.instantiationService.createInstance(FileLogger, options?.name || basename(resource), resource, logger.getLevel(), !!options?.donotUseFormatters));
|
||||
return logger;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,11 +23,7 @@ export class LoggerService extends AbstractLoggerService implements ILoggerServi
|
|||
|
||||
protected doCreateLogger(resource: URI, logLevel: LogLevel, options?: ILoggerOptions): ILogger {
|
||||
if (resource.scheme === Schemas.file) {
|
||||
const logger = new SpdLogLogger(options?.name || generateUuid(), resource.fsPath, !options?.donotRotate, logLevel);
|
||||
if (options?.donotUseFormatters) {
|
||||
(<SpdLogLogger>logger).clearFormatters();
|
||||
}
|
||||
return logger;
|
||||
return new SpdLogLogger(options?.name || generateUuid(), resource.fsPath, !options?.donotRotate, !!options?.donotUseFormatters, logLevel);
|
||||
} else {
|
||||
return new FileLogger(options?.name ?? basename(resource), resource, logLevel, !!options?.donotUseFormatters, this.fileService);
|
||||
}
|
||||
|
|
|
@ -7,12 +7,16 @@ import * as spdlog from 'spdlog';
|
|||
import { ByteSize } from 'vs/platform/files/common/files';
|
||||
import { AbstractMessageLogger, ILogger, LogLevel } from 'vs/platform/log/common/log';
|
||||
|
||||
async function createSpdLogLogger(name: string, logfilePath: string, filesize: number, filecount: number): Promise<spdlog.Logger | null> {
|
||||
async function createSpdLogLogger(name: string, logfilePath: string, filesize: number, filecount: number, donotUseFormatters: boolean): Promise<spdlog.Logger | null> {
|
||||
// Do not crash if spdlog cannot be loaded
|
||||
try {
|
||||
const _spdlog = await import('spdlog');
|
||||
_spdlog.setFlushOn(LogLevel.Trace);
|
||||
return _spdlog.createAsyncRotatingLogger(name, logfilePath, filesize, filecount);
|
||||
const logger = await _spdlog.createAsyncRotatingLogger(name, logfilePath, filesize, filecount);
|
||||
if (donotUseFormatters) {
|
||||
logger.clearFormatters();
|
||||
}
|
||||
return logger;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
@ -49,14 +53,15 @@ export class SpdLogLogger extends AbstractMessageLogger implements ILogger {
|
|||
private _logger: spdlog.Logger | undefined;
|
||||
|
||||
constructor(
|
||||
private readonly name: string,
|
||||
private readonly filepath: string,
|
||||
private readonly rotating: boolean,
|
||||
level: LogLevel
|
||||
name: string,
|
||||
filepath: string,
|
||||
rotating: boolean,
|
||||
donotUseFormatters: boolean,
|
||||
level: LogLevel,
|
||||
) {
|
||||
super();
|
||||
this.setLevel(level);
|
||||
this._loggerCreationPromise = this._createSpdLogLogger();
|
||||
this._loggerCreationPromise = this._createSpdLogLogger(name, filepath, rotating, donotUseFormatters);
|
||||
this._register(this.onDidChangeLogLevel(level => {
|
||||
if (this._logger) {
|
||||
this._logger.setLevel(level);
|
||||
|
@ -64,20 +69,18 @@ export class SpdLogLogger extends AbstractMessageLogger implements ILogger {
|
|||
}));
|
||||
}
|
||||
|
||||
private _createSpdLogLogger(): Promise<void> {
|
||||
const filecount = this.rotating ? 6 : 1;
|
||||
private async _createSpdLogLogger(name: string, filepath: string, rotating: boolean, donotUseFormatters: boolean): Promise<void> {
|
||||
const filecount = rotating ? 6 : 1;
|
||||
const filesize = (30 / filecount) * ByteSize.MB;
|
||||
return createSpdLogLogger(this.name, this.filepath, filesize, filecount)
|
||||
.then(logger => {
|
||||
if (logger) {
|
||||
this._logger = logger;
|
||||
this._logger.setLevel(this.getLevel());
|
||||
for (const { level, message } of this.buffer) {
|
||||
log(this._logger, level, message);
|
||||
}
|
||||
this.buffer = [];
|
||||
}
|
||||
});
|
||||
const logger = await createSpdLogLogger(name, filepath, filesize, filecount, donotUseFormatters);
|
||||
if (logger) {
|
||||
this._logger = logger;
|
||||
this._logger.setLevel(this.getLevel());
|
||||
for (const { level, message } of this.buffer) {
|
||||
log(this._logger, level, message);
|
||||
}
|
||||
this.buffer = [];
|
||||
}
|
||||
}
|
||||
|
||||
protected log(level: LogLevel, message: string): void {
|
||||
|
@ -88,14 +91,6 @@ export class SpdLogLogger extends AbstractMessageLogger implements ILogger {
|
|||
}
|
||||
}
|
||||
|
||||
clearFormatters(): void {
|
||||
if (this._logger) {
|
||||
this._logger.clearFormatters();
|
||||
} else {
|
||||
this._loggerCreationPromise.then(() => this.clearFormatters());
|
||||
}
|
||||
}
|
||||
|
||||
override flush(): void {
|
||||
if (this._logger) {
|
||||
this._logger.flush();
|
||||
|
|
|
@ -57,7 +57,7 @@ else {
|
|||
// Running out of sources
|
||||
if (Object.keys(product).length === 0) {
|
||||
Object.assign(product, {
|
||||
version: '1.62.0-dev',
|
||||
version: '1.63.0-dev',
|
||||
nameShort: 'Code - OSS Dev',
|
||||
nameLong: 'Code - OSS Dev',
|
||||
applicationName: 'code-oss',
|
||||
|
|
|
@ -24,6 +24,8 @@ export interface ResolvedOptions {
|
|||
export interface TunnelDescription {
|
||||
remoteAddress: { port: number, host: string };
|
||||
localAddress: { port: number, host: string } | string;
|
||||
privacy?: string;
|
||||
protocol?: string;
|
||||
}
|
||||
export interface TunnelInformation {
|
||||
environmentTunnels?: TunnelDescription[];
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as net from 'net';
|
||||
import { findFreePortFaster } from 'vs/base/node/ports';
|
||||
import { BROWSER_RESTRICTED_PORTS, findFreePortFaster } from 'vs/base/node/ports';
|
||||
import { NodeSocket } from 'vs/base/parts/ipc/node/ipc.net';
|
||||
import { nodeSocketFactory } from 'vs/platform/remote/node/nodeSocketFactory';
|
||||
|
||||
|
@ -18,8 +18,18 @@ import { AbstractTunnelService, isAllInterfaces, ISharedTunnelsService as IShare
|
|||
import { ISignService } from 'vs/platform/sign/common/sign';
|
||||
|
||||
async function createRemoteTunnel(options: IConnectionOptions, defaultTunnelHost: string, tunnelRemoteHost: string, tunnelRemotePort: number, tunnelLocalPort?: number): Promise<RemoteTunnel> {
|
||||
const tunnel = new NodeRemoteTunnel(options, defaultTunnelHost, tunnelRemoteHost, tunnelRemotePort, tunnelLocalPort);
|
||||
return tunnel.waitForReady();
|
||||
let readyTunnel: NodeRemoteTunnel | undefined;
|
||||
for (let attempts = 3; attempts; attempts--) {
|
||||
if (readyTunnel) {
|
||||
readyTunnel.dispose();
|
||||
}
|
||||
const tunnel = new NodeRemoteTunnel(options, defaultTunnelHost, tunnelRemoteHost, tunnelRemotePort, tunnelLocalPort);
|
||||
readyTunnel = await tunnel.waitForReady();
|
||||
if ((tunnelLocalPort && BROWSER_RESTRICTED_PORTS[tunnelLocalPort]) || !BROWSER_RESTRICTED_PORTS[readyTunnel.tunnelLocalPort]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return readyTunnel!;
|
||||
}
|
||||
|
||||
class NodeRemoteTunnel extends Disposable implements RemoteTunnel {
|
||||
|
|
|
@ -75,7 +75,7 @@ class CliMain extends Disposable {
|
|||
|
||||
const environmentService = new ServerEnvironmentService(this.args, productService);
|
||||
services.set(IServerEnvironmentService, environmentService);
|
||||
const logService: ILogService = new LogService(new SpdLogLogger(RemoteExtensionLogFileName, join(environmentService.logsPath, `${RemoteExtensionLogFileName}.log`), true, getLogLevel(environmentService)));
|
||||
const logService: ILogService = new LogService(new SpdLogLogger(RemoteExtensionLogFileName, join(environmentService.logsPath, `${RemoteExtensionLogFileName}.log`), true, false, getLogLevel(environmentService)));
|
||||
services.set(ILogService, logService);
|
||||
logService.trace(`Remote configuration data at ${this.remoteDataFolder}`);
|
||||
logService.trace('process arguments:', this.args);
|
||||
|
|
|
@ -1043,7 +1043,7 @@ const getOrCreateSpdLogService: (environmentService: IServerEnvironmentService)
|
|||
let _logService: ILogService | null;
|
||||
return function getLogService(environmentService: IServerEnvironmentService): ILogService {
|
||||
if (!_logService) {
|
||||
_logService = new LogService(new SpdLogLogger(RemoteExtensionLogFileName, join(environmentService.logsPath, `${RemoteExtensionLogFileName}.log`), true, getLogLevel(environmentService)));
|
||||
_logService = new LogService(new SpdLogLogger(RemoteExtensionLogFileName, join(environmentService.logsPath, `${RemoteExtensionLogFileName}.log`), true, false, getLogLevel(environmentService)));
|
||||
}
|
||||
return _logService;
|
||||
};
|
||||
|
|
40
src/vs/vscode.d.ts
vendored
40
src/vs/vscode.d.ts
vendored
|
@ -818,7 +818,7 @@ declare module 'vscode' {
|
|||
/**
|
||||
* The optional ThemeColor of the icon. The color is currently only used in {@link TreeItem}.
|
||||
*/
|
||||
readonly color?: ThemeColor;
|
||||
readonly color?: ThemeColor | undefined;
|
||||
|
||||
/**
|
||||
* Creates a reference to a theme icon.
|
||||
|
@ -1107,13 +1107,13 @@ declare module 'vscode' {
|
|||
/**
|
||||
* The selections in this text editor. The primary selection is always at index 0.
|
||||
*/
|
||||
selections: Selection[];
|
||||
selections: readonly Selection[];
|
||||
|
||||
/**
|
||||
* The current visible ranges in the editor (vertically).
|
||||
* This accounts only for vertical scrolling, and not for horizontal scrolling.
|
||||
*/
|
||||
readonly visibleRanges: Range[];
|
||||
readonly visibleRanges: readonly Range[];
|
||||
|
||||
/**
|
||||
* Text editor options.
|
||||
|
@ -2711,7 +2711,7 @@ declare module 'vscode' {
|
|||
/*
|
||||
* If specified the expression overrides the extracted expression.
|
||||
*/
|
||||
readonly expression?: string;
|
||||
readonly expression?: string | undefined;
|
||||
|
||||
/**
|
||||
* Creates a new evaluatable expression object.
|
||||
|
@ -2778,7 +2778,7 @@ declare module 'vscode' {
|
|||
/**
|
||||
* If specified the name of the variable to look up.
|
||||
*/
|
||||
readonly variableName?: string;
|
||||
readonly variableName?: string | undefined;
|
||||
/**
|
||||
* How to perform the lookup.
|
||||
*/
|
||||
|
@ -2807,7 +2807,7 @@ declare module 'vscode' {
|
|||
/**
|
||||
* If specified the expression overrides the extracted expression.
|
||||
*/
|
||||
readonly expression?: string;
|
||||
readonly expression?: string | undefined;
|
||||
/**
|
||||
* Creates a new InlineValueEvaluatableExpression object.
|
||||
*
|
||||
|
@ -4907,7 +4907,7 @@ declare module 'vscode' {
|
|||
* An optional word pattern that describes valid contents for the given ranges.
|
||||
* If no pattern is provided, the language configuration's word pattern will be used.
|
||||
*/
|
||||
readonly wordPattern?: RegExp;
|
||||
readonly wordPattern: RegExp | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -6577,7 +6577,7 @@ declare module 'vscode' {
|
|||
* Whether the task that is part of this group is the default for the group.
|
||||
* This property cannot be set through API, and is controlled by a user's task configurations.
|
||||
*/
|
||||
readonly isDefault?: boolean;
|
||||
readonly isDefault: boolean | undefined;
|
||||
|
||||
/**
|
||||
* The ID of the task group. Is one of TaskGroup.Clean.id, TaskGroup.Build.id, TaskGroup.Rebuild.id, or TaskGroup.Test.id.
|
||||
|
@ -6913,7 +6913,7 @@ declare module 'vscode' {
|
|||
/**
|
||||
* The task's scope.
|
||||
*/
|
||||
readonly scope?: TaskScope.Global | TaskScope.Workspace | WorkspaceFolder;
|
||||
readonly scope: TaskScope.Global | TaskScope.Workspace | WorkspaceFolder | undefined;
|
||||
|
||||
/**
|
||||
* The task's name
|
||||
|
@ -8656,7 +8656,7 @@ declare module 'vscode' {
|
|||
/**
|
||||
* The currently visible editors or an empty array.
|
||||
*/
|
||||
export let visibleTextEditors: TextEditor[];
|
||||
export let visibleTextEditors: readonly TextEditor[];
|
||||
|
||||
/**
|
||||
* An {@link Event} which fires when the {@link window.activeTextEditor active editor}
|
||||
|
@ -8669,7 +8669,7 @@ declare module 'vscode' {
|
|||
* An {@link Event} which fires when the array of {@link window.visibleTextEditors visible editors}
|
||||
* has changed.
|
||||
*/
|
||||
export const onDidChangeVisibleTextEditors: Event<TextEditor[]>;
|
||||
export const onDidChangeVisibleTextEditors: Event<readonly TextEditor[]>;
|
||||
|
||||
/**
|
||||
* An {@link Event} which fires when the selection in an editor has changed.
|
||||
|
@ -9354,7 +9354,7 @@ declare module 'vscode' {
|
|||
/**
|
||||
* Selected elements.
|
||||
*/
|
||||
readonly selection: T[];
|
||||
readonly selection: readonly T[];
|
||||
|
||||
}
|
||||
|
||||
|
@ -9388,7 +9388,7 @@ declare module 'vscode' {
|
|||
/**
|
||||
* Currently selected elements.
|
||||
*/
|
||||
readonly selection: T[];
|
||||
readonly selection: readonly T[];
|
||||
|
||||
/**
|
||||
* Event that is fired when the {@link TreeView.selection selection} has changed
|
||||
|
@ -13078,7 +13078,7 @@ declare module 'vscode' {
|
|||
/**
|
||||
* The host.
|
||||
*/
|
||||
readonly host?: string;
|
||||
readonly host?: string | undefined;
|
||||
|
||||
/**
|
||||
* Create a description for a debug adapter running as a socket based server.
|
||||
|
@ -13251,15 +13251,15 @@ declare module 'vscode' {
|
|||
/**
|
||||
* An optional expression for conditional breakpoints.
|
||||
*/
|
||||
readonly condition?: string;
|
||||
readonly condition?: string | undefined;
|
||||
/**
|
||||
* An optional expression that controls how many hits of the breakpoint are ignored.
|
||||
*/
|
||||
readonly hitCondition?: string;
|
||||
readonly hitCondition?: string | undefined;
|
||||
/**
|
||||
* An optional message that gets logged when this breakpoint is hit. Embedded expressions within {} are interpolated by the debug adapter.
|
||||
*/
|
||||
readonly logMessage?: string;
|
||||
readonly logMessage?: string | undefined;
|
||||
|
||||
protected constructor(enabled?: boolean, condition?: string, hitCondition?: string, logMessage?: string);
|
||||
}
|
||||
|
@ -13387,7 +13387,7 @@ declare module 'vscode' {
|
|||
/**
|
||||
* List of breakpoints.
|
||||
*/
|
||||
export let breakpoints: Breakpoint[];
|
||||
export let breakpoints: readonly Breakpoint[];
|
||||
|
||||
/**
|
||||
* An {@link Event} which fires when the {@link debug.activeDebugSession active debug session}
|
||||
|
@ -14322,7 +14322,7 @@ declare module 'vscode' {
|
|||
* The process of running tests should resolve the children of any test
|
||||
* items who have not yet been resolved.
|
||||
*/
|
||||
readonly include: TestItem[] | undefined;
|
||||
readonly include: readonly TestItem[] | undefined;
|
||||
|
||||
/**
|
||||
* An array of tests the user has marked as excluded from the test included
|
||||
|
@ -14331,7 +14331,7 @@ declare module 'vscode' {
|
|||
* May be omitted if no exclusions were requested. Test controllers should
|
||||
* not run excluded tests or any children of excluded tests.
|
||||
*/
|
||||
readonly exclude: TestItem[] | undefined;
|
||||
readonly exclude: readonly TestItem[] | undefined;
|
||||
|
||||
/**
|
||||
* The profile used for this request. This will always be defined
|
||||
|
|
124
src/vs/vscode.proposed.d.ts
vendored
124
src/vs/vscode.proposed.d.ts
vendored
|
@ -16,8 +16,7 @@
|
|||
|
||||
declare module 'vscode' {
|
||||
|
||||
// eslint-disable-next-line vscode-dts-region-comments
|
||||
//#region @alexdima - resolvers
|
||||
//#region resolvers: @alexdima
|
||||
|
||||
export interface MessageOptions {
|
||||
/**
|
||||
|
@ -268,7 +267,7 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region editor insets: https://github.com/microsoft/vscode/issues/85682
|
||||
//#region editorInsets: https://github.com/microsoft/vscode/issues/85682
|
||||
|
||||
export interface WebviewEditorInset {
|
||||
readonly editor: TextEditor;
|
||||
|
@ -285,7 +284,7 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region read/write in chunks: https://github.com/microsoft/vscode/issues/84515
|
||||
//#region fsChunks: https://github.com/microsoft/vscode/issues/84515
|
||||
|
||||
export interface FileSystemProvider {
|
||||
open?(resource: Uri, options: { create: boolean; }): number | Thenable<number>;
|
||||
|
@ -296,7 +295,7 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region TextSearchProvider: https://github.com/microsoft/vscode/issues/59921
|
||||
//#region textSearchProvider: https://github.com/microsoft/vscode/issues/59921
|
||||
|
||||
/**
|
||||
* The parameters of a query for text search.
|
||||
|
@ -553,7 +552,7 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region FileSearchProvider: https://github.com/microsoft/vscode/issues/73524
|
||||
//#region fileSearchProvider: https://github.com/microsoft/vscode/issues/73524
|
||||
|
||||
/**
|
||||
* The parameters of a query for file search.
|
||||
|
@ -719,7 +718,7 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region diff command: https://github.com/microsoft/vscode/issues/84899
|
||||
//#region diffCommand: https://github.com/microsoft/vscode/issues/84899
|
||||
|
||||
/**
|
||||
* The contiguous set of modified lines in a diff.
|
||||
|
@ -753,7 +752,7 @@ declare module 'vscode' {
|
|||
//#endregion
|
||||
|
||||
// eslint-disable-next-line vscode-dts-region-comments
|
||||
//#region @roblourens: new debug session option for simple UI 'managedByParent' (see https://github.com/microsoft/vscode/issues/128588)
|
||||
//#region notebookDebugOptions: @roblourens: new debug session option for simple UI 'managedByParent' (see https://github.com/microsoft/vscode/issues/128588)
|
||||
|
||||
/**
|
||||
* Options for {@link debug.startDebugging starting a debug session}.
|
||||
|
@ -775,29 +774,7 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
// eslint-disable-next-line vscode-dts-region-comments
|
||||
//#region @weinand: variables view action contributions
|
||||
|
||||
/**
|
||||
* A DebugProtocolVariableContainer is an opaque stand-in type for the intersection of the Scope and Variable types defined in the Debug Adapter Protocol.
|
||||
* See https://microsoft.github.io/debug-adapter-protocol/specification#Types_Scope and https://microsoft.github.io/debug-adapter-protocol/specification#Types_Variable.
|
||||
*/
|
||||
export interface DebugProtocolVariableContainer {
|
||||
// Properties: the intersection of DAP's Scope and Variable types.
|
||||
}
|
||||
|
||||
/**
|
||||
* A DebugProtocolVariable is an opaque stand-in type for the Variable type defined in the Debug Adapter Protocol.
|
||||
* See https://microsoft.github.io/debug-adapter-protocol/specification#Types_Variable.
|
||||
*/
|
||||
export interface DebugProtocolVariable {
|
||||
// Properties: see details [here](https://microsoft.github.io/debug-adapter-protocol/specification#Base_Protocol_Variable).
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
// eslint-disable-next-line vscode-dts-region-comments
|
||||
//#region @joaomoreno: SCM validation
|
||||
// #region scmValidation: @joaomoreno
|
||||
|
||||
/**
|
||||
* Represents the validation type of the Source Control input.
|
||||
|
@ -852,8 +829,7 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
// eslint-disable-next-line vscode-dts-region-comments
|
||||
//#region @joaomoreno: SCM selected provider
|
||||
//#region scmSelectedProvider: @joaomoreno
|
||||
|
||||
export interface SourceControl {
|
||||
|
||||
|
@ -870,7 +846,7 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region Terminal data write event https://github.com/microsoft/vscode/issues/78502
|
||||
//#region terminalDataWriteEvent: https://github.com/microsoft/vscode/issues/78502
|
||||
|
||||
export interface TerminalDataWriteEvent {
|
||||
/**
|
||||
|
@ -894,7 +870,7 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region Terminal dimensions property and change event https://github.com/microsoft/vscode/issues/55718
|
||||
//#region terminalDimensions: https://github.com/microsoft/vscode/issues/55718
|
||||
|
||||
/**
|
||||
* An {@link Event} which fires when a {@link Terminal}'s dimensions change.
|
||||
|
@ -928,7 +904,7 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region Terminal location https://github.com/microsoft/vscode/issues/45407
|
||||
//#region terminalLocation: https://github.com/microsoft/vscode/issues/45407
|
||||
|
||||
export interface TerminalOptions {
|
||||
location?: TerminalLocation | TerminalEditorLocationOptions | TerminalSplitLocationOptions;
|
||||
|
@ -968,7 +944,7 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region Terminal name change event https://github.com/microsoft/vscode/issues/114898
|
||||
//#region terminalNameChangeEvent: https://github.com/microsoft/vscode/issues/114898
|
||||
|
||||
export interface Pseudoterminal {
|
||||
/**
|
||||
|
@ -992,8 +968,7 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
// eslint-disable-next-line vscode-dts-region-comments
|
||||
//#region @jrieken -> exclusive document filters
|
||||
//#region exclusiveDocumentFilters: @jrieken
|
||||
|
||||
export interface DocumentFilter {
|
||||
readonly exclusive?: boolean;
|
||||
|
@ -1001,13 +976,13 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region Tree View: https://github.com/microsoft/vscode/issues/61313 @alexr00
|
||||
//#region treeViewReveal: https://github.com/microsoft/vscode/issues/61313 @alexr00
|
||||
export interface TreeView<T> extends Disposable {
|
||||
reveal(element: T | undefined, options?: { select?: boolean, focus?: boolean, expand?: boolean | number; }): Thenable<void>;
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region Custom Tree View Drag and Drop https://github.com/microsoft/vscode/issues/32592
|
||||
//#region treeViewDragAndDrop: https://github.com/microsoft/vscode/issues/32592
|
||||
/**
|
||||
* A data provider that provides tree data
|
||||
*/
|
||||
|
@ -1071,7 +1046,7 @@ declare module 'vscode' {
|
|||
}
|
||||
//#endregion
|
||||
|
||||
//#region Task presentation group: https://github.com/microsoft/vscode/issues/47265
|
||||
//#region taskPresentationGroup: https://github.com/microsoft/vscode/issues/47265
|
||||
export interface TaskPresentationOptions {
|
||||
/**
|
||||
* Controls whether the task is executed in a specific terminal group using split panes.
|
||||
|
@ -1085,7 +1060,7 @@ declare module 'vscode' {
|
|||
}
|
||||
//#endregion
|
||||
|
||||
//#region Custom editor move https://github.com/microsoft/vscode/issues/86146
|
||||
//#region customEditorMove: https://github.com/microsoft/vscode/issues/86146
|
||||
|
||||
// TODO: Also for custom editor
|
||||
|
||||
|
@ -1109,7 +1084,7 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region allow QuickPicks to skip sorting: https://github.com/microsoft/vscode/issues/73904
|
||||
//#region quickPickSortByLabel: https://github.com/microsoft/vscode/issues/73904
|
||||
|
||||
export interface QuickPick<T extends QuickPickItem> extends QuickInput {
|
||||
/**
|
||||
|
@ -1120,7 +1095,7 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region https://github.com/microsoft/vscode/issues/124970, Cell Execution State
|
||||
//#region notebookCellExecutionState: https://github.com/microsoft/vscode/issues/124970
|
||||
|
||||
/**
|
||||
* The execution state of a notebook cell.
|
||||
|
@ -1167,7 +1142,7 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region https://github.com/microsoft/vscode/issues/106744, Notebook, deprecated & misc
|
||||
//#region notebookDeprecated: https://github.com/microsoft/vscode/issues/106744
|
||||
|
||||
export interface NotebookCellOutput {
|
||||
id: string;
|
||||
|
@ -1175,7 +1150,7 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region https://github.com/microsoft/vscode/issues/106744, NotebookEditor
|
||||
//#region notebookEditor: https://github.com/microsoft/vscode/issues/106744
|
||||
|
||||
/**
|
||||
* Represents a notebook editor that is attached to a {@link NotebookDocument notebook}.
|
||||
|
@ -1340,7 +1315,7 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region https://github.com/microsoft/vscode/issues/106744, NotebookEditorEdit
|
||||
//#region notebookEditorEdit: https://github.com/microsoft/vscode/issues/106744
|
||||
|
||||
// todo@API add NotebookEdit-type which handles all these cases?
|
||||
// export class NotebookEdit {
|
||||
|
@ -1388,7 +1363,7 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region https://github.com/microsoft/vscode/issues/106744, NotebookEditorDecorationType
|
||||
//#region notebookEditorDecorationType: https://github.com/microsoft/vscode/issues/106744
|
||||
|
||||
export interface NotebookEditor {
|
||||
setDecorations(decorationType: NotebookEditorDecorationType, range: NotebookRange): void;
|
||||
|
@ -1411,7 +1386,7 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region https://github.com/microsoft/vscode/issues/106744, NotebookConcatTextDocument
|
||||
//#region notebookConcatTextDocument: https://github.com/microsoft/vscode/issues/106744
|
||||
|
||||
export namespace notebooks {
|
||||
/**
|
||||
|
@ -1446,8 +1421,7 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region https://github.com/microsoft/vscode/issues/106744, NotebookContentProvider
|
||||
|
||||
//#region notebookContentProvider: https://github.com/microsoft/vscode/issues/106744
|
||||
|
||||
interface NotebookDocumentBackup {
|
||||
/**
|
||||
|
@ -1507,7 +1481,7 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region https://github.com/microsoft/vscode/issues/106744, LiveShare
|
||||
//#region notebookLiveShare: https://github.com/microsoft/vscode/issues/106744
|
||||
|
||||
export interface NotebookRegistrationData {
|
||||
displayName: string;
|
||||
|
@ -1524,7 +1498,7 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region @https://github.com/microsoft/vscode/issues/123601, notebook messaging
|
||||
//#region notebookMessaging: https://github.com/microsoft/vscode/issues/123601
|
||||
|
||||
/**
|
||||
* Represents a script that is loaded into the notebook renderer before rendering output. This allows
|
||||
|
@ -1591,7 +1565,7 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region @eamodio - timeline: https://github.com/microsoft/vscode/issues/84297
|
||||
//#region timeline: https://github.com/microsoft/vscode/issues/84297
|
||||
|
||||
export class TimelineItem {
|
||||
/**
|
||||
|
@ -1749,7 +1723,7 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region https://github.com/microsoft/vscode/issues/91555
|
||||
//#region tokenInformation: https://github.com/microsoft/vscode/issues/91555
|
||||
|
||||
export enum StandardTokenType {
|
||||
Other = 0,
|
||||
|
@ -1769,7 +1743,7 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region https://github.com/microsoft/vscode/issues/16221
|
||||
//#region inlayHints: https://github.com/microsoft/vscode/issues/16221
|
||||
|
||||
// todo@API Split between Inlay- and OverlayHints (InlayHint are for a position, OverlayHints for a non-empty range)
|
||||
// todo@API add "mini-markdown" for links and styles
|
||||
|
@ -1852,7 +1826,7 @@ declare module 'vscode' {
|
|||
}
|
||||
//#endregion
|
||||
|
||||
//#region https://github.com/microsoft/vscode/issues/104436
|
||||
//#region extensionRuntime: https://github.com/microsoft/vscode/issues/104436
|
||||
|
||||
export enum ExtensionRuntime {
|
||||
/**
|
||||
|
@ -1871,7 +1845,7 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region https://github.com/microsoft/vscode/issues/102091
|
||||
//#region textDocumentNotebook: https://github.com/microsoft/vscode/issues/102091
|
||||
|
||||
export interface TextDocument {
|
||||
|
||||
|
@ -1883,7 +1857,7 @@ declare module 'vscode' {
|
|||
}
|
||||
//#endregion
|
||||
|
||||
//#region proposed test APIs https://github.com/microsoft/vscode/issues/107467
|
||||
//#region testObserver: https://github.com/microsoft/vscode/issues/107467
|
||||
export namespace tests {
|
||||
/**
|
||||
* Requests that tests be run by their controller.
|
||||
|
@ -2079,7 +2053,7 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region Opener service (https://github.com/microsoft/vscode/issues/109277)
|
||||
//#region externalUriOpener: https://github.com/microsoft/vscode/issues/109277
|
||||
|
||||
/**
|
||||
* Details if an `ExternalUriOpener` can open a uri.
|
||||
|
@ -2237,7 +2211,7 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region https://github.com/Microsoft/vscode/issues/15178
|
||||
//#region tabs: https://github.com/Microsoft/vscode/issues/15178
|
||||
|
||||
/**
|
||||
* Represents a tab within the window
|
||||
|
@ -2332,7 +2306,7 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region https://github.com/microsoft/vscode/issues/120173
|
||||
//#region workspaceTrust: https://github.com/microsoft/vscode/issues/120173
|
||||
/**
|
||||
* The object describing the properties of the workspace trust request
|
||||
*/
|
||||
|
@ -2356,7 +2330,7 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region https://github.com/microsoft/vscode/issues/115616 @alexr00
|
||||
//#region portAttributesProvider: https://github.com/microsoft/vscode/issues/115616 @alexr00
|
||||
export enum PortAutoForwardAction {
|
||||
Notify = 1,
|
||||
OpenBrowser = 2,
|
||||
|
@ -2411,19 +2385,7 @@ declare module 'vscode' {
|
|||
}
|
||||
//#endregion
|
||||
|
||||
//#region https://github.com/microsoft/vscode/issues/119904 @eamodio
|
||||
|
||||
export interface SourceControlInputBox {
|
||||
|
||||
/**
|
||||
* Sets focus to the input.
|
||||
*/
|
||||
focus(): void;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region https://github.com/microsoft/vscode/issues/124024 @hediet @alexdima
|
||||
//#region inlineCompletionProvider: https://github.com/microsoft/vscode/issues/124024 @hediet @alexdima
|
||||
|
||||
export namespace languages {
|
||||
/**
|
||||
|
@ -2545,7 +2507,7 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region https://github.com/microsoft/vscode/issues/126280 @mjbvz
|
||||
//#region notebookMime: https://github.com/microsoft/vscode/issues/126280 @mjbvz
|
||||
|
||||
export interface NotebookCellData {
|
||||
/**
|
||||
|
@ -2573,7 +2535,7 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region https://github.com/microsoft/vscode/issues/123713 @connor4312
|
||||
//#region testCoverage: https://github.com/microsoft/vscode/issues/123713 @connor4312
|
||||
export interface TestRun {
|
||||
/**
|
||||
* Test coverage provider for this result. An extension can defer setting
|
||||
|
@ -2764,7 +2726,7 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region https://github.com/microsoft/vscode/issues/129037
|
||||
//#region languageStatus: https://github.com/microsoft/vscode/issues/129037
|
||||
|
||||
enum LanguageStatusSeverity {
|
||||
Information = 0,
|
||||
|
@ -2791,7 +2753,7 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region @eamodio https://github.com/microsoft/vscode/issues/133935
|
||||
//#region scmActionButton: https://github.com/microsoft/vscode/issues/133935
|
||||
|
||||
export interface SourceControl {
|
||||
actionButton?: Command;
|
||||
|
|
|
@ -10,7 +10,7 @@ import { IActiveCodeEditor, IViewZone } from 'vs/editor/browser/editorBrowser';
|
|||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { reviveWebviewContentOptions } from 'vs/workbench/api/browser/mainThreadWebviews';
|
||||
import { ExtHostContext, ExtHostEditorInsetsShape, IExtHostContext, IWebviewOptions, MainContext, MainThreadEditorInsetsShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostContext, ExtHostEditorInsetsShape, IExtHostContext, IWebviewContentOptions, MainContext, MainThreadEditorInsetsShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { IWebviewService, IWebviewElement } from 'vs/workbench/contrib/webview/browser/webview';
|
||||
import { extHostNamedCustomer } from '../common/extHostCustomers';
|
||||
|
||||
|
@ -70,7 +70,7 @@ export class MainThreadEditorInsets implements MainThreadEditorInsetsShape {
|
|||
this._disposables.dispose();
|
||||
}
|
||||
|
||||
async $createEditorInset(handle: number, id: string, uri: UriComponents, line: number, height: number, options: IWebviewOptions, extensionId: ExtensionIdentifier, extensionLocation: UriComponents): Promise<void> {
|
||||
async $createEditorInset(handle: number, id: string, uri: UriComponents, line: number, height: number, options: IWebviewContentOptions, extensionId: ExtensionIdentifier, extensionLocation: UriComponents): Promise<void> {
|
||||
|
||||
let editor: IActiveCodeEditor | undefined;
|
||||
id = id.substr(0, id.indexOf(',')); //todo@jrieken HACK
|
||||
|
@ -121,7 +121,7 @@ export class MainThreadEditorInsets implements MainThreadEditorInsetsShape {
|
|||
inset.webview.html = value;
|
||||
}
|
||||
|
||||
$setOptions(handle: number, options: IWebviewOptions): void {
|
||||
$setOptions(handle: number, options: IWebviewContentOptions): void {
|
||||
const inset = this.getInset(handle);
|
||||
inset.webview.contentOptions = reviveWebviewContentOptions(options);
|
||||
}
|
||||
|
|
|
@ -431,15 +431,6 @@ export class MainThreadSCM implements MainThreadSCMShape {
|
|||
repository.input.visible = visible;
|
||||
}
|
||||
|
||||
$setInputBoxFocus(sourceControlHandle: number): void {
|
||||
const repository = this._repositories.get(sourceControlHandle);
|
||||
if (!repository) {
|
||||
return;
|
||||
}
|
||||
|
||||
repository.input.setFocus();
|
||||
}
|
||||
|
||||
$showValidationMessage(sourceControlHandle: number, message: string | IMarkdownString, type: InputValidationType) {
|
||||
const repository = this._repositories.get(sourceControlHandle);
|
||||
if (!repository) {
|
||||
|
|
|
@ -153,7 +153,9 @@ export class MainThreadTunnelService extends Disposable implements MainThreadTun
|
|||
return (await this.tunnelService.tunnels).map(tunnel => {
|
||||
return {
|
||||
remoteAddress: { port: tunnel.tunnelRemotePort, host: tunnel.tunnelRemoteHost },
|
||||
localAddress: tunnel.localAddress
|
||||
localAddress: tunnel.localAddress,
|
||||
privacy: tunnel.privacy,
|
||||
protocol: tunnel.protocol
|
||||
};
|
||||
});
|
||||
}
|
||||
|
|
|
@ -5,17 +5,17 @@
|
|||
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { MainThreadWebviews, reviveWebviewContentOptions, reviveWebviewExtension } from 'vs/workbench/api/browser/mainThreadWebviews';
|
||||
import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
|
||||
import { EditorGroupColumn, columnToEditorGroup, editorGroupToColumn } from 'vs/workbench/services/editor/common/editorGroupColumn';
|
||||
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
|
||||
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
|
||||
import { WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview';
|
||||
import { WebviewInput } from 'vs/workbench/contrib/webviewPanel/browser/webviewEditorInput';
|
||||
import { WebviewIcons } from 'vs/workbench/contrib/webviewPanel/browser/webviewIconManager';
|
||||
import { ICreateWebViewShowOptions, IWebviewWorkbenchService } from 'vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService';
|
||||
import { columnToEditorGroup, editorGroupToColumn } from 'vs/workbench/services/editor/common/editorGroupColumn';
|
||||
import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
|
@ -151,13 +151,8 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc
|
|||
extensionData: extHostProtocol.WebviewExtensionDescription,
|
||||
handle: extHostProtocol.WebviewHandle,
|
||||
viewType: string,
|
||||
initData: {
|
||||
title: string;
|
||||
webviewOptions: extHostProtocol.IWebviewOptions;
|
||||
panelOptions: extHostProtocol.IWebviewPanelOptions;
|
||||
serializeBuffersForPostMessage: boolean;
|
||||
},
|
||||
showOptions: { viewColumn?: EditorGroupColumn, preserveFocus?: boolean; },
|
||||
initData: extHostProtocol.IWebviewInitData,
|
||||
showOptions: extHostProtocol.WebviewPanelShowOptions,
|
||||
): void {
|
||||
const mainThreadShowOptions: ICreateWebViewShowOptions = Object.create(null);
|
||||
if (showOptions) {
|
||||
|
@ -192,7 +187,7 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc
|
|||
webview.setName(value);
|
||||
}
|
||||
|
||||
public $setIconPath(handle: extHostProtocol.WebviewHandle, value: { light: UriComponents, dark: UriComponents; } | undefined): void {
|
||||
public $setIconPath(handle: extHostProtocol.WebviewHandle, value: extHostProtocol.IWebviewIconPath | undefined): void {
|
||||
const webview = this.getWebviewInput(handle);
|
||||
webview.iconPath = reviveWebviewIcon(value);
|
||||
}
|
||||
|
@ -316,12 +311,14 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc
|
|||
}
|
||||
}
|
||||
|
||||
function reviveWebviewIcon(
|
||||
value: { light: UriComponents, dark: UriComponents; } | undefined
|
||||
): WebviewIcons | undefined {
|
||||
return value
|
||||
? { light: URI.revive(value.light), dark: URI.revive(value.dark) }
|
||||
: undefined;
|
||||
function reviveWebviewIcon(value: extHostProtocol.IWebviewIconPath | undefined): WebviewIcons | undefined {
|
||||
if (!value) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
light: URI.revive(value.light),
|
||||
dark: URI.revive(value.dark),
|
||||
};
|
||||
}
|
||||
|
||||
function reviveWebviewOptions(panelOptions: extHostProtocol.IWebviewPanelOptions): WebviewOptions {
|
||||
|
|
|
@ -55,7 +55,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
|||
webview.html = value;
|
||||
}
|
||||
|
||||
public $setOptions(handle: extHostProtocol.WebviewHandle, options: extHostProtocol.IWebviewOptions): void {
|
||||
public $setOptions(handle: extHostProtocol.WebviewHandle, options: extHostProtocol.IWebviewContentOptions): void {
|
||||
const webview = this.getWebview(handle);
|
||||
webview.contentOptions = reviveWebviewContentOptions(options);
|
||||
}
|
||||
|
@ -123,10 +123,13 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
|||
}
|
||||
|
||||
export function reviveWebviewExtension(extensionData: extHostProtocol.WebviewExtensionDescription): WebviewExtensionDescription {
|
||||
return { id: extensionData.id, location: URI.revive(extensionData.location) };
|
||||
return {
|
||||
id: extensionData.id,
|
||||
location: URI.revive(extensionData.location),
|
||||
};
|
||||
}
|
||||
|
||||
export function reviveWebviewContentOptions(webviewOptions: extHostProtocol.IWebviewOptions): WebviewContentOptions {
|
||||
export function reviveWebviewContentOptions(webviewOptions: extHostProtocol.IWebviewContentOptions): WebviewContentOptions {
|
||||
return {
|
||||
allowScripts: webviewOptions.enableScripts,
|
||||
allowForms: webviewOptions.enableForms,
|
||||
|
|
|
@ -618,11 +618,11 @@ export interface MainThreadTelemetryShape extends IDisposable {
|
|||
}
|
||||
|
||||
export interface MainThreadEditorInsetsShape extends IDisposable {
|
||||
$createEditorInset(handle: number, id: string, uri: UriComponents, line: number, height: number, options: IWebviewOptions, extensionId: ExtensionIdentifier, extensionLocation: UriComponents): Promise<void>;
|
||||
$createEditorInset(handle: number, id: string, uri: UriComponents, line: number, height: number, options: IWebviewContentOptions, extensionId: ExtensionIdentifier, extensionLocation: UriComponents): Promise<void>;
|
||||
$disposeEditorInset(handle: number): void;
|
||||
|
||||
$setHtml(handle: number, value: string): void;
|
||||
$setOptions(handle: number, options: IWebviewOptions): void;
|
||||
$setOptions(handle: number, options: IWebviewContentOptions): void;
|
||||
$postMessage(handle: number, value: any): Promise<boolean>;
|
||||
}
|
||||
|
||||
|
@ -681,12 +681,12 @@ export interface IWebviewPortMapping {
|
|||
readonly extensionHostPort: number;
|
||||
}
|
||||
|
||||
export interface IWebviewOptions {
|
||||
export interface IWebviewContentOptions {
|
||||
readonly enableScripts?: boolean;
|
||||
readonly enableForms?: boolean;
|
||||
readonly enableCommandUris?: boolean;
|
||||
readonly localResourceRoots?: ReadonlyArray<UriComponents>;
|
||||
readonly portMapping?: ReadonlyArray<IWebviewPortMapping>;
|
||||
readonly localResourceRoots?: readonly UriComponents[];
|
||||
readonly portMapping?: readonly IWebviewPortMapping[];
|
||||
}
|
||||
|
||||
export interface IWebviewPanelOptions {
|
||||
|
@ -729,27 +729,34 @@ export interface WebviewMessageArrayBufferReference {
|
|||
|
||||
export interface MainThreadWebviewsShape extends IDisposable {
|
||||
$setHtml(handle: WebviewHandle, value: string): void;
|
||||
$setOptions(handle: WebviewHandle, options: IWebviewOptions): void;
|
||||
$setOptions(handle: WebviewHandle, options: IWebviewContentOptions): void;
|
||||
$postMessage(handle: WebviewHandle, value: string, ...buffers: VSBuffer[]): Promise<boolean>
|
||||
}
|
||||
|
||||
export interface IWebviewIconPath {
|
||||
readonly light: UriComponents;
|
||||
readonly dark: UriComponents;
|
||||
}
|
||||
|
||||
export interface IWebviewInitData {
|
||||
readonly title: string;
|
||||
readonly webviewOptions: IWebviewContentOptions;
|
||||
readonly panelOptions: IWebviewPanelOptions;
|
||||
readonly serializeBuffersForPostMessage: boolean;
|
||||
}
|
||||
|
||||
export interface MainThreadWebviewPanelsShape extends IDisposable {
|
||||
$createWebviewPanel(
|
||||
extension: WebviewExtensionDescription,
|
||||
handle: WebviewHandle,
|
||||
viewType: string,
|
||||
initData: {
|
||||
title: string;
|
||||
webviewOptions: IWebviewOptions;
|
||||
panelOptions: IWebviewPanelOptions;
|
||||
serializeBuffersForPostMessage: boolean;
|
||||
},
|
||||
initData: IWebviewInitData,
|
||||
showOptions: WebviewPanelShowOptions,
|
||||
): void;
|
||||
$disposeWebview(handle: WebviewHandle): void;
|
||||
$reveal(handle: WebviewHandle, showOptions: WebviewPanelShowOptions): void;
|
||||
$setTitle(handle: WebviewHandle, value: string): void;
|
||||
$setIconPath(handle: WebviewHandle, value: { light: UriComponents, dark: UriComponents; } | undefined): void;
|
||||
$setIconPath(handle: WebviewHandle, value: IWebviewIconPath | undefined): void;
|
||||
|
||||
$registerSerializer(viewType: string, options: { serializeBuffersForPostMessage: boolean }): void;
|
||||
$unregisterSerializer(viewType: string): void;
|
||||
|
@ -796,7 +803,7 @@ export interface ExtHostWebviewPanelsShape {
|
|||
initData: {
|
||||
title: string;
|
||||
state: any;
|
||||
webviewOptions: IWebviewOptions;
|
||||
webviewOptions: IWebviewContentOptions;
|
||||
panelOptions: IWebviewPanelOptions;
|
||||
},
|
||||
position: EditorGroupColumn,
|
||||
|
@ -810,7 +817,7 @@ export interface ExtHostCustomEditorsShape {
|
|||
viewType: string,
|
||||
initData: {
|
||||
title: string;
|
||||
webviewOptions: IWebviewOptions;
|
||||
webviewOptions: IWebviewContentOptions;
|
||||
panelOptions: IWebviewPanelOptions;
|
||||
},
|
||||
position: EditorGroupColumn,
|
||||
|
@ -1096,7 +1103,6 @@ export interface MainThreadSCMShape extends IDisposable {
|
|||
$setInputBoxValue(sourceControlHandle: number, value: string): void;
|
||||
$setInputBoxPlaceholder(sourceControlHandle: number, placeholder: string): void;
|
||||
$setInputBoxVisibility(sourceControlHandle: number, visible: boolean): void;
|
||||
$setInputBoxFocus(sourceControlHandle: number): void;
|
||||
$showValidationMessage(sourceControlHandle: number, message: string | IMarkdownString, type: InputValidationType): void;
|
||||
$setValidationProviderIsEnabled(sourceControlHandle: number, enabled: boolean): void;
|
||||
}
|
||||
|
|
|
@ -253,7 +253,7 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor
|
|||
viewType: string,
|
||||
initData: {
|
||||
title: string;
|
||||
webviewOptions: extHostProtocol.IWebviewOptions;
|
||||
webviewOptions: extHostProtocol.IWebviewContentOptions;
|
||||
panelOptions: extHostProtocol.IWebviewPanelOptions;
|
||||
},
|
||||
position: EditorGroupColumn,
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
import { ILoggerService, LogService } from 'vs/platform/log/common/log';
|
||||
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
|
||||
import { ExtensionHostLogFileName } from 'vs/workbench/services/extensions/common/extensions';
|
||||
|
||||
export class ExtHostLogService extends LogService {
|
||||
|
||||
|
@ -14,7 +15,7 @@ export class ExtHostLogService extends LogService {
|
|||
@ILoggerService loggerService: ILoggerService,
|
||||
@IExtHostInitDataService initData: IExtHostInitDataService,
|
||||
) {
|
||||
super(loggerService.createLogger(initData.logFile));
|
||||
super(loggerService.createLogger(initData.logFile, { name: ExtensionHostLogFileName }));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -266,16 +266,6 @@ export class ExtHostSCMInputBox implements vscode.SourceControlInputBox {
|
|||
// noop
|
||||
}
|
||||
|
||||
focus(): void {
|
||||
checkProposedApiEnabled(this._extension);
|
||||
|
||||
if (!this._visible) {
|
||||
this.visible = true;
|
||||
}
|
||||
|
||||
this._proxy.$setInputBoxFocus(this._sourceControlHandle);
|
||||
}
|
||||
|
||||
showValidationMessage(message: string | vscode.MarkdownString, type: vscode.SourceControlInputBoxValidationType) {
|
||||
checkProposedApiEnabled(this._extension);
|
||||
|
||||
|
|
|
@ -1776,7 +1776,7 @@ export enum TaskPanelKind {
|
|||
@es5ClassCompat
|
||||
export class TaskGroup implements vscode.TaskGroup {
|
||||
|
||||
isDefault?: boolean;
|
||||
isDefault: boolean | undefined;
|
||||
private _id: string;
|
||||
|
||||
public static Clean: TaskGroup = new TaskGroup('clean', 'Clean');
|
||||
|
|
|
@ -176,7 +176,7 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
|
|||
this._logService.warn(`${extensionId} created a webview without a content security policy: https://aka.ms/vscode-webview-missing-csp`);
|
||||
}
|
||||
|
||||
public createNewWebview(handle: string, options: extHostProtocol.IWebviewOptions, extension: IExtensionDescription): ExtHostWebview {
|
||||
public createNewWebview(handle: string, options: extHostProtocol.IWebviewContentOptions, extension: IExtensionDescription): ExtHostWebview {
|
||||
const webview = new ExtHostWebview(handle, this._webviewProxy, reviveOptions(options), this.initData, this.workspace, extension, this._deprecationService);
|
||||
this._webviews.set(handle, webview);
|
||||
|
||||
|
@ -202,7 +202,7 @@ export function serializeWebviewOptions(
|
|||
extension: IExtensionDescription,
|
||||
workspace: IExtHostWorkspace | undefined,
|
||||
options: vscode.WebviewOptions,
|
||||
): extHostProtocol.IWebviewOptions {
|
||||
): extHostProtocol.IWebviewContentOptions {
|
||||
return {
|
||||
enableCommandUris: options.enableCommandUris,
|
||||
enableScripts: options.enableScripts,
|
||||
|
@ -212,7 +212,7 @@ export function serializeWebviewOptions(
|
|||
};
|
||||
}
|
||||
|
||||
export function reviveOptions(options: extHostProtocol.IWebviewOptions): vscode.WebviewOptions {
|
||||
export function reviveOptions(options: extHostProtocol.IWebviewContentOptions): vscode.WebviewOptions {
|
||||
return {
|
||||
enableCommandUris: options.enableCommandUris,
|
||||
enableScripts: options.enableScripts,
|
||||
|
|
|
@ -281,7 +281,7 @@ export class ExtHostWebviewPanels implements extHostProtocol.ExtHostWebviewPanel
|
|||
initData: {
|
||||
title: string;
|
||||
state: any;
|
||||
webviewOptions: extHostProtocol.IWebviewOptions;
|
||||
webviewOptions: extHostProtocol.IWebviewContentOptions;
|
||||
panelOptions: extHostProtocol.IWebviewPanelOptions;
|
||||
},
|
||||
position: EditorGroupColumn
|
||||
|
|
|
@ -14,11 +14,7 @@ export class ExtHostLoggerService extends BaseExtHostLoggerService {
|
|||
|
||||
protected override doCreateLogger(resource: URI, logLevel: LogLevel, options?: ILoggerOptions): ILogger {
|
||||
if (resource.scheme === Schemas.file) {
|
||||
const logger = new SpdLogLogger(options?.name || generateUuid(), resource.fsPath, !options?.donotRotate, logLevel);
|
||||
if (options?.donotUseFormatters) {
|
||||
(<SpdLogLogger>logger).clearFormatters();
|
||||
}
|
||||
return logger;
|
||||
return new SpdLogLogger(options?.name || generateUuid(), resource.fsPath, !options?.donotRotate, !!options?.donotUseFormatters, logLevel);
|
||||
}
|
||||
return super.doCreateLogger(resource, logLevel, options);
|
||||
}
|
||||
|
|
|
@ -59,11 +59,15 @@ export class ToggleActivityBarVisibilityAction extends Action2 {
|
|||
category: CATEGORIES.View,
|
||||
f1: true,
|
||||
toggled: ContextKeyExpr.equals('config.workbench.activityBar.visible', true),
|
||||
menu: {
|
||||
menu: [{
|
||||
id: MenuId.MenubarAppearanceMenu,
|
||||
group: '2_workbench_layout',
|
||||
order: 4
|
||||
}
|
||||
}, {
|
||||
id: MenuId.LayoutControlMenu,
|
||||
group: '0_workbench_layout',
|
||||
order: 3
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -226,11 +230,15 @@ registerAction2(class extends Action2 {
|
|||
category: CATEGORIES.View,
|
||||
f1: true,
|
||||
toggled: EditorAreaVisibleContext,
|
||||
menu: {
|
||||
menu: [{
|
||||
id: MenuId.MenubarAppearanceMenu,
|
||||
group: '2_workbench_layout',
|
||||
order: 5
|
||||
}
|
||||
}, {
|
||||
id: MenuId.LayoutControlMenu,
|
||||
group: '0_workbench_layout',
|
||||
order: 5
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -274,40 +282,53 @@ class ToggleSidebarVisibilityAction extends Action2 {
|
|||
|
||||
registerAction2(ToggleSidebarVisibilityAction);
|
||||
|
||||
MenuRegistry.appendMenuItems([{
|
||||
id: MenuId.ViewContainerTitleContext,
|
||||
item: {
|
||||
group: '3_workbench_layout_move',
|
||||
command: {
|
||||
id: ToggleSidebarVisibilityAction.ID,
|
||||
title: localize('compositePart.hideSideBarLabel', "Hide Side Bar"),
|
||||
},
|
||||
when: ContextKeyExpr.and(SideBarVisibleContext, ContextKeyExpr.equals('viewContainerLocation', ViewContainerLocationToString(ViewContainerLocation.Sidebar))),
|
||||
order: 2
|
||||
MenuRegistry.appendMenuItems([
|
||||
{
|
||||
id: MenuId.ViewContainerTitleContext,
|
||||
item: {
|
||||
group: '3_workbench_layout_move',
|
||||
command: {
|
||||
id: ToggleSidebarVisibilityAction.ID,
|
||||
title: localize('compositePart.hideSideBarLabel', "Hide Side Bar"),
|
||||
},
|
||||
when: ContextKeyExpr.and(SideBarVisibleContext, ContextKeyExpr.equals('viewContainerLocation', ViewContainerLocationToString(ViewContainerLocation.Sidebar))),
|
||||
order: 2
|
||||
}
|
||||
}, {
|
||||
id: MenuId.ViewTitleContext,
|
||||
item: {
|
||||
group: '3_workbench_layout_move',
|
||||
command: {
|
||||
id: ToggleSidebarVisibilityAction.ID,
|
||||
title: localize('compositePart.hideSideBarLabel', "Hide Side Bar"),
|
||||
},
|
||||
when: ContextKeyExpr.and(SideBarVisibleContext, ContextKeyExpr.equals('viewLocation', ViewContainerLocationToString(ViewContainerLocation.Sidebar))),
|
||||
order: 2
|
||||
}
|
||||
}, {
|
||||
id: MenuId.MenubarAppearanceMenu,
|
||||
item: {
|
||||
group: '2_workbench_layout',
|
||||
command: {
|
||||
id: ToggleSidebarVisibilityAction.ID,
|
||||
title: localize({ key: 'miShowSidebar', comment: ['&& denotes a mnemonic'] }, "Show &&Side Bar"),
|
||||
toggled: SideBarVisibleContext
|
||||
},
|
||||
order: 1
|
||||
}
|
||||
}, {
|
||||
id: MenuId.LayoutControlMenu,
|
||||
item: {
|
||||
group: '0_workbench_layout',
|
||||
command: {
|
||||
id: ToggleSidebarVisibilityAction.ID,
|
||||
title: localize('miShowSidebarNoMnnemonic', "Show Side Bar"),
|
||||
toggled: SideBarVisibleContext
|
||||
},
|
||||
order: 0
|
||||
}
|
||||
}
|
||||
}, {
|
||||
id: MenuId.ViewTitleContext,
|
||||
item: {
|
||||
group: '3_workbench_layout_move',
|
||||
command: {
|
||||
id: ToggleSidebarVisibilityAction.ID,
|
||||
title: localize('compositePart.hideSideBarLabel', "Hide Side Bar"),
|
||||
},
|
||||
when: ContextKeyExpr.and(SideBarVisibleContext, ContextKeyExpr.equals('viewLocation', ViewContainerLocationToString(ViewContainerLocation.Sidebar))),
|
||||
order: 2
|
||||
}
|
||||
}, {
|
||||
id: MenuId.MenubarAppearanceMenu,
|
||||
item: {
|
||||
group: '2_workbench_layout',
|
||||
command: {
|
||||
id: ToggleSidebarVisibilityAction.ID,
|
||||
title: localize({ key: 'miShowSidebar', comment: ['&& denotes a mnemonic'] }, "Show &&Side Bar"),
|
||||
toggled: SideBarVisibleContext
|
||||
},
|
||||
order: 1
|
||||
}
|
||||
}]);
|
||||
]);
|
||||
|
||||
// --- Toggle Statusbar Visibility
|
||||
|
||||
|
@ -328,11 +349,15 @@ export class ToggleStatusbarVisibilityAction extends Action2 {
|
|||
category: CATEGORIES.View,
|
||||
f1: true,
|
||||
toggled: ContextKeyExpr.equals('config.workbench.statusBar.visible', true),
|
||||
menu: {
|
||||
menu: [{
|
||||
id: MenuId.MenubarAppearanceMenu,
|
||||
group: '2_workbench_layout',
|
||||
order: 3
|
||||
}
|
||||
}, {
|
||||
id: MenuId.LayoutControlMenu,
|
||||
group: '0_workbench_layout',
|
||||
order: 1
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/
|
|||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { CATEGORIES, Extensions as WorkbenchExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions';
|
||||
import { ActiveAuxiliaryContext, AuxiliaryBarVisibleContext } from 'vs/workbench/common/auxiliarybar';
|
||||
import { AuxiliaryBarVisibleContext } from 'vs/workbench/common/auxiliarybar';
|
||||
import { ViewContainerLocation, ViewContainerLocationToString } from 'vs/workbench/common/views';
|
||||
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite';
|
||||
|
@ -62,6 +62,19 @@ class FocusAuxiliaryBarAction extends Action {
|
|||
}
|
||||
|
||||
MenuRegistry.appendMenuItems([
|
||||
{
|
||||
id: MenuId.LayoutControlMenu,
|
||||
item: {
|
||||
group: '0_workbench_layout',
|
||||
command: {
|
||||
id: ToggleAuxiliaryBarAction.ID,
|
||||
title: localize({ key: 'miShowAuxiliaryBar', comment: ['&& denotes a mnemonic'] }, "Show Si&&de Panel"),
|
||||
toggled: AuxiliaryBarVisibleContext
|
||||
},
|
||||
when: ContextKeyExpr.equals('config.workbench.experimental.sidePanel.enabled', true),
|
||||
order: 4
|
||||
}
|
||||
},
|
||||
{
|
||||
id: MenuId.MenubarAppearanceMenu,
|
||||
item: {
|
||||
|
@ -69,7 +82,7 @@ MenuRegistry.appendMenuItems([
|
|||
command: {
|
||||
id: ToggleAuxiliaryBarAction.ID,
|
||||
title: localize({ key: 'miShowAuxiliaryBar', comment: ['&& denotes a mnemonic'] }, "Show Si&&de Panel"),
|
||||
toggled: ActiveAuxiliaryContext
|
||||
toggled: AuxiliaryBarVisibleContext
|
||||
},
|
||||
when: ContextKeyExpr.equals('config.workbench.experimental.sidePanel.enabled', true),
|
||||
order: 5
|
||||
|
|
|
@ -12,7 +12,7 @@ import { IEditorPaneRegistry, IEditorPaneDescriptor } from 'vs/workbench/browser
|
|||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IEditorProgressService, IOperation, LongRunningOperation } from 'vs/platform/progress/common/progress';
|
||||
import { IEditorProgressService, LongRunningOperation } from 'vs/platform/progress/common/progress';
|
||||
import { IEditorGroupView, DEFAULT_EDITOR_MIN_DIMENSIONS, DEFAULT_EDITOR_MAX_DIMENSIONS } from 'vs/workbench/browser/parts/editor/editor';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { assertIsDefined } from 'vs/base/common/types';
|
||||
|
@ -146,20 +146,8 @@ export class EditorPanes extends Disposable {
|
|||
// Editor pane
|
||||
const pane = this.doShowEditorPane(descriptor);
|
||||
|
||||
// Show progress while setting input after a certain timeout.
|
||||
// If the workbench is opening be more relaxed about progress
|
||||
// showing by increasing the delay a little bit to reduce flicker.
|
||||
const operation = this.editorOperation.start(this.layoutService.isRestored() ? 800 : 3200);
|
||||
|
||||
// Apply input to pane
|
||||
let changed: boolean;
|
||||
let cancelled: boolean;
|
||||
try {
|
||||
changed = await this.doSetInput(pane, operation, editor, options, context);
|
||||
cancelled = !operation.isCurrent();
|
||||
} finally {
|
||||
operation.stop();
|
||||
}
|
||||
const { changed, cancelled } = await this.doSetInput(pane, editor, options, context);
|
||||
|
||||
// Focus unless cancelled
|
||||
if (!cancelled) {
|
||||
|
@ -263,22 +251,36 @@ export class EditorPanes extends Disposable {
|
|||
this._onDidChangeSizeConstraints.fire(undefined);
|
||||
}
|
||||
|
||||
private async doSetInput(editorPane: EditorPane, operation: IOperation, editor: EditorInput, options: IEditorOptions | undefined, context: IEditorOpenContext): Promise<boolean> {
|
||||
const forceReload = options?.forceReload;
|
||||
private async doSetInput(editorPane: EditorPane, editor: EditorInput, options: IEditorOptions | undefined, context: IEditorOpenContext): Promise<{ changed: boolean, cancelled: boolean }> {
|
||||
|
||||
// If the input did not change, return early and only
|
||||
// apply the options unless the options instruct us to
|
||||
// force open it even if it is the same
|
||||
const inputMatches = editorPane.input?.matches(editor);
|
||||
|
||||
// If the input did not change, return early and only apply the options
|
||||
// unless the options instruct us to force open it even if it is the same
|
||||
if (inputMatches && !forceReload) {
|
||||
if (inputMatches && !options?.forceReload) {
|
||||
editorPane.setOptions(options);
|
||||
|
||||
return { changed: false, cancelled: false };
|
||||
}
|
||||
|
||||
// Otherwise set the input to the editor pane
|
||||
else {
|
||||
// Start a new editor input operation to report progress
|
||||
// and to support cancellation. Any new operation that is
|
||||
// started will cancel the previous one.
|
||||
const operation = this.editorOperation.start(this.layoutService.isRestored() ? 800 : 3200);
|
||||
|
||||
// Set the input to the editor pane
|
||||
let cancelled = false;
|
||||
try {
|
||||
await editorPane.setInput(editor, options, context, operation.token);
|
||||
|
||||
if (!operation.isCurrent()) {
|
||||
cancelled = true;
|
||||
}
|
||||
} finally {
|
||||
operation.stop();
|
||||
}
|
||||
|
||||
return !inputMatches;
|
||||
return { changed: !inputMatches, cancelled };
|
||||
}
|
||||
|
||||
private doHideActiveEditorPane(): void {
|
||||
|
|
|
@ -18,8 +18,9 @@ import { ViewContainerLocation, ViewContainerLocations } from 'vs/workbench/comm
|
|||
import { IBadge } from 'vs/workbench/services/activity/common/activity';
|
||||
import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite';
|
||||
import { IDisposable } from 'vs/workbench/workbench.web.api';
|
||||
import { IView } from 'vs/base/browser/ui/grid/grid';
|
||||
|
||||
export interface IPaneCompositePart {
|
||||
export interface IPaneCompositePart extends IView {
|
||||
|
||||
readonly onDidPaneCompositeOpen: Event<IPaneComposite>;
|
||||
readonly onDidPaneCompositeClose: Event<IPaneComposite>;
|
||||
|
|
|
@ -298,6 +298,17 @@ MenuRegistry.appendMenuItems([
|
|||
},
|
||||
order: 5
|
||||
}
|
||||
}, {
|
||||
id: MenuId.LayoutControlMenu,
|
||||
item: {
|
||||
group: '0_workbench_layout',
|
||||
command: {
|
||||
id: TogglePanelAction.ID,
|
||||
title: localize({ key: 'miShowPanel', comment: ['&& denotes a mnemonic'] }, "Show &&Panel"),
|
||||
toggled: ActivePanelContext
|
||||
},
|
||||
order: 4
|
||||
}
|
||||
}, {
|
||||
id: MenuId.ViewTitleContext,
|
||||
item: {
|
||||
|
|
|
@ -132,11 +132,34 @@
|
|||
margin-left: auto;
|
||||
}
|
||||
|
||||
.monaco-workbench.mac:not(web) .part.titlebar > .window-controls-container {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
width: 28px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.monaco-workbench.mac:not(web) .part.titlebar > .window-controls-container.show-layout-control {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.monaco-workbench.fullscreen .part.titlebar > .window-controls-container {
|
||||
display: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.titlebar > .window-controls-container.show-layout-control {
|
||||
width: 160px;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.titlebar > .window-controls-container > .layout-dropdown-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.titlebar > .window-controls-container.show-layout-control > .layout-dropdown-container {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.titlebar > .window-controls-container > .window-icon {
|
||||
display: inline-block;
|
||||
line-height: 30px;
|
||||
|
|
|
@ -8,7 +8,7 @@ import { IMenuService, MenuId, IMenu, SubmenuItemAction, registerAction2, Action
|
|||
import { registerThemingParticipant, IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { MenuBarVisibility, getTitleBarStyle, IWindowOpenable, getMenuBarVisibility } from 'vs/platform/windows/common/windows';
|
||||
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IAction, Action, SubmenuAction, Separator } from 'vs/base/common/actions';
|
||||
import { IAction, Action, SubmenuAction, Separator, IActionRunner, ActionRunner, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions';
|
||||
import { addDisposableListener, Dimension, EventType } from 'vs/base/browser/dom';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { isMacintosh, isWeb, isIOS, isNative } from 'vs/base/common/platform';
|
||||
|
@ -38,6 +38,7 @@ import { KeyCode } from 'vs/base/common/keyCodes';
|
|||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { IsMacNativeContext, IsWebContext } from 'vs/platform/contextkey/common/contextkeys';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
|
||||
export type IOpenRecentAction = IAction & { uri: URI, remoteAuthority?: string };
|
||||
|
||||
|
@ -374,6 +375,7 @@ export class CustomMenubarControl extends MenubarControl {
|
|||
private alwaysOnMnemonics: boolean = false;
|
||||
private focusInsideMenubar: boolean = false;
|
||||
private visible: boolean = true;
|
||||
private actionRunner: IActionRunner;
|
||||
private readonly webNavigationMenu = this._register(this.menuService.createMenu(MenuId.MenubarHomeMenu, this.contextKeyService));
|
||||
|
||||
private readonly _onVisibilityChange: Emitter<boolean>;
|
||||
|
@ -394,6 +396,7 @@ export class CustomMenubarControl extends MenubarControl {
|
|||
@IAccessibilityService accessibilityService: IAccessibilityService,
|
||||
@IThemeService private readonly themeService: IThemeService,
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
|
||||
@ITelemetryService private readonly telemetryService: ITelemetryService,
|
||||
@IHostService hostService: IHostService,
|
||||
@ICommandService commandService: ICommandService
|
||||
) {
|
||||
|
@ -402,6 +405,11 @@ export class CustomMenubarControl extends MenubarControl {
|
|||
this._onVisibilityChange = this._register(new Emitter<boolean>());
|
||||
this._onFocusStateChange = this._register(new Emitter<boolean>());
|
||||
|
||||
this.actionRunner = this._register(new ActionRunner());
|
||||
this.actionRunner.onDidRun(e => {
|
||||
this.telemetryService.publicLog2<WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification>('workbenchActionExecuted', { id: e.action.id, from: 'menu' });
|
||||
});
|
||||
|
||||
this.workspacesService.getRecentlyOpened().then((recentlyOpened) => {
|
||||
this.recentlyOpened = recentlyOpened;
|
||||
});
|
||||
|
@ -811,6 +819,7 @@ export class CustomMenubarControl extends MenubarControl {
|
|||
enableMnemonics: this.currentEnableMenuBarMnemonics,
|
||||
disableAltFocus: this.currentDisableMenuBarAltFocus,
|
||||
visibility: this.currentMenubarVisibility,
|
||||
actionRunner: this.actionRunner,
|
||||
getKeybinding: (action) => this.keybindingService.lookupKeybinding(action.id),
|
||||
alwaysOnMnemonics: this.alwaysOnMnemonics,
|
||||
compactMode: this.currentCompactMenuMode,
|
||||
|
|
|
@ -12,7 +12,7 @@ import { getZoomFactor } from 'vs/base/browser/browser';
|
|||
import { MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility } from 'vs/platform/windows/common/windows';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { IAction, SubmenuAction } from 'vs/base/common/actions';
|
||||
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { DisposableStore, dispose } from 'vs/base/common/lifecycle';
|
||||
|
@ -28,14 +28,14 @@ import { trim } from 'vs/base/common/strings';
|
|||
import { EventType, EventHelper, Dimension, isAncestor, append, $, addDisposableListener, runAtThisOrScheduleAtNextAnimationFrame, prepend } from 'vs/base/browser/dom';
|
||||
import { CustomMenubarControl } from 'vs/workbench/browser/parts/titlebar/menubarControl';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { template } from 'vs/base/common/labels';
|
||||
import { mnemonicButtonLabel, template } from 'vs/base/common/labels';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
|
||||
import { IMenuService, IMenu, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { IMenuService, IMenu, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
|
@ -43,6 +43,10 @@ import { Schemas } from 'vs/base/common/network';
|
|||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { Codicon, iconRegistry } from 'vs/base/common/codicons';
|
||||
import { getVirtualWorkspaceLocation } from 'vs/platform/remote/common/remoteHosts';
|
||||
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem';
|
||||
import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
|
||||
export class TitlebarPart extends Part implements ITitleService {
|
||||
|
||||
|
@ -66,10 +70,13 @@ export class TitlebarPart extends Part implements ITitleService {
|
|||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
protected title!: HTMLElement;
|
||||
|
||||
protected customMenubar: CustomMenubarControl | undefined;
|
||||
protected appIcon: HTMLElement | undefined;
|
||||
private appIconBadge: HTMLElement | undefined;
|
||||
protected menubar?: HTMLElement;
|
||||
protected windowControls: HTMLElement | undefined;
|
||||
private layoutToolbar: ActionBar | undefined;
|
||||
protected lastLayoutDimensions: Dimension | undefined;
|
||||
private titleBarStyle: 'native' | 'custom';
|
||||
|
||||
|
@ -91,12 +98,13 @@ export class TitlebarPart extends Part implements ITitleService {
|
|||
@IWorkbenchEnvironmentService protected readonly environmentService: IWorkbenchEnvironmentService,
|
||||
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
|
||||
@IInstantiationService protected readonly instantiationService: IInstantiationService,
|
||||
@IKeybindingService private readonly keybindingService: IKeybindingService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@ILabelService private readonly labelService: ILabelService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
|
||||
@IMenuService menuService: IMenuService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IMenuService private readonly menuService: IMenuService,
|
||||
@IContextKeyService private readonly contextKeyService: IContextKeyService,
|
||||
@IHostService private readonly hostService: IHostService,
|
||||
@IProductService private readonly productService: IProductService,
|
||||
) {
|
||||
|
@ -143,6 +151,10 @@ export class TitlebarPart extends Part implements ITitleService {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.titleBarStyle !== 'native' && this.windowControls && event.affectsConfiguration('workbench.experimental.layoutControl.enabled')) {
|
||||
this.windowControls.classList.toggle('show-layout-control', this.layoutControlEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
protected onMenubarVisibilityChanged(visible: boolean): void {
|
||||
|
@ -395,6 +407,53 @@ export class TitlebarPart extends Part implements ITitleService {
|
|||
this.titleUpdater.schedule();
|
||||
}
|
||||
|
||||
if (this.titleBarStyle !== 'native') {
|
||||
this.windowControls = append(this.element, $('div.window-controls-container'));
|
||||
this.windowControls.classList.toggle('show-layout-control', this.layoutControlEnabled);
|
||||
|
||||
const layoutDropdownContainer = append(this.windowControls, $('div.layout-dropdown-container'));
|
||||
this.layoutToolbar = new ActionBar(layoutDropdownContainer,
|
||||
{
|
||||
ariaLabel: localize('layoutMenu', "Configure Layout"),
|
||||
actionViewItemProvider: action => {
|
||||
if (action instanceof SubmenuAction) {
|
||||
return new DropdownMenuActionViewItem(action, action.actions, this.contextMenuService, {
|
||||
classNames: Codicon.editorLayout.classNamesArray,
|
||||
anchorAlignmentProvider: () => AnchorAlignment.RIGHT,
|
||||
keybindingProvider: action => this.keybindingService.lookupKeybinding(action.id)
|
||||
});
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
const menu = this._register(this.menuService.createMenu(MenuId.LayoutControlMenu, this.contextKeyService));
|
||||
const updateLayoutMenu = () => {
|
||||
if (!this.layoutToolbar) {
|
||||
return;
|
||||
}
|
||||
|
||||
const actions: IAction[] = [];
|
||||
const toDispose = createAndFillInContextMenuActions(menu, undefined, { primary: [], secondary: actions });
|
||||
|
||||
this.layoutToolbar.clear();
|
||||
this.layoutToolbar.push(new SubmenuAction('stenir', localize('layoutMenu', "Configure Layout"), actions.map(action => {
|
||||
if (action instanceof MenuItemAction) {
|
||||
(action as IAction).label = mnemonicButtonLabel(typeof action.item.title === 'string'
|
||||
? action.item.title
|
||||
: action.item.title.mnemonicTitle ?? action.item.title.value, true);
|
||||
}
|
||||
return action;
|
||||
})));
|
||||
|
||||
toDispose.dispose();
|
||||
};
|
||||
|
||||
menu.onDidChange(updateLayoutMenu);
|
||||
updateLayoutMenu();
|
||||
}
|
||||
|
||||
// Context menu on title
|
||||
[EventType.CONTEXT_MENU, EventType.MOUSE_DOWN].forEach(event => {
|
||||
this._register(addDisposableListener(this.title, event, e => {
|
||||
|
@ -413,6 +472,10 @@ export class TitlebarPart extends Part implements ITitleService {
|
|||
return;
|
||||
}
|
||||
|
||||
if (e.target && this.layoutToolbar && isAncestor(e.target as HTMLElement, this.layoutToolbar.getContainer())) {
|
||||
return;
|
||||
}
|
||||
|
||||
const active = document.activeElement;
|
||||
setTimeout(() => {
|
||||
if (active instanceof HTMLElement) {
|
||||
|
@ -507,6 +570,10 @@ export class TitlebarPart extends Part implements ITitleService {
|
|||
return getMenuBarVisibility(this.configurationService);
|
||||
}
|
||||
|
||||
private get layoutControlEnabled(): boolean {
|
||||
return this.configurationService.getValue<boolean>('workbench.experimental.layoutControl.enabled');
|
||||
}
|
||||
|
||||
updateLayout(dimension: Dimension): void {
|
||||
this.lastLayoutDimensions = dimension;
|
||||
|
||||
|
|
|
@ -356,6 +356,11 @@ const registry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Con
|
|||
// On Mac, the delay is 1500.
|
||||
'default': isMacintosh ? 1500 : 500
|
||||
},
|
||||
'workbench.experimental.layoutControl.enabled': {
|
||||
'type': 'boolean',
|
||||
'default': product.quality !== 'stable',
|
||||
'description': localize('layoutControlEnabled', "Controls whether the layout control button in the custom title bar is enabled."),
|
||||
},
|
||||
'workbench.experimental.sidePanel.enabled': {
|
||||
'type': 'boolean',
|
||||
'default': false,
|
||||
|
|
|
@ -5,20 +5,20 @@
|
|||
|
||||
import { localize } from 'vs/nls';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { EditorAction, ServicesAccessor, registerEditorAction } from 'vs/editor/browser/editorExtensions';
|
||||
import { ServicesAccessor } from 'vs/editor/browser/editorExtensions';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { CATEGORIES } from 'vs/workbench/common/actions';
|
||||
import { Action2, registerAction2 } from 'vs/platform/actions/common/actions';
|
||||
|
||||
class InspectKeyMap extends EditorAction {
|
||||
class InspectKeyMap extends Action2 {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.inspectKeyMappings',
|
||||
label: localize('workbench.action.inspectKeyMap', "Developer: Inspect Key Mappings"),
|
||||
alias: 'Developer: Inspect Key Mappings',
|
||||
precondition: undefined
|
||||
title: { value: localize('workbench.action.inspectKeyMap', "Inspect Key Mappings"), original: 'Inspect Key Mappings' },
|
||||
category: CATEGORIES.Developer,
|
||||
f1: true
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ class InspectKeyMap extends EditorAction {
|
|||
}
|
||||
}
|
||||
|
||||
registerEditorAction(InspectKeyMap);
|
||||
registerAction2(InspectKeyMap);
|
||||
|
||||
class InspectKeyMapJSON extends Action2 {
|
||||
|
||||
|
|
|
@ -727,7 +727,7 @@ registerThemingParticipant((theme, collector) => {
|
|||
if (debugIconBreakpointCurrentStackframeForegroundColor) {
|
||||
collector.addRule(`
|
||||
.monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugStackframe)},
|
||||
.monaco-editor .debug-top-stack-frame-column::before {
|
||||
.monaco-editor .debug-top-stack-frame-column {
|
||||
color: ${debugIconBreakpointCurrentStackframeForegroundColor} !important;
|
||||
}
|
||||
`);
|
||||
|
|
|
@ -17,7 +17,6 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
|||
import { distinct } from 'vs/base/common/arrays';
|
||||
import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity';
|
||||
import { debugStackframe, debugStackframeFocused } from 'vs/workbench/contrib/debug/browser/debugIcons';
|
||||
import { noBreakWhitespace } from 'vs/base/common/strings';
|
||||
|
||||
export const topStackFrameColor = registerColor('editor.stackFrameHighlightBackground', { dark: '#ffff0033', light: '#ffff6673', hc: '#ffff0033' }, localize('topStackFrameLineHighlight', 'Background color for the highlight of line at the top stack frame position.'));
|
||||
export const focusedStackFrameColor = registerColor('editor.focusedStackFrameHighlightBackground', { dark: '#7abd7a4d', light: '#cee7ce73', hc: '#7abd7a4d' }, localize('focusedStackFrameLineHighlight', 'Background color for the highlight of line at focused stack frame position.'));
|
||||
|
@ -82,7 +81,7 @@ export function createDecorationsForStackFrame(stackFrame: IStackFrame, isFocuse
|
|||
options: {
|
||||
description: 'top-stack-frame-inline-decoration',
|
||||
before: {
|
||||
content: noBreakWhitespace,
|
||||
content: '\uEB8B',
|
||||
inlineClassName: noCharactersBefore ? 'debug-top-stack-frame-column start-of-line' : 'debug-top-stack-frame-column',
|
||||
inlineClassNameAffectsLetterSpacing: true
|
||||
},
|
||||
|
|
|
@ -574,13 +574,16 @@ class Launch extends AbstractLaunch implements ILaunch {
|
|||
} catch {
|
||||
// launch.json not found: create one by collecting launch configs from debugConfigProviders
|
||||
content = await this.getInitialConfigurationContent(this.workspace.uri, type, token);
|
||||
if (content) {
|
||||
created = true; // pin only if config file is created #8727
|
||||
try {
|
||||
await this.textFileService.write(resource, content);
|
||||
} catch (error) {
|
||||
throw new Error(nls.localize('DebugConfig.failed', "Unable to create 'launch.json' file inside the '.vscode' folder ({0}).", error.message));
|
||||
}
|
||||
if (!content) {
|
||||
// Cancelled
|
||||
return { editor: null, created: false };
|
||||
}
|
||||
|
||||
created = true; // pin only if config file is created #8727
|
||||
try {
|
||||
await this.textFileService.write(resource, content);
|
||||
} catch (error) {
|
||||
throw new Error(nls.localize('DebugConfig.failed', "Unable to create 'launch.json' file inside the '.vscode' folder ({0}).", error.message));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,12 +20,6 @@
|
|||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.codicon-debug-breakpoint.codicon-debug-stackframe-focused::after,
|
||||
.codicon-debug-breakpoint.codicon-debug-stackframe::after {
|
||||
content: '\eb8a';
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.monaco-editor .inline-breakpoint-widget.line-start {
|
||||
left: -8px !important;
|
||||
}
|
||||
|
@ -38,10 +32,17 @@
|
|||
}
|
||||
|
||||
.monaco-editor .debug-top-stack-frame-column {
|
||||
font: normal normal normal 16px/1 codicon;
|
||||
text-rendering: auto;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
margin-left: 0;
|
||||
margin-right: 4px;
|
||||
margin-top: -1px; /* TODO @misolori: figure out a way to not use negative margin for alignment */
|
||||
align-items: center;
|
||||
width: 0.9em;
|
||||
display: inline-flex;
|
||||
vertical-align: middle;
|
||||
margin-top: -1px; /* TODO @misolori: figure out a way to not use negative margin for alignment */
|
||||
}
|
||||
|
||||
/* Do not push text with inline decoration when decoration on start of line */
|
||||
|
@ -49,21 +50,7 @@
|
|||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translate(-17px, -50%);
|
||||
}
|
||||
|
||||
.monaco-editor .debug-top-stack-frame-column {
|
||||
display: inline-flex;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.monaco-editor .debug-top-stack-frame-column::before {
|
||||
content: '\eb8b';
|
||||
font: normal normal normal 16px/1 codicon;
|
||||
text-rendering: auto;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
margin-left: 0;
|
||||
margin-right: 4px;
|
||||
margin-top: 0px; /* TODO @misolori: figure out a way to not use negative margin for alignment */
|
||||
}
|
||||
|
||||
.monaco-editor .inline-breakpoint-widget {
|
||||
|
|
|
@ -227,12 +227,13 @@ export class ExperimentService extends Disposable implements IExperimentService
|
|||
}
|
||||
|
||||
protected async getExperiments(): Promise<IRawExperiment[] | null> {
|
||||
if (!this.productService.experimentsUrl || this.configurationService.getValue('workbench.enableExperiments') === false) {
|
||||
const experimentsUrl = this.configurationService.getValue<string>('_workbench.experimentsUrl') || this.productService.experimentsUrl;
|
||||
if (!experimentsUrl || this.configurationService.getValue('workbench.enableExperiments') === false) {
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
const context = await this.requestService.request({ type: 'GET', url: this.productService.experimentsUrl }, CancellationToken.None);
|
||||
const context = await this.requestService.request({ type: 'GET', url: experimentsUrl }, CancellationToken.None);
|
||||
if (context.res.statusCode !== 200) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -396,6 +396,12 @@
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
.extension-editor > .body > .content > .details > .additional-details-container .resources-container > .resources > .resource {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.extension-editor > .body > .content > .details > .additional-details-container .resources-container > .resources > .resource:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
|
|
@ -19,8 +19,6 @@ interface OpenersMemento {
|
|||
[id: string]: RegisteredExternalOpener;
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
export class ContributedExternalUriOpenersStore extends Disposable {
|
||||
|
||||
private static readonly STORAGE_ID = 'externalUriOpeners';
|
||||
|
@ -37,8 +35,8 @@ export class ContributedExternalUriOpenersStore extends Disposable {
|
|||
|
||||
this._memento = new Memento(ContributedExternalUriOpenersStore.STORAGE_ID, storageService);
|
||||
this._mementoObject = this._memento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE);
|
||||
for (const id of Object.keys(this._mementoObject || {})) {
|
||||
this.add(id, this._mementoObject[id].extensionId, { isCurrentlyRegistered: false });
|
||||
for (const [id, value] of Object.entries(this._mementoObject || {})) {
|
||||
this.add(id, value.extensionId, { isCurrentlyRegistered: false });
|
||||
}
|
||||
|
||||
this.invalidateOpenersOnExtensionsChanged();
|
||||
|
|
|
@ -258,7 +258,12 @@ export class InteractiveEditor extends EditorPane {
|
|||
cellExecuteToolbar: MenuId.InteractiveCellExecute,
|
||||
cellExecutePrimary: undefined
|
||||
},
|
||||
cellEditorContributions: [],
|
||||
cellEditorContributions: EditorExtensionsRegistry.getSomeEditorContributions([
|
||||
SelectionClipboardContributionID,
|
||||
ContextMenuController.ID,
|
||||
ModesHoverController.ID,
|
||||
MarkerController.ID
|
||||
]),
|
||||
options: this.#notebookOptions
|
||||
});
|
||||
|
||||
|
@ -270,6 +275,9 @@ export class InteractiveEditor extends EditorPane {
|
|||
top: INPUT_EDITOR_PADDING,
|
||||
bottom: INPUT_EDITOR_PADDING
|
||||
},
|
||||
hover: {
|
||||
enabled: true
|
||||
}
|
||||
}
|
||||
}, {
|
||||
...{
|
||||
|
|
|
@ -446,7 +446,7 @@ registerAction2(class ExecuteCellInsertBelow extends NotebookCellAction {
|
|||
constructor() {
|
||||
super({
|
||||
id: EXECUTE_CELL_INSERT_BELOW,
|
||||
precondition: executeThisCellCondition,
|
||||
precondition: ContextKeyExpr.or(executeThisCellCondition, NOTEBOOK_CELL_TYPE.isEqualTo('markup')),
|
||||
title: localize('notebookActions.executeAndInsertBelow', "Execute Notebook Cell and Insert Below"),
|
||||
keybinding: {
|
||||
when: NOTEBOOK_CELL_LIST_FOCUSED,
|
||||
|
@ -460,14 +460,17 @@ registerAction2(class ExecuteCellInsertBelow extends NotebookCellAction {
|
|||
const idx = context.notebookEditor.getCellIndex(context.cell);
|
||||
const modeService = accessor.get(IModeService);
|
||||
const newFocusMode = context.cell.focusMode === CellFocusMode.Editor ? 'editor' : 'container';
|
||||
const executionP = runCell(accessor, context);
|
||||
const newCell = insertCell(modeService, context.notebookEditor, idx, CellKind.Code, 'below');
|
||||
|
||||
const newCell = insertCell(modeService, context.notebookEditor, idx, context.cell.cellKind, 'below');
|
||||
if (newCell) {
|
||||
context.notebookEditor.focusNotebookCell(newCell, newFocusMode);
|
||||
}
|
||||
|
||||
return executionP;
|
||||
if (context.cell.cellKind === CellKind.Markup) {
|
||||
context.cell.updateEditState(CellEditState.Preview, EXECUTE_CELL_INSERT_BELOW);
|
||||
} else {
|
||||
runCell(accessor, context);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue