提交 44e28513 编写于 作者: J Johannes Rieken

Merge branch 'master' into joh/extbisect

{
"name": "code-oss-dev",
"version": "1.51.0",
"version": "1.52.0",
"distro": "d32b7eb5181a656f005556e20c54bc5fd73fd080",
"author": {
"name": "Microsoft Corporation"
......@@ -67,7 +67,7 @@
"vscode-nsfw": "1.2.9",
"vscode-oniguruma": "1.3.1",
"vscode-proxy-agent": "^0.5.2",
"vscode-ripgrep": "^1.9.0",
"vscode-ripgrep": "1.10.0",
"vscode-sqlite3": "4.0.10",
"vscode-textmate": "5.2.0",
"xterm": "4.10.0-beta.4",
......
......@@ -18,7 +18,7 @@
"vscode-nsfw": "1.2.9",
"vscode-oniguruma": "1.3.1",
"vscode-proxy-agent": "^0.5.2",
"vscode-ripgrep": "^1.9.0",
"vscode-ripgrep": "1.10.0",
"vscode-textmate": "5.2.0",
"xterm": "4.10.0-beta.4",
"xterm-addon-search": "0.8.0-beta.3",
......
......@@ -410,10 +410,10 @@ vscode-proxy-agent@^0.5.2:
https-proxy-agent "^2.2.3"
socks-proxy-agent "^4.0.1"
vscode-ripgrep@^1.9.0:
version "1.9.0"
resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.9.0.tgz#d6cdea4d290f3c2919472cdcfe2440d5fb1f99db"
integrity sha512-7jyAC/NNfvMPZgCVkyqIn0STYJ7wIk3PF2qA2cX1sEutx1g/e2VtgKAodXnfpreJq4993JT/BSIigOv/0lBSzg==
vscode-ripgrep@1.10.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.10.0.tgz#fc725f5eae22343ed47d32c615c0536cfecfde2b"
integrity sha512-lHM0MfFo7Fln7qHNpnCQzWhRsDPWmu6o6eO0xW9MD0qjUUiNYGLZ6zdHbIhNxj2I1bVA7bXIgHKcnrEPRn5Rnw==
dependencies:
https-proxy-agent "^4.0.0"
proxy-from-env "^1.1.0"
......
......@@ -752,7 +752,7 @@ export class DiffReview extends Disposable {
switch (type) {
case DiffEntryType.Equal:
if (originalLine === modifiedLine) {
ariaLabel = nls.localize({ key: 'unchangedLine', comment: ['The placholders are contents of the line and should not be translated.'] }, "{0} unchanged line {1}", lineContent, originalLine);
ariaLabel = nls.localize({ key: 'unchangedLine', comment: ['The placeholders are contents of the line and should not be translated.'] }, "{0} unchanged line {1}", lineContent, originalLine);
} else {
ariaLabel = nls.localize('equalLine', "{0} original line {1} modified line {2}", lineContent, originalLine, modifiedLine);
}
......
......@@ -3026,6 +3026,10 @@ export interface ISuggestOptions {
* Enable or disable the suggest status bar.
*/
showStatusBar?: boolean;
/**
* Show details inline with the label. Defaults to true.
*/
showStatusDetailsInline?: boolean;
/**
* Show method-suggestions.
*/
......@@ -3149,6 +3153,7 @@ class EditorSuggest extends BaseEditorOption<EditorOption.suggest, InternalSugge
shareSuggestSelections: false,
showIcons: true,
showStatusBar: false,
showStatusDetailsInline: true,
showMethods: true,
showFunctions: true,
showConstructors: true,
......@@ -3220,6 +3225,12 @@ class EditorSuggest extends BaseEditorOption<EditorOption.suggest, InternalSugge
default: defaults.showStatusBar,
description: nls.localize('suggest.showStatusBar', "Controls the visibility of the status bar at the bottom of the suggest widget.")
},
'editor.suggest.showStatusDetailsInline': {
type: 'boolean',
default: defaults.showStatusDetailsInline,
description: nls.localize('suggest.showStatusDetailsInline', "Controls whether sugget details show inline with the label or only in the details widget")
},
'editor.suggest.maxVisibleSuggestions': {
type: 'number',
deprecationMessage: nls.localize('suggest.maxVisibleSuggestions.dep', "This setting is deprecated. The suggest widget can now be resized."),
......@@ -3385,6 +3396,7 @@ class EditorSuggest extends BaseEditorOption<EditorOption.suggest, InternalSugge
shareSuggestSelections: EditorBooleanOption.boolean(input.shareSuggestSelections, this.defaultValue.shareSuggestSelections),
showIcons: EditorBooleanOption.boolean(input.showIcons, this.defaultValue.showIcons),
showStatusBar: EditorBooleanOption.boolean(input.showStatusBar, this.defaultValue.showStatusBar),
showStatusDetailsInline: EditorBooleanOption.boolean(input.showStatusDetailsInline, this.defaultValue.showStatusDetailsInline),
showMethods: EditorBooleanOption.boolean(input.showMethods, this.defaultValue.showMethods),
showFunctions: EditorBooleanOption.boolean(input.showFunctions, this.defaultValue.showFunctions),
showConstructors: EditorBooleanOption.boolean(input.showConstructors, this.defaultValue.showConstructors),
......
......@@ -30,15 +30,14 @@ import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common
const SEARCH_STRING_MAX_LENGTH = 524288;
export function getSelectionSearchString(editor: ICodeEditor, seedSearchStringFromSelection: 'single' | 'multiple' = 'single'): string | null {
export function getSelectionSearchString(editor: ICodeEditor): string | null {
if (!editor.hasModel()) {
return null;
}
const selection = editor.getSelection();
// if selection spans multiple lines, default search string to empty
if (seedSearchStringFromSelection === 'single' && selection.startLineNumber === selection.endLineNumber) {
if (selection.startLineNumber === selection.endLineNumber) {
if (selection.isEmpty()) {
const wordAtPosition = editor.getConfiguredWordAtPosition(selection.getStartPosition());
if (wordAtPosition) {
......@@ -49,10 +48,6 @@ export function getSelectionSearchString(editor: ICodeEditor, seedSearchStringFr
return editor.getModel().getValueInRange(selection);
}
}
} else if (seedSearchStringFromSelection === 'multiple') {
if (editor.getModel().getValueLengthInRange(selection) < SEARCH_STRING_MAX_LENGTH) {
return editor.getModel().getValueInRange(selection);
}
}
return null;
......@@ -66,7 +61,7 @@ export const enum FindStartFocusAction {
export interface IFindStartOptions {
forceRevealReplace: boolean;
seedSearchStringFromSelection: 'none' | 'single' | 'multiple';
seedSearchStringFromSelection: boolean;
seedSearchStringFromGlobalClipboard: boolean;
shouldFocus: FindStartFocusAction;
shouldAnimate: boolean;
......@@ -127,7 +122,7 @@ export class CommonFindController extends Disposable implements IEditorContribut
if (shouldRestartFind) {
this._start({
forceRevealReplace: false,
seedSearchStringFromSelection: 'none',
seedSearchStringFromSelection: false && this._editor.getOption(EditorOption.find).seedSearchStringFromSelection,
seedSearchStringFromGlobalClipboard: false,
shouldFocus: FindStartFocusAction.NoFocusChange,
shouldAnimate: false,
......@@ -283,8 +278,8 @@ export class CommonFindController extends Disposable implements IEditorContribut
isRevealed: true
};
if (opts.seedSearchStringFromSelection === 'single') {
let selectionSearchString = getSelectionSearchString(this._editor, opts.seedSearchStringFromSelection);
if (opts.seedSearchStringFromSelection) {
let selectionSearchString = getSelectionSearchString(this._editor);
if (selectionSearchString) {
if (this._state.isRegex) {
stateChanges.searchString = strings.escapeRegExpCharacters(selectionSearchString);
......@@ -292,11 +287,6 @@ export class CommonFindController extends Disposable implements IEditorContribut
stateChanges.searchString = selectionSearchString;
}
}
} else if (opts.seedSearchStringFromSelection === 'multiple' && !opts.updateSearchScope) {
let selectionSearchString = getSelectionSearchString(this._editor, opts.seedSearchStringFromSelection);
if (selectionSearchString) {
stateChanges.searchString = selectionSearchString;
}
}
if (!stateChanges.searchString && opts.seedSearchStringFromGlobalClipboard) {
......@@ -503,7 +493,7 @@ export class StartFindAction extends MultiEditorAction {
if (controller) {
await controller.start({
forceRevealReplace: false,
seedSearchStringFromSelection: editor.getOption(EditorOption.find).seedSearchStringFromSelection ? 'single' : 'none',
seedSearchStringFromSelection: editor.getOption(EditorOption.find).seedSearchStringFromSelection,
seedSearchStringFromGlobalClipboard: editor.getOption(EditorOption.find).globalFindClipboard,
shouldFocus: FindStartFocusAction.FocusFindInput,
shouldAnimate: true,
......@@ -538,7 +528,7 @@ export class StartFindWithSelectionAction extends EditorAction {
if (controller) {
await controller.start({
forceRevealReplace: false,
seedSearchStringFromSelection: 'multiple',
seedSearchStringFromSelection: true,
seedSearchStringFromGlobalClipboard: false,
shouldFocus: FindStartFocusAction.NoFocusChange,
shouldAnimate: true,
......@@ -556,7 +546,7 @@ export abstract class MatchFindAction extends EditorAction {
if (controller && !this._run(controller)) {
await controller.start({
forceRevealReplace: false,
seedSearchStringFromSelection: (controller.getState().searchString.length === 0) && editor.getOption(EditorOption.find).seedSearchStringFromSelection ? 'single' : 'multiple',
seedSearchStringFromSelection: (controller.getState().searchString.length === 0) && editor.getOption(EditorOption.find).seedSearchStringFromSelection,
seedSearchStringFromGlobalClipboard: true,
shouldFocus: FindStartFocusAction.NoFocusChange,
shouldAnimate: true,
......@@ -669,7 +659,7 @@ export abstract class SelectionMatchFindAction extends EditorAction {
if (!this._run(controller)) {
await controller.start({
forceRevealReplace: false,
seedSearchStringFromSelection: editor.getOption(EditorOption.find).seedSearchStringFromSelection ? 'single' : 'none',
seedSearchStringFromSelection: editor.getOption(EditorOption.find).seedSearchStringFromSelection,
seedSearchStringFromGlobalClipboard: false,
shouldFocus: FindStartFocusAction.NoFocusChange,
shouldAnimate: true,
......@@ -775,7 +765,7 @@ export class StartFindReplaceAction extends MultiEditorAction {
if (controller) {
await controller.start({
forceRevealReplace: true,
seedSearchStringFromSelection: seedSearchStringFromSelection ? 'single' : 'none',
seedSearchStringFromSelection: seedSearchStringFromSelection,
seedSearchStringFromGlobalClipboard: editor.getOption(EditorOption.find).seedSearchStringFromSelection,
shouldFocus: shouldFocus,
shouldAnimate: true,
......
......@@ -272,7 +272,7 @@ suite('FindController', async () => {
findController.setSearchString(testRegexString);
await findController.start({
forceRevealReplace: false,
seedSearchStringFromSelection: 'none',
seedSearchStringFromSelection: false,
seedSearchStringFromGlobalClipboard: false,
shouldFocus: FindStartFocusAction.FocusFindInput,
shouldAnimate: false,
......@@ -298,7 +298,7 @@ suite('FindController', async () => {
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
await findController.start({
forceRevealReplace: false,
seedSearchStringFromSelection: 'none',
seedSearchStringFromSelection: false,
seedSearchStringFromGlobalClipboard: false,
shouldFocus: FindStartFocusAction.NoFocusChange,
shouldAnimate: false,
......@@ -524,9 +524,9 @@ suite('FindController query options persistence', async () => {
], { serviceCollection: serviceCollection, find: { autoFindInSelection: 'always', globalFindClipboard: false } }, async (editor) => {
// clipboardState = '';
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
const findConfig: IFindStartOptions = {
const findConfig = {
forceRevealReplace: false,
seedSearchStringFromSelection: 'none',
seedSearchStringFromSelection: false,
seedSearchStringFromGlobalClipboard: false,
shouldFocus: FindStartFocusAction.NoFocusChange,
shouldAnimate: false,
......@@ -558,7 +558,7 @@ suite('FindController query options persistence', async () => {
await findController.start({
forceRevealReplace: false,
seedSearchStringFromSelection: 'none',
seedSearchStringFromSelection: false,
seedSearchStringFromGlobalClipboard: false,
shouldFocus: FindStartFocusAction.NoFocusChange,
shouldAnimate: false,
......@@ -582,7 +582,7 @@ suite('FindController query options persistence', async () => {
await findController.start({
forceRevealReplace: false,
seedSearchStringFromSelection: 'none',
seedSearchStringFromSelection: false,
seedSearchStringFromGlobalClipboard: false,
shouldFocus: FindStartFocusAction.NoFocusChange,
shouldAnimate: false,
......@@ -607,7 +607,7 @@ suite('FindController query options persistence', async () => {
await findController.start({
forceRevealReplace: false,
seedSearchStringFromSelection: 'none',
seedSearchStringFromSelection: false,
seedSearchStringFromGlobalClipboard: false,
shouldFocus: FindStartFocusAction.NoFocusChange,
shouldAnimate: false,
......
......@@ -214,6 +214,12 @@ export class ItemRenderer implements IListRenderer<CompletionItem, ISuggestionTe
data.root.title = `${textLabel}${completion.label.parameters ?? ''} ${completion.label.qualifier ?? ''} ${completion.label.type ?? ''}`;
}
if (this._editor.getOption(EditorOption.suggest).showStatusDetailsInline) {
show(data.detailsLabel);
} else {
hide(data.detailsLabel);
}
if (canExpandCompletionItem(element)) {
data.right.classList.add('can-expand-details');
show(data.readMore);
......
......@@ -3705,6 +3705,10 @@ declare namespace monaco.editor {
* Enable or disable the suggest status bar.
*/
showStatusBar?: boolean;
/**
* Show details inline with the label. Defaults to true.
*/
showStatusDetailsInline?: boolean;
/**
* Show method-suggestions.
*/
......
......@@ -227,9 +227,9 @@ class ConfigurationRegistry implements IConfigurationRegistry {
for (const defaultConfiguration of defaultConfigurations) {
for (const key in defaultConfiguration) {
properties.push(key);
this.defaultValues[key] = defaultConfiguration[key];
if (OVERRIDE_PROPERTY_PATTERN.test(key)) {
this.defaultValues[key] = { ...(this.defaultValues[key] || {}), ...defaultConfiguration[key] };
const property: IConfigurationPropertySchema = {
type: 'object',
default: this.defaultValues[key],
......@@ -240,6 +240,7 @@ class ConfigurationRegistry implements IConfigurationRegistry {
this.configurationProperties[key] = property;
this.defaultLanguageConfigurationOverridesNode.properties![key] = property;
} else {
this.defaultValues[key] = defaultConfiguration[key];
const property = this.configurationProperties[key];
if (property) {
this.updatePropertyDefaultValue(key, property);
......
/*---------------------------------------------------------------------------------------------
* 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 { Registry } from 'vs/platform/registry/common/platform';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
suite('ConfigurationRegistry', () => {
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
test('configuration override', async () => {
configurationRegistry.registerConfiguration({
'id': '_test_default',
'type': 'object',
'properties': {
'config': {
'type': 'object',
}
}
});
configurationRegistry.registerDefaultConfigurations([{ 'config': { a: 1, b: 2 } }]);
configurationRegistry.registerDefaultConfigurations([{ '[lang]': { a: 2, c: 3 } }]);
assert.deepEqual(configurationRegistry.getConfigurationProperties()['config'].default, { a: 1, b: 2 });
assert.deepEqual(configurationRegistry.getConfigurationProperties()['[lang]'].default, { a: 2, c: 3 });
});
test('configuration override defaults - merges defaults', async () => {
configurationRegistry.registerDefaultConfigurations([{ '[lang]': { a: 1, b: 2 } }]);
configurationRegistry.registerDefaultConfigurations([{ '[lang]': { a: 2, c: 3 } }]);
assert.deepEqual(configurationRegistry.getConfigurationProperties()['[lang]'].default, { a: 2, b: 2, c: 3 });
});
test('configuration defaults - overrides defaults', async () => {
configurationRegistry.registerConfiguration({
'id': '_test_default',
'type': 'object',
'properties': {
'config': {
'type': 'object',
}
}
});
configurationRegistry.registerDefaultConfigurations([{ 'config': { a: 1, b: 2 } }]);
configurationRegistry.registerDefaultConfigurations([{ 'config': { a: 2, c: 3 } }]);
assert.deepEqual(configurationRegistry.getConfigurationProperties()['config'].default, { a: 2, c: 3 });
});
});
......@@ -10,7 +10,6 @@ import { ITelemetryAppender, validateTelemetryData } from 'vs/platform/telemetry
export class TelemetryLogAppender extends Disposable implements ITelemetryAppender {
private commonPropertiesRegex = /^sessionID$|^version$|^timestamp$|^commitHash$|^common\./;
private readonly logger: ILogger;
constructor(
......@@ -28,14 +27,7 @@ export class TelemetryLogAppender extends Disposable implements ITelemetryAppend
}
log(eventName: string, data: any): void {
let strippedData: { [key: string]: any } = {};
Object.keys(data).forEach(key => {
if (!this.commonPropertiesRegex.test(key)) {
strippedData[key] = data[key];
}
});
strippedData = validateTelemetryData(strippedData);
this.logger.trace(`telemetry/${eventName}`, strippedData);
this.logger.trace(`telemetry/${eventName}`, validateTelemetryData(data));
}
}
......@@ -96,7 +96,7 @@ import { isStandalone } from 'vs/base/browser/browser';
nls.localize('workbench.editor.pinnedTabSizing.compact', "A pinned tab will show in a compact form with only icon or first letter of the editor name."),
nls.localize('workbench.editor.pinnedTabSizing.shrink', "A pinned tab shrinks to a compact fixed size showing parts of the editor name.")
],
'markdownDescription': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'pinnedTabSizing' }, "Controls the sizing of pinned editor tabs. Pinned tabs are sorted to the begining of all opened tabs and typically do not close until unpinned. This value is ignored when `#workbench.editor.showTabs#` is `false`.")
'markdownDescription': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'pinnedTabSizing' }, "Controls the sizing of pinned editor tabs. Pinned tabs are sorted to the beginning of all opened tabs and typically do not close until unpinned. This value is ignored when `#workbench.editor.showTabs#` is `false`.")
},
'workbench.editor.splitSizing': {
'type': 'string',
......@@ -412,7 +412,7 @@ import { isStandalone } from 'vs/base/browser/browser';
nls.localize('window.confirmBeforeClose.never', "Never explicitly ask for confirmation unless data loss is imminent.")
],
'default': isWeb && !isStandalone ? 'keyboardOnly' : 'never', // on by default in web, unless PWA
'description': nls.localize('confirmBeforeCloseWeb', "Controls wether to show a confirmation dialog before closing the browser tab or window. Note that even if enabled, browsers may still decide to close a tab or window without confirmation and that this setting is only a hint that may not work in all cases."),
'description': nls.localize('confirmBeforeCloseWeb', "Controls whether to show a confirmation dialog before closing the browser tab or window. Note that even if enabled, browsers may still decide to close a tab or window without confirmation and that this setting is only a hint that may not work in all cases."),
'scope': ConfigurationScope.APPLICATION,
'included': isWeb
}
......
......@@ -348,7 +348,7 @@ class DebugHoverAccessibilityProvider implements IListAccessibilityProvider<IExp
}
getAriaLabel(element: IExpression): string {
return nls.localize({ key: 'variableAriaLabel', comment: ['Do not translate placholders. Placeholders are name and value of a variable.'] }, "{0}, value {1}, variables, debug", element.name, element.value);
return nls.localize({ key: 'variableAriaLabel', comment: ['Do not translate placeholders. Placeholders are name and value of a variable.'] }, "{0}, value {1}, variables, debug", element.name, element.value);
}
}
......
......@@ -1868,7 +1868,7 @@ export const CELL_TOOLBAR_SEPERATOR = registerColor('notebook.cellToolbarSeparat
dark: Color.fromHex('#808080').transparent(0.35),
light: Color.fromHex('#808080').transparent(0.35),
hc: contrastBorder
}, nls.localize('notebook.cellToolbarSeparator', "The color of the seperator in the cell bottom toolbar"));
}, nls.localize('notebook.cellToolbarSeparator', "The color of the separator in the cell bottom toolbar"));
export const focusedCellBackground = registerColor('notebook.focusedCellBackground', {
dark: transparent(PANEL_BORDER, .4),
......
......@@ -26,7 +26,7 @@ const schema: IJSONSchema = {
description: nls.localize('JsonSchema.version', 'The config\'s version number')
},
_runner: {
deprecationMessage: nls.localize('JsonSchema._runner', 'The runner has graduated. Use the offical runner property')
deprecationMessage: nls.localize('JsonSchema._runner', 'The runner has graduated. Use the official runner property')
},
runner: {
type: 'string',
......
......@@ -219,7 +219,7 @@ export class NativeWindow extends Disposable {
],
{
cancelId: 1,
detail: nls.localize('proxyDetail', "The proxy {0} requires a userame and password.", `${payload.authInfo.host}:${payload.authInfo.port}`),
detail: nls.localize('proxyDetail', "The proxy {0} requires a username and password.", `${payload.authInfo.host}:${payload.authInfo.port}`),
checkbox: {
label: nls.localize('rememberCredentials', "Remember my credentials"),
checked: rememberCredentials
......
......@@ -221,6 +221,8 @@ export class SimpleFileDialog {
return defaultUri.scheme;
}
return available[0];
} else if (defaultUri) {
return defaultUri.scheme;
}
return Schemas.file;
}
......
......@@ -697,7 +697,7 @@ async function _loadColorTheme(extensionResourceLoaderService: IExtensionResourc
result.semanticTokenRules.push(rule);
}
} catch (e) {
return Promise.reject(new Error(nls.localize({ key: 'error.invalidformat.semanticTokenColors', comment: ['{0} will be replaced by a path. Values in quotes should not be translated.'] }, "Problem parsing color theme file: {0}. Property 'semanticTokenColors' conatains a invalid selector", themeLocation.toString())));
return Promise.reject(new Error(nls.localize({ key: 'error.invalidformat.semanticTokenColors', comment: ['{0} will be replaced by a path. Values in quotes should not be translated.'] }, "Problem parsing color theme file: {0}. Property 'semanticTokenColors' contains a invalid selector", themeLocation.toString())));
}
}
}
......
......@@ -9735,10 +9735,10 @@ vscode-proxy-agent@^0.5.2:
https-proxy-agent "^2.2.3"
socks-proxy-agent "^4.0.1"
vscode-ripgrep@^1.9.0:
version "1.9.0"
resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.9.0.tgz#d6cdea4d290f3c2919472cdcfe2440d5fb1f99db"
integrity sha512-7jyAC/NNfvMPZgCVkyqIn0STYJ7wIk3PF2qA2cX1sEutx1g/e2VtgKAodXnfpreJq4993JT/BSIigOv/0lBSzg==
vscode-ripgrep@1.10.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.10.0.tgz#fc725f5eae22343ed47d32c615c0536cfecfde2b"
integrity sha512-lHM0MfFo7Fln7qHNpnCQzWhRsDPWmu6o6eO0xW9MD0qjUUiNYGLZ6zdHbIhNxj2I1bVA7bXIgHKcnrEPRn5Rnw==
dependencies:
https-proxy-agent "^4.0.0"
proxy-from-env "^1.1.0"
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册