mirror of
https://github.com/Microsoft/vscode
synced 2024-08-27 21:09:43 +00:00
Merge branch 'master' of github.com:microsoft/vscode
This commit is contained in:
commit
e95b3adcf0
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 === '>'
|
||||
);
|
||||
}
|
||||
|
|
|
@ -180,6 +180,14 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"configurationDefaults": {
|
||||
"[html]": {
|
||||
"editor.suggest.insertMode": "replace"
|
||||
},
|
||||
"[handlebars]": {
|
||||
"editor.suggest.insertMode": "replace"
|
||||
}
|
||||
},
|
||||
"jsonValidation": [
|
||||
{
|
||||
"fileMatch": "*.html-data.json",
|
||||
|
|
|
@ -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 {};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
},
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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.' });
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "code-oss-dev",
|
||||
"version": "1.41.0",
|
||||
"distro": "30dcc7436405dfa68898d0f3843551c589fc9008",
|
||||
"distro": "2e24565c8e7c0009905c302d27a0ca7cac7efdc3",
|
||||
"author": {
|
||||
"name": "Microsoft Corporation"
|
||||
},
|
||||
|
|
|
@ -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);
|
||||
|
|
98
src/vs/vscode.proposed.d.ts
vendored
98
src/vs/vscode.proposed.d.ts
vendored
|
@ -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>;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue