Merge branch 'master' of github.com:microsoft/vscode

This commit is contained in:
Jackson Kearl 2019-12-02 14:41:51 -08:00
commit e95b3adcf0
20 changed files with 521 additions and 248 deletions

View file

@ -8,7 +8,7 @@ This project incorporates components from the projects listed below. The origina
1. atom/language-clojure version 0.22.7 (https://github.com/atom/language-clojure)
2. atom/language-coffee-script version 0.49.3 (https://github.com/atom/language-coffee-script)
3. atom/language-java version 0.31.3 (https://github.com/atom/language-java)
4. atom/language-sass version 0.61.4 (https://github.com/atom/language-sass)
4. atom/language-sass version 0.62.1 (https://github.com/atom/language-sass)
5. atom/language-shellscript version 0.26.0 (https://github.com/atom/language-shellscript)
6. atom/language-xml version 0.35.2 (https://github.com/atom/language-xml)
7. Colorsublime-Themes version 0.1.0 (https://github.com/Colorsublime/Colorsublime-Themes)
@ -27,13 +27,13 @@ This project incorporates components from the projects listed below. The origina
20. Ionic documentation version 1.2.4 (https://github.com/ionic-team/ionic-site)
21. ionide/ionide-fsgrammar (https://github.com/ionide/ionide-fsgrammar)
22. jeff-hykin/cpp-textmate-grammar version 1.12.11 (https://github.com/jeff-hykin/cpp-textmate-grammar)
23. jeff-hykin/cpp-textmate-grammar version 1.14.9 (https://github.com/jeff-hykin/cpp-textmate-grammar)
23. jeff-hykin/cpp-textmate-grammar version 1.14.13 (https://github.com/jeff-hykin/cpp-textmate-grammar)
24. js-beautify version 1.6.8 (https://github.com/beautify-web/js-beautify)
25. Jxck/assert version 1.0.0 (https://github.com/Jxck/assert)
26. language-docker (https://github.com/moby/moby)
27. language-go version 0.44.3 (https://github.com/atom/language-go)
28. language-less version 0.34.2 (https://github.com/atom/language-less)
29. language-php version 0.44.2 (https://github.com/atom/language-php)
29. language-php version 0.44.3 (https://github.com/atom/language-php)
30. language-rust version 0.4.12 (https://github.com/zargony/atom-language-rust)
31. MagicStack/MagicPython version 1.1.1 (https://github.com/MagicStack/MagicPython)
32. marked version 0.6.2 (https://github.com/markedjs/marked)
@ -589,7 +589,7 @@ END OF HTML 5.1 W3C Working Draft NOTICES AND INFORMATION
=========================================
MIT License
Copyright (c) 2017 Yuki Ueda
Copyright (c) 2019 Yuki Ueda
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View file

@ -5,8 +5,8 @@
import * as fs from 'fs';
import * as path from 'path';
import { commands, CompletionItem, CompletionItemKind, ExtensionContext, languages, Position, Range, SnippetString, TextEdit, window, workspace } from 'vscode';
import { Disposable, LanguageClient, LanguageClientOptions, ServerOptions, TransportKind } from 'vscode-languageclient';
import { commands, CompletionItem, CompletionItemKind, ExtensionContext, languages, Position, Range, SnippetString, TextEdit, window, workspace, TextDocument, CompletionContext, CancellationToken, ProviderResult, CompletionList } from 'vscode';
import { Disposable, LanguageClient, LanguageClientOptions, ServerOptions, TransportKind, ProvideCompletionItemsSignature } from 'vscode-languageclient';
import * as nls from 'vscode-nls';
import { getCustomDataPathsFromAllExtensions, getCustomDataPathsInAllWorkspaces } from './customData';
@ -43,6 +43,31 @@ export function activate(context: ExtensionContext) {
},
initializationOptions: {
dataPaths
},
middleware: {
// testing the replace / insert mode
provideCompletionItem(document: TextDocument, position: Position, context: CompletionContext, token: CancellationToken, next: ProvideCompletionItemsSignature): ProviderResult<CompletionItem[] | CompletionList> {
function updateRanges(item: CompletionItem) {
const range = item.range;
if (range && range.end.isAfter(position) && range.start.isBeforeOrEqual(position)) {
item.range2 = { inserting: new Range(range.start, position), replacing: range };
item.range = undefined;
}
}
function updateProposals(r: CompletionItem[] | CompletionList | null | undefined): CompletionItem[] | CompletionList | null | undefined {
if (r) {
(Array.isArray(r) ? r : r.items).forEach(updateRanges);
}
return r;
}
const isThenable = <T>(obj: ProviderResult<T>): obj is Thenable<T> => obj && (<any>obj)['then'];
const r = next(document, position, context, token);
if (isThenable<CompletionItem[] | CompletionList | null | undefined>(r)) {
return r.then(updateProposals);
}
return updateProposals(r);
}
}
};

View file

@ -783,6 +783,17 @@
}
}
],
"configurationDefaults": {
"[css]": {
"editor.suggest.insertMode": "replace"
},
"[scss]": {
"editor.suggest.insertMode": "replace"
},
"[less]": {
"editor.suggest.insertMode": "replace"
}
},
"jsonValidation": [
{
"fileMatch": "*.css-data.json",

View file

@ -8,8 +8,8 @@ import * as fs from 'fs';
import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle();
import { languages, ExtensionContext, IndentAction, Position, TextDocument, Range, CompletionItem, CompletionItemKind, SnippetString, workspace, Disposable, FormattingOptions, CancellationToken, ProviderResult, TextEdit } from 'vscode';
import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind, RequestType, TextDocumentPositionParams, DocumentRangeFormattingParams, DocumentRangeFormattingRequest } from 'vscode-languageclient';
import { languages, ExtensionContext, IndentAction, Position, TextDocument, Range, CompletionItem, CompletionItemKind, SnippetString, workspace, Disposable, FormattingOptions, CancellationToken, ProviderResult, TextEdit, CompletionContext, CompletionList } from 'vscode';
import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind, RequestType, TextDocumentPositionParams, DocumentRangeFormattingParams, DocumentRangeFormattingRequest, ProvideCompletionItemsSignature } from 'vscode-languageclient';
import { EMPTY_ELEMENTS } from './htmlEmptyTagsShared';
import { activateTagClosing } from './tagClosing';
import TelemetryReporter from 'vscode-extension-telemetry';
@ -72,6 +72,31 @@ export function activate(context: ExtensionContext) {
dataPaths,
provideFormatter: false, // tell the server to not provide formatting capability and ignore the `html.format.enable` setting.
},
middleware: {
// testing the replace / insert mode
provideCompletionItem(document: TextDocument, position: Position, context: CompletionContext, token: CancellationToken, next: ProvideCompletionItemsSignature): ProviderResult<CompletionItem[] | CompletionList> {
function updateRanges(item: CompletionItem) {
const range = item.range;
if (range && range.end.isAfter(position) && range.start.isBeforeOrEqual(position)) {
item.range2 = { inserting: new Range(range.start, position), replacing: range };
item.range = undefined;
}
}
function updateProposals(r: CompletionItem[] | CompletionList | null | undefined): CompletionItem[] | CompletionList | null | undefined {
if (r) {
(Array.isArray(r) ? r : r.items).forEach(updateRanges);
}
return r;
}
const isThenable = <T>(obj: ProviderResult<T>): obj is Thenable<T> => obj && (<any>obj)['then'];
const r = next(document, position, context, token);
if (isThenable<CompletionItem[] | CompletionList | null | undefined>(r)) {
return r.then(updateProposals);
}
return updateProposals(r);
}
}
};
// Create the language client and start the client.

View file

@ -45,8 +45,8 @@ export function activateMatchingTagPosition(
isEnabled = true;
}
let prevCursorCount = 0;
let cursorCount = 0;
let prevCursors: readonly Selection[] = [];
let cursors: readonly Selection[] = [];
let inMirrorMode = false;
function onDidChangeTextEditorSelection(event: TextEditorSelectionChangeEvent) {
@ -54,67 +54,67 @@ export function activateMatchingTagPosition(
return;
}
prevCursorCount = cursorCount;
cursorCount = event.selections.length;
prevCursors = cursors;
cursors = event.selections;
if (cursorCount === 1) {
if (inMirrorMode && prevCursorCount === 2) {
return;
if (cursors.length === 1) {
if (inMirrorMode && prevCursors.length === 2) {
if (cursors[0].isEqual(prevCursors[0]) || cursors[0].isEqual(prevCursors[1])) {
return;
}
}
if (event.selections[0].isEmpty) {
matchingTagPositionProvider(event.textEditor.document, event.selections[0].active).then(position => {
if (position && window.activeTextEditor) {
inMirrorMode = true;
const newCursor = new Selection(position.line, position.character, position.line, position.character);
window.activeTextEditor.selections = [...window.activeTextEditor.selections, newCursor];
matchingTagPositionProvider(event.textEditor.document, event.selections[0].active).then(matchingTagPosition => {
if (matchingTagPosition && window.activeTextEditor) {
const charBeforeAndAfterPositionsRoughtlyEqual = isCharBeforeAndAfterPositionsRoughtlyEqual(
event.textEditor.document,
event.selections[0].anchor,
new Position(matchingTagPosition.line, matchingTagPosition.character)
);
if (charBeforeAndAfterPositionsRoughtlyEqual) {
inMirrorMode = true;
const newCursor = new Selection(
matchingTagPosition.line,
matchingTagPosition.character,
matchingTagPosition.line,
matchingTagPosition.character
);
window.activeTextEditor.selections = [...window.activeTextEditor.selections, newCursor];
}
}
});
}
}
if (cursorCount === 2 && inMirrorMode) {
if (cursors.length === 2 && inMirrorMode) {
// Check two cases
if (event.selections[0].isEmpty && event.selections[1].isEmpty) {
const charBeforePrimarySelection = getCharBefore(event.textEditor.document, event.selections[0].anchor);
const charAfterPrimarySelection = getCharAfter(event.textEditor.document, event.selections[0].anchor);
const charBeforeSecondarySelection = getCharBefore(event.textEditor.document, event.selections[1].anchor);
const charAfterSecondarySelection = getCharAfter(event.textEditor.document, event.selections[1].anchor);
const charBeforeAndAfterPositionsRoughtlyEqual = isCharBeforeAndAfterPositionsRoughtlyEqual(
event.textEditor.document,
event.selections[0].anchor,
event.selections[1].anchor
);
// Exit mirror mode when cursor position no longer mirror
// Unless it's in the case of `<|></|>`
const charBeforeBothPositionRoughlyEqual =
charBeforePrimarySelection === charBeforeSecondarySelection ||
(charBeforePrimarySelection === '/' && charBeforeSecondarySelection === '<') ||
(charBeforeSecondarySelection === '/' && charBeforePrimarySelection === '<');
const charAfterBothPositionRoughlyEqual =
charAfterPrimarySelection === charAfterSecondarySelection ||
(charAfterPrimarySelection === ' ' && charAfterSecondarySelection === '>') ||
(charAfterSecondarySelection === ' ' && charAfterPrimarySelection === '>');
if (!charBeforeBothPositionRoughlyEqual || !charAfterBothPositionRoughlyEqual) {
if (!charBeforeAndAfterPositionsRoughtlyEqual) {
inMirrorMode = false;
window.activeTextEditor!.selections = [window.activeTextEditor!.selections[0]];
return;
} else {
// Need to cleanup in the case of <div |></div |>
if (
charBeforePrimarySelection === ' ' &&
charAfterPrimarySelection === '>' &&
charBeforeSecondarySelection === ' ' &&
charAfterSecondarySelection === '>'
shouldDoCleanupForHtmlAttributeInput(
event.textEditor.document,
event.selections[0].anchor,
event.selections[1].anchor
)
) {
const primaryBeforeSecondary =
event.textEditor.document.offsetAt(event.selections[0].anchor) <
event.textEditor.document.offsetAt(event.selections[1].anchor);
if (primaryBeforeSecondary) {
inMirrorMode = false;
const cleanupEdit = new WorkspaceEdit();
const cleanupRange = new Range(event.selections[1].anchor.translate(0, -1), event.selections[1].anchor);
cleanupEdit.replace(event.textEditor.document.uri, cleanupRange, '');
window.activeTextEditor!.selections = [window.activeTextEditor!.selections[0]];
workspace.applyEdit(cleanupEdit);
}
inMirrorMode = false;
const cleanupEdit = new WorkspaceEdit();
const cleanupRange = new Range(event.selections[1].anchor.translate(0, -1), event.selections[1].anchor);
cleanupEdit.replace(event.textEditor.document.uri, cleanupRange, '');
window.activeTextEditor!.selections = [window.activeTextEditor!.selections[0]];
workspace.applyEdit(cleanupEdit);
}
}
}
@ -141,3 +141,43 @@ function getCharAfter(document: TextDocument, position: Position) {
return document.getText(new Range(position, document.positionAt(offset + 1)));
}
// Check if chars before and after the two positions are equal
// For the chars before, `<` and `/` are consiered equal to handle the case of `<|></|>`
function isCharBeforeAndAfterPositionsRoughtlyEqual(document: TextDocument, firstPos: Position, secondPos: Position) {
const charBeforePrimarySelection = getCharBefore(document, firstPos);
const charAfterPrimarySelection = getCharAfter(document, firstPos);
const charBeforeSecondarySelection = getCharBefore(document, secondPos);
const charAfterSecondarySelection = getCharAfter(document, secondPos);
// Exit mirror mode when cursor position no longer mirror
// Unless it's in the case of `<|></|>`
const charBeforeBothPositionRoughlyEqual =
charBeforePrimarySelection === charBeforeSecondarySelection ||
(charBeforePrimarySelection === '/' && charBeforeSecondarySelection === '<') ||
(charBeforeSecondarySelection === '/' && charBeforePrimarySelection === '<');
const charAfterBothPositionRoughlyEqual =
charAfterPrimarySelection === charAfterSecondarySelection ||
(charAfterPrimarySelection === ' ' && charAfterSecondarySelection === '>') ||
(charAfterSecondarySelection === ' ' && charAfterPrimarySelection === '>');
return charBeforeBothPositionRoughlyEqual && charAfterBothPositionRoughlyEqual;
}
function shouldDoCleanupForHtmlAttributeInput(document: TextDocument, firstPos: Position, secondPos: Position) {
// Need to cleanup in the case of <div |></div |>
const charBeforePrimarySelection = getCharBefore(document, firstPos);
const charAfterPrimarySelection = getCharAfter(document, firstPos);
const charBeforeSecondarySelection = getCharBefore(document, secondPos);
const charAfterSecondarySelection = getCharAfter(document, secondPos);
const primaryBeforeSecondary = document.offsetAt(firstPos) < document.offsetAt(secondPos);
return (
primaryBeforeSecondary &&
charBeforePrimarySelection === ' ' &&
charAfterPrimarySelection === '>' &&
charBeforeSecondarySelection === ' ' &&
charAfterSecondarySelection === '>'
);
}

View file

@ -180,6 +180,14 @@
}
}
},
"configurationDefaults": {
"[html]": {
"editor.suggest.insertMode": "replace"
},
"[handlebars]": {
"editor.suggest.insertMode": "replace"
}
},
"jsonValidation": [
{
"fileMatch": "*.html-data.json",

View file

@ -10,8 +10,16 @@ import { xhr, XHRResponse, getErrorStatusDescription } from 'request-light';
const localize = nls.loadMessageBundle();
import { workspace, window, languages, commands, ExtensionContext, extensions, Uri, LanguageConfiguration, Diagnostic, StatusBarAlignment, TextEditor, TextDocument, FormattingOptions, CancellationToken, ProviderResult, TextEdit, Range, Disposable } from 'vscode';
import { LanguageClient, LanguageClientOptions, RequestType, ServerOptions, TransportKind, NotificationType, DidChangeConfigurationNotification, HandleDiagnosticsSignature, ResponseError, DocumentRangeFormattingParams, DocumentRangeFormattingRequest } from 'vscode-languageclient';
import {
workspace, window, languages, commands, ExtensionContext, extensions, Uri, LanguageConfiguration,
Diagnostic, StatusBarAlignment, TextEditor, TextDocument, FormattingOptions, CancellationToken,
ProviderResult, TextEdit, Range, Position, Disposable, CompletionItem, CompletionList, CompletionContext
} from 'vscode';
import {
LanguageClient, LanguageClientOptions, RequestType, ServerOptions, TransportKind, NotificationType,
DidChangeConfigurationNotification, HandleDiagnosticsSignature, ResponseError, DocumentRangeFormattingParams,
DocumentRangeFormattingRequest, ProvideCompletionItemsSignature
} from 'vscode-languageclient';
import TelemetryReporter from 'vscode-extension-telemetry';
import { hash } from './utils/hash';
@ -132,6 +140,29 @@ export function activate(context: ExtensionContext) {
}
next(uri, diagnostics);
},
// testing the replace / insert mode
provideCompletionItem(document: TextDocument, position: Position, context: CompletionContext, token: CancellationToken, next: ProvideCompletionItemsSignature): ProviderResult<CompletionItem[] | CompletionList> {
function updateRanges(item: CompletionItem) {
const range = item.range;
if (range && range.end.isAfter(position) && range.start.isBeforeOrEqual(position)) {
item.range2 = { inserting: new Range(range.start, position), replacing: range };
item.range = undefined;
}
}
function updateProposals(r: CompletionItem[] | CompletionList | null | undefined): CompletionItem[] | CompletionList | null | undefined {
if (r) {
(Array.isArray(r) ? r : r.items).forEach(updateRanges);
}
return r;
}
const isThenable = <T>(obj: ProviderResult<T>): obj is Thenable<T> => obj && (<any>obj)['then'];
const r = next(document, position, context, token);
if (isThenable<CompletionItem[] | CompletionList | null | undefined>(r)) {
return r.then(updateProposals);
}
return updateProposals(r);
}
}
};
@ -422,5 +453,4 @@ function readJSONFile(location: string) {
console.log(`Problems reading ${location}: ${e}`);
return {};
}
}

View file

@ -1,113 +1,114 @@
{
"name": "json-language-features",
"displayName": "%displayName%",
"description": "%description%",
"version": "1.0.0",
"publisher": "vscode",
"license": "MIT",
"aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217",
"engines": {
"vscode": "0.10.x"
},
"icon": "icons/json.png",
"activationEvents": [
"onLanguage:json",
"onLanguage:jsonc"
],
"main": "./client/out/jsonMain",
"enableProposedApi": true,
"scripts": {
"compile": "gulp compile-extension:json-language-features-client compile-extension:json-language-features-server",
"watch": "gulp watch-extension:json-language-features-client watch-extension:json-language-features-server",
"postinstall": "cd server && yarn install",
"install-client-next": "yarn add vscode-languageclient@next"
},
"categories": [
"Programming Languages"
],
"contributes": {
"configuration": {
"id": "json",
"order": 20,
"type": "object",
"title": "JSON",
"properties": {
"json.schemas": {
"type": "array",
"scope": "resource",
"description": "%json.schemas.desc%",
"items": {
"type": "object",
"default": {
"fileMatch": [
"/myfile"
],
"url": "schemaURL"
},
"properties": {
"url": {
"type": "string",
"default": "/user.schema.json",
"description": "%json.schemas.url.desc%"
},
"fileMatch": {
"type": "array",
"items": {
"type": "string",
"default": "MyFile.json",
"description": "%json.schemas.fileMatch.item.desc%"
},
"minItems": 1,
"description": "%json.schemas.fileMatch.desc%"
},
"schema": {
"$ref": "http://json-schema.org/draft-07/schema#",
"description": "%json.schemas.schema.desc%"
}
}
}
},
"json.format.enable": {
"type": "boolean",
"scope": "window",
"default": true,
"description": "%json.format.enable.desc%"
},
"json.trace.server": {
"type": "string",
"scope": "window",
"enum": [
"off",
"messages",
"verbose"
],
"default": "off",
"description": "%json.tracing.desc%"
},
"json.colorDecorators.enable": {
"type": "boolean",
"scope": "window",
"default": true,
"description": "%json.colorDecorators.enable.desc%",
"deprecationMessage": "%json.colorDecorators.enable.deprecationMessage%"
}
}
"name": "json-language-features",
"displayName": "%displayName%",
"description": "%description%",
"version": "1.0.0",
"publisher": "vscode",
"license": "MIT",
"aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217",
"engines": {
"vscode": "0.10.x"
},
"configurationDefaults": {
"[json]": {
"editor.quickSuggestions": {
"strings": true
"icon": "icons/json.png",
"activationEvents": [
"onLanguage:json",
"onLanguage:jsonc"
],
"main": "./client/out/jsonMain",
"enableProposedApi": true,
"scripts": {
"compile": "gulp compile-extension:json-language-features-client compile-extension:json-language-features-server",
"watch": "gulp watch-extension:json-language-features-client watch-extension:json-language-features-server",
"postinstall": "cd server && yarn install",
"install-client-next": "yarn add vscode-languageclient@next"
},
"categories": [
"Programming Languages"
],
"contributes": {
"configuration": {
"id": "json",
"order": 20,
"type": "object",
"title": "JSON",
"properties": {
"json.schemas": {
"type": "array",
"scope": "resource",
"description": "%json.schemas.desc%",
"items": {
"type": "object",
"default": {
"fileMatch": [
"/myfile"
],
"url": "schemaURL"
},
"properties": {
"url": {
"type": "string",
"default": "/user.schema.json",
"description": "%json.schemas.url.desc%"
},
"fileMatch": {
"type": "array",
"items": {
"type": "string",
"default": "MyFile.json",
"description": "%json.schemas.fileMatch.item.desc%"
},
"minItems": 1,
"description": "%json.schemas.fileMatch.desc%"
},
"schema": {
"$ref": "http://json-schema.org/draft-07/schema#",
"description": "%json.schemas.schema.desc%"
}
}
}
},
"json.format.enable": {
"type": "boolean",
"scope": "window",
"default": true,
"description": "%json.format.enable.desc%"
},
"json.trace.server": {
"type": "string",
"scope": "window",
"enum": [
"off",
"messages",
"verbose"
],
"default": "off",
"description": "%json.tracing.desc%"
},
"json.colorDecorators.enable": {
"type": "boolean",
"scope": "window",
"default": true,
"description": "%json.colorDecorators.enable.desc%",
"deprecationMessage": "%json.colorDecorators.enable.deprecationMessage%"
}
}
},
"configurationDefaults": {
"[json]": {
"editor.quickSuggestions": {
"strings": true
},
"editor.suggest.insertMode": "replace"
}
}
}
},
"dependencies": {
"request-light": "^0.2.5",
"vscode-extension-telemetry": "0.1.1",
"vscode-languageclient": "^6.0.0-next.3",
"vscode-nls": "^4.1.1"
},
"devDependencies": {
"@types/node": "^12.11.7"
}
},
"dependencies": {
"request-light": "^0.2.5",
"vscode-extension-telemetry": "0.1.1",
"vscode-languageclient": "^6.0.0-next.3",
"vscode-nls": "^4.1.1"
},
"devDependencies": {
"@types/node": "^12.11.7"
}
}

View file

@ -14,7 +14,7 @@
"dependencies": {
"jsonc-parser": "^2.2.0",
"request-light": "^0.2.5",
"vscode-json-languageservice": "^3.4.7",
"vscode-json-languageservice": "^3.4.8",
"vscode-languageserver": "^6.0.0-next.3",
"vscode-uri": "^2.1.1"
},

View file

@ -80,10 +80,10 @@ request-light@^0.2.5:
https-proxy-agent "^2.2.3"
vscode-nls "^4.1.1"
vscode-json-languageservice@^3.4.7:
version "3.4.7"
resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.4.7.tgz#8d85f3c1d46a1e58e9867d747552fb8c83d934fd"
integrity sha512-y3MN2+/yph3yoIHGmHu4ScYpm285L58XVvfGkd49xTQzLja4apxSbwzsYcP9QsqS0W7KuvoyiPhqksiudoMwjg==
vscode-json-languageservice@^3.4.8:
version "3.4.8"
resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.4.8.tgz#2fc14e0a2603ed704ab57e29f3bcce75106be2bd"
integrity sha512-8h3VjUU4r37LVleIV7YGYs6MlnwHs4qR002cJsL3Z/ebrKzgab1OkwzGocAAJY21rnLhVy4KjnIy7oN1XGPlqA==
dependencies:
jsonc-parser "^2.2.0"
vscode-languageserver-textdocument "^1.0.0-next.4"

View file

@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Location, getLocation, createScanner, SyntaxKind, ScanError } from 'jsonc-parser';
import { Location, getLocation, createScanner, SyntaxKind, ScanError, JSONScanner } from 'jsonc-parser';
import { basename } from 'path';
import { BowerJSONContribution } from './bowerJSONContribution';
import { PackageJSONContribution } from './packageJSONContribution';
@ -111,7 +111,7 @@ export class JSONCompletionItemProvider implements CompletionItemProvider {
add: (suggestion: CompletionItem) => {
if (!proposed[suggestion.label]) {
proposed[suggestion.label] = true;
suggestion.range = overwriteRange;
suggestion.range2 = { replacing: overwriteRange, inserting: new Range(overwriteRange.start, overwriteRange.start) };
items.push(suggestion);
}
},
@ -123,8 +123,9 @@ export class JSONCompletionItemProvider implements CompletionItemProvider {
let collectPromise: Thenable<any> | null = null;
if (location.isAtPropertyKey) {
const addValue = !location.previousNode || !location.previousNode.colonOffset;
const isLast = this.isLast(document, position);
const scanner = createScanner(document.getText(), true);
const addValue = !location.previousNode || !this.hasColonAfter(scanner, location.previousNode.offset + location.previousNode.length);
const isLast = this.isLast(scanner, document.offsetAt(position));
collectPromise = this.jsonContribution.collectPropertySuggestions(fileName, location, currentWord, addValue, isLast, collector);
} else {
if (location.path.length === 0) {
@ -153,15 +154,19 @@ export class JSONCompletionItemProvider implements CompletionItemProvider {
return text.substring(i + 1, position.character);
}
private isLast(document: TextDocument, position: Position): boolean {
const scanner = createScanner(document.getText(), true);
scanner.setPosition(document.offsetAt(position));
private isLast(scanner: JSONScanner, offset: number): boolean {
scanner.setPosition(offset);
let nextToken = scanner.scan();
if (nextToken === SyntaxKind.StringLiteral && scanner.getTokenError() === ScanError.UnexpectedEndOfString) {
nextToken = scanner.scan();
}
return nextToken === SyntaxKind.CloseBraceToken || nextToken === SyntaxKind.EOF;
}
private hasColonAfter(scanner: JSONScanner, offset: number): boolean {
scanner.setPosition(offset);
return scanner.scan() === SyntaxKind.ColonToken;
}
}
export const xhrDisabled = () => Promise.reject({ responseText: 'Use of online resources is disabled.' });

View file

@ -1,7 +1,7 @@
{
"name": "code-oss-dev",
"version": "1.41.0",
"distro": "30dcc7436405dfa68898d0f3843551c589fc9008",
"distro": "2e24565c8e7c0009905c302d27a0ca7cac7efdc3",
"author": {
"name": "Microsoft Corporation"
},

View file

@ -274,7 +274,11 @@ export class TextAreaInput extends Disposable {
this._register(dom.addDisposableListener(textArea.domNode, 'compositionend', (e: CompositionEvent) => {
this._lastTextAreaEvent = TextAreaInputEventType.compositionend;
// https://github.com/microsoft/monaco-editor/issues/1663
// On iOS 13.2, Chinese system IME randomly trigger an additional compositionend event with empty data
if (!this._isDoingComposition) {
return;
}
if (compositionDataInValid(e.locale)) {
// https://github.com/Microsoft/monaco-editor/issues/339
const [newState, typeInput] = deduceInputFromTextAreaValue(/*couldBeEmojiInput*/false, /*couldBeTypingAtOffset0*/false);

View file

@ -83,14 +83,6 @@ declare module 'vscode' {
build(): Uint32Array;
}
/**
* A certain token (at index `i` is encoded using 5 uint32 integers):
* - at index `5*i` - `deltaLine`: token line number, relative to `SemanticColoringArea.line`
* - at index `5*i+1` - `deltaStart`: token start character offset inside the line (relative to 0 or the previous token if they are on the same line)
* - at index `5*i+2` - `length`: the length of the token
* - at index `5*i+3` - `tokenType`: will be looked up in `SemanticColoringLegend.tokenTypes`
* - at index `5*i+4` - `tokenModifiers`: each set bit will be looked up in `SemanticColoringLegend.tokenModifiers`
*/
export class SemanticTokens {
readonly resultId?: string;
readonly data: Uint32Array;
@ -123,6 +115,96 @@ declare module 'vscode' {
* semantic tokens.
*/
export interface SemanticTokensProvider {
/**
* A file can contain many tokens, perhaps even hundreds of thousands tokens. Therefore, to improve
* the memory consumption around describing semantic tokens, we have decided to avoid allocating objects
* and we have decided to represent tokens from a file as an array of integers.
*
*
* In short, each token takes 5 integers to represent, so a specific token i in the file consists of the following fields:
* - at index `5*i` - `deltaLine`: token line number, relative to the previous token
* - at index `5*i+1` - `deltaStart`: token start character, relative to the previous token (relative to 0 or the previous token's start if they are on the same line)
* - at index `5*i+2` - `length`: the length of the token. A token cannot be multiline.
* - at index `5*i+3` - `tokenType`: will be looked up in `SemanticTokensLegend.tokenTypes`
* - at index `5*i+4` - `tokenModifiers`: each set bit will be looked up in `SemanticTokensLegend.tokenModifiers`
*
*
* Here is an example for encoding a file with 3 tokens:
* ```
* [ { line: 2, startChar: 5, length: 3, tokenType: "properties", tokenModifiers: ["private", "static"] },
* { line: 2, startChar: 10, length: 4, tokenType: "types", tokenModifiers: [] },
* { line: 5, startChar: 2, length: 7, tokenType: "classes", tokenModifiers: [] } ]
* ```
*
* 1. First of all, a legend must be devised. This legend must be provided up-front and capture all possible token types.
* For this example, we will choose the following legend which is passed in when registering the provider:
* ```
* { tokenTypes: ['', 'properties', 'types', 'classes'],
* tokenModifiers: ['', 'private', 'static'] }
* ```
*
* 2. The first transformation is to encode `tokenType` and `tokenModifiers` as integers using the legend. Token types are looked
* up by index, so a `tokenType` value of `1` means `tokenTypes[1]`. Token modifiers are a set and they are looked up by a bitmap,
* so a `tokenModifier` value of `6` is first viewed as a bitmap `0b110`, so it will mean `[tokenModifiers[1], tokenModifiers[2]]` because
* bits 1 and 2 are set. Using this legend, the tokens now are:
* ```
* [ { line: 2, startChar: 5, length: 3, tokenType: 1, tokenModifiers: 6 }, // 6 is 0b110
* { line: 2, startChar: 10, length: 4, tokenType: 2, tokenModifiers: 0 },
* { line: 5, startChar: 2, length: 7, tokenType: 3, tokenModifiers: 0 } ]
* ```
*
* 3. Then, we will encode each token relative to the previous token in the file:
* ```
* [ { deltaLine: 2, deltaStartChar: 5, length: 3, tokenType: 1, tokenModifiers: 6 },
* // this token is on the same line as the first one, so the startChar is made relative
* { deltaLine: 0, deltaStartChar: 5, length: 4, tokenType: 2, tokenModifiers: 0 },
* // this token is on a different line than the second one, so the startChar remains unchanged
* { deltaLine: 3, deltaStartChar: 2, length: 7, tokenType: 3, tokenModifiers: 0 } ]
* ```
*
* 4. Finally, the integers are organized in a single array, which is a memory friendly representation:
* ```
* // 1st token, 2nd token, 3rd token
* [ 2,5,3,1,6, 0,5,4,2,0, 3,2,7,3,0 ]
* ```
*
* In principle, each call to `provideSemanticTokens` expects a complete representations of the semantic tokens.
* It is possible to simply return all the tokens at each call.
*
* But oftentimes, a small edit in the file will result in a small change to the above delta-based represented tokens.
* (In fact, that is why the above tokens are delta-encoded relative to their corresponding previous tokens).
* In such a case, if VS Code passes in the previous result id, it is possible for an advanced tokenization provider
* to return a delta to the integers array.
*
* To continue with the previous example, suppose a new line has been pressed at the beginning of the file, such that
* all the tokens are now one line lower, and that a new token has appeared since the last result on line 4.
* For example, the tokens might look like:
* ```
* [ { line: 3, startChar: 5, length: 3, tokenType: "properties", tokenModifiers: ["private", "static"] },
* { line: 3, startChar: 10, length: 4, tokenType: "types", tokenModifiers: [] },
* { line: 4, startChar: 3, length: 5, tokenType: "properties", tokenModifiers: ["static"] },
* { line: 6, startChar: 2, length: 7, tokenType: "classes", tokenModifiers: [] } ]
* ```
*
* The integer encoding of all new tokens would be:
* ```
* [ 3,5,3,1,6, 0,5,4,2,0, 1,3,5,1,2, 2,2,7,3,0 ]
* ```
*
* A smart tokens provider can compute a diff from the previous result to the new result
* ```
* [ 2,5,3,1,6, 0,5,4,2,0, 3,2,7,3,0 ]
* [ 3,5,3,1,6, 0,5,4,2,0, 1,3,5,1,2, 2,2,7,3,0 ]
* ```
* and return as simple integer edits the diff:
* ```
* { edits: [
* { start: 0, deleteCount: 1, data: [3] } // replace integer at offset 0 with 3
* { start: 10, deleteCount: 1, data: [1,3,5,1,2,2] } // replace integer at offset 10 with [1,3,5,1,2,2]
* ]}
* ```
* All indices expressed in the returned diff represent indices in the old result array, so they all refer to the previous result state.
*/
provideSemanticTokens(document: TextDocument, options: SemanticTokensRequestOptions, token: CancellationToken): ProviderResult<SemanticTokens | SemanticTokensEdits>;
}

View file

@ -271,18 +271,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
webviewInput.webview.options = options;
webviewInput.webview.extension = extension;
const model = await this._customEditorService.models.loadOrCreate(webviewInput.getResource(), webviewInput.viewType);
model.onUndo(edits => { this._proxy.$undoEdits(handle, edits.map(x => x.data)); });
model.onApplyEdit(edits => {
const editsToApply = edits.filter(x => x.source !== webviewInput).map(x => x.data);
if (editsToApply.length) {
this._proxy.$applyEdits(handle, editsToApply);
}
});
model.onWillSave(e => { e.waitUntil(this._proxy.$onSave(handle)); });
model.onWillSaveAs(e => { e.waitUntil(this._proxy.$onSaveAs(handle, e.resource.toJSON(), e.targetResource.toJSON())); });
const model = await this.getModel(webviewInput);
webviewInput.onDisposeWebview(() => {
this._customEditorService.models.disposeModel(model);
});
@ -315,6 +304,31 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
this._editorProviders.delete(viewType);
}
public async $registerCapabilities(handle: extHostProtocol.WebviewPanelHandle, capabilities: readonly extHostProtocol.WebviewEditorCapabilities[]): Promise<void> {
const webviewInput = this.getWebviewInput(handle);
const model = await this.getModel(webviewInput);
const capabilitiesSet = new Set(capabilities);
if (capabilitiesSet.has(extHostProtocol.WebviewEditorCapabilities.Editable)) {
model.onUndo(edits => { this._proxy.$undoEdits(handle, edits.map(x => x.data)); });
model.onApplyEdit(edits => {
const editsToApply = edits.filter(x => x.source !== webviewInput).map(x => x.data);
if (editsToApply.length) {
this._proxy.$applyEdits(handle, editsToApply);
}
});
model.onWillSave(e => { e.waitUntil(this._proxy.$onSave(handle)); });
model.onWillSaveAs(e => { e.waitUntil(this._proxy.$onSaveAs(handle, e.resource.toJSON(), e.targetResource.toJSON())); });
}
}
private getModel(webviewInput: WebviewInput) {
return this._customEditorService.models.loadOrCreate(webviewInput.getResource(), webviewInput.viewType);
}
public $onEdit(handle: extHostProtocol.WebviewPanelHandle, editData: any): void {
const webview = this.getWebviewInput(handle);
if (!(webview instanceof CustomFileEditorInput)) {

View file

@ -556,6 +556,10 @@ export interface WebviewExtensionDescription {
readonly location: UriComponents;
}
export enum WebviewEditorCapabilities {
Editable,
}
export interface MainThreadWebviewsShape extends IDisposable {
$createWebviewPanel(extension: WebviewExtensionDescription, handle: WebviewPanelHandle, viewType: string, title: string, showOptions: WebviewPanelShowOptions, options: modes.IWebviewPanelOptions & modes.IWebviewOptions): void;
$disposeWebview(handle: WebviewPanelHandle): void;
@ -573,6 +577,7 @@ export interface MainThreadWebviewsShape extends IDisposable {
$registerEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions): void;
$unregisterEditorProvider(viewType: string): void;
$registerCapabilities(handle: WebviewPanelHandle, capabilities: readonly WebviewEditorCapabilities[]): void;
$onEdit(handle: WebviewPanelHandle, editJson: any): void;
}

View file

@ -16,7 +16,7 @@ import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor';
import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview';
import * as vscode from 'vscode';
import { ExtHostWebviewsShape, IMainContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelViewStateData } from './extHost.protocol';
import { ExtHostWebviewsShape, IMainContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelViewStateData, WebviewEditorCapabilities } from './extHost.protocol';
import { Disposable as VSCodeDisposable } from './extHostTypes';
type IconPath = URI | { light: URI, dark: URI };
@ -257,12 +257,11 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa
}
async _onSave(): Promise<void> {
await assertIsDefined(this._capabilities).editingCapability?.save();
await assertIsDefined(this._capabilities?.editingCapability)?.save();
}
async _onSaveAs(resource: vscode.Uri, targetResource: vscode.Uri): Promise<void> {
await assertIsDefined(this._capabilities).editingCapability?.saveAs(resource, targetResource);
await assertIsDefined(this._capabilities?.editingCapability)?.saveAs(resource, targetResource);
}
private assertNotDisposed() {
@ -450,6 +449,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
this._webviewPanels.set(handle, revivedPanel);
const capabilities = await provider.resolveWebviewEditor({ resource: URI.revive(input.resource) }, revivedPanel);
revivedPanel._setCapabilities(capabilities);
this.registerCapabilites(handle, capabilities);
// TODO: the first set of edits should likely be passed when resolving
if (input.edits.length) {
@ -480,6 +480,14 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
private getWebviewPanel(handle: WebviewPanelHandle): ExtHostWebviewEditor | undefined {
return this._webviewPanels.get(handle);
}
private registerCapabilites(handle: WebviewPanelHandle, capabilities: vscode.WebviewEditorCapabilities) {
const declaredCapabilites: WebviewEditorCapabilities[] = [];
if (capabilities.editingCapability) {
declaredCapabilites.push(WebviewEditorCapabilities.Editable);
}
this._proxy.$registerCapabilities(handle, declaredCapabilites);
}
}
function convertWebviewOptions(

View file

@ -117,27 +117,13 @@ export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput {
return false;
}
// Preserve view state by opening the editor first. In addition
// this allows the user to review the contents of the editor.
// let viewState: IEditorViewState | undefined = undefined;
// const editor = await this.editorService.openEditor(this, undefined, group);
// if (isTextEditor(editor)) {
// viewState = editor.getViewState();
// }
let dialogPath = this._editorResource;
// if (this._editorResource.scheme === Schemas.untitled) {
// dialogPath = this.suggestFileName(resource);
// }
const target = await this.promptForPath(this._editorResource, dialogPath, options?.availableFileSystems);
if (!target) {
return false; // save cancelled
}
await this._model.saveAs(this._editorResource, target, options);
return true;
return await this._model.saveAs(this._editorResource, target, options);
}
public revert(options?: IRevertOptions): Promise<boolean> {
@ -156,15 +142,22 @@ export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput {
// Help user to find a name for the file by opening it first
await this.editorService.openEditor({ resource, options: { revealIfOpened: true, preserveFocus: true } });
return this.fileDialogService.pickFileToSave({});//this.getSaveDialogOptions(defaultUri, availableFileSystems));
return this.fileDialogService.pickFileToSave({
availableFileSystems,
defaultUri
});
}
public handleMove(groupId: GroupIdentifier, uri: URI, options?: ITextEditorOptions): IEditorInput | undefined {
const webview = assertIsDefined(this.takeOwnershipOfWebview());
return this.instantiationService.createInstance(CustomFileEditorInput,
uri,
this.viewType,
generateUuid(),
new Lazy(() => webview));
public handleMove(_groupId: GroupIdentifier, uri: URI, options?: ITextEditorOptions): IEditorInput | undefined {
const editorInfo = this.customEditorService.getCustomEditor(this.viewType);
if (editorInfo?.matches(uri)) {
const webview = assertIsDefined(this.takeOwnershipOfWebview());
return this.instantiationService.createInstance(CustomFileEditorInput,
uri,
this.viewType,
generateUuid(),
new Lazy(() => webview));
}
return undefined;
}
}

View file

@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/
import { coalesce, distinct, find, mergeSort } from 'vs/base/common/arrays';
import * as glob from 'vs/base/common/glob';
import { Lazy } from 'vs/base/common/lazy';
import { Disposable } from 'vs/base/common/lifecycle';
import { basename, isEqual } from 'vs/base/common/resources';
@ -32,14 +31,14 @@ import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/wo
import { CustomFileEditorInput } from './customEditorInput';
const defaultEditorId = 'default';
const defaultEditorInfo: CustomEditorInfo = {
const defaultEditorInfo = new CustomEditorInfo({
id: defaultEditorId,
displayName: nls.localize('promptOpenWith.defaultEditor', "VS Code's standard text editor"),
selector: [
{ filenamePattern: '*' }
],
priority: CustomEditorPriority.default,
};
});
export class CustomEditorInfoStore {
private readonly contributedEditors = new Map<string, CustomEditorInfo>();
@ -64,7 +63,7 @@ export class CustomEditorInfoStore {
public getContributedEditors(resource: URI): readonly CustomEditorInfo[] {
return Array.from(this.contributedEditors.values()).filter(customEditor =>
customEditor.selector.some(selector => matches(selector, resource)));
customEditor.matches(resource));
}
}
@ -97,12 +96,12 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ
for (const extension of extensions) {
for (const webviewEditorContribution of extension.value) {
this._editorInfoStore.add({
this._editorInfoStore.add(new CustomEditorInfo({
id: webviewEditorContribution.viewType,
displayName: webviewEditorContribution.displayName,
selector: webviewEditorContribution.selector || [],
priority: webviewEditorContribution.priority || CustomEditorPriority.default,
});
}));
}
}
this.updateContexts();
@ -127,6 +126,10 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ
return { resource, viewType: activeInput.viewType };
}
public getCustomEditor(viewType: string): CustomEditorInfo | undefined {
return this._editorInfoStore.get(viewType);
}
public getContributedCustomEditors(resource: URI): readonly CustomEditorInfo[] {
return this._editorInfoStore.getContributedEditors(resource);
}
@ -134,7 +137,7 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ
public getUserConfiguredCustomEditors(resource: URI): readonly CustomEditorInfo[] {
const rawAssociations = this.configurationService.getValue<CustomEditorsAssociations>(customEditorsAssociationsKey) || [];
return coalesce(rawAssociations
.filter(association => matches(association, resource))
.filter(association => CustomEditorInfo.selectorMatches(association, resource))
.map(association => this._editorInfoStore.get(association.viewType)));
}
@ -405,16 +408,6 @@ function priorityToRank(priority: CustomEditorPriority): number {
}
}
function matches(selector: CustomEditorSelector, resource: URI): boolean {
if (selector.filenamePattern) {
if (glob.match(selector.filenamePattern.toLowerCase(), basename(resource).toLowerCase())) {
return true;
}
}
return false;
}
registerThemingParticipant((theme, collector) => {
const shadow = theme.getColor(colorRegistry.scrollbarShadow);
if (shadow) {

View file

@ -4,11 +4,13 @@
*--------------------------------------------------------------------------------------------*/
import { Event } from 'vs/base/common/event';
import * as glob from 'vs/base/common/glob';
import { basename } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { EditorInput, IEditor, ISaveOptions, IRevertOptions } from 'vs/workbench/common/editor';
import { EditorInput, IEditor, IRevertOptions, ISaveOptions } from 'vs/workbench/common/editor';
import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IWorkingCopy } from 'vs/workbench/services/workingCopy/common/workingCopyService';
@ -29,6 +31,7 @@ export interface ICustomEditorService {
readonly activeCustomEditor: ICustomEditor | undefined;
getCustomEditor(viewType: string): CustomEditorInfo | undefined;
getContributedCustomEditors(resource: URI): readonly CustomEditorInfo[];
getUserConfiguredCustomEditors(resource: URI): readonly CustomEditorInfo[];
@ -87,9 +90,35 @@ export interface CustomEditorSelector {
readonly filenamePattern?: string;
}
export interface CustomEditorInfo {
readonly id: string;
readonly displayName: string;
readonly priority: CustomEditorPriority;
readonly selector: readonly CustomEditorSelector[];
export class CustomEditorInfo {
public readonly id: string;
public readonly displayName: string;
public readonly priority: CustomEditorPriority;
public readonly selector: readonly CustomEditorSelector[];
constructor(descriptor: {
readonly id: string;
readonly displayName: string;
readonly priority: CustomEditorPriority;
readonly selector: readonly CustomEditorSelector[];
}) {
this.id = descriptor.id;
this.displayName = descriptor.displayName;
this.priority = descriptor.priority;
this.selector = descriptor.selector;
}
matches(resource: URI): boolean {
return this.selector.some(selector => CustomEditorInfo.selectorMatches(selector, resource));
}
static selectorMatches(selector: CustomEditorSelector, resource: URI): boolean {
if (selector.filenamePattern) {
if (glob.match(selector.filenamePattern.toLowerCase(), basename(resource).toLowerCase())) {
return true;
}
}
return false;
}
}