diff --git a/README.md b/README.md index 137fa8e3212..e54e736c74c 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ [![Build Status](https://travis-ci.org/Microsoft/vscode.svg?branch=master)](https://travis-ci.org/Microsoft/vscode) [![Build status](https://ci.appveyor.com/api/projects/status/vuhlhg80tj3e2a0l?svg=true)](https://ci.appveyor.com/project/VSCode/vscode) [![Coverage Status](https://img.shields.io/coveralls/Microsoft/vscode/master.svg)](https://coveralls.io/github/Microsoft/vscode?branch=master) +[![Gitter](https://img.shields.io/badge/chat-on%20gitter-blue.svg)](https://gitter.im/Microsoft/vscode) [VS Code](https://code.visualstudio.com) is a new type of tool that combines the simplicity of a code editor with what developers need for their core edit-build-debug cycle. Code diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 33cae599a0a..93145a6cb12 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -40,7 +40,7 @@ const nodeModules = ['electron', 'original-fs'] const builtInExtensions = [ { name: 'ms-vscode.node-debug', version: '1.7.8' }, - { name: 'ms-vscode.node-debug2', version: '1.7.2' } + { name: 'ms-vscode.node-debug2', version: '1.8.0' } ]; const vscodeEntryPoints = _.flatten([ diff --git a/build/monaco/monaco.d.ts.recipe b/build/monaco/monaco.d.ts.recipe index ea1aa360a41..c150910415f 100644 --- a/build/monaco/monaco.d.ts.recipe +++ b/build/monaco/monaco.d.ts.recipe @@ -69,9 +69,7 @@ export interface ICommandHandler { declare module monaco.languages { #includeAll(vs/editor/browser/standalone/standaloneLanguages;modes.=>;editorCommon.=>editor.;IMarkerData=>editor.IMarkerData): -#include(vs/editor/common/modes/languageConfigurationRegistry): CommentRule, LanguageConfiguration -#include(vs/editor/common/modes/supports/onEnter): IndentationRule, OnEnterRule -#include(vs/editor/common/modes/supports/electricCharacter): IBracketElectricCharacterContribution, IDocComment +#includeAll(vs/editor/common/modes/languageConfiguration): #includeAll(vs/editor/common/modes;editorCommon.IRange=>IRange;editorCommon.IPosition=>IPosition;editorCommon.=>editor.;IToken2=>IToken;ILineTokens2=>ILineTokens;IState2=>IState): #include(vs/editor/common/services/modeService): ILanguageExtensionPoint #includeAll(vs/editor/common/modes/monarch/monarchTypes): diff --git a/build/win32/i18n/Default.ko.isl b/build/win32/i18n/Default.ko.isl index c1838717f58..baacde2fc3c 100644 --- a/build/win32/i18n/Default.ko.isl +++ b/build/win32/i18n/Default.ko.isl @@ -127,7 +127,7 @@ SelectDirBrowseLabel= DiskSpaceMBLabel= [mb]MB ũ ʿմϴ. CannotInstallToNetworkDrive=ġ α׷ Ʈũ ̺꿡 ġ ϴ. CannotInstallToUNCPath=ġ α׷ UNC ο ġ ϴ. -InvalidPath=̺ ڿ Բ ü θ Էؾ մϴ. :%n%nC:APP%n%nǴ UNC :%n%nservershare +InvalidPath=̺ ڿ Բ ü θ Էؾ մϴ. :%n%nC:\APP%n%nǴ UNC :%n%n\server\share InvalidDrive= ̺곪 UNC ų ׸ ׼ ϴ. ٸ ̺곪 UNC ϼ. DiskSpaceWarningTitle=ũ DiskSpaceWarning=ġ α׷ ġϷ ġ  %1KB ʿ ̺ %2KBۿ ϴ.%n%n׷ Ͻðڽϱ? @@ -231,9 +231,9 @@ ErrorFunctionFailed=%1 ErrorFunctionFailedWithMessage=%1 , ڵ %2.%n%3 ErrorExecutingProgram= :%n%1 ; *** Registry errors -ErrorRegOpenKey=Ʈ Ű ߻:%n%1%2 -ErrorRegCreateKey=Ʈ Ű ߻:%n%1%2 -ErrorRegWriteKey=Ʈ Ű ϴ ߻:%n%1%2 +ErrorRegOpenKey=Ʈ Ű ߻:%n%1\%2 +ErrorRegCreateKey=Ʈ Ű ߻:%n%1\%2 +ErrorRegWriteKey=Ʈ Ű ϴ ߻:%n%1\%2 ; *** INI errors ErrorIniEntry= "%1" INI ׸ ߿ ߻߽ϴ. ; *** File copying errors diff --git a/build/win32/i18n/Default.zh-cn.isl b/build/win32/i18n/Default.zh-cn.isl index 9a191721993..8b47bfe0543 100644 --- a/build/win32/i18n/Default.zh-cn.isl +++ b/build/win32/i18n/Default.zh-cn.isl @@ -127,7 +127,7 @@ SelectDirBrowseLabel= DiskSpaceMBLabel=Ҫ [mb] MB ô̿ռ䡣 CannotInstallToNetworkDrive=װ޷װ CannotInstallToUNCPath=װ޷װ UNC · -InvalidPath=ŵ·(:%n%nC:APP%n%n)¸ʽ UNC ·:%n%nservershare +InvalidPath=ŵ·(:%n%nC:\APP%n%n)¸ʽ UNC ·:%n%n\server\share InvalidDrive=ѡ UNC ڻ򲻿ɷʡѡ DiskSpaceWarningTitle=̿ռ䲻 DiskSpaceWarning=װҪ %1 KB ÿռװѡ %2 KB ÿռ䡣%n%nǷҪ? @@ -231,9 +231,9 @@ ErrorFunctionFailed=%1 ʧ ErrorFunctionFailedWithMessage=%1 ʧܣ %2%n%3 ErrorExecutingProgram=޷ִļ:%n%1 ; *** Registry errors -ErrorRegOpenKey=עʱ:%n%1%2 -ErrorRegCreateKey=עʱ:%n%1%2 -ErrorRegWriteKey=дעʱ:%n%1%2 +ErrorRegOpenKey=עʱ:%n%1\%2 +ErrorRegCreateKey=עʱ:%n%1\%2 +ErrorRegWriteKey=дעʱ:%n%1\%2 ; *** INI errors ErrorIniEntry=ļ%1д INI ʱ ; *** File copying errors diff --git a/build/win32/i18n/Default.zh-tw.isl b/build/win32/i18n/Default.zh-tw.isl index a8d62487098..1432747a213 100644 --- a/build/win32/i18n/Default.zh-tw.isl +++ b/build/win32/i18n/Default.zh-tw.isl @@ -127,7 +127,7 @@ SelectDirBrowseLabel= DiskSpaceMBLabel=ܤֶ [mb] MB iκϺЪŶC CannotInstallToNetworkDrive=w˵{Lkw˨ϺоC CannotInstallToUNCPath=w˵{Lkw˨ UNC |C -InvalidPath=J]tϺоN|AҦp:%n%nC:APP%n%nοJUC榡 UNC |:%n%nA@ +InvalidPath=J]tϺоN|AҦp:%n%nC:\APP%n%nοJUC榡 UNC |:%n%n\A\@ InvalidDrive=Ϻо UNC @ΤsbεLksCпLϺо UNC @ΡC DiskSpaceWarningTitle=ϺЪŶ DiskSpaceWarning=w˵{ܤֻݭn %1 KB iΪŶ~wˡAҿϺоiΪŶu %2 KBC%n%nn~? @@ -231,9 +231,9 @@ ErrorFunctionFailed=%1 ErrorFunctionFailedWithMessage=%1 ; NX %2C%n%3 ErrorExecutingProgram=Lkɮ:%n%1 ; *** Registry errors -ErrorRegOpenKey=}ҵnXɵoͿ~:%n%1%2 -ErrorRegCreateKey=إߵnXɵoͿ~:%n%1%2 -ErrorRegWriteKey=gJnXɵoͿ~:%n%1%2 +ErrorRegOpenKey=}ҵnXɵoͿ~:%n%1\%2 +ErrorRegCreateKey=إߵnXɵoͿ~:%n%1\%2 +ErrorRegWriteKey=gJnXɵoͿ~:%n%1\%2 ; *** INI errors ErrorIniEntry=bɮ "%1" إ INI خɵoͿ~C ; *** File copying errors diff --git a/extensions/typescript/src/protocol.d.ts b/extensions/typescript/src/protocol.d.ts new file mode 100644 index 00000000000..f6cf09b5c9b --- /dev/null +++ b/extensions/typescript/src/protocol.d.ts @@ -0,0 +1,1700 @@ +/** + * Declaration module describing the TypeScript Server protocol + */ +declare namespace ts.server.protocol { + namespace CommandTypes { + type Brace = "brace"; + type BraceCompletion = "braceCompletion"; + type Change = "change"; + type Close = "close"; + type Completions = "completions"; + type CompletionDetails = "completionEntryDetails"; + type CompileOnSaveAffectedFileList = "compileOnSaveAffectedFileList"; + type CompileOnSaveEmitFile = "compileOnSaveEmitFile"; + type Configure = "configure"; + type Definition = "definition"; + type Exit = "exit"; + type Format = "format"; + type Formatonkey = "formatonkey"; + type Geterr = "geterr"; + type GeterrForProject = "geterrForProject"; + type SemanticDiagnosticsSync = "semanticDiagnosticsSync"; + type SyntacticDiagnosticsSync = "syntacticDiagnosticsSync"; + type NavBar = "navbar"; + type Navto = "navto"; + type NavTree = "navtree"; + type NavTreeFull = "navtree-full"; + type Occurrences = "occurrences"; + type DocumentHighlights = "documentHighlights"; + type Open = "open"; + type Quickinfo = "quickinfo"; + type References = "references"; + type Reload = "reload"; + type Rename = "rename"; + type Saveto = "saveto"; + type SignatureHelp = "signatureHelp"; + type TypeDefinition = "typeDefinition"; + type ProjectInfo = "projectInfo"; + type ReloadProjects = "reloadProjects"; + type Unknown = "unknown"; + type OpenExternalProject = "openExternalProject"; + type OpenExternalProjects = "openExternalProjects"; + type CloseExternalProject = "closeExternalProject"; + type TodoComments = "todoComments"; + type Indentation = "indentation"; + type DocCommentTemplate = "docCommentTemplate"; + type CompilerOptionsForInferredProjects = "compilerOptionsForInferredProjects"; + } + /** + * A TypeScript Server message + */ + interface Message { + /** + * Sequence number of the message + */ + seq: number; + /** + * One of "request", "response", or "event" + */ + type: "request" | "response" | "event"; + } + /** + * Client-initiated request message + */ + interface Request extends Message { + /** + * The command to execute + */ + command: string; + /** + * Object containing arguments for the command + */ + arguments?: any; + } + /** + * Request to reload the project structure for all the opened files + */ + interface ReloadProjectsRequest extends Message { + command: CommandTypes.ReloadProjects; + } + /** + * Server-initiated event message + */ + interface Event extends Message { + /** + * Name of event + */ + event: string; + /** + * Event-specific information + */ + body?: any; + } + /** + * Response by server to client request message. + */ + interface Response extends Message { + /** + * Sequence number of the request message. + */ + request_seq: number; + /** + * Outcome of the request. + */ + success: boolean; + /** + * The command requested. + */ + command: string; + /** + * Contains error message if success === false. + */ + message?: string; + /** + * Contains message body if success === true. + */ + body?: any; + } + /** + * Arguments for FileRequest messages. + */ + interface FileRequestArgs { + /** + * The file for the request (absolute pathname required). + */ + file: string; + projectFileName?: string; + } + /** + * Requests a JS Doc comment template for a given position + */ + interface DocCommentTemplateRequest extends FileLocationRequest { + command: CommandTypes.DocCommentTemplate; + } + /** + * Response to DocCommentTemplateRequest + */ + interface DocCommandTemplateResponse extends Response { + body?: TextInsertion; + } + /** + * A request to get TODO comments from the file + */ + interface TodoCommentRequest extends FileRequest { + command: CommandTypes.TodoComments; + arguments: TodoCommentRequestArgs; + } + /** + * Arguments for TodoCommentRequest request. + */ + interface TodoCommentRequestArgs extends FileRequestArgs { + /** + * Array of target TodoCommentDescriptors that describes TODO comments to be found + */ + descriptors: TodoCommentDescriptor[]; + } + /** + * Response for TodoCommentRequest request. + */ + interface TodoCommentsResponse extends Response { + body?: TodoComment[]; + } + /** + * A request to get indentation for a location in file + */ + interface IndentationRequest extends FileLocationRequest { + command: CommandTypes.Indentation; + arguments: IndentationRequestArgs; + } + /** + * Response for IndentationRequest request. + */ + interface IndentationResponse extends Response { + body?: IndentationResult; + } + /** + * Indentation result representing where indentation should be placed + */ + interface IndentationResult { + /** + * The base position in the document that the indent should be relative to + */ + position: number; + /** + * The number of columns the indent should be at relative to the position's column. + */ + indentation: number; + } + /** + * Arguments for IndentationRequest request. + */ + interface IndentationRequestArgs extends FileLocationRequestArgs { + /** + * An optional set of settings to be used when computing indentation. + * If argument is omitted - then it will use settings for file that were previously set via 'configure' request or global settings. + */ + options?: EditorSettings; + } + /** + * Arguments for ProjectInfoRequest request. + */ + interface ProjectInfoRequestArgs extends FileRequestArgs { + /** + * Indicate if the file name list of the project is needed + */ + needFileNameList: boolean; + } + /** + * A request to get the project information of the current file. + */ + interface ProjectInfoRequest extends Request { + command: CommandTypes.ProjectInfo; + arguments: ProjectInfoRequestArgs; + } + /** + * A request to retrieve compiler options diagnostics for a project + */ + interface CompilerOptionsDiagnosticsRequest extends Request { + arguments: CompilerOptionsDiagnosticsRequestArgs; + } + /** + * Arguments for CompilerOptionsDiagnosticsRequest request. + */ + interface CompilerOptionsDiagnosticsRequestArgs { + /** + * Name of the project to retrieve compiler options diagnostics. + */ + projectFileName: string; + } + /** + * Response message body for "projectInfo" request + */ + interface ProjectInfo { + /** + * For configured project, this is the normalized path of the 'tsconfig.json' file + * For inferred project, this is undefined + */ + configFileName: string; + /** + * The list of normalized file name in the project, including 'lib.d.ts' + */ + fileNames?: string[]; + /** + * Indicates if the project has a active language service instance + */ + languageServiceDisabled?: boolean; + } + /** + * Represents diagnostic info that includes location of diagnostic in two forms + * - start position and length of the error span + * - startLocation and endLocation - a pair of Location objects that store start/end line and offset of the error span. + */ + interface DiagnosticWithLinePosition { + message: string; + start: number; + length: number; + startLocation: Location; + endLocation: Location; + category: string; + code: number; + } + /** + * Response message for "projectInfo" request + */ + interface ProjectInfoResponse extends Response { + body?: ProjectInfo; + } + /** + * Request whose sole parameter is a file name. + */ + interface FileRequest extends Request { + arguments: FileRequestArgs; + } + /** + * Instances of this interface specify a location in a source file: + * (file, line, character offset), where line and character offset are 1-based. + */ + interface FileLocationRequestArgs extends FileRequestArgs { + /** + * The line number for the request (1-based). + */ + line: number; + /** + * The character offset (on the line) for the request (1-based). + */ + offset: number; + } + /** + * A request whose arguments specify a file location (file, line, col). + */ + interface FileLocationRequest extends FileRequest { + arguments: FileLocationRequestArgs; + } + /** + * Arguments for EncodedSemanticClassificationsRequest request. + */ + interface EncodedSemanticClassificationsRequestArgs extends FileRequestArgs { + /** + * Start position of the span. + */ + start: number; + /** + * Length of the span. + */ + length: number; + } + /** + * Arguments in document highlight request; include: filesToSearch, file, + * line, offset. + */ + interface DocumentHighlightsRequestArgs extends FileLocationRequestArgs { + /** + * List of files to search for document highlights. + */ + filesToSearch: string[]; + } + /** + * Go to definition request; value of command field is + * "definition". Return response giving the file locations that + * define the symbol found in file at location line, col. + */ + interface DefinitionRequest extends FileLocationRequest { + command: CommandTypes.Definition; + } + /** + * Go to type request; value of command field is + * "typeDefinition". Return response giving the file locations that + * define the type for the symbol found in file at location line, col. + */ + interface TypeDefinitionRequest extends FileLocationRequest { + command: CommandTypes.TypeDefinition; + } + /** + * Location in source code expressed as (one-based) line and character offset. + */ + interface Location { + line: number; + offset: number; + } + /** + * Object found in response messages defining a span of text in source code. + */ + interface TextSpan { + /** + * First character of the definition. + */ + start: Location; + /** + * One character past last character of the definition. + */ + end: Location; + } + /** + * Object found in response messages defining a span of text in a specific source file. + */ + interface FileSpan extends TextSpan { + /** + * File containing text span. + */ + file: string; + } + /** + * Definition response message. Gives text range for definition. + */ + interface DefinitionResponse extends Response { + body?: FileSpan[]; + } + /** + * Definition response message. Gives text range for definition. + */ + interface TypeDefinitionResponse extends Response { + body?: FileSpan[]; + } + /** + * Implementation response message. Gives text range for implementations. + */ + interface ImplementationResponse extends Response { + body?: FileSpan[]; + } + /** + * Request to get brace completion for a location in the file. + */ + interface BraceCompletionRequest extends FileLocationRequest { + command: CommandTypes.BraceCompletion; + arguments: BraceCompletionRequestArgs; + } + /** + * Argument for BraceCompletionRequest request. + */ + interface BraceCompletionRequestArgs extends FileLocationRequestArgs { + /** + * Kind of opening brace + */ + openingBrace: string; + } + /** + * Get occurrences request; value of command field is + * "occurrences". Return response giving spans that are relevant + * in the file at a given line and column. + */ + interface OccurrencesRequest extends FileLocationRequest { + command: CommandTypes.Occurrences; + } + interface OccurrencesResponseItem extends FileSpan { + /** + * True if the occurrence is a write location, false otherwise. + */ + isWriteAccess: boolean; + } + interface OccurrencesResponse extends Response { + body?: OccurrencesResponseItem[]; + } + /** + * Get document highlights request; value of command field is + * "documentHighlights". Return response giving spans that are relevant + * in the file at a given line and column. + */ + interface DocumentHighlightsRequest extends FileLocationRequest { + command: CommandTypes.DocumentHighlights; + arguments: DocumentHighlightsRequestArgs; + } + /** + * Span augmented with extra information that denotes the kind of the highlighting to be used for span. + * Kind is taken from HighlightSpanKind type. + */ + interface HighlightSpan extends TextSpan { + kind: string; + } + /** + * Represents a set of highligh spans for a give name + */ + interface DocumentHighlightsItem { + /** + * File containing highlight spans. + */ + file: string; + /** + * Spans to highlight in file. + */ + highlightSpans: HighlightSpan[]; + } + /** + * Response for a DocumentHighlightsRequest request. + */ + interface DocumentHighlightsResponse extends Response { + body?: DocumentHighlightsItem[]; + } + /** + * Find references request; value of command field is + * "references". Return response giving the file locations that + * reference the symbol found in file at location line, col. + */ + interface ReferencesRequest extends FileLocationRequest { + command: CommandTypes.References; + } + interface ReferencesResponseItem extends FileSpan { + /** Text of line containing the reference. Including this + * with the response avoids latency of editor loading files + * to show text of reference line (the server already has + * loaded the referencing files). + */ + lineText: string; + /** + * True if reference is a write location, false otherwise. + */ + isWriteAccess: boolean; + /** + * True if reference is a definition, false otherwise. + */ + isDefinition: boolean; + } + /** + * The body of a "references" response message. + */ + interface ReferencesResponseBody { + /** + * The file locations referencing the symbol. + */ + refs: ReferencesResponseItem[]; + /** + * The name of the symbol. + */ + symbolName: string; + /** + * The start character offset of the symbol (on the line provided by the references request). + */ + symbolStartOffset: number; + /** + * The full display name of the symbol. + */ + symbolDisplayString: string; + } + /** + * Response to "references" request. + */ + interface ReferencesResponse extends Response { + body?: ReferencesResponseBody; + } + /** + * Argument for RenameRequest request. + */ + interface RenameRequestArgs extends FileLocationRequestArgs { + /** + * Should text at specified location be found/changed in comments? + */ + findInComments?: boolean; + /** + * Should text at specified location be found/changed in strings? + */ + findInStrings?: boolean; + } + /** + * Rename request; value of command field is "rename". Return + * response giving the file locations that reference the symbol + * found in file at location line, col. Also return full display + * name of the symbol so that client can print it unambiguously. + */ + interface RenameRequest extends FileLocationRequest { + command: CommandTypes.Rename; + arguments: RenameRequestArgs; + } + /** + * Information about the item to be renamed. + */ + interface RenameInfo { + /** + * True if item can be renamed. + */ + canRename: boolean; + /** + * Error message if item can not be renamed. + */ + localizedErrorMessage?: string; + /** + * Display name of the item to be renamed. + */ + displayName: string; + /** + * Full display name of item to be renamed. + */ + fullDisplayName: string; + /** + * The items's kind (such as 'className' or 'parameterName' or plain 'text'). + */ + kind: string; + /** + * Optional modifiers for the kind (such as 'public'). + */ + kindModifiers: string; + } + /** + * A group of text spans, all in 'file'. + */ + interface SpanGroup { + /** The file to which the spans apply */ + file: string; + /** The text spans in this group */ + locs: TextSpan[]; + } + interface RenameResponseBody { + /** + * Information about the item to be renamed. + */ + info: RenameInfo; + /** + * An array of span groups (one per file) that refer to the item to be renamed. + */ + locs: SpanGroup[]; + } + /** + * Rename response message. + */ + interface RenameResponse extends Response { + body?: RenameResponseBody; + } + /** + * Represents a file in external project. + * External project is project whose set of files, compilation options and open\close state + * is maintained by the client (i.e. if all this data come from .csproj file in Visual Studio). + * External project will exist even if all files in it are closed and should be closed explicity. + * If external project includes one or more tsconfig.json/jsconfig.json files then tsserver will + * create configured project for every config file but will maintain a link that these projects were created + * as a result of opening external project so they should be removed once external project is closed. + */ + interface ExternalFile { + /** + * Name of file file + */ + fileName: string; + /** + * Script kind of the file + */ + scriptKind?: ScriptKindName | ts.ScriptKind; + /** + * Whether file has mixed content (i.e. .cshtml file that combines html markup with C#/JavaScript) + */ + hasMixedContent?: boolean; + /** + * Content of the file + */ + content?: string; + } + /** + * Represent an external project + */ + interface ExternalProject { + /** + * Project name + */ + projectFileName: string; + /** + * List of root files in project + */ + rootFiles: ExternalFile[]; + /** + * Compiler options for the project + */ + options: ExternalProjectCompilerOptions; + /** + * Explicitly specified typing options for the project + */ + typingOptions?: TypingOptions; + } + interface CompileOnSaveMixin { + /** + * If compile on save is enabled for the project + */ + compileOnSave?: boolean; + } + /** + * For external projects, some of the project settings are sent together with + * compiler settings. + */ + type ExternalProjectCompilerOptions = CompilerOptions & CompileOnSaveMixin; + /** + * Represents a set of changes that happen in project + */ + interface ProjectChanges { + /** + * List of added files + */ + added: string[]; + /** + * List of removed files + */ + removed: string[]; + } + /** + * Information found in a configure request. + */ + interface ConfigureRequestArguments { + /** + * Information about the host, for example 'Emacs 24.4' or + * 'Sublime Text version 3075' + */ + hostInfo?: string; + /** + * If present, tab settings apply only to this file. + */ + file?: string; + /** + * The format options to use during formatting and other code editing features. + */ + formatOptions?: FormatCodeSettings; + } + /** + * Configure request; value of command field is "configure". Specifies + * host information, such as host type, tab size, and indent size. + */ + interface ConfigureRequest extends Request { + command: CommandTypes.Configure; + arguments: ConfigureRequestArguments; + } + /** + * Response to "configure" request. This is just an acknowledgement, so + * no body field is required. + */ + interface ConfigureResponse extends Response { + } + /** + * Information found in an "open" request. + */ + interface OpenRequestArgs extends FileRequestArgs { + /** + * Used when a version of the file content is known to be more up to date than the one on disk. + * Then the known content will be used upon opening instead of the disk copy + */ + fileContent?: string; + /** + * Used to specify the script kind of the file explicitly. It could be one of the following: + * "TS", "JS", "TSX", "JSX" + */ + scriptKindName?: ScriptKindName; + } + type ScriptKindName = "TS" | "JS" | "TSX" | "JSX"; + /** + * Open request; value of command field is "open". Notify the + * server that the client has file open. The server will not + * monitor the filesystem for changes in this file and will assume + * that the client is updating the server (using the change and/or + * reload messages) when the file changes. Server does not currently + * send a response to an open request. + */ + interface OpenRequest extends Request { + command: CommandTypes.Open; + arguments: OpenRequestArgs; + } + /** + * Request to open or update external project + */ + interface OpenExternalProjectRequest extends Request { + command: CommandTypes.OpenExternalProject; + arguments: OpenExternalProjectArgs; + } + /** + * Arguments to OpenExternalProjectRequest request + */ + type OpenExternalProjectArgs = ExternalProject; + /** + * Request to open multiple external projects + */ + interface OpenExternalProjectsRequest extends Request { + command: CommandTypes.OpenExternalProjects; + arguments: OpenExternalProjectsArgs; + } + /** + * Arguments to OpenExternalProjectsRequest + */ + interface OpenExternalProjectsArgs { + /** + * List of external projects to open or update + */ + projects: ExternalProject[]; + } + /** + * Response to OpenExternalProjectRequest request. This is just an acknowledgement, so + * no body field is required. + */ + interface OpenExternalProjectResponse extends Response { + } + /** + * Response to OpenExternalProjectsRequest request. This is just an acknowledgement, so + * no body field is required. + */ + interface OpenExternalProjectsResponse extends Response { + } + /** + * Request to close external project. + */ + interface CloseExternalProjectRequest extends Request { + command: CommandTypes.CloseExternalProject; + arguments: CloseExternalProjectRequestArgs; + } + /** + * Arguments to CloseExternalProjectRequest request + */ + interface CloseExternalProjectRequestArgs { + /** + * Name of the project to close + */ + projectFileName: string; + } + /** + * Response to CloseExternalProjectRequest request. This is just an acknowledgement, so + * no body field is required. + */ + interface CloseExternalProjectResponse extends Response { + } + /** + * Request to set compiler options for inferred projects. + * External projects are opened / closed explicitly. + * Configured projects are opened when user opens loose file that has 'tsconfig.json' or 'jsconfig.json' anywhere in one of containing folders. + * This configuration file will be used to obtain a list of files and configuration settings for the project. + * Inferred projects are created when user opens a loose file that is not the part of external project + * or configured project and will contain only open file and transitive closure of referenced files if 'useOneInferredProject' is false, + * or all open loose files and its transitive closure of referenced files if 'useOneInferredProject' is true. + */ + interface SetCompilerOptionsForInferredProjectsRequest extends Request { + command: CommandTypes.CompilerOptionsForInferredProjects; + arguments: SetCompilerOptionsForInferredProjectsArgs; + } + /** + * Argument for SetCompilerOptionsForInferredProjectsRequest request. + */ + interface SetCompilerOptionsForInferredProjectsArgs { + /** + * Compiler options to be used with inferred projects. + */ + options: ExternalProjectCompilerOptions; + } + /** + * Response to SetCompilerOptionsForInferredProjectsResponse request. This is just an acknowledgement, so + * no body field is required. + */ + interface SetCompilerOptionsForInferredProjectsResponse extends Response { + } + /** + * Exit request; value of command field is "exit". Ask the server process + * to exit. + */ + interface ExitRequest extends Request { + command: CommandTypes.Exit; + } + /** + * Close request; value of command field is "close". Notify the + * server that the client has closed a previously open file. If + * file is still referenced by open files, the server will resume + * monitoring the filesystem for changes to file. Server does not + * currently send a response to a close request. + */ + interface CloseRequest extends FileRequest { + command: CommandTypes.Close; + } + /** + * Request to obtain the list of files that should be regenerated if target file is recompiled. + * NOTE: this us query-only operation and does not generate any output on disk. + */ + interface CompileOnSaveAffectedFileListRequest extends FileRequest { + command: CommandTypes.CompileOnSaveAffectedFileList; + } + /** + * Contains a list of files that should be regenerated in a project + */ + interface CompileOnSaveAffectedFileListSingleProject { + /** + * Project name + */ + projectFileName: string; + /** + * List of files names that should be recompiled + */ + fileNames: string[]; + } + /** + * Response for CompileOnSaveAffectedFileListRequest request; + */ + interface CompileOnSaveAffectedFileListResponse extends Response { + body: CompileOnSaveAffectedFileListSingleProject[]; + } + /** + * Request to recompile the file. All generated outputs (.js, .d.ts or .js.map files) is written on disk. + */ + interface CompileOnSaveEmitFileRequest extends FileRequest { + command: CommandTypes.CompileOnSaveEmitFile; + arguments: CompileOnSaveEmitFileRequestArgs; + } + /** + * Arguments for CompileOnSaveEmitFileRequest + */ + interface CompileOnSaveEmitFileRequestArgs extends FileRequestArgs { + /** + * if true - then file should be recompiled even if it does not have any changes. + */ + forced?: boolean; + } + /** + * Quickinfo request; value of command field is + * "quickinfo". Return response giving a quick type and + * documentation string for the symbol found in file at location + * line, col. + */ + interface QuickInfoRequest extends FileLocationRequest { + command: CommandTypes.Quickinfo; + } + /** + * Body of QuickInfoResponse. + */ + interface QuickInfoResponseBody { + /** + * The symbol's kind (such as 'className' or 'parameterName' or plain 'text'). + */ + kind: string; + /** + * Optional modifiers for the kind (such as 'public'). + */ + kindModifiers: string; + /** + * Starting file location of symbol. + */ + start: Location; + /** + * One past last character of symbol. + */ + end: Location; + /** + * Type and kind of symbol. + */ + displayString: string; + /** + * Documentation associated with symbol. + */ + documentation: string; + } + /** + * Quickinfo response message. + */ + interface QuickInfoResponse extends Response { + body?: QuickInfoResponseBody; + } + /** + * Arguments for format messages. + */ + interface FormatRequestArgs extends FileLocationRequestArgs { + /** + * Last line of range for which to format text in file. + */ + endLine: number; + /** + * Character offset on last line of range for which to format text in file. + */ + endOffset: number; + /** + * Format options to be used. + */ + options?: FormatCodeSettings; + } + /** + * Format request; value of command field is "format". Return + * response giving zero or more edit instructions. The edit + * instructions will be sorted in file order. Applying the edit + * instructions in reverse to file will result in correctly + * reformatted text. + */ + interface FormatRequest extends FileLocationRequest { + command: CommandTypes.Format; + arguments: FormatRequestArgs; + } + /** + * Object found in response messages defining an editing + * instruction for a span of text in source code. The effect of + * this instruction is to replace the text starting at start and + * ending one character before end with newText. For an insertion, + * the text span is empty. For a deletion, newText is empty. + */ + interface CodeEdit { + /** + * First character of the text span to edit. + */ + start: Location; + /** + * One character past last character of the text span to edit. + */ + end: Location; + /** + * Replace the span defined above with this string (may be + * the empty string). + */ + newText: string; + } + /** + * Format and format on key response message. + */ + interface FormatResponse extends Response { + body?: CodeEdit[]; + } + /** + * Arguments for format on key messages. + */ + interface FormatOnKeyRequestArgs extends FileLocationRequestArgs { + /** + * Key pressed (';', '\n', or '}'). + */ + key: string; + options?: FormatCodeSettings; + } + /** + * Format on key request; value of command field is + * "formatonkey". Given file location and key typed (as string), + * return response giving zero or more edit instructions. The + * edit instructions will be sorted in file order. Applying the + * edit instructions in reverse to file will result in correctly + * reformatted text. + */ + interface FormatOnKeyRequest extends FileLocationRequest { + command: CommandTypes.Formatonkey; + arguments: FormatOnKeyRequestArgs; + } + /** + * Arguments for completions messages. + */ + interface CompletionsRequestArgs extends FileLocationRequestArgs { + /** + * Optional prefix to apply to possible completions. + */ + prefix?: string; + } + /** + * Completions request; value of command field is "completions". + * Given a file location (file, line, col) and a prefix (which may + * be the empty string), return the possible completions that + * begin with prefix. + */ + interface CompletionsRequest extends FileLocationRequest { + command: CommandTypes.Completions; + arguments: CompletionsRequestArgs; + } + /** + * Arguments for completion details request. + */ + interface CompletionDetailsRequestArgs extends FileLocationRequestArgs { + /** + * Names of one or more entries for which to obtain details. + */ + entryNames: string[]; + } + /** + * Completion entry details request; value of command field is + * "completionEntryDetails". Given a file location (file, line, + * col) and an array of completion entry names return more + * detailed information for each completion entry. + */ + interface CompletionDetailsRequest extends FileLocationRequest { + command: CommandTypes.CompletionDetails; + arguments: CompletionDetailsRequestArgs; + } + /** + * Part of a symbol description. + */ + interface SymbolDisplayPart { + /** + * Text of an item describing the symbol. + */ + text: string; + /** + * The symbol's kind (such as 'className' or 'parameterName' or plain 'text'). + */ + kind: string; + } + /** + * An item found in a completion response. + */ + interface CompletionEntry { + /** + * The symbol's name. + */ + name: string; + /** + * The symbol's kind (such as 'className' or 'parameterName'). + */ + kind: string; + /** + * Optional modifiers for the kind (such as 'public'). + */ + kindModifiers: string; + /** + * A string that is used for comparing completion items so that they can be ordered. This + * is often the same as the name but may be different in certain circumstances. + */ + sortText: string; + /** + * An optional span that indicates the text to be replaced by this completion item. If present, + * this span should be used instead of the default one. + */ + replacementSpan?: TextSpan; + } + /** + * Additional completion entry details, available on demand + */ + interface CompletionEntryDetails { + /** + * The symbol's name. + */ + name: string; + /** + * The symbol's kind (such as 'className' or 'parameterName'). + */ + kind: string; + /** + * Optional modifiers for the kind (such as 'public'). + */ + kindModifiers: string; + /** + * Display parts of the symbol (similar to quick info). + */ + displayParts: SymbolDisplayPart[]; + /** + * Documentation strings for the symbol. + */ + documentation: SymbolDisplayPart[]; + } + interface CompletionsResponse extends Response { + body?: CompletionEntry[]; + } + interface CompletionDetailsResponse extends Response { + body?: CompletionEntryDetails[]; + } + /** + * Signature help information for a single parameter + */ + interface SignatureHelpParameter { + /** + * The parameter's name + */ + name: string; + /** + * Documentation of the parameter. + */ + documentation: SymbolDisplayPart[]; + /** + * Display parts of the parameter. + */ + displayParts: SymbolDisplayPart[]; + /** + * Whether the parameter is optional or not. + */ + isOptional: boolean; + } + /** + * Represents a single signature to show in signature help. + */ + interface SignatureHelpItem { + /** + * Whether the signature accepts a variable number of arguments. + */ + isVariadic: boolean; + /** + * The prefix display parts. + */ + prefixDisplayParts: SymbolDisplayPart[]; + /** + * The suffix display parts. + */ + suffixDisplayParts: SymbolDisplayPart[]; + /** + * The separator display parts. + */ + separatorDisplayParts: SymbolDisplayPart[]; + /** + * The signature helps items for the parameters. + */ + parameters: SignatureHelpParameter[]; + /** + * The signature's documentation + */ + documentation: SymbolDisplayPart[]; + } + /** + * Signature help items found in the response of a signature help request. + */ + interface SignatureHelpItems { + /** + * The signature help items. + */ + items: SignatureHelpItem[]; + /** + * The span for which signature help should appear on a signature + */ + applicableSpan: TextSpan; + /** + * The item selected in the set of available help items. + */ + selectedItemIndex: number; + /** + * The argument selected in the set of parameters. + */ + argumentIndex: number; + /** + * The argument count + */ + argumentCount: number; + } + /** + * Arguments of a signature help request. + */ + interface SignatureHelpRequestArgs extends FileLocationRequestArgs { + } + /** + * Signature help request; value of command field is "signatureHelp". + * Given a file location (file, line, col), return the signature + * help. + */ + interface SignatureHelpRequest extends FileLocationRequest { + command: CommandTypes.SignatureHelp; + arguments: SignatureHelpRequestArgs; + } + /** + * Response object for a SignatureHelpRequest. + */ + interface SignatureHelpResponse extends Response { + body?: SignatureHelpItems; + } + /** + * Synchronous request for semantic diagnostics of one file. + */ + interface SemanticDiagnosticsSyncRequest extends FileRequest { + command: CommandTypes.SemanticDiagnosticsSync; + arguments: SemanticDiagnosticsSyncRequestArgs; + } + interface SemanticDiagnosticsSyncRequestArgs extends FileRequestArgs { + includeLinePosition?: boolean; + } + /** + * Response object for synchronous sematic diagnostics request. + */ + interface SemanticDiagnosticsSyncResponse extends Response { + body?: Diagnostic[] | DiagnosticWithLinePosition[]; + } + /** + * Synchronous request for syntactic diagnostics of one file. + */ + interface SyntacticDiagnosticsSyncRequest extends FileRequest { + command: CommandTypes.SyntacticDiagnosticsSync; + arguments: SyntacticDiagnosticsSyncRequestArgs; + } + interface SyntacticDiagnosticsSyncRequestArgs extends FileRequestArgs { + includeLinePosition?: boolean; + } + /** + * Response object for synchronous syntactic diagnostics request. + */ + interface SyntacticDiagnosticsSyncResponse extends Response { + body?: Diagnostic[] | DiagnosticWithLinePosition[]; + } + /** + * Arguments for GeterrForProject request. + */ + interface GeterrForProjectRequestArgs { + /** + * the file requesting project error list + */ + file: string; + /** + * Delay in milliseconds to wait before starting to compute + * errors for the files in the file list + */ + delay: number; + } + /** + * GeterrForProjectRequest request; value of command field is + * "geterrForProject". It works similarly with 'Geterr', only + * it request for every file in this project. + */ + interface GeterrForProjectRequest extends Request { + command: CommandTypes.GeterrForProject; + arguments: GeterrForProjectRequestArgs; + } + /** + * Arguments for geterr messages. + */ + interface GeterrRequestArgs { + /** + * List of file names for which to compute compiler errors. + * The files will be checked in list order. + */ + files: string[]; + /** + * Delay in milliseconds to wait before starting to compute + * errors for the files in the file list + */ + delay: number; + } + /** + * Geterr request; value of command field is "geterr". Wait for + * delay milliseconds and then, if during the wait no change or + * reload messages have arrived for the first file in the files + * list, get the syntactic errors for the file, field requests, + * and then get the semantic errors for the file. Repeat with a + * smaller delay for each subsequent file on the files list. Best + * practice for an editor is to send a file list containing each + * file that is currently visible, in most-recently-used order. + */ + interface GeterrRequest extends Request { + command: CommandTypes.Geterr; + arguments: GeterrRequestArgs; + } + /** + * Item of diagnostic information found in a DiagnosticEvent message. + */ + interface Diagnostic { + /** + * Starting file location at which text applies. + */ + start: Location; + /** + * The last file location at which the text applies. + */ + end: Location; + /** + * Text of diagnostic message. + */ + text: string; + } + interface DiagnosticEventBody { + /** + * The file for which diagnostic information is reported. + */ + file: string; + /** + * An array of diagnostic information items. + */ + diagnostics: Diagnostic[]; + } + /** + * Event message for "syntaxDiag" and "semanticDiag" event types. + * These events provide syntactic and semantic errors for a file. + */ + interface DiagnosticEvent extends Event { + body?: DiagnosticEventBody; + } + interface ConfigFileDiagnosticEventBody { + /** + * The file which trigged the searching and error-checking of the config file + */ + triggerFile: string; + /** + * The name of the found config file. + */ + configFile: string; + /** + * An arry of diagnostic information items for the found config file. + */ + diagnostics: Diagnostic[]; + } + /** + * Event message for "configFileDiag" event type. + * This event provides errors for a found config file. + */ + interface ConfigFileDiagnosticEvent extends Event { + body?: ConfigFileDiagnosticEventBody; + event: "configFileDiag"; + } + /** + * Arguments for reload request. + */ + interface ReloadRequestArgs extends FileRequestArgs { + /** + * Name of temporary file from which to reload file + * contents. May be same as file. + */ + tmpfile: string; + } + /** + * Reload request message; value of command field is "reload". + * Reload contents of file with name given by the 'file' argument + * from temporary file with name given by the 'tmpfile' argument. + * The two names can be identical. + */ + interface ReloadRequest extends FileRequest { + command: CommandTypes.Reload; + arguments: ReloadRequestArgs; + } + /** + * Response to "reload" request. This is just an acknowledgement, so + * no body field is required. + */ + interface ReloadResponse extends Response { + } + /** + * Arguments for saveto request. + */ + interface SavetoRequestArgs extends FileRequestArgs { + /** + * Name of temporary file into which to save server's view of + * file contents. + */ + tmpfile: string; + } + /** + * Saveto request message; value of command field is "saveto". + * For debugging purposes, save to a temporaryfile (named by + * argument 'tmpfile') the contents of file named by argument + * 'file'. The server does not currently send a response to a + * "saveto" request. + */ + interface SavetoRequest extends FileRequest { + command: CommandTypes.Saveto; + arguments: SavetoRequestArgs; + } + /** + * Arguments for navto request message. + */ + interface NavtoRequestArgs extends FileRequestArgs { + /** + * Search term to navigate to from current location; term can + * be '.*' or an identifier prefix. + */ + searchValue: string; + /** + * Optional limit on the number of items to return. + */ + maxResultCount?: number; + projectFileName?: string; + } + /** + * Navto request message; value of command field is "navto". + * Return list of objects giving file locations and symbols that + * match the search term given in argument 'searchTerm'. The + * context for the search is given by the named file. + */ + interface NavtoRequest extends FileRequest { + command: CommandTypes.Navto; + arguments: NavtoRequestArgs; + } + /** + * An item found in a navto response. + */ + interface NavtoItem { + /** + * The symbol's name. + */ + name: string; + /** + * The symbol's kind (such as 'className' or 'parameterName'). + */ + kind: string; + /** + * exact, substring, or prefix. + */ + matchKind?: string; + /** + * If this was a case sensitive or insensitive match. + */ + isCaseSensitive?: boolean; + /** + * Optional modifiers for the kind (such as 'public'). + */ + kindModifiers?: string; + /** + * The file in which the symbol is found. + */ + file: string; + /** + * The location within file at which the symbol is found. + */ + start: Location; + /** + * One past the last character of the symbol. + */ + end: Location; + /** + * Name of symbol's container symbol (if any); for example, + * the class name if symbol is a class member. + */ + containerName?: string; + /** + * Kind of symbol's container symbol (if any). + */ + containerKind?: string; + } + /** + * Navto response message. Body is an array of navto items. Each + * item gives a symbol that matched the search term. + */ + interface NavtoResponse extends Response { + body?: NavtoItem[]; + } + /** + * Arguments for change request message. + */ + interface ChangeRequestArgs extends FormatRequestArgs { + /** + * Optional string to insert at location (file, line, offset). + */ + insertString?: string; + } + /** + * Change request message; value of command field is "change". + * Update the server's view of the file named by argument 'file'. + * Server does not currently send a response to a change request. + */ + interface ChangeRequest extends FileLocationRequest { + command: CommandTypes.Change; + arguments: ChangeRequestArgs; + } + /** + * Response to "brace" request. + */ + interface BraceResponse extends Response { + body?: TextSpan[]; + } + /** + * Brace matching request; value of command field is "brace". + * Return response giving the file locations of matching braces + * found in file at location line, offset. + */ + interface BraceRequest extends FileLocationRequest { + command: CommandTypes.Brace; + } + /** + * NavBar items request; value of command field is "navbar". + * Return response giving the list of navigation bar entries + * extracted from the requested file. + */ + interface NavBarRequest extends FileRequest { + command: CommandTypes.NavBar; + } + /** + * NavTree request; value of command field is "navtree". + * Return response giving the navigation tree of the requested file. + */ + interface NavTreeRequest extends FileRequest { + command: CommandTypes.NavTree; + } + interface NavigationBarItem { + /** + * The item's display text. + */ + text: string; + /** + * The symbol's kind (such as 'className' or 'parameterName'). + */ + kind: string; + /** + * Optional modifiers for the kind (such as 'public'). + */ + kindModifiers?: string; + /** + * The definition locations of the item. + */ + spans: TextSpan[]; + /** + * Optional children. + */ + childItems?: NavigationBarItem[]; + /** + * Number of levels deep this item should appear. + */ + indent: number; + } + /** protocol.NavigationTree is identical to ts.NavigationTree, except using protocol.TextSpan instead of ts.TextSpan */ + interface NavigationTree { + text: string; + kind: string; + kindModifiers: string; + spans: TextSpan[]; + childItems?: NavigationTree[]; + } + interface NavBarResponse extends Response { + body?: NavigationBarItem[]; + } + interface NavTreeResponse extends Response { + body?: NavigationTree; + } + namespace IndentStyle { + type None = "None"; + type Block = "Block"; + type Smart = "Smart"; + } + type IndentStyle = IndentStyle.None | IndentStyle.Block | IndentStyle.Smart; + interface EditorSettings { + baseIndentSize?: number; + indentSize?: number; + tabSize?: number; + newLineCharacter?: string; + convertTabsToSpaces?: boolean; + indentStyle?: IndentStyle | ts.IndentStyle; + } + interface FormatCodeSettings extends EditorSettings { + insertSpaceAfterCommaDelimiter?: boolean; + insertSpaceAfterSemicolonInForStatements?: boolean; + insertSpaceBeforeAndAfterBinaryOperators?: boolean; + insertSpaceAfterKeywordsInControlFlowStatements?: boolean; + insertSpaceAfterFunctionKeywordForAnonymousFunctions?: boolean; + insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis?: boolean; + insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets?: boolean; + insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces?: boolean; + insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces?: boolean; + placeOpenBraceOnNewLineForFunctions?: boolean; + placeOpenBraceOnNewLineForControlBlocks?: boolean; + } + interface CompilerOptions { + allowJs?: boolean; + allowSyntheticDefaultImports?: boolean; + allowUnreachableCode?: boolean; + allowUnusedLabels?: boolean; + baseUrl?: string; + charset?: string; + declaration?: boolean; + declarationDir?: string; + disableSizeLimit?: boolean; + emitBOM?: boolean; + emitDecoratorMetadata?: boolean; + experimentalDecorators?: boolean; + forceConsistentCasingInFileNames?: boolean; + inlineSourceMap?: boolean; + inlineSources?: boolean; + isolatedModules?: boolean; + jsx?: JsxEmit | ts.JsxEmit; + lib?: string[]; + locale?: string; + mapRoot?: string; + maxNodeModuleJsDepth?: number; + module?: ModuleKind | ts.ModuleKind; + moduleResolution?: ModuleResolutionKind | ts.ModuleResolutionKind; + newLine?: NewLineKind | ts.NewLineKind; + noEmit?: boolean; + noEmitHelpers?: boolean; + noEmitOnError?: boolean; + noErrorTruncation?: boolean; + noFallthroughCasesInSwitch?: boolean; + noImplicitAny?: boolean; + noImplicitReturns?: boolean; + noImplicitThis?: boolean; + noUnusedLocals?: boolean; + noUnusedParameters?: boolean; + noImplicitUseStrict?: boolean; + noLib?: boolean; + noResolve?: boolean; + out?: string; + outDir?: string; + outFile?: string; + paths?: MapLike; + preserveConstEnums?: boolean; + project?: string; + reactNamespace?: string; + removeComments?: boolean; + rootDir?: string; + rootDirs?: string[]; + skipLibCheck?: boolean; + skipDefaultLibCheck?: boolean; + sourceMap?: boolean; + sourceRoot?: string; + strictNullChecks?: boolean; + suppressExcessPropertyErrors?: boolean; + suppressImplicitAnyIndexErrors?: boolean; + target?: ScriptTarget | ts.ScriptTarget; + traceResolution?: boolean; + types?: string[]; + /** Paths used to used to compute primary types search locations */ + typeRoots?: string[]; + [option: string]: CompilerOptionsValue | undefined; + } + namespace JsxEmit { + type None = "None"; + type Preserve = "Preserve"; + type React = "React"; + } + type JsxEmit = JsxEmit.None | JsxEmit.Preserve | JsxEmit.React; + namespace ModuleKind { + type None = "None"; + type CommonJS = "CommonJS"; + type AMD = "AMD"; + type UMD = "UMD"; + type System = "System"; + type ES6 = "ES6"; + type ES2015 = "ES2015"; + } + type ModuleKind = ModuleKind.None | ModuleKind.CommonJS | ModuleKind.AMD | ModuleKind.UMD | ModuleKind.System | ModuleKind.ES6 | ModuleKind.ES2015; + namespace ModuleResolutionKind { + type Classic = "Classic"; + type Node = "Node"; + } + type ModuleResolutionKind = ModuleResolutionKind.Classic | ModuleResolutionKind.Node; + namespace NewLineKind { + type Crlf = "Crlf"; + type Lf = "Lf"; + } + type NewLineKind = NewLineKind.Crlf | NewLineKind.Lf; + namespace ScriptTarget { + type ES3 = "ES3"; + type ES5 = "ES5"; + type ES6 = "ES6"; + type ES2015 = "ES2015"; + } + type ScriptTarget = ScriptTarget.ES3 | ScriptTarget.ES5 | ScriptTarget.ES6 | ScriptTarget.ES2015; +} +declare namespace ts.server.protocol { + + interface TextInsertion { + newText: string; + /** The position in newText the caret should point to after the insertion. */ + caretOffset: number; + } + + interface TodoCommentDescriptor { + text: string; + priority: number; + } + + interface TodoComment { + descriptor: TodoCommentDescriptor; + message: string; + position: number; + } + + interface TypingOptions { + enableAutoDiscovery?: boolean; + include?: string[]; + exclude?: string[]; + [option: string]: string[] | boolean | undefined; + } + + interface MapLike { + [index: string]: T; + } + + type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike; +} +declare namespace ts { + // these types are empty stubs for types from services and should not be used directly + export type ScriptKind = never; + export type IndentStyle = never; + export type JsxEmit = never; + export type ModuleKind = never; + export type ModuleResolutionKind = never; + export type NewLineKind = never; + export type ScriptTarget = never; +} +import protocol = ts.server.protocol; +export = protocol; +export as namespace protocol; \ No newline at end of file diff --git a/extensions/typescript/src/protocol.ts b/extensions/typescript/src/protocol.ts deleted file mode 100644 index 7810de8d087..00000000000 --- a/extensions/typescript/src/protocol.ts +++ /dev/null @@ -1,1754 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -export namespace CommandTypes { - export type Brace = 'brace'; - export type BraceCompletion = 'braceCompletion'; - export type Change = 'change'; - export type Close = 'close'; - export type Completions = 'completions'; - export type CompletionDetails = 'completionEntryDetails'; - export type CompileOnSaveAffectedFileList = 'compileOnSaveAffectedFileList'; - export type CompileOnSaveEmitFile = 'compileOnSaveEmitFile'; - export type Configure = 'configure'; - export type Definition = 'definition'; - export type Exit = 'exit'; - export type Format = 'format'; - export type Formatonkey = 'formatonkey'; - export type Geterr = 'geterr'; - export type GeterrForProject = 'geterrForProject'; - export type SemanticDiagnosticsSync = 'semanticDiagnosticsSync'; - export type SyntacticDiagnosticsSync = 'syntacticDiagnosticsSync'; - export type NavBar = 'navbar'; - export type Navto = 'navto'; - export type NavTree = 'navtree'; - export type NavTreeFull = 'navtree-full'; - export type Occurrences = 'occurrences'; - export type DocumentHighlights = 'documentHighlights'; - export type Open = 'open'; - export type Quickinfo = 'quickinfo'; - export type References = 'references'; - export type Reload = 'reload'; - export type Rename = 'rename'; - export type Saveto = 'saveto'; - export type SignatureHelp = 'signatureHelp'; - export type TypeDefinition = 'typeDefinition'; - export type ProjectInfo = 'projectInfo'; - export type ReloadProjects = 'reloadProjects'; - export type Unknown = 'unknown'; - export type OpenExternalProject = 'openExternalProject'; - export type OpenExternalProjects = 'openExternalProjects'; - export type CloseExternalProject = 'closeExternalProject'; - export type TodoComments = 'todoComments'; - export type Indentation = 'indentation'; - export type DocCommentTemplate = 'docCommentTemplate'; - export type CompilerOptionsForInferredProjects = 'compilerOptionsForInferredProjects'; -} -/** - * A TypeScript Server message - */ -export interface Message { - /** - * Sequence number of the message - */ - seq: number; - /** - * One of "request", "response", or "event" - */ - type: string; -} -/** - * Client-initiated request message - */ -export interface Request extends Message { - /** - * The command to execute - */ - command: string; - /** - * Object containing arguments for the command - */ - arguments?: any; -} -/** - * Request to reload the project structure for all the opened files - */ -export interface ReloadProjectsRequest extends Message { - command: CommandTypes.ReloadProjects; -} -/** - * Server-initiated event message - */ -export interface Event extends Message { - /** - * Name of event - */ - event: string; - /** - * Event-specific information - */ - body?: any; -} -/** - * Response by server to client request message. - */ -export interface Response extends Message { - /** - * Sequence number of the request message. - */ - request_seq: number; - /** - * Outcome of the request. - */ - success: boolean; - /** - * The command requested. - */ - command: string; - /** - * Contains error message if success === false. - */ - message?: string; - /** - * Contains message body if success === true. - */ - body?: any; -} -/** - * Arguments for FileRequest messages. - */ -export interface FileRequestArgs { - /** - * The file for the request (absolute pathname required). - */ - file: string; - projectFileName?: string; -} -/** - * Requests a JS Doc comment template for a given position - */ -export interface DocCommentTemplateRequest extends FileLocationRequest { - command: CommandTypes.DocCommentTemplate; -} -/** - * Response to DocCommentTemplateRequest - */ -export interface DocCommandTemplateResponse extends Response { - body?: TextInsertion; -} -/** - * A request to get TODO comments from the file - */ -export interface TodoCommentRequest extends FileRequest { - command: CommandTypes.TodoComments; - arguments: TodoCommentRequestArgs; -} -/** - * Arguments for TodoCommentRequest request. - */ -export interface TodoCommentRequestArgs extends FileRequestArgs { - /** - * Array of target TodoCommentDescriptors that describes TODO comments to be found - */ - descriptors: TodoCommentDescriptor[]; -} -/** - * Response for TodoCommentRequest request. - */ -export interface TodoCommentsResponse extends Response { - body?: TodoComment[]; -} -/** - * A request to get indentation for a location in file - */ -export interface IndentationRequest extends FileLocationRequest { - command: CommandTypes.Indentation; - arguments: IndentationRequestArgs; -} -/** - * Response for IndentationRequest request. - */ -export interface IndentationResponse extends Response { - body?: IndentationResult; -} -/** - * Indentation result representing where indentation should be placed - */ -export interface IndentationResult { - /** - * The base position in the document that the indent should be relative to - */ - position: number; - /** - * The number of columns the indent should be at relative to the position's column. - */ - indentation: number; -} -/** - * Arguments for IndentationRequest request. - */ -export interface IndentationRequestArgs extends FileLocationRequestArgs { - /** - * An optional set of settings to be used when computing indentation. - * If argument is omitted - then it will use settings for file that were previously set via 'configure' request or global settings. - */ - options?: EditorSettings; -} -/** - * Arguments for ProjectInfoRequest request. - */ -export interface ProjectInfoRequestArgs extends FileRequestArgs { - /** - * Indicate if the file name list of the project is needed - */ - needFileNameList: boolean; -} -/** - * A request to get the project information of the current file. - */ -export interface ProjectInfoRequest extends Request { - command: CommandTypes.ProjectInfo; - arguments: ProjectInfoRequestArgs; -} -/** - * A request to retrieve compiler options diagnostics for a project - */ -export interface CompilerOptionsDiagnosticsRequest extends Request { - arguments: CompilerOptionsDiagnosticsRequestArgs; -} -/** - * Arguments for CompilerOptionsDiagnosticsRequest request. - */ -export interface CompilerOptionsDiagnosticsRequestArgs { - /** - * Name of the project to retrieve compiler options diagnostics. - */ - projectFileName: string; -} -/** - * Response message body for "projectInfo" request - */ -export interface ProjectInfo { - /** - * For configured project, this is the normalized path of the 'tsconfig.json' file - * For inferred project, this is undefined - */ - configFileName: string; - /** - * The list of normalized file name in the project, including 'lib.d.ts' - */ - fileNames?: string[]; - /** - * Indicates if the project has a active language service instance - */ - languageServiceDisabled?: boolean; -} -/** - * Represents diagnostic info that includes location of diagnostic in two forms - * - start position and length of the error span - * - startLocation and endLocation - a pair of Location objects that store start/end line and offset of the error span. - */ -export interface DiagnosticWithLinePosition { - message: string; - start: number; - length: number; - startLocation: Location; - endLocation: Location; - category: string; - code: number; -} -/** - * Response message for "projectInfo" request - */ -export interface ProjectInfoResponse extends Response { - body?: ProjectInfo; -} -/** - * Request whose sole parameter is a file name. - */ -export interface FileRequest extends Request { - arguments: FileRequestArgs; -} -/** - * Instances of this interface specify a location in a source file: - * (file, line, character offset), where line and character offset are 1-based. - */ -export interface FileLocationRequestArgs extends FileRequestArgs { - /** - * The line number for the request (1-based). - */ - line: number; - /** - * The character offset (on the line) for the request (1-based). - */ - offset: number; -} -/** - * A request whose arguments specify a file location (file, line, col). - */ -export interface FileLocationRequest extends FileRequest { - arguments: FileLocationRequestArgs; -} -/** - * Arguments for EncodedSemanticClassificationsRequest request. - */ -export interface EncodedSemanticClassificationsRequestArgs extends FileRequestArgs { - /** - * Start position of the span. - */ - start: number; - /** - * Length of the span. - */ - length: number; -} -/** - * Arguments in document highlight request; include: filesToSearch, file, - * line, offset. - */ -export interface DocumentHighlightsRequestArgs extends FileLocationRequestArgs { - /** - * List of files to search for document highlights. - */ - filesToSearch: string[]; -} -/** - * Go to definition request; value of command field is - * "definition". Return response giving the file locations that - * define the symbol found in file at location line, col. - */ -export interface DefinitionRequest extends FileLocationRequest { - command: CommandTypes.Definition; -} -/** - * Go to type request; value of command field is - * "typeDefinition". Return response giving the file locations that - * define the type for the symbol found in file at location line, col. - */ -export interface TypeDefinitionRequest extends FileLocationRequest { - command: CommandTypes.TypeDefinition; -} -/** - * Location in source code expressed as (one-based) line and character offset. - */ -export interface Location { - line: number; - offset: number; -} -/** - * Object found in response messages defining a span of text in source code. - */ -export interface TextSpan { - /** - * First character of the definition. - */ - start: Location; - /** - * One character past last character of the definition. - */ - end: Location; -} -/** - * Object found in response messages defining a span of text in a specific source file. - */ -export interface FileSpan extends TextSpan { - /** - * File containing text span. - */ - file: string; -} -/** - * Definition response message. Gives text range for definition. - */ -export interface DefinitionResponse extends Response { - body?: FileSpan[]; -} -/** - * Definition response message. Gives text range for definition. - */ -export interface TypeDefinitionResponse extends Response { - body?: FileSpan[]; -} -/** - * Implementation response message. Gives text range for implementations. - */ -export interface ImplementationResponse extends Response { - body?: FileSpan[]; -} -/** - * Request to get brace completion for a location in the file. - */ -export interface BraceCompletionRequest extends FileLocationRequest { - command: CommandTypes.BraceCompletion; - arguments: BraceCompletionRequestArgs; -} -/** - * Argument for BraceCompletionRequest request. - */ -export interface BraceCompletionRequestArgs extends FileLocationRequestArgs { - /** - * Kind of opening brace - */ - openingBrace: string; -} -/** - * Get occurrences request; value of command field is - * "occurrences". Return response giving spans that are relevant - * in the file at a given line and column. - */ -export interface OccurrencesRequest extends FileLocationRequest { - command: CommandTypes.Occurrences; -} -export interface OccurrencesResponseItem extends FileSpan { - /** - * True if the occurrence is a write location, false otherwise. - */ - isWriteAccess: boolean; -} -export interface OccurrencesResponse extends Response { - body?: OccurrencesResponseItem[]; -} -/** - * Get document highlights request; value of command field is - * "documentHighlights". Return response giving spans that are relevant - * in the file at a given line and column. - */ -export interface DocumentHighlightsRequest extends FileLocationRequest { - command: CommandTypes.DocumentHighlights; - arguments: DocumentHighlightsRequestArgs; -} -/** - * Span augmented with extra information that denotes the kind of the highlighting to be used for span. - * Kind is taken from HighlightSpanKind type. - */ -export interface HighlightSpan extends TextSpan { - kind: string; -} -/** - * Represents a set of highligh spans for a give name - */ -export interface DocumentHighlightsItem { - /** - * File containing highlight spans. - */ - file: string; - /** - * Spans to highlight in file. - */ - highlightSpans: HighlightSpan[]; -} -/** - * Response for a DocumentHighlightsRequest request. - */ -export interface DocumentHighlightsResponse extends Response { - body?: DocumentHighlightsItem[]; -} -/** - * Find references request; value of command field is - * "references". Return response giving the file locations that - * reference the symbol found in file at location line, col. - */ -export interface ReferencesRequest extends FileLocationRequest { - command: CommandTypes.References; -} -export interface ReferencesResponseItem extends FileSpan { - /** Text of line containing the reference. Including this - * with the response avoids latency of editor loading files - * to show text of reference line (the server already has - * loaded the referencing files). - */ - lineText: string; - /** - * True if reference is a write location, false otherwise. - */ - isWriteAccess: boolean; - /** - * True if reference is a definition, false otherwise. - */ - isDefinition: boolean; -} -/** - * The body of a "references" response message. - */ -export interface ReferencesResponseBody { - /** - * The file locations referencing the symbol. - */ - refs: ReferencesResponseItem[]; - /** - * The name of the symbol. - */ - symbolName: string; - /** - * The start character offset of the symbol (on the line provided by the references request). - */ - symbolStartOffset: number; - /** - * The full display name of the symbol. - */ - symbolDisplayString: string; -} -/** - * Response to "references" request. - */ -export interface ReferencesResponse extends Response { - body?: ReferencesResponseBody; -} -/** - * Argument for RenameRequest request. - */ -export interface RenameRequestArgs extends FileLocationRequestArgs { - /** - * Should text at specified location be found/changed in comments? - */ - findInComments?: boolean; - /** - * Should text at specified location be found/changed in strings? - */ - findInStrings?: boolean; -} -/** - * Rename request; value of command field is "rename". Return - * response giving the file locations that reference the symbol - * found in file at location line, col. Also return full display - * name of the symbol so that client can print it unambiguously. - */ -export interface RenameRequest extends FileLocationRequest { - command: CommandTypes.Rename; - arguments: RenameRequestArgs; -} -/** - * Information about the item to be renamed. - */ -export interface RenameInfo { - /** - * True if item can be renamed. - */ - canRename: boolean; - /** - * Error message if item can not be renamed. - */ - localizedErrorMessage?: string; - /** - * Display name of the item to be renamed. - */ - displayName: string; - /** - * Full display name of item to be renamed. - */ - fullDisplayName: string; - /** - * The items's kind (such as 'className' or 'parameterName' or plain 'text'). - */ - kind: string; - /** - * Optional modifiers for the kind (such as 'public'). - */ - kindModifiers: string; -} -/** - * A group of text spans, all in 'file'. - */ -export interface SpanGroup { - /** The file to which the spans apply */ - file: string; - /** The text spans in this group */ - locs: TextSpan[]; -} -export interface RenameResponseBody { - /** - * Information about the item to be renamed. - */ - info: RenameInfo; - /** - * An array of span groups (one per file) that refer to the item to be renamed. - */ - locs: SpanGroup[]; -} -/** - * Rename response message. - */ -export interface RenameResponse extends Response { - body?: RenameResponseBody; -} -/** - * Represents a file in external project. - * External project is project whose set of files, compilation options and open\close state - * is maintained by the client (i.e. if all this data come from .csproj file in Visual Studio). - * External project will exist even if all files in it are closed and should be closed explicity. - * If external project includes one or more tsconfig.json/jsconfig.json files then tsserver will - * create configured project for every config file but will maintain a link that these projects were created - * as a result of opening external project so they should be removed once external project is closed. - */ -export interface ExternalFile { - /** - * Name of file file - */ - fileName: string; - /** - * Script kind of the file - */ - scriptKind?: ScriptKind; - /** - * Whether file has mixed content (i.e. .cshtml file that combines html markup with C#/JavaScript) - */ - hasMixedContent?: boolean; - /** - * Content of the file - */ - content?: string; -} -/** - * Represent an external project - */ -export interface ExternalProject { - /** - * Project name - */ - projectFileName: string; - /** - * List of root files in project - */ - rootFiles: ExternalFile[]; - /** - * Compiler options for the project - */ - options: ExternalProjectCompilerOptions; - /** - * Explicitly specified typing options for the project - */ - typingOptions?: TypingOptions; -} -/** - * For external projects, some of the project settings are sent together with - * compiler settings. - */ -export interface ExternalProjectCompilerOptions extends CompilerOptions { - /** - * If compile on save is enabled for the project - */ - compileOnSave?: boolean; -} -/** - * Contains information about current project version - */ -export interface ProjectVersionInfo { - /** - * Project name - */ - projectName: string; - /** - * true if project is inferred or false if project is external or configured - */ - isInferred: boolean; - /** - * Project version - */ - version: number; - /** - * Current set of compiler options for project - */ - options: CompilerOptions; -} -/** - * Represents a set of changes that happen in project - */ -export interface ProjectChanges { - /** - * List of added files - */ - added: string[]; - /** - * List of removed files - */ - removed: string[]; -} -/** - * Describes set of files in the project. - * info might be omitted in case of inferred projects - * if files is set - then this is the entire set of files in the project - * if changes is set - then this is the set of changes that should be applied to existing project - * otherwise - assume that nothing is changed - */ -export interface ProjectFiles { - /** - * Information abount project verison - */ - info?: ProjectVersionInfo; - /** - * List of files in project (might be omitted if current state of project can be computed using only information from 'changes') - */ - files?: string[]; - /** - * Set of changes in project (omitted if the entire set of files in project should be replaced) - */ - changes?: ProjectChanges; -} -/** - * Combines project information with project level errors. - */ -export interface ProjectFilesWithDiagnostics extends ProjectFiles { - /** - * List of errors in project - */ - projectErrors: DiagnosticWithLinePosition[]; -} -/** - * Information found in a configure request. - */ -export interface ConfigureRequestArguments { - /** - * Information about the host, for example 'Emacs 24.4' or - * 'Sublime Text version 3075' - */ - hostInfo?: string; - /** - * If present, tab settings apply only to this file. - */ - file?: string; - /** - * The format options to use during formatting and other code editing features. - */ - formatOptions?: FormatCodeSettings; -} -/** - * Configure request; value of command field is "configure". Specifies - * host information, such as host type, tab size, and indent size. - */ -export interface ConfigureRequest extends Request { - command: CommandTypes.Configure; - arguments: ConfigureRequestArguments; -} -/** - * Response to "configure" request. This is just an acknowledgement, so - * no body field is required. - */ -export interface ConfigureResponse extends Response { -} -/** - * Information found in an "open" request. - */ -export interface OpenRequestArgs extends FileRequestArgs { - /** - * Used when a version of the file content is known to be more up to date than the one on disk. - * Then the known content will be used upon opening instead of the disk copy - */ - fileContent?: string; - /** - * Used to specify the script kind of the file explicitly. It could be one of the following: - * "TS", "JS", "TSX", "JSX" - */ - scriptKindName?: 'TS' | 'JS' | 'TSX' | 'JSX'; -} -/** - * Open request; value of command field is "open". Notify the - * server that the client has file open. The server will not - * monitor the filesystem for changes in this file and will assume - * that the client is updating the server (using the change and/or - * reload messages) when the file changes. Server does not currently - * send a response to an open request. - */ -export interface OpenRequest extends Request { - command: CommandTypes.Open; - arguments: OpenRequestArgs; -} -/** - * Request to open or update external project - */ -export interface OpenExternalProjectRequest extends Request { - command: CommandTypes.OpenExternalProject; - arguments: OpenExternalProjectArgs; -} -/** - * Arguments to OpenExternalProjectRequest request - */ -export type OpenExternalProjectArgs = ExternalProject; -/** - * Request to open multiple external projects - */ -export interface OpenExternalProjectsRequest extends Request { - command: CommandTypes.OpenExternalProjects; - arguments: OpenExternalProjectsArgs; -} -/** - * Arguments to OpenExternalProjectsRequest - */ -export interface OpenExternalProjectsArgs { - /** - * List of external projects to open or update - */ - projects: ExternalProject[]; -} -/** - * Response to OpenExternalProjectRequest request. This is just an acknowledgement, so - * no body field is required. - */ -export interface OpenExternalProjectResponse extends Response { -} -/** - * Response to OpenExternalProjectsRequest request. This is just an acknowledgement, so - * no body field is required. - */ -export interface OpenExternalProjectsResponse extends Response { -} -/** - * Request to close external project. - */ -export interface CloseExternalProjectRequest extends Request { - command: CommandTypes.CloseExternalProject; - arguments: CloseExternalProjectRequestArgs; -} -/** - * Arguments to CloseExternalProjectRequest request - */ -export interface CloseExternalProjectRequestArgs { - /** - * Name of the project to close - */ - projectFileName: string; -} -/** - * Response to CloseExternalProjectRequest request. This is just an acknowledgement, so - * no body field is required. - */ -export interface CloseExternalProjectResponse extends Response { -} -/** - * Arguments to SynchronizeProjectListRequest - */ -export interface SynchronizeProjectListRequestArgs { - /** - * List of last known projects - */ - knownProjects: ProjectVersionInfo[]; -} -/** - * Request to set compiler options for inferred projects. - * External projects are opened / closed explicitly. - * Configured projects are opened when user opens loose file that has 'tsconfig.json' or 'jsconfig.json' anywhere in one of containing folders. - * This configuration file will be used to obtain a list of files and configuration settings for the project. - * Inferred projects are created when user opens a loose file that is not the part of external project - * or configured project and will contain only open file and transitive closure of referenced files if 'useOneInferredProject' is false, - * or all open loose files and its transitive closure of referenced files if 'useOneInferredProject' is true. - */ -export interface SetCompilerOptionsForInferredProjectsRequest extends Request { - command: CommandTypes.CompilerOptionsForInferredProjects; - arguments: SetCompilerOptionsForInferredProjectsArgs; -} -/** - * Argument for SetCompilerOptionsForInferredProjectsRequest request. - */ -export interface SetCompilerOptionsForInferredProjectsArgs { - /** - * Compiler options to be used with inferred projects. - */ - options: ExternalProjectCompilerOptions; -} -/** - * Response to SetCompilerOptionsForInferredProjectsResponse request. This is just an acknowledgement, so - * no body field is required. - */ -export interface SetCompilerOptionsForInferredProjectsResponse extends Response { -} -/** - * Exit request; value of command field is "exit". Ask the server process - * to exit. - */ -export interface ExitRequest extends Request { - command: CommandTypes.Exit; -} -/** - * Close request; value of command field is "close". Notify the - * server that the client has closed a previously open file. If - * file is still referenced by open files, the server will resume - * monitoring the filesystem for changes to file. Server does not - * currently send a response to a close request. - */ -export interface CloseRequest extends FileRequest { - command: CommandTypes.Close; -} -/** - * Request to obtain the list of files that should be regenerated if target file is recompiled. - * NOTE: this us query-only operation and does not generate any output on disk. - */ -export interface CompileOnSaveAffectedFileListRequest extends FileRequest { - command: CommandTypes.CompileOnSaveAffectedFileList; -} -/** - * Contains a list of files that should be regenerated in a project - */ -export interface CompileOnSaveAffectedFileListSingleProject { - /** - * Project name - */ - projectFileName: string; - /** - * List of files names that should be recompiled - */ - fileNames: string[]; -} -/** - * Response for CompileOnSaveAffectedFileListRequest request; - */ -export interface CompileOnSaveAffectedFileListResponse extends Response { - body: CompileOnSaveAffectedFileListSingleProject[]; -} -/** - * Request to recompile the file. All generated outputs (.js, .d.ts or .js.map files) is written on disk. - */ -export interface CompileOnSaveEmitFileRequest extends FileRequest { - command: CommandTypes.CompileOnSaveEmitFile; - arguments: CompileOnSaveEmitFileRequestArgs; -} -/** - * Arguments for CompileOnSaveEmitFileRequest - */ -export interface CompileOnSaveEmitFileRequestArgs extends FileRequestArgs { - /** - * if true - then file should be recompiled even if it does not have any changes. - */ - forced?: boolean; -} -/** - * Quickinfo request; value of command field is - * "quickinfo". Return response giving a quick type and - * documentation string for the symbol found in file at location - * line, col. - */ -export interface QuickInfoRequest extends FileLocationRequest { - command: CommandTypes.Quickinfo; -} -/** - * Body of QuickInfoResponse. - */ -export interface QuickInfoResponseBody { - /** - * The symbol's kind (such as 'className' or 'parameterName' or plain 'text'). - */ - kind: string; - /** - * Optional modifiers for the kind (such as 'public'). - */ - kindModifiers: string; - /** - * Starting file location of symbol. - */ - start: Location; - /** - * One past last character of symbol. - */ - end: Location; - /** - * Type and kind of symbol. - */ - displayString: string; - /** - * Documentation associated with symbol. - */ - documentation: string; -} -/** - * Quickinfo response message. - */ -export interface QuickInfoResponse extends Response { - body?: QuickInfoResponseBody; -} -/** - * Arguments for format messages. - */ -export interface FormatRequestArgs extends FileLocationRequestArgs { - /** - * Last line of range for which to format text in file. - */ - endLine: number; - /** - * Character offset on last line of range for which to format text in file. - */ - endOffset: number; - /** - * Format options to be used. - */ - options?: FormatCodeSettings; -} -/** - * Format request; value of command field is "format". Return - * response giving zero or more edit instructions. The edit - * instructions will be sorted in file order. Applying the edit - * instructions in reverse to file will result in correctly - * reformatted text. - */ -export interface FormatRequest extends FileLocationRequest { - command: CommandTypes.Format; - arguments: FormatRequestArgs; -} -/** - * Object found in response messages defining an editing - * instruction for a span of text in source code. The effect of - * this instruction is to replace the text starting at start and - * ending one character before end with newText. For an insertion, - * the text span is empty. For a deletion, newText is empty. - */ -export interface CodeEdit { - /** - * First character of the text span to edit. - */ - start: Location; - /** - * One character past last character of the text span to edit. - */ - end: Location; - /** - * Replace the span defined above with this string (may be - * the empty string). - */ - newText: string; -} -/** - * Format and format on key response message. - */ -export interface FormatResponse extends Response { - body?: CodeEdit[]; -} -/** - * Arguments for format on key messages. - */ -export interface FormatOnKeyRequestArgs extends FileLocationRequestArgs { - /** - * Key pressed (';', '\n', or '}'). - */ - key: string; - options?: FormatCodeSettings; -} -/** - * Format on key request; value of command field is - * "formatonkey". Given file location and key typed (as string), - * return response giving zero or more edit instructions. The - * edit instructions will be sorted in file order. Applying the - * edit instructions in reverse to file will result in correctly - * reformatted text. - */ -export interface FormatOnKeyRequest extends FileLocationRequest { - command: CommandTypes.Formatonkey; - arguments: FormatOnKeyRequestArgs; -} -/** - * Arguments for completions messages. - */ -export interface CompletionsRequestArgs extends FileLocationRequestArgs { - /** - * Optional prefix to apply to possible completions. - */ - prefix?: string; -} -/** - * Completions request; value of command field is "completions". - * Given a file location (file, line, col) and a prefix (which may - * be the empty string), return the possible completions that - * begin with prefix. - */ -export interface CompletionsRequest extends FileLocationRequest { - command: CommandTypes.Completions; - arguments: CompletionsRequestArgs; -} -/** - * Arguments for completion details request. - */ -export interface CompletionDetailsRequestArgs extends FileLocationRequestArgs { - /** - * Names of one or more entries for which to obtain details. - */ - entryNames: string[]; -} -/** - * Completion entry details request; value of command field is - * "completionEntryDetails". Given a file location (file, line, - * col) and an array of completion entry names return more - * detailed information for each completion entry. - */ -export interface CompletionDetailsRequest extends FileLocationRequest { - command: CommandTypes.CompletionDetails; - arguments: CompletionDetailsRequestArgs; -} -/** - * Part of a symbol description. - */ -export interface SymbolDisplayPart { - /** - * Text of an item describing the symbol. - */ - text: string; - /** - * The symbol's kind (such as 'className' or 'parameterName' or plain 'text'). - */ - kind: string; -} -/** - * An item found in a completion response. - */ -export interface CompletionEntry { - /** - * The symbol's name. - */ - name: string; - /** - * The symbol's kind (such as 'className' or 'parameterName'). - */ - kind: string; - /** - * Optional modifiers for the kind (such as 'public'). - */ - kindModifiers: string; - /** - * A string that is used for comparing completion items so that they can be ordered. This - * is often the same as the name but may be different in certain circumstances. - */ - sortText: string; - /** - * An optional span that indicates the text to be replaced by this completion item. If present, - * this span should be used instead of the default one. - */ - replacementSpan?: TextSpan; -} -/** - * Additional completion entry details, available on demand - */ -export interface CompletionEntryDetails { - /** - * The symbol's name. - */ - name: string; - /** - * The symbol's kind (such as 'className' or 'parameterName'). - */ - kind: string; - /** - * Optional modifiers for the kind (such as 'public'). - */ - kindModifiers: string; - /** - * Display parts of the symbol (similar to quick info). - */ - displayParts: SymbolDisplayPart[]; - /** - * Documentation strings for the symbol. - */ - documentation: SymbolDisplayPart[]; -} -export interface CompletionsResponse extends Response { - body?: CompletionEntry[]; -} -export interface CompletionDetailsResponse extends Response { - body?: CompletionEntryDetails[]; -} -/** - * Signature help information for a single parameter - */ -export interface SignatureHelpParameter { - /** - * The parameter's name - */ - name: string; - /** - * Documentation of the parameter. - */ - documentation: SymbolDisplayPart[]; - /** - * Display parts of the parameter. - */ - displayParts: SymbolDisplayPart[]; - /** - * Whether the parameter is optional or not. - */ - isOptional: boolean; -} -/** - * Represents a single signature to show in signature help. - */ -export interface SignatureHelpItem { - /** - * Whether the signature accepts a variable number of arguments. - */ - isVariadic: boolean; - /** - * The prefix display parts. - */ - prefixDisplayParts: SymbolDisplayPart[]; - /** - * The suffix display parts. - */ - suffixDisplayParts: SymbolDisplayPart[]; - /** - * The separator display parts. - */ - separatorDisplayParts: SymbolDisplayPart[]; - /** - * The signature helps items for the parameters. - */ - parameters: SignatureHelpParameter[]; - /** - * The signature's documentation - */ - documentation: SymbolDisplayPart[]; -} -/** - * Signature help items found in the response of a signature help request. - */ -export interface SignatureHelpItems { - /** - * The signature help items. - */ - items: SignatureHelpItem[]; - /** - * The span for which signature help should appear on a signature - */ - applicableSpan: TextSpan; - /** - * The item selected in the set of available help items. - */ - selectedItemIndex: number; - /** - * The argument selected in the set of parameters. - */ - argumentIndex: number; - /** - * The argument count - */ - argumentCount: number; -} -/** - * Arguments of a signature help request. - */ -export interface SignatureHelpRequestArgs extends FileLocationRequestArgs { -} -/** - * Signature help request; value of command field is "signatureHelp". - * Given a file location (file, line, col), return the signature - * help. - */ -export interface SignatureHelpRequest extends FileLocationRequest { - command: CommandTypes.SignatureHelp; - arguments: SignatureHelpRequestArgs; -} -/** - * Response object for a SignatureHelpRequest. - */ -export interface SignatureHelpResponse extends Response { - body?: SignatureHelpItems; -} -/** - * Synchronous request for semantic diagnostics of one file. - */ -export interface SemanticDiagnosticsSyncRequest extends FileRequest { - command: CommandTypes.SemanticDiagnosticsSync; - arguments: SemanticDiagnosticsSyncRequestArgs; -} -export interface SemanticDiagnosticsSyncRequestArgs extends FileRequestArgs { - includeLinePosition?: boolean; -} -/** - * Response object for synchronous sematic diagnostics request. - */ -export interface SemanticDiagnosticsSyncResponse extends Response { - body?: Diagnostic[] | DiagnosticWithLinePosition[]; -} -/** - * Synchronous request for syntactic diagnostics of one file. - */ -export interface SyntacticDiagnosticsSyncRequest extends FileRequest { - command: CommandTypes.SyntacticDiagnosticsSync; - arguments: SyntacticDiagnosticsSyncRequestArgs; -} -export interface SyntacticDiagnosticsSyncRequestArgs extends FileRequestArgs { - includeLinePosition?: boolean; -} -/** - * Response object for synchronous syntactic diagnostics request. - */ -export interface SyntacticDiagnosticsSyncResponse extends Response { - body?: Diagnostic[] | DiagnosticWithLinePosition[]; -} -/** -* Arguments for GeterrForProject request. -*/ -export interface GeterrForProjectRequestArgs { - /** - * the file requesting project error list - */ - file: string; - /** - * Delay in milliseconds to wait before starting to compute - * errors for the files in the file list - */ - delay: number; -} -/** - * GeterrForProjectRequest request; value of command field is - * "geterrForProject". It works similarly with 'Geterr', only - * it request for every file in this project. - */ -export interface GeterrForProjectRequest extends Request { - command: CommandTypes.GeterrForProject; - arguments: GeterrForProjectRequestArgs; -} -/** - * Arguments for geterr messages. - */ -export interface GeterrRequestArgs { - /** - * List of file names for which to compute compiler errors. - * The files will be checked in list order. - */ - files: string[]; - /** - * Delay in milliseconds to wait before starting to compute - * errors for the files in the file list - */ - delay: number; -} -/** - * Geterr request; value of command field is "geterr". Wait for - * delay milliseconds and then, if during the wait no change or - * reload messages have arrived for the first file in the files - * list, get the syntactic errors for the file, field requests, - * and then get the semantic errors for the file. Repeat with a - * smaller delay for each subsequent file on the files list. Best - * practice for an editor is to send a file list containing each - * file that is currently visible, in most-recently-used order. - */ -export interface GeterrRequest extends Request { - command: CommandTypes.Geterr; - arguments: GeterrRequestArgs; -} -/** - * Item of diagnostic information found in a DiagnosticEvent message. - */ -export interface Diagnostic { - /** - * Starting file location at which text applies. - */ - start: Location; - /** - * The last file location at which the text applies. - */ - end: Location; - /** - * Text of diagnostic message. - */ - text: string; -} -export interface DiagnosticEventBody { - /** - * The file for which diagnostic information is reported. - */ - file: string; - /** - * An array of diagnostic information items. - */ - diagnostics: Diagnostic[]; -} -/** - * Event message for "syntaxDiag" and "semanticDiag" event types. - * These events provide syntactic and semantic errors for a file. - */ -export interface DiagnosticEvent extends Event { - body?: DiagnosticEventBody; -} -export interface ConfigFileDiagnosticEventBody { - /** - * The file which trigged the searching and error-checking of the config file - */ - triggerFile: string; - /** - * The name of the found config file. - */ - configFile: string; - /** - * An arry of diagnostic information items for the found config file. - */ - diagnostics: Diagnostic[]; -} -/** - * Event message for "configFileDiag" event type. - * This event provides errors for a found config file. - */ -export interface ConfigFileDiagnosticEvent extends Event { - body?: ConfigFileDiagnosticEventBody; - event: 'configFileDiag'; -} -/** - * Arguments for reload request. - */ -export interface ReloadRequestArgs extends FileRequestArgs { - /** - * Name of temporary file from which to reload file - * contents. May be same as file. - */ - tmpfile: string; -} -/** - * Reload request message; value of command field is "reload". - * Reload contents of file with name given by the 'file' argument - * from temporary file with name given by the 'tmpfile' argument. - * The two names can be identical. - */ -export interface ReloadRequest extends FileRequest { - command: CommandTypes.Reload; - arguments: ReloadRequestArgs; -} -/** - * Response to "reload" request. This is just an acknowledgement, so - * no body field is required. - */ -export interface ReloadResponse extends Response { -} -/** - * Arguments for saveto request. - */ -export interface SavetoRequestArgs extends FileRequestArgs { - /** - * Name of temporary file into which to save server's view of - * file contents. - */ - tmpfile: string; -} -/** - * Saveto request message; value of command field is "saveto". - * For debugging purposes, save to a temporaryfile (named by - * argument 'tmpfile') the contents of file named by argument - * 'file'. The server does not currently send a response to a - * "saveto" request. - */ -export interface SavetoRequest extends FileRequest { - command: CommandTypes.Saveto; - arguments: SavetoRequestArgs; -} -/** - * Arguments for navto request message. - */ -export interface NavtoRequestArgs extends FileRequestArgs { - /** - * Search term to navigate to from current location; term can - * be '.*' or an identifier prefix. - */ - searchValue: string; - /** - * Optional limit on the number of items to return. - */ - maxResultCount?: number; - projectFileName?: string; -} -/** - * Navto request message; value of command field is "navto". - * Return list of objects giving file locations and symbols that - * match the search term given in argument 'searchTerm'. The - * context for the search is given by the named file. - */ -export interface NavtoRequest extends FileRequest { - command: CommandTypes.Navto; - arguments: NavtoRequestArgs; -} -/** - * An item found in a navto response. - */ -export interface NavtoItem { - /** - * The symbol's name. - */ - name: string; - /** - * The symbol's kind (such as 'className' or 'parameterName'). - */ - kind: string; - /** - * exact, substring, or prefix. - */ - matchKind?: string; - /** - * If this was a case sensitive or insensitive match. - */ - isCaseSensitive?: boolean; - /** - * Optional modifiers for the kind (such as 'public'). - */ - kindModifiers?: string; - /** - * The file in which the symbol is found. - */ - file: string; - /** - * The location within file at which the symbol is found. - */ - start: Location; - /** - * One past the last character of the symbol. - */ - end: Location; - /** - * Name of symbol's container symbol (if any); for example, - * the class name if symbol is a class member. - */ - containerName?: string; - /** - * Kind of symbol's container symbol (if any). - */ - containerKind?: string; -} -/** - * Navto response message. Body is an array of navto items. Each - * item gives a symbol that matched the search term. - */ -export interface NavtoResponse extends Response { - body?: NavtoItem[]; -} -/** - * Arguments for change request message. - */ -export interface ChangeRequestArgs extends FormatRequestArgs { - /** - * Optional string to insert at location (file, line, offset). - */ - insertString?: string; -} -/** - * Change request message; value of command field is "change". - * Update the server's view of the file named by argument 'file'. - * Server does not currently send a response to a change request. - */ -export interface ChangeRequest extends FileLocationRequest { - command: CommandTypes.Change; - arguments: ChangeRequestArgs; -} -/** - * Response to "brace" request. - */ -export interface BraceResponse extends Response { - body?: TextSpan[]; -} -/** - * Brace matching request; value of command field is "brace". - * Return response giving the file locations of matching braces - * found in file at location line, offset. - */ -export interface BraceRequest extends FileLocationRequest { - command: CommandTypes.Brace; -} -/** - * NavBar items request; value of command field is "navbar". - * Return response giving the list of navigation bar entries - * extracted from the requested file. - */ -export interface NavBarRequest extends FileRequest { - command: CommandTypes.NavBar; -} -/** - * NavTree request; value of command field is "navtree". - * Return response giving the navigation tree of the requested file. - */ -export interface NavTreeRequest extends FileRequest { - command: CommandTypes.NavTree; -} -export interface NavigationBarItem { - /** - * The item's display text. - */ - text: string; - /** - * The symbol's kind (such as 'className' or 'parameterName'). - */ - kind: string; - /** - * Optional modifiers for the kind (such as 'public'). - */ - kindModifiers?: string; - /** - * The definition locations of the item. - */ - spans: TextSpan[]; - /** - * Optional children. - */ - childItems?: NavigationBarItem[]; - /** - * Number of levels deep this item should appear. - */ - indent: number; -} -/** protocol.NavigationTree is identical to ts.NavigationTree, except using protocol.TextSpan instead of ts.TextSpan */ -export interface NavigationTree { - text: string; - kind: string; - kindModifiers: string; - spans: TextSpan[]; - childItems?: NavigationTree[]; -} -export interface NavBarResponse extends Response { - body?: NavigationBarItem[]; -} -export interface NavTreeResponse extends Response { - body?: NavigationTree; -} - -export interface TextInsertion { - newText: string; - /** The position in newText the caret should point to after the insertion. */ - caretOffset: number; -} - -export interface TodoCommentDescriptor { - text: string; - priority: number; -} - -export interface TodoComment { - descriptor: TodoCommentDescriptor; - message: string; - position: number; -} - -export interface EditorSettings { - baseIndentSize?: number; - indentSize?: number; - tabSize?: number; - newLineCharacter?: string; - convertTabsToSpaces?: boolean; - indentStyle?: IndentStyle; -} - -export const enum IndentStyle { - None = 0, - Block = 1, - Smart = 2, -} - -export const enum ScriptKind { - Unknown = 0, - JS = 1, - JSX = 2, - TS = 3, - TSX = 4, -} - -export interface TypingOptions { - enableAutoDiscovery?: boolean; - include?: string[]; - exclude?: string[]; - [option: string]: string[] | boolean | undefined; -} - -export interface CompilerOptions { - allowJs?: boolean; - allowSyntheticDefaultImports?: boolean; - allowUnreachableCode?: boolean; - allowUnusedLabels?: boolean; - baseUrl?: string; - charset?: string; - declaration?: boolean; - declarationDir?: string; - disableSizeLimit?: boolean; - emitBOM?: boolean; - emitDecoratorMetadata?: boolean; - experimentalDecorators?: boolean; - forceConsistentCasingInFileNames?: boolean; - inlineSourceMap?: boolean; - inlineSources?: boolean; - isolatedModules?: boolean; - jsx?: JsxEmit; - lib?: string[]; - locale?: string; - mapRoot?: string; - maxNodeModuleJsDepth?: number; - module?: ModuleKind; - moduleResolution?: ModuleResolutionKind; - newLine?: NewLineKind; - noEmit?: boolean; - noEmitHelpers?: boolean; - noEmitOnError?: boolean; - noErrorTruncation?: boolean; - noFallthroughCasesInSwitch?: boolean; - noImplicitAny?: boolean; - noImplicitReturns?: boolean; - noImplicitThis?: boolean; - noUnusedLocals?: boolean; - noUnusedParameters?: boolean; - noImplicitUseStrict?: boolean; - noLib?: boolean; - noResolve?: boolean; - out?: string; - outDir?: string; - outFile?: string; - paths?: MapLike; - preserveConstEnums?: boolean; - project?: string; - reactNamespace?: string; - removeComments?: boolean; - rootDir?: string; - rootDirs?: string[]; - skipLibCheck?: boolean; - skipDefaultLibCheck?: boolean; - sourceMap?: boolean; - sourceRoot?: string; - strictNullChecks?: boolean; - suppressExcessPropertyErrors?: boolean; - suppressImplicitAnyIndexErrors?: boolean; - target?: ScriptTarget; - traceResolution?: boolean; - types?: string[]; - /** Paths used to used to compute primary types search locations */ - typeRoots?: string[]; - [option: string]: CompilerOptionsValue | undefined; -} - -export const enum JsxEmit { - None = 0, - Preserve = 1, - React = 2, -} - -export const enum ModuleKind { - None = 0, - CommonJS = 1, - AMD = 2, - UMD = 3, - System = 4, - ES6 = 5, - ES2015 = 5, -} - -export const enum ModuleResolutionKind { - Classic = 1, - NodeJs = 2, -} - -export const enum NewLineKind { - CarriageReturnLineFeed = 0, - LineFeed = 1, -} - -export interface MapLike { - [index: string]: T; -} - -export const enum ScriptTarget { - ES3 = 0, - ES5 = 1, - ES6 = 2, - ES2015 = 2, - Latest = 2, -} - -export type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike; - -export interface FormatCodeSettings extends EditorSettings { - insertSpaceAfterCommaDelimiter?: boolean; - insertSpaceAfterSemicolonInForStatements?: boolean; - insertSpaceBeforeAndAfterBinaryOperators?: boolean; - insertSpaceAfterKeywordsInControlFlowStatements?: boolean; - insertSpaceAfterFunctionKeywordForAnonymousFunctions?: boolean; - insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis?: boolean; - insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets?: boolean; - insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces?: boolean; - insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces?: boolean; - placeOpenBraceOnNewLineForFunctions?: boolean; - placeOpenBraceOnNewLineForControlBlocks?: boolean; -} diff --git a/extensions/typescript/src/typescriptServiceClient.ts b/extensions/typescript/src/typescriptServiceClient.ts index 14a280ede6a..20f07aee919 100644 --- a/extensions/typescript/src/typescriptServiceClient.ts +++ b/extensions/typescript/src/typescriptServiceClient.ts @@ -456,9 +456,10 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient this.execute('configure', configureOptions); if (this.apiVersion.has206Features()) { let compilerOptions: Proto.ExternalProjectCompilerOptions = { - module: Proto.ModuleKind.CommonJS, - target: Proto.ScriptTarget.ES6, + module: 'CommonJS', + target: 'ES6', allowSyntheticDefaultImports: true, + allowNonTsExtensions: true, allowJs: true, }; let args: Proto.SetCompilerOptionsForInferredProjectsArgs = { diff --git a/i18n/chs/extensions/typescript/out/features/bufferSyncSupport.i18n.json b/i18n/chs/extensions/typescript/out/features/bufferSyncSupport.i18n.json new file mode 100644 index 00000000000..d29465e0bee --- /dev/null +++ b/i18n/chs/extensions/typescript/out/features/bufferSyncSupport.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "versionMismatch": "版本不匹配! 全局 tsc ({0}) != VS Code 的语言服务({1})。可能出现不一致的编译错误" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/platform/extensions/node/extensionValidator.i18n.json b/i18n/chs/src/vs/platform/extensions/node/extensionValidator.i18n.json index 5d7cec095f2..5f20018eb9c 100644 --- a/i18n/chs/src/vs/platform/extensions/node/extensionValidator.i18n.json +++ b/i18n/chs/src/vs/platform/extensions/node/extensionValidator.i18n.json @@ -4,6 +4,18 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { + "extensionDescription.activationEvents1": "属性“{0}”可以省略或其类型必须是 \"string[]\"", + "extensionDescription.activationEvents2": "必须同时指定或同时省略属性”{0}“和”{1}“", + "extensionDescription.empty": "已获得空扩展说明", + "extensionDescription.engines": "属性“{0}”为必需且其类型必须为 \"object\"", + "extensionDescription.engines.vscode": "属性“{0}”是必需的,其类型必须是“字符串”", + "extensionDescription.extensionDependencies": "属性“{0}”可以省略或其类型必须是 \"string[]\"", + "extensionDescription.main1": "属性“{0}”可以省略,或者其类型必须是“字符串”", + "extensionDescription.main2": "应在扩展文件夹({1})中包含 \"main\" ({0})。这可能会使扩展不可移植。", + "extensionDescription.main3": "必须同时指定或同时省略属性”{0}“和”{1}“", + "extensionDescription.name": "属性“{0}”是必需的,其类型必须是“字符串”", + "extensionDescription.publisher": "属性“{0}”是必需的,其类型必须是“字符串”", + "extensionDescription.version": "属性“{0}”是必需的,其类型必须是“字符串”", "notSemver": "扩展版本与 semver 不兼容。", "versionMismatch": "扩展与 Code {0} 不兼容。扩展需要: {1}。", "versionSpecificity1": "\"engines.vscode\" ({0}) 中指定的版本不够具体。对于 1.0.0 之前的 vscode 版本,请至少定义主要和次要想要的版本。例如: ^0.10.0、0.10.x、0.11.0 等。", diff --git a/i18n/chs/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json b/i18n/chs/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json new file mode 100644 index 00000000000..8b6ad71cd4e --- /dev/null +++ b/i18n/chs/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/html/browser/webview.i18n.json b/i18n/chs/src/vs/workbench/parts/html/browser/webview.i18n.json new file mode 100644 index 00000000000..8b6ad71cd4e --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/html/browser/webview.i18n.json @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json b/i18n/chs/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json index a6b875e53e5..ea1131e6bd6 100644 --- a/i18n/chs/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json +++ b/i18n/chs/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json @@ -6,5 +6,5 @@ { "defaultLabel": "输入", "patternDescription": "使用 Glob 模式", - "patternHelpInclude": "要匹配的模式。例如,****/*.js** 与所有 JavaScript 文件匹配,或 **myFolder/**** 与包含所有子级的文件夹匹配。\n\n**Reference**:\n***** 匹配 0 个或更多字符\n**?** 匹配 1 个字符\n****** 匹配零个或更多目录\n**[a-z]** 匹配一系列字符\n**{a,b}** 匹配任何一种模式)" + "patternHelpInclude": "要匹配的模式。例如,**\\*\\*/*.js** 与所有 JavaScript 文件匹配,或 **myFolder/\\*\\*** 与包含所有子级的文件夹匹配。\n\n**Reference**:\n**\\*** 匹配 0 个或更多字符\n**?** 匹配 1 个字符\n**\\*\\*** 匹配零个或更多目录\n**[a-z]** 匹配一系列字符\n**{a,b}** 匹配任何一种模式)" } \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json b/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json index 7cb1eee293f..8b232c3dc4e 100644 --- a/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json +++ b/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json @@ -14,7 +14,7 @@ "terminal.integrated.setLocaleVariables": "控制是否在终端启动时设置区域设置变量,在 OS X 上默认设置为 true,在其他平台上为 false。", "terminal.integrated.shell.linux": "终端在 Linux 上使用的 shell 的路径。", "terminal.integrated.shell.osx": "终端在 OS X 上使用的 shell 的路径。", - "terminal.integrated.shell.windows": "终端在 Windows 上使用的 shell 的路径。使用随 Windows 一起提供的 shell 时(cmd、PowerShell 或 Ubuntu 上的 Bash),相对 C:WindowsSystem32,首选 C:Windowssysnative 以使用 64 位版本。", + "terminal.integrated.shell.windows": "终端在 Windows 上使用的 shell 的路径。使用随 Windows 一起提供的 shell 时(cmd、PowerShell 或 Ubuntu 上的 Bash),相对 C:\\Windows\\System32,首选 C:\\Windows\\sysnative 以使用 64 位版本。", "terminal.integrated.shellArgs.linux": "在 Linux 终端上时要使用的命令行参数。", "terminal.integrated.shellArgs.osx": "在 OS X 终端上时要使用的命令行参数。", "terminal.integrated.shellArgs.windows": "在 Windows 终端上时使用的命令行参数。", diff --git a/i18n/chs/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json b/i18n/chs/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json index 787539d2d7f..92537c5af50 100644 --- a/i18n/chs/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json +++ b/i18n/chs/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json @@ -4,11 +4,7 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "insiderBuilds": "日常会员版本和发布!", - "license": "读取许可证", - "licenseChanged": "我们的许可条款已更改,请检查它们。", - "neverShowAgain": "不再显示", - "read the release notes": "欢迎使用 {0} v{1}! 是否要阅读发布说明?", - "readmore": "阅读更多内容", - "release notes": "发行说明" + "release notes": "发行说明", + "updateChannel": "配置是否从更新通道接收自动更新。更改后需要重启。", + "updateConfigurationTitle": "更新" } \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/update/electron-browser/update.i18n.json b/i18n/chs/src/vs/workbench/parts/update/electron-browser/update.i18n.json new file mode 100644 index 00000000000..ebd875e2739 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/update/electron-browser/update.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "downloadNow": "立即下载", + "later": "稍后", + "noUpdatesAvailable": "当前没有可用的更新。", + "read the release notes": "欢迎使用 {0} v{1}! 是否要阅读发布说明?", + "releaseNotes": "发行说明", + "showReleaseNotes": "显示发行说明", + "thereIsUpdateAvailable": "存在可用更新。", + "updateAvailable": "{0} 将在重启后更新。", + "updateNow": "立即更新" +} \ No newline at end of file diff --git a/i18n/cht/extensions/typescript/out/features/bufferSyncSupport.i18n.json b/i18n/cht/extensions/typescript/out/features/bufferSyncSupport.i18n.json new file mode 100644 index 00000000000..0054200abda --- /dev/null +++ b/i18n/cht/extensions/typescript/out/features/bufferSyncSupport.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "versionMismatch": "版本不符! 全域 TSC ({0}) != VS Code 的語言服務 ({1})。可能會發生編譯不一致的錯誤" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/platform/extensions/node/extensionValidator.i18n.json b/i18n/cht/src/vs/platform/extensions/node/extensionValidator.i18n.json index 1f67dac2d67..d245943b9c2 100644 --- a/i18n/cht/src/vs/platform/extensions/node/extensionValidator.i18n.json +++ b/i18n/cht/src/vs/platform/extensions/node/extensionValidator.i18n.json @@ -4,6 +4,18 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { + "extensionDescription.activationEvents1": "屬性 `{0}` 可以省略或必須屬於 `string[]` 類型", + "extensionDescription.activationEvents2": "屬性 `{0}` 和 `{1}` 必須同時指定或同時忽略", + "extensionDescription.empty": "得到空白擴充功能描述", + "extensionDescription.engines": "屬性 '{0}' 為強制項目且必須屬於 `object` 類型", + "extensionDescription.engines.vscode": "屬性 '{0}' 為強制項目且必須屬於 `string` 類型", + "extensionDescription.extensionDependencies": "屬性 `{0}` 可以省略或必須屬於 `string[]` 類型", + "extensionDescription.main1": "屬性 `{0}` 可以省略或必須屬於 `string` 類型", + "extensionDescription.main2": "`main` ({0}) 必須包含在擴充功能的資料夾 ({1}) 中。這可能會使擴充功能無法移植。", + "extensionDescription.main3": "屬性 `{0}` 和 `{1}` 必須同時指定或同時忽略", + "extensionDescription.name": "屬性 '{0}' 為強制項目且必須屬於 `string` 類型", + "extensionDescription.publisher": "屬性 '{0}' 為強制項目且必須屬於 `string` 類型", + "extensionDescription.version": "屬性 '{0}' 為強制項目且必須屬於 `string` 類型", "notSemver": "擴充功能版本與 semver 不相容。", "versionMismatch": "擴充功能與 Code {0} 不相容。擴充功能需要: {1}。", "versionSpecificity1": "在 `engines.vscode` ({0}) 中指定的版本不夠具體。對於 1.0.0 之前的 vscode 版本,請至少定義所需的主要和次要版本。 例如 ^0.10.0、0.10.x、0.11.0 等。", diff --git a/i18n/cht/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json b/i18n/cht/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json new file mode 100644 index 00000000000..8b6ad71cd4e --- /dev/null +++ b/i18n/cht/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/html/browser/webview.i18n.json b/i18n/cht/src/vs/workbench/parts/html/browser/webview.i18n.json new file mode 100644 index 00000000000..8b6ad71cd4e --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/html/browser/webview.i18n.json @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json b/i18n/cht/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json index c9e97f86ffd..416c1c9a889 100644 --- a/i18n/cht/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json +++ b/i18n/cht/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json @@ -6,5 +6,5 @@ { "defaultLabel": "輸入", "patternDescription": "使用 Glob 模式", - "patternHelpInclude": "要比對的模式。例如 ****/*.js** 可比對所有 JavaScript 檔案,或 **myFolder/**** 可比對資料夾和所有子系。\n\n**參考**:\n***** 比對 0 或多個字元\n**?** 比對 1 個字元\n****** 比對 0 或多個目錄\n**[a-z]** 比對某個範圍的字元\n**{a,b}** 比對任何模式)" + "patternHelpInclude": "要比對的模式。例如 **\\*\\*/*.js** 可比對所有 JavaScript 檔案,或 **myFolder/\\*\\*** 可比對資料夾和所有子系。\n\n**參考**:\n**\\*** 比對 0 或多個字元\n**?** 比對 1 個字元\n**\\*\\*** 比對 0 或多個目錄\n**[a-z]** 比對某個範圍的字元\n**{a,b}** 比對任何模式)" } \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json b/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json index 6eb687d5d39..fdb41747ffa 100644 --- a/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json +++ b/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json @@ -14,7 +14,7 @@ "terminal.integrated.setLocaleVariables": "控制是否在終端機啟動時設定地區設定變數,這在 OS X 上預設為 true,在其他平台則為 false。", "terminal.integrated.shell.linux": "終端機在 Linux 上使用的殼層路徑。", "terminal.integrated.shell.osx": "終端機在 OS X 上使用的殼層路徑。", - "terminal.integrated.shell.windows": "終端機在 Windows 上使用的殼層路徑。使用隨附於 Windows 的殼層 (cmd、PowerShell 或 Bash on Ubuntu) 時,建議選擇 C:Windowssysnative 而非 C:WindowsSystem32,以使用 64 位元版本。", + "terminal.integrated.shell.windows": "終端機在 Windows 上使用的殼層路徑。使用隨附於 Windows 的殼層 (cmd、PowerShell 或 Bash on Ubuntu) 時,建議選擇 C:\\Windows\\sysnative 而非 C:\\Windows\\System32,以使用 64 位元版本。", "terminal.integrated.shellArgs.linux": "在 Linux 終端機要使用的命令列引數。", "terminal.integrated.shellArgs.osx": "在 OS X 終端機要使用的命令列引數。", "terminal.integrated.shellArgs.windows": "在 Windows 終端機上時要使用的命令列引數。", diff --git a/i18n/cht/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json b/i18n/cht/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json index 9d686ce07e6..3b7ac4411af 100644 --- a/i18n/cht/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json +++ b/i18n/cht/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json @@ -4,11 +4,7 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "insiderBuilds": "每日都有測試人員組建及版本!", - "license": "閱讀授權", - "licenseChanged": "授權條款已有所變更,請仔細閱讀。", - "neverShowAgain": "不要再顯示", - "read the release notes": "歡迎使用 {0} v{1}! 您要閱讀版本資訊嗎?", - "readmore": "閱讀其他資訊", - "release notes": "版本資訊" + "release notes": "版本資訊", + "updateChannel": "設定是否要從更新頻道接收自動更新。變更後需要重新啟動。", + "updateConfigurationTitle": "更新" } \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/update/electron-browser/update.i18n.json b/i18n/cht/src/vs/workbench/parts/update/electron-browser/update.i18n.json new file mode 100644 index 00000000000..8e7812a2f26 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/update/electron-browser/update.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "downloadNow": "立即下載", + "later": "稍後", + "noUpdatesAvailable": "目前沒有可用的更新。", + "read the release notes": "歡迎使用 {0} v{1}! 您要閱讀版本資訊嗎?", + "releaseNotes": "版本資訊", + "showReleaseNotes": "顯示版本資訊", + "thereIsUpdateAvailable": "已有更新可用。", + "updateAvailable": "{0} 重新啟動後將會更新。", + "updateNow": "立即更新" +} \ No newline at end of file diff --git a/i18n/deu/extensions/typescript/out/features/bufferSyncSupport.i18n.json b/i18n/deu/extensions/typescript/out/features/bufferSyncSupport.i18n.json new file mode 100644 index 00000000000..fef7c40a2aa --- /dev/null +++ b/i18n/deu/extensions/typescript/out/features/bufferSyncSupport.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "versionMismatch": "Versionskonflikt zwischen dem globalen TSC ({0}) und dem Sprachdienst von VS Code ({1}). Dies kann zu Kompilierungsfehlern aufgrund von Inkonsistenzen führen." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/platform/extensions/node/extensionValidator.i18n.json b/i18n/deu/src/vs/platform/extensions/node/extensionValidator.i18n.json index 7780c255b5a..46840664e62 100644 --- a/i18n/deu/src/vs/platform/extensions/node/extensionValidator.i18n.json +++ b/i18n/deu/src/vs/platform/extensions/node/extensionValidator.i18n.json @@ -4,6 +4,18 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { + "extensionDescription.activationEvents1": "Die Eigenschaft \"{0}\" kann ausgelassen werden oder muss vom Typ \"string[]\" sein.", + "extensionDescription.activationEvents2": "Die Eigenschaften \"{0}\" und \"{1}\" müssen beide angegeben oder beide ausgelassen werden.", + "extensionDescription.empty": "Es wurde eine leere Extensionbeschreibung abgerufen.", + "extensionDescription.engines": "Die Eigenschaft \"{0}\" ist erforderlich und muss vom Typ \"object\" sein.", + "extensionDescription.engines.vscode": "Die Eigenschaft \"{0}\" ist erforderlich. Sie muss vom Typ \"string\" sein.", + "extensionDescription.extensionDependencies": "Die Eigenschaft \"{0}\" kann ausgelassen werden oder muss vom Typ \"string[]\" sein.", + "extensionDescription.main1": "Die Eigenschaft \"{0}\" kann ausgelassen werden oder muss vom Typ \"string\" sein.", + "extensionDescription.main2": "Es wurde erwartet, dass \"main\" ({0}) im Ordner ({1}) der Extension enthalten ist. Dies führt ggf. dazu, dass die Extension nicht portierbar ist.", + "extensionDescription.main3": "Die Eigenschaften \"{0}\" und \"{1}\" müssen beide angegeben oder beide ausgelassen werden.", + "extensionDescription.name": "Die Eigenschaft \"{0}\" ist erforderlich. Sie muss vom Typ \"string\" sein.", + "extensionDescription.publisher": "Die Eigenschaft \"{0}\" ist erforderlich. Sie muss vom Typ \"string\" sein.", + "extensionDescription.version": "Die Eigenschaft \"{0}\" ist erforderlich. Sie muss vom Typ \"string\" sein.", "notSemver": "Die Extensionversion ist nicht mit \"semver\" kompatibel.", "versionMismatch": "Die Extension ist nicht mit dem Code {0} kompatibel. Die Extension erfordert {1}.", "versionSpecificity1": "Die in \"engines.vscode\" ({0}) angegebene Version ist nicht spezifisch genug. Definieren Sie für VS Code-Versionen vor Version 1.0.0 bitte mindestens die gewünschte Haupt- und Nebenversion, z. B. ^0.10.0, 0.10.x, 0.11.0 usw.", diff --git a/i18n/deu/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json b/i18n/deu/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json new file mode 100644 index 00000000000..8b6ad71cd4e --- /dev/null +++ b/i18n/deu/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/html/browser/webview.i18n.json b/i18n/deu/src/vs/workbench/parts/html/browser/webview.i18n.json new file mode 100644 index 00000000000..8b6ad71cd4e --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/html/browser/webview.i18n.json @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json b/i18n/deu/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json index fc8c3c3798e..1417954e7f0 100644 --- a/i18n/deu/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json +++ b/i18n/deu/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json @@ -6,5 +6,5 @@ { "defaultLabel": "Eingabe", "patternDescription": "Globmuster verwenden", - "patternHelpInclude": "Das Muster, mit dem eine Übereinstimmung vorliegen soll, z. B. ****/*.js** für alle JavaScript-Dateien oder **meinOrdner/**** für eine Übereinstimmung mit diesem Ordner und allen seinen untergeordneten Elementen.\n\n**Referenz**:\n***** stimmt mit 0 oder mehr Zeichen überein.\n**?** stimmt mit 1 Zeichen überein.\n****** stimmt mit null oder mehr Verzeichnissen überein.\n**[a-z]** stimmt mit einem Zeichenbereich überein.\n**{a,b}** stimmt mit einem beliebigen Muster überein." + "patternHelpInclude": "Das Muster, mit dem eine Übereinstimmung vorliegen soll, z. B. **\\*\\*/*.js** für alle JavaScript-Dateien oder **meinOrdner/\\*\\*** für eine Übereinstimmung mit diesem Ordner und allen seinen untergeordneten Elementen.\n\n**Referenz**:\n**\\*** stimmt mit 0 oder mehr Zeichen überein.\n**?** stimmt mit 1 Zeichen überein.\n**\\*\\*** stimmt mit null oder mehr Verzeichnissen überein.\n**[a-z]** stimmt mit einem Zeichenbereich überein.\n**{a,b}** stimmt mit einem beliebigen Muster überein." } \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json b/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json index 7c61dc84b31..505f32b3f45 100644 --- a/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json +++ b/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json @@ -14,7 +14,7 @@ "terminal.integrated.setLocaleVariables": "Steuert, ob Gebietsschemavariablen beim Start des Terminals festgelegt werden. Der Standardwert ist unter OS X TRUE und FALSE auf anderen Plattformen.", "terminal.integrated.shell.linux": "Der Pfad der Shell, den das Terminal unter Linux verwendet.", "terminal.integrated.shell.osx": "Der Pfad der Shell, den das Terminal unter OS X verwendet.", - "terminal.integrated.shell.windows": "Der Pfad der Shell, den das Terminal unter Windows verwendet. Wenn Shells aus dem Lieferumfang von Windows (cmd, PowerShell oder Bash für Ubuntu) verwendet werden, sollten Sie \"C:Windowssysnative\" dem Pfad \"C:WindowsSystem32\" vorziehen, um die 64-Bit-Versionen zu verwenden.", + "terminal.integrated.shell.windows": "Der Pfad der Shell, den das Terminal unter Windows verwendet. Wenn Shells aus dem Lieferumfang von Windows (cmd, PowerShell oder Bash für Ubuntu) verwendet werden, sollten Sie \"C:\\Windows\\sysnative\" dem Pfad \"C:\\Windows\\System32\" vorziehen, um die 64-Bit-Versionen zu verwenden.", "terminal.integrated.shellArgs.linux": "Die Befehlszeilenargumente, die für das Linux-Terminal verwendet werden sollen.", "terminal.integrated.shellArgs.osx": "Die Befehlszeilenargumente, die für das OS X-Terminal verwendet werden sollen.", "terminal.integrated.shellArgs.windows": "Die Befehlszeilenargumente, die im Windows-Terminal verwendet werden sollen.", diff --git a/i18n/deu/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json b/i18n/deu/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json index 4477f299f4c..4159ef40257 100644 --- a/i18n/deu/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json +++ b/i18n/deu/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json @@ -4,11 +4,7 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "insiderBuilds": "Tägliche Insider-Builds und Releases!", - "license": "Lizenz lesen", - "licenseChanged": "Unsere Lizenzbedingungen haben sich geändert. Bitte lesen Sie sie.", - "neverShowAgain": "Nicht mehr anzeigen", - "read the release notes": "Willkommen bei {0} v{1}! Möchten Sie die Hinweise zu dieser Version lesen?", - "readmore": "Weitere Informationen", - "release notes": "Anmerkungen zu dieser Version" + "release notes": "Anmerkungen zu dieser Version", + "updateChannel": "Konfiguriert, ob automatische Updates aus einem Updatekanal empfangen werden sollen. Erfordert einen Neustart nach der Änderung.", + "updateConfigurationTitle": "Update" } \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/update/electron-browser/update.i18n.json b/i18n/deu/src/vs/workbench/parts/update/electron-browser/update.i18n.json new file mode 100644 index 00000000000..3a32b5d2ad0 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/update/electron-browser/update.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "downloadNow": "Jetzt herunterladen", + "later": "Später", + "noUpdatesAvailable": "Zurzeit sind keine Updates verfügbar.", + "read the release notes": "Willkommen bei {0} v{1}! Möchten Sie die Hinweise zu dieser Version lesen?", + "releaseNotes": "Anmerkungen zu dieser Version", + "showReleaseNotes": "Anmerkungen zu dieser Version anzeigen", + "thereIsUpdateAvailable": "Ein Update ist verfügbar.", + "updateAvailable": "{0} wird nach dem Neustart aktualisiert.", + "updateNow": "Jetzt aktualisieren" +} \ No newline at end of file diff --git a/i18n/esn/extensions/typescript/out/features/bufferSyncSupport.i18n.json b/i18n/esn/extensions/typescript/out/features/bufferSyncSupport.i18n.json new file mode 100644 index 00000000000..80f2e018c49 --- /dev/null +++ b/i18n/esn/extensions/typescript/out/features/bufferSyncSupport.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "versionMismatch": "Las versiones no coinciden; global tsc ({0}) != servicio de lenguaje de VS Code ({1}). Pueden producirse errores de compilación incoherente." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/platform/extensions/node/extensionValidator.i18n.json b/i18n/esn/src/vs/platform/extensions/node/extensionValidator.i18n.json index 36a130c3817..5e53cde70d2 100644 --- a/i18n/esn/src/vs/platform/extensions/node/extensionValidator.i18n.json +++ b/i18n/esn/src/vs/platform/extensions/node/extensionValidator.i18n.json @@ -4,6 +4,18 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { + "extensionDescription.activationEvents1": "la propiedad `{0}` se puede omitir o debe ser de tipo \"string[]\"", + "extensionDescription.activationEvents2": "las propiedades `{0}` y `{1}` deben especificarse u omitirse conjuntamente", + "extensionDescription.empty": "Se obtuvo una descripción vacía de la extensión.", + "extensionDescription.engines": "la propiedad `{0}` es obligatoria y debe ser de tipo \"object\"", + "extensionDescription.engines.vscode": "la propiedad `{0}` es obligatoria y debe ser de tipo \"string\"", + "extensionDescription.extensionDependencies": "la propiedad `{0}` se puede omitir o debe ser de tipo \"string[]\"", + "extensionDescription.main1": "la propiedad `{0}` se puede omitir o debe ser de tipo \"string\"", + "extensionDescription.main2": "Se esperaba que \"main\" ({0}) se hubiera incluido en la carpeta de la extensión ({1}). Esto puede hacer que la extensión no sea portátil.", + "extensionDescription.main3": "las propiedades `{0}` y `{1}` deben especificarse u omitirse conjuntamente", + "extensionDescription.name": "la propiedad `{0}` es obligatoria y debe ser de tipo \"string\"", + "extensionDescription.publisher": "la propiedad `{0}` es obligatoria y debe ser de tipo \"string\"", + "extensionDescription.version": "la propiedad `{0}` es obligatoria y debe ser de tipo \"string\"", "notSemver": "La versión de la extensión no es compatible con semver.", "versionMismatch": "La extensión no es compatible con {0} de Code y requiere: {1}.", "versionSpecificity1": "La versión indicada en \"engines.vscode\" ({0}) no es suficientemente específica. Para las versiones de vscode anteriores a la 1.0.0, defina como mínimo la versión principal y secundaria deseadas. Por ejemplo: ^0.10.0, 0.10.x, 0.11.0, etc.", diff --git a/i18n/esn/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json b/i18n/esn/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json new file mode 100644 index 00000000000..8b6ad71cd4e --- /dev/null +++ b/i18n/esn/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/html/browser/webview.i18n.json b/i18n/esn/src/vs/workbench/parts/html/browser/webview.i18n.json new file mode 100644 index 00000000000..8b6ad71cd4e --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/html/browser/webview.i18n.json @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json b/i18n/esn/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json index 589353be0f6..5616db383dd 100644 --- a/i18n/esn/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json +++ b/i18n/esn/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json @@ -6,5 +6,5 @@ { "defaultLabel": "entrada", "patternDescription": "Usar patrones globales", - "patternHelpInclude": "El patrón de coincidencia, por ejemplo, ****/*.js** para todos los archivos JavaScript o **miCarpeta/**** para encontrar coincidencias de esa carpeta con todos los elementos secundarios.\n\n**Referencia**:\n***** encuentra coincidencias con 0 o más caracteres\n**?** encuentra coincidencias con 1 carácter\n****** encuentra coincidencias con cero o más directorios\n**[a-z]** encuentra coincidencias con un rango de caracteres\n**{a,b}** encuentra coincidencias con cualquiera de los patrones" + "patternHelpInclude": "El patrón de coincidencia, por ejemplo, **\\*\\*/*.js** para todos los archivos JavaScript o **miCarpeta/\\*\\*** para encontrar coincidencias de esa carpeta con todos los elementos secundarios.\n\n**Referencia**:\n**\\*** encuentra coincidencias con 0 o más caracteres\n**?** encuentra coincidencias con 1 carácter\n**\\*\\*** encuentra coincidencias con cero o más directorios\n**[a-z]** encuentra coincidencias con un rango de caracteres\n**{a,b}** encuentra coincidencias con cualquiera de los patrones" } \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json b/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json index 05fb579997f..b7aed6124d7 100644 --- a/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json +++ b/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json @@ -14,7 +14,7 @@ "terminal.integrated.setLocaleVariables": "Controla si las variables de configuración regional se definen al inicio del terminal. El valor predeterminado es true en OS X y false en las demás plataformas.", "terminal.integrated.shell.linux": "La ruta de acceso del shell que el terminal usa en Linux.", "terminal.integrated.shell.osx": "La ruta de acceso del shell que el terminal usa en OS X.", - "terminal.integrated.shell.windows": "Ruta de acceso del shell que el terminal utiliza en Windows. Cuando se usan shells incluidos en Windows (cmd, PowerShell o Bash en Ubuntu), se recomienda C:Windowssysnative en lugar de C:WindowsSystem32 para usar las versiones de 64 bits.", + "terminal.integrated.shell.windows": "Ruta de acceso del shell que el terminal utiliza en Windows. Cuando se usan shells incluidos en Windows (cmd, PowerShell o Bash en Ubuntu), se recomienda C:\\Windows\\sysnative en lugar de C:\\Windows\\System32 para usar las versiones de 64 bits.", "terminal.integrated.shellArgs.linux": "Los argumentos de la línea de comandos que se usarán en el terminal de Linux.", "terminal.integrated.shellArgs.osx": "Los argumentos de la línea de comandos que se usarán en el terminal de OS X.", "terminal.integrated.shellArgs.windows": "Argumentos de la línea de comandos que se usan cuando se utiliza el terminal Windows.", diff --git a/i18n/esn/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json b/i18n/esn/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json index fac0fb29c48..a330e7d4304 100644 --- a/i18n/esn/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json +++ b/i18n/esn/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json @@ -4,11 +4,7 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "insiderBuilds": "Compilaciones y versiones de Insider todos los días", - "license": "Leer licencia", - "licenseChanged": "Los términos de licencia han cambiado, revíselos.", - "neverShowAgain": "No volver a mostrar", - "read the release notes": "{0} v{1}. ¿Quiere leer las notas de la versión?", - "readmore": "Más información", - "release notes": "Notas de la versión" + "release notes": "Notas de la versión", + "updateChannel": "Configure si recibirá actualizaciones automáticas de un canal de actualización. Es necesario reiniciar tras el cambio.", + "updateConfigurationTitle": "Actualización" } \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/update/electron-browser/update.i18n.json b/i18n/esn/src/vs/workbench/parts/update/electron-browser/update.i18n.json new file mode 100644 index 00000000000..9ecf305e3c5 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/update/electron-browser/update.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "downloadNow": "Descargar ahora", + "later": "Más tarde", + "noUpdatesAvailable": "Actualmente no hay actualizaciones disponibles.", + "read the release notes": "{0} v{1}. ¿Quiere leer las notas de la versión?", + "releaseNotes": "Notas de la versión", + "showReleaseNotes": "Mostrar las notas de la versión", + "thereIsUpdateAvailable": "Hay una actualización disponible.", + "updateAvailable": "{0} se actualizará después de reiniciarse.", + "updateNow": "Actualizar ahora" +} \ No newline at end of file diff --git a/i18n/fra/extensions/typescript/out/features/bufferSyncSupport.i18n.json b/i18n/fra/extensions/typescript/out/features/bufferSyncSupport.i18n.json new file mode 100644 index 00000000000..bb0e3ed7d0c --- /dev/null +++ b/i18n/fra/extensions/typescript/out/features/bufferSyncSupport.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "versionMismatch": "Incompatibilité de version ! global tsc ({0}) != Service de langage de VS Code ({1}). Des erreurs de compilation incohérentes risquent de se produire" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/platform/extensions/node/extensionValidator.i18n.json b/i18n/fra/src/vs/platform/extensions/node/extensionValidator.i18n.json index a90ab569c63..8c184146f1e 100644 --- a/i18n/fra/src/vs/platform/extensions/node/extensionValidator.i18n.json +++ b/i18n/fra/src/vs/platform/extensions/node/extensionValidator.i18n.json @@ -4,6 +4,18 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { + "extensionDescription.activationEvents1": "la propriété '{0}' peut être omise ou doit être de type 'string[]'", + "extensionDescription.activationEvents2": "les propriétés '{0}' et '{1}' doivent être toutes les deux spécifiées ou toutes les deux omises", + "extensionDescription.empty": "Description d'extension vide obtenue", + "extensionDescription.engines": "la propriété '{0}' est obligatoire et doit être de type 'object'", + "extensionDescription.engines.vscode": "la propriété '{0}' est obligatoire et doit être de type 'string'", + "extensionDescription.extensionDependencies": "la propriété '{0}' peut être omise ou doit être de type 'string[]'", + "extensionDescription.main1": "La propriété '{0}' peut être omise ou doit être de type 'string'", + "extensionDescription.main2": "'main' ({0}) est censé être inclus dans le dossier ({1}) de l'extension. Cela risque de rendre l'extension non portable.", + "extensionDescription.main3": "les propriétés '{0}' et '{1}' doivent être toutes les deux spécifiées ou toutes les deux omises", + "extensionDescription.name": "la propriété '{0}' est obligatoire et doit être de type 'string'", + "extensionDescription.publisher": "la propriété '{0}' est obligatoire et doit être de type 'string'", + "extensionDescription.version": "la propriété '{0}' est obligatoire et doit être de type 'string'", "notSemver": "La version de l'extension n'est pas compatible avec SemVer.", "versionMismatch": "L'extension n'est pas compatible avec le code {0}. L'extension nécessite {1}.", "versionSpecificity1": "La version spécifiée dans 'engines.vscode' ({0}) n'est pas assez précise. Pour les versions de vscode antérieures à 1.0.0, définissez au minimum les versions majeure et mineure souhaitées. Par exemple : ^0.10.0, 0.10.x, 0.11.0, etc.", diff --git a/i18n/fra/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json b/i18n/fra/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json new file mode 100644 index 00000000000..8b6ad71cd4e --- /dev/null +++ b/i18n/fra/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/html/browser/webview.i18n.json b/i18n/fra/src/vs/workbench/parts/html/browser/webview.i18n.json new file mode 100644 index 00000000000..8b6ad71cd4e --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/html/browser/webview.i18n.json @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json b/i18n/fra/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json index a03deaecd49..c34d5e51a86 100644 --- a/i18n/fra/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json +++ b/i18n/fra/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json @@ -6,5 +6,5 @@ { "defaultLabel": "entrée", "patternDescription": "Utiliser des modèles Glob", - "patternHelpInclude": "Modèle de correspondance. Exemple : ****/*.js** pour établir une correspondance avec tous les fichiers JavaScript, ou **myFolder/**** pour établir une correspondance avec le dossier indiqué et tous ses enfants.\n\n**Référence** :\n***** correspond à 0 caractère ou plus\n**?** correspond à 1 caractère\n****** correspond à zéro répertoire ou plus\n**[a-z]** correspond à une plage de caractères\n**{a,b}** correspond à tous les modèles)" + "patternHelpInclude": "Modèle de correspondance. Exemple : **\\*\\*/*.js** pour établir une correspondance avec tous les fichiers JavaScript, ou **myFolder/\\*\\*** pour établir une correspondance avec le dossier indiqué et tous ses enfants.\n\n**Référence** :\n**\\*** correspond à 0 caractère ou plus\n**?** correspond à 1 caractère\n**\\*\\*** correspond à zéro répertoire ou plus\n**[a-z]** correspond à une plage de caractères\n**{a,b}** correspond à tous les modèles)" } \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json b/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json index 26c7ebb1aae..a75f6f332e2 100644 --- a/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json +++ b/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json @@ -14,7 +14,7 @@ "terminal.integrated.setLocaleVariables": "Contrôle si les variables locales sont définies au démarrage du terminal. La valeur par défaut est true sur OS X, false sur les autres plateformes.", "terminal.integrated.shell.linux": "Chemin de l'interpréteur de commandes utilisé par le terminal sur Linux.", "terminal.integrated.shell.osx": "Chemin de l'interpréteur de commandes utilisé par le terminal sur OS X.", - "terminal.integrated.shell.windows": "Chemin de l'interpréteur de commandes utilisé par le terminal sur Windows. Quand vous vous servez des interpréteurs de commandes fournis avec Windows (cmd, PowerShell ou Bash sur Ubuntu), préférez C:Windowssysnative à C:WindowsSystem32 pour utiliser les versions 64 bits.", + "terminal.integrated.shell.windows": "Chemin de l'interpréteur de commandes utilisé par le terminal sur Windows. Quand vous vous servez des interpréteurs de commandes fournis avec Windows (cmd, PowerShell ou Bash sur Ubuntu), préférez C:\\Windows\\sysnative à C:\\Windows\\System32 pour utiliser les versions 64 bits.", "terminal.integrated.shellArgs.linux": "Arguments de ligne de commande à utiliser sur le terminal Linux.", "terminal.integrated.shellArgs.osx": "Arguments de ligne de commande à utiliser sur le terminal OS X.", "terminal.integrated.shellArgs.windows": "Arguments de ligne de commande à utiliser sur le terminal Windows.", diff --git a/i18n/fra/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json b/i18n/fra/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json index 9a8138b5a52..0d130f62642 100644 --- a/i18n/fra/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json +++ b/i18n/fra/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json @@ -4,11 +4,7 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "insiderBuilds": "Builds et mises en production Insider quotidiennes !", - "license": "Lire la licence", - "licenseChanged": "Nos termes du contrat de licence ont changé. Prenez un instant pour les consulter.", - "neverShowAgain": "Ne plus afficher", - "read the release notes": "Bienvenue dans {0} v{1} ! Voulez-vous lire les notes de publication ?", - "readmore": "Lire la suite", - "release notes": "Notes de publication" + "release notes": "Notes de publication", + "updateChannel": "Indiquez si vous recevez des mises à jour automatiques en provenance d'un canal de mises à jour. Un redémarrage est nécessaire en cas de modification.", + "updateConfigurationTitle": "Mettre à jour" } \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/update/electron-browser/update.i18n.json b/i18n/fra/src/vs/workbench/parts/update/electron-browser/update.i18n.json new file mode 100644 index 00000000000..d32e46b4a03 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/update/electron-browser/update.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "downloadNow": "Télécharger maintenant", + "later": "Plus tard", + "noUpdatesAvailable": "Aucune mise à jour n'est disponible actuellement.", + "read the release notes": "Bienvenue dans {0} v{1} ! Voulez-vous lire les notes de publication ?", + "releaseNotes": "Notes de publication", + "showReleaseNotes": "Afficher les notes de publication", + "thereIsUpdateAvailable": "Une mise à jour est disponible.", + "updateAvailable": "{0} sera mis à jour après avoir redémarré.", + "updateNow": "Mettre à jour maintenant" +} \ No newline at end of file diff --git a/i18n/ita/extensions/typescript/out/features/bufferSyncSupport.i18n.json b/i18n/ita/extensions/typescript/out/features/bufferSyncSupport.i18n.json new file mode 100644 index 00000000000..0156142fe46 --- /dev/null +++ b/i18n/ita/extensions/typescript/out/features/bufferSyncSupport.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "versionMismatch": "Le versioni non corrispondono. Compilatore tsc globale ({0}) != servizio di linguaggio di Visual Studio Code ({1}). Potrebbero verificarsi errori di compilazione incoerente" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/platform/extensions/node/extensionValidator.i18n.json b/i18n/ita/src/vs/platform/extensions/node/extensionValidator.i18n.json index ffdec9e2868..fe35f560e06 100644 --- a/i18n/ita/src/vs/platform/extensions/node/extensionValidator.i18n.json +++ b/i18n/ita/src/vs/platform/extensions/node/extensionValidator.i18n.json @@ -4,6 +4,18 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { + "extensionDescription.activationEvents1": "la proprietà `{0}` può essere omessa o deve essere di tipo `string[]`", + "extensionDescription.activationEvents2": "le proprietà `{0}` e `{1}` devono essere specificate o omesse entrambi", + "extensionDescription.empty": "La descrizione dell'estensione restituita è vuota", + "extensionDescription.engines": "la proprietà `{0}` è obbligatoria e deve essere di tipo `object`", + "extensionDescription.engines.vscode": "la proprietà `{0}` è obbligatoria e deve essere di tipo `string`", + "extensionDescription.extensionDependencies": "la proprietà `{0}` può essere omessa o deve essere di tipo `string[]`", + "extensionDescription.main1": "la proprietà `{0}` può essere omessa o deve essere di tipo `string`", + "extensionDescription.main2": "Valore previsto di `main` ({0}) da includere nella cartella dell'estensione ({1}). L'estensione potrebbe non essere più portatile.", + "extensionDescription.main3": "le proprietà `{0}` e `{1}` devono essere specificate o omesse entrambi", + "extensionDescription.name": "la proprietà `{0}` è obbligatoria e deve essere di tipo `string`", + "extensionDescription.publisher": "la proprietà `{0}` è obbligatoria e deve essere di tipo `string`", + "extensionDescription.version": "la proprietà `{0}` è obbligatoria e deve essere di tipo `string`", "notSemver": "La versione dell'estensione non è compatibile con semver.", "versionMismatch": "L'estensione non è compatibile con Visual Studio Code {0}. Per l'estensione è richiesto: {1}.", "versionSpecificity1": "La versione specificata in `engines.vscode` ({0}) non è abbastanza specifica. Per le versioni di vscode precedenti alla 1.0.0, definire almeno le versioni principale e secondaria desiderate, ad esempio ^0.10.0, 0.10.x, 0.11.0 e così via.", diff --git a/i18n/ita/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json b/i18n/ita/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json new file mode 100644 index 00000000000..8b6ad71cd4e --- /dev/null +++ b/i18n/ita/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/html/browser/webview.i18n.json b/i18n/ita/src/vs/workbench/parts/html/browser/webview.i18n.json new file mode 100644 index 00000000000..8b6ad71cd4e --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/html/browser/webview.i18n.json @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json b/i18n/ita/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json index 61955f70727..75ede700884 100644 --- a/i18n/ita/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json +++ b/i18n/ita/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json @@ -6,5 +6,5 @@ { "defaultLabel": "input", "patternDescription": "Usa criteri GLOB", - "patternHelpInclude": "Criterio da usare per la ricerca, ad esempio ****/*.js** per cercare tutti i file JavaScript oppure **cartellaPersonale/**** per cercare la cartella specificata con tutti gli elementi figlio.\n\n**Informazioni di riferimento**:\n***** consente di cercare 0 o più caratteri\n**?** consente di cercare 1 carattere\n****** consente di cercare zero o più directory\n**[a-z]** consente di cercare un intervallo di caratteri\n**{a,b}** consente di cercare uno qualsiasi dei criteri" + "patternHelpInclude": "Criterio da usare per la ricerca, ad esempio **\\*\\*/*.js** per cercare tutti i file JavaScript oppure **cartellaPersonale/\\*\\*** per cercare la cartella specificata con tutti gli elementi figlio.\n\n**Informazioni di riferimento**:\n**\\*** consente di cercare 0 o più caratteri\n**?** consente di cercare 1 carattere\n**\\*\\*** consente di cercare zero o più directory\n**[a-z]** consente di cercare un intervallo di caratteri\n**{a,b}** consente di cercare uno qualsiasi dei criteri" } \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json b/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json index ec6ca436f47..ab8c70bff06 100644 --- a/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json +++ b/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json @@ -14,7 +14,7 @@ "terminal.integrated.setLocaleVariables": "Controlla se le variabili delle impostazioni locali sono impostate all'avvio del terminale. Il valore predefinito è true per OS X e false per altre piattaforme.", "terminal.integrated.shell.linux": "Percorso della shell usata dal terminale in Linux.", "terminal.integrated.shell.osx": "Percorso della shell usata dal terminale in OS X.", - "terminal.integrated.shell.windows": "Percorso della shell usata dal terminale in Windows. Quando si usano le shell incluse in Windows (cmd, PowerShell o Bash in Ubuntu), preferire C:Windowssysnative rispetto a C:WindowsSystem32 per usare le versioni a 64 bit.", + "terminal.integrated.shell.windows": "Percorso della shell usata dal terminale in Windows. Quando si usano le shell incluse in Windows (cmd, PowerShell o Bash in Ubuntu), preferire C:\\Windows\\sysnative rispetto a C:\\Windows\\System32 per usare le versioni a 64 bit.", "terminal.integrated.shellArgs.linux": "Argomenti della riga di comando da usare nel terminale Linux.", "terminal.integrated.shellArgs.osx": "Argomenti della riga di comando da usare nel terminale OS X.", "terminal.integrated.shellArgs.windows": "Argomenti della riga di comando da usare nel terminale Windows.", diff --git a/i18n/ita/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json b/i18n/ita/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json index 007679a33d1..8abff8f4950 100644 --- a/i18n/ita/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json +++ b/i18n/ita/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json @@ -4,11 +4,7 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "insiderBuilds": "Build e versioni di Insider disponibili ogni giorno", - "license": "Leggi licenza", - "licenseChanged": "I termini della licenza sono cambiati. Leggerli con attenzione.", - "neverShowAgain": "Non visualizzare più questo messaggio", - "read the release notes": "Benvenuti in {0} versione {1}. Leggere le note sulla versione?", - "readmore": "Altre informazioni", - "release notes": "Note sulla versione" + "release notes": "Note sulla versione", + "updateChannel": "Consente di configurare la ricezione degli aggiornamenti automatici da un canale di aggiornamento. Richiede un riavvio dopo la modifica.", + "updateConfigurationTitle": "Aggiorna" } \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/update/electron-browser/update.i18n.json b/i18n/ita/src/vs/workbench/parts/update/electron-browser/update.i18n.json new file mode 100644 index 00000000000..8d953bfa690 --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/update/electron-browser/update.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "downloadNow": "Scarica ora", + "later": "In seguito", + "noUpdatesAvailable": "Al momento non sono disponibili aggiornamenti.", + "read the release notes": "Benvenuti in {0} versione {1}. Leggere le note sulla versione?", + "releaseNotes": "Note sulla versione", + "showReleaseNotes": "Mostra note sulla versione", + "thereIsUpdateAvailable": "È disponibile un aggiornamento.", + "updateAvailable": "{0} verrà aggiornato dopo il riavvio.", + "updateNow": "Aggiorna adesso" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/typescript/out/features/bufferSyncSupport.i18n.json b/i18n/jpn/extensions/typescript/out/features/bufferSyncSupport.i18n.json new file mode 100644 index 00000000000..3bf9b8e27df --- /dev/null +++ b/i18n/jpn/extensions/typescript/out/features/bufferSyncSupport.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "versionMismatch": "グローバルな tsc ({0}) と VS Code の言語サービス ({1}) の間にバージョンの不一致があります。非整合のコンパイル エラーを引き起こす可能性があります" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/platform/extensions/node/extensionValidator.i18n.json b/i18n/jpn/src/vs/platform/extensions/node/extensionValidator.i18n.json index cb0bf585257..c5cfd6a00b4 100644 --- a/i18n/jpn/src/vs/platform/extensions/node/extensionValidator.i18n.json +++ b/i18n/jpn/src/vs/platform/extensions/node/extensionValidator.i18n.json @@ -4,6 +4,18 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { + "extensionDescription.activationEvents1": "プロパティ `{0}` は省略するか、型 `string[]` にする必要があります", + "extensionDescription.activationEvents2": "プロパティ `{0}` と `{1}` は、両方とも指定するか両方とも省略しなければなりません", + "extensionDescription.empty": "空の拡張機能の説明を入手しました", + "extensionDescription.engines": "プロパティ `{0}` は必須で、型 `object` でなければなりません", + "extensionDescription.engines.vscode": "プロパティ `{0}` は必須で、型 `string` でなければなりません", + "extensionDescription.extensionDependencies": "プロパティ `{0}` は省略するか、型 `string[]` にする必要があります", + "extensionDescription.main1": "プロパティ `{0}` は省略するか、型 `string` にする必要があります", + "extensionDescription.main2": "拡張機能のフォルダー ({1}) の中に `main` ({0}) が含まれることが予期されます。これにより拡張機能を移植できなくなる可能性があります。", + "extensionDescription.main3": "プロパティ `{0}` と `{1}` は、両方とも指定するか両方とも省略しなければなりません", + "extensionDescription.name": "プロパティ `{0}` は必須で、型 `string` でなければなりません", + "extensionDescription.publisher": "プロパティ `{0}` は必須で、型 `string` でなければなりません", + "extensionDescription.version": "プロパティ `{0}` は必須で、型 `string` でなければなりません", "notSemver": "拡張機能のバージョンが semver と互換性がありません。", "versionMismatch": "拡張機能が Code {0} と互換性がありません。拡張機能に必要なバージョン: {1}。", "versionSpecificity1": "`engines.vscode` ({0}) で指定されたバージョンが十分に特定されていません。1.0.0 より前の vscode バージョンの場合は、少なくとも想定されているメジャー バージョンとマイナー バージョンを定義してください。例 ^0.10.0、0.10.x、0.11.0 など。", diff --git a/i18n/jpn/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json b/i18n/jpn/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json new file mode 100644 index 00000000000..8b6ad71cd4e --- /dev/null +++ b/i18n/jpn/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/html/browser/webview.i18n.json b/i18n/jpn/src/vs/workbench/parts/html/browser/webview.i18n.json new file mode 100644 index 00000000000..8b6ad71cd4e --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/html/browser/webview.i18n.json @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json b/i18n/jpn/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json index 5ec02b7593a..6d9dac3d9a4 100644 --- a/i18n/jpn/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json +++ b/i18n/jpn/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json @@ -6,5 +6,5 @@ { "defaultLabel": "入力", "patternDescription": "glob パターンを使用する", - "patternHelpInclude": "一致するパターン。たとえば、****/*.js** はすべての JavaScript ファイルと一致し、**myFolder/**** は当該フォルダーとすべての子と一致します。\n\n**参照情報**:\n***** は 0 個以上の文字と一致します\n**?** は 1 文字と一致します\n****** は 0 個以上のディレクトリと一致します\n**[a-z]** は文字の範囲と一致します\n**{a,b}** は任意のパターンと一致します)" + "patternHelpInclude": "一致するパターン。たとえば、**\\*\\*/*.js** はすべての JavaScript ファイルと一致し、**myFolder/\\*\\*** は当該フォルダーとすべての子と一致します。\n\n**参照情報**:\n**\\*** は 0 個以上の文字と一致します\n**?** は 1 文字と一致します\n**\\*\\*** は 0 個以上のディレクトリと一致します\n**[a-z]** は文字の範囲と一致します\n**{a,b}** は任意のパターンと一致します)" } \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json index 95e5ea3526a..cfac5788f27 100644 --- a/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json +++ b/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json @@ -14,7 +14,7 @@ "terminal.integrated.setLocaleVariables": "端末の開始時にロケール変数を設定するかどうかを制御します。OS X では既定で true になり、その他のプラットフォームでは false です。", "terminal.integrated.shell.linux": "端末が Linux で使用するシェルのパス。", "terminal.integrated.shell.osx": "端末が OS X で使用するシェルのパス。", - "terminal.integrated.shell.windows": "端末が Windows で使用するシェルのパス。Windows に付属のシェル (cmd、PowerShell、または Bash on Ubuntu) を使用する場合、64 ビット バージョンを使用するには、C:WindowsSystem32 ではなく、C:Windowssysnative を選びます。", + "terminal.integrated.shell.windows": "端末が Windows で使用するシェルのパス。Windows に付属のシェル (cmd、PowerShell、または Bash on Ubuntu) を使用する場合、64 ビット バージョンを使用するには、C:\\Windows\\System32 ではなく、C:\\Windows\\sysnative を選びます。", "terminal.integrated.shellArgs.linux": "Linux 端末で使用するコマンド ライン引数。", "terminal.integrated.shellArgs.osx": "OS X 端末で使用するコマンド ライン引数。", "terminal.integrated.shellArgs.windows": "Windows ターミナル上の場合に使用されるコマンド ライン引数。", diff --git a/i18n/jpn/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json index c092dbd7301..80418f582df 100644 --- a/i18n/jpn/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json +++ b/i18n/jpn/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json @@ -4,11 +4,7 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "insiderBuilds": "Insider ビルドが毎日リリースされます。", - "license": "ライセンスの閲覧", - "licenseChanged": "ライセンス条項が変更されました。内容をご確認ください。", - "neverShowAgain": "今後は表示しない", - "read the release notes": "{0} v{1} へようこそ! リリース ノートを確認しますか?", - "readmore": "詳細を参照", - "release notes": "リリース ノート" + "release notes": "リリース ノート", + "updateChannel": "更新チャネルから自動更新を受信するかどうかを構成します。変更後に再起動が必要です。", + "updateConfigurationTitle": "更新" } \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/update/electron-browser/update.i18n.json b/i18n/jpn/src/vs/workbench/parts/update/electron-browser/update.i18n.json new file mode 100644 index 00000000000..795d31e5be7 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/update/electron-browser/update.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "downloadNow": "今すぐダウンロード", + "later": "後で", + "noUpdatesAvailable": "現在入手可能な更新はありません。", + "read the release notes": "{0} v{1} へようこそ! リリース ノートを確認しますか?", + "releaseNotes": "リリース ノート", + "showReleaseNotes": "リリース ノートの表示", + "thereIsUpdateAvailable": "利用可能な更新プログラムがあります。", + "updateAvailable": "{0} は再起動後に更新されます。", + "updateNow": "今すぐ更新" +} \ No newline at end of file diff --git a/i18n/kor/extensions/typescript/out/features/bufferSyncSupport.i18n.json b/i18n/kor/extensions/typescript/out/features/bufferSyncSupport.i18n.json new file mode 100644 index 00000000000..fd8c9eae4f4 --- /dev/null +++ b/i18n/kor/extensions/typescript/out/features/bufferSyncSupport.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "versionMismatch": "버전이 일치하지 않습니다. 전역 tsc({0})가 VS 코드의 언어 서비스({1})와 다릅니다. 일관되지 않은 컴파일 오류가 발생할 수 있습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/platform/extensions/node/extensionValidator.i18n.json b/i18n/kor/src/vs/platform/extensions/node/extensionValidator.i18n.json index 38ea9a4e918..3d32acd9962 100644 --- a/i18n/kor/src/vs/platform/extensions/node/extensionValidator.i18n.json +++ b/i18n/kor/src/vs/platform/extensions/node/extensionValidator.i18n.json @@ -4,6 +4,18 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { + "extensionDescription.activationEvents1": "속성 `{0}`은(는) 생략할 수 있으며 `string[]` 형식이어야 합니다.", + "extensionDescription.activationEvents2": "속성 `{0}` 및 `{1}`은(는) 둘 다 지정하거나 둘 다 생략해야 합니다.", + "extensionDescription.empty": "가져온 확장 설명이 비어 있습니다.", + "extensionDescription.engines": "속성 `{0}`은(는) 필수이며 `object` 형식이어야 합니다.", + "extensionDescription.engines.vscode": "속성 `{0}`은(는) 필수이며 `string` 형식이어야 합니다.", + "extensionDescription.extensionDependencies": "속성 `{0}`은(는) 생략할 수 있으며 `string[]` 형식이어야 합니다.", + "extensionDescription.main1": "속성 `{0}`은(는) 생략할 수 있으며 `string` 형식이어야 합니다.", + "extensionDescription.main2": "확장의 폴더({1}) 내에 포함할 `main`({0})이 필요합니다. 이로 인해 확장이 이식 불가능한 상태가 될 수 있습니다.", + "extensionDescription.main3": "속성 `{0}` 및 `{1}`은(는) 둘 다 지정하거나 둘 다 생략해야 합니다.", + "extensionDescription.name": "속성 `{0}`은(는) 필수이며 `string` 형식이어야 합니다.", + "extensionDescription.publisher": "속성 `{0}`은(는) 필수이며 `string` 형식이어야 합니다.", + "extensionDescription.version": "속성 `{0}`은(는) 필수이며 `string` 형식이어야 합니다.", "notSemver": "확장 버전이 semver와 호환되지 않습니다.", "versionMismatch": "확장이 Code {0}과(와) 호환되지 않습니다. 확장에 {1}이(가) 필요합니다.", "versionSpecificity1": "`engines.vscode`({0})에 지정된 버전이 명확하지 않습니다. vscode 버전이 1.0.0 이전이면 최소한 원하는 주 버전과 부 버전을 정의하세요( 예: ^0.10.0, 0.10.x, 0.11.0 등).", diff --git a/i18n/kor/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json b/i18n/kor/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json new file mode 100644 index 00000000000..8b6ad71cd4e --- /dev/null +++ b/i18n/kor/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/html/browser/webview.i18n.json b/i18n/kor/src/vs/workbench/parts/html/browser/webview.i18n.json new file mode 100644 index 00000000000..8b6ad71cd4e --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/html/browser/webview.i18n.json @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json b/i18n/kor/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json index a57ef0099dd..c8c19eb6a4a 100644 --- a/i18n/kor/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json +++ b/i18n/kor/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json @@ -6,5 +6,5 @@ { "defaultLabel": "입력", "patternDescription": "Glob 패턴 사용", - "patternHelpInclude": "일치 패턴입니다. 예: ****/*.js** - 모든 JavaScript 파일을 일치시킵니다. **myFolder/**** - 해당 폴더와 모든 자식을 일치시킵니다.\n\n**참조**:\n*****은 0개 이상의 문자를 일치시킵니다.\n**?**은 1개의 문자를 일치시킵니다.\n******은 0개 이상의 디렉터리를 일치시킵니다.\n**[a-z]**는 문자 범위를 일치시킵니다.\n**{a,b}**는 패턴을 일치시킵니다." + "patternHelpInclude": "일치 패턴입니다. 예: **\\*\\*/*.js** - 모든 JavaScript 파일을 일치시킵니다. **myFolder/\\*\\*** - 해당 폴더와 모든 자식을 일치시킵니다.\n\n**참조**:\n**\\***은 0개 이상의 문자를 일치시킵니다.\n**?**은 1개의 문자를 일치시킵니다.\n**\\*\\***은 0개 이상의 디렉터리를 일치시킵니다.\n**[a-z]**는 문자 범위를 일치시킵니다.\n**{a,b}**는 패턴을 일치시킵니다." } \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json b/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json index 99b8a051924..47d3b7db30a 100644 --- a/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json +++ b/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json @@ -14,7 +14,7 @@ "terminal.integrated.setLocaleVariables": "로캘 변수가 터미널 시작 시 설정되는지 여부를 제어하며, 기본값은 OS X에서 true이고 기타 플랫폼에서 false입니다.", "terminal.integrated.shell.linux": "터미널이 Linux에서 사용하는 셸의 경로입니다.", "terminal.integrated.shell.osx": "터미널이 OS X에서 사용하는 셸의 경로입니다.", - "terminal.integrated.shell.windows": "터미널이 Windows에서 사용하는 셸의 경로입니다. Windows와 함께 제공되는 셸을 사용하는 경우(cmd, PowerShell 또는 Ubuntu의 Bash) 64비트 버전을 사용하려면 C:WindowsSystem32보다 C:Windowssysnative가 더 좋습니다.", + "terminal.integrated.shell.windows": "터미널이 Windows에서 사용하는 셸의 경로입니다. Windows와 함께 제공되는 셸을 사용하는 경우(cmd, PowerShell 또는 Ubuntu의 Bash) 64비트 버전을 사용하려면 C:\\Windows\\System32보다 C:\\Windows\\sysnative가 더 좋습니다.", "terminal.integrated.shellArgs.linux": "Linux 터미널에 있을 때 사용할 명령줄 인수입니다.", "terminal.integrated.shellArgs.osx": "OS X 터미널에 있을 때 사용할 명령줄 인수입니다.", "terminal.integrated.shellArgs.windows": "Windows 터미널에 있을 때 사용할 명령줄 인수입니다.", diff --git a/i18n/kor/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json b/i18n/kor/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json index b1dbf23ce44..98422076df4 100644 --- a/i18n/kor/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json +++ b/i18n/kor/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json @@ -4,11 +4,7 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "insiderBuilds": "참가자 빌드가 매일 릴리스됩니다.", - "license": "라이선스 읽기", - "licenseChanged": "사용 조건이 변경되었습니다. 자세히 읽어보세요.", - "neverShowAgain": "다시 표시 안 함", - "read the release notes": "{0} v{1}을(를) 시작합니다. 릴리스 정보를 확인하시겠습니까?", - "readmore": "자세히 알아보기", - "release notes": "릴리스 정보" + "release notes": "릴리스 정보", + "updateChannel": "업데이트 채널에서 자동 업데이트를 받을지 여부를 구성합니다. 변경 후 다시 시작해야 합니다.", + "updateConfigurationTitle": "업데이트" } \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/update/electron-browser/update.i18n.json b/i18n/kor/src/vs/workbench/parts/update/electron-browser/update.i18n.json new file mode 100644 index 00000000000..05d4842aec1 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/update/electron-browser/update.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "downloadNow": "지금 다운로드", + "later": "나중에", + "noUpdatesAvailable": "현재 사용 가능한 업데이트가 없습니다.", + "read the release notes": "{0} v{1}을(를) 시작합니다. 릴리스 정보를 확인하시겠습니까?", + "releaseNotes": "릴리스 정보", + "showReleaseNotes": "릴리스 정보 표시", + "thereIsUpdateAvailable": "사용 가능한 업데이트가 있습니다.", + "updateAvailable": "다시 시작하면 {0}이(가) 업데이트됩니다.", + "updateNow": "지금 업데이트" +} \ No newline at end of file diff --git a/i18n/rus/extensions/typescript/out/features/bufferSyncSupport.i18n.json b/i18n/rus/extensions/typescript/out/features/bufferSyncSupport.i18n.json new file mode 100644 index 00000000000..8c6ad54ecbe --- /dev/null +++ b/i18n/rus/extensions/typescript/out/features/bufferSyncSupport.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "versionMismatch": "Обнаружено несоответствие глобального tsc ({0}) и службы языка VS Code ({1}). Это может привести к ошибкам согласованности компиляции." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/platform/extensions/node/extensionValidator.i18n.json b/i18n/rus/src/vs/platform/extensions/node/extensionValidator.i18n.json index 087ec6e6394..6997905ff56 100644 --- a/i18n/rus/src/vs/platform/extensions/node/extensionValidator.i18n.json +++ b/i18n/rus/src/vs/platform/extensions/node/extensionValidator.i18n.json @@ -4,6 +4,18 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { + "extensionDescription.activationEvents1": "свойство \"{0}\" может быть опущено или должно быть типа \"string []\"", + "extensionDescription.activationEvents2": "оба свойства, \"{0}\" и \"{1}\", должны быть либо указаны, либо опущены", + "extensionDescription.empty": "Пустое описание расширения", + "extensionDescription.engines": "свойство \"{0}\" является обязательным и должно быть типа object", + "extensionDescription.engines.vscode": "свойство \"{0}\" является обязательным и должно иметь тип string", + "extensionDescription.extensionDependencies": "свойство \"{0}\" может быть опущено или должно быть типа \"string []\"", + "extensionDescription.main1": "свойство \"{0}\" может быть опущено или должно иметь тип string", + "extensionDescription.main2": "Ожидается, что функция main ({0}) будет включена в папку расширения ({1}). Из-за этого расширение может стать непереносимым.", + "extensionDescription.main3": "оба свойства, \"{0}\" и \"{1}\", должны быть либо указаны, либо опущены", + "extensionDescription.name": "свойство \"{0}\" является обязательным и должно иметь тип string", + "extensionDescription.publisher": "свойство \"{0}\" является обязательным и должно иметь тип string", + "extensionDescription.version": "свойство \"{0}\" является обязательным и должно иметь тип string", "notSemver": "Версия расширения несовместима с semver.", "versionMismatch": "Расширение несовместимо с кодом \"{0}\". Расширению требуется: {1}.", "versionSpecificity1": "Версия, указанная в engines.vscode ({0}), недостаточно конкретная. Для версий vscode до 1.0.0 укажите по крайней мере основной и дополнительный номер версии. Например, 0.10.0, 0.10.x, 0.11.0 и т. д.", diff --git a/i18n/rus/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json b/i18n/rus/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json new file mode 100644 index 00000000000..8b6ad71cd4e --- /dev/null +++ b/i18n/rus/src/vs/workbench/browser/actions/toggleEditorLayout.i18n.json @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/html/browser/webview.i18n.json b/i18n/rus/src/vs/workbench/parts/html/browser/webview.i18n.json new file mode 100644 index 00000000000..8b6ad71cd4e --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/html/browser/webview.i18n.json @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json b/i18n/rus/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json index 017bae6c1b9..1a555808611 100644 --- a/i18n/rus/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json +++ b/i18n/rus/src/vs/workbench/parts/search/browser/patternInputWidget.i18n.json @@ -6,5 +6,5 @@ { "defaultLabel": "ввод", "patternDescription": "Использовать стандартные маски", - "patternHelpInclude": "Шаблон для сопоставления. Например, ****/*.js** соответствует всем файлам JavaScript, а **myFolder/**** соответствует этой папке со всеми дочерними элементами.\n\n**Ссылка**:\n***** соответствует 0 или более символов\n**?** соответствует 1 символу\n****** соответствует 0 или более каталогов\n**[a-z]** соответствует диапазону символов\n**{a,b}** соответствует любому из шаблонов" + "patternHelpInclude": "Шаблон для сопоставления. Например, **\\*\\*/*.js** соответствует всем файлам JavaScript, а **myFolder/\\*\\*** соответствует этой папке со всеми дочерними элементами.\n\n**Ссылка**:\n**\\*** соответствует 0 или более символов\n**?** соответствует 1 символу\n**\\*\\*** соответствует 0 или более каталогов\n**[a-z]** соответствует диапазону символов\n**{a,b}** соответствует любому из шаблонов" } \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json b/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json index 28e793eb978..8c89616f65f 100644 --- a/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json +++ b/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json @@ -14,7 +14,7 @@ "terminal.integrated.setLocaleVariables": "Управляет заданием переменных при запуске терминала, значение по умолчанию: \"True\" для OS X и \"False\" для других платформ.", "terminal.integrated.shell.linux": "Путь оболочки, который используется терминалом в Linux.", "terminal.integrated.shell.osx": "Путь оболочки, который используется терминалом в OS X.", - "terminal.integrated.shell.windows": "Путь оболочки, который используется терминалом в Windows. При работе с оболочкой, поставляемой с Windows (cmd, PowerShell или Bash на Ubuntu), укажите C:Windowssysnative вместо C:WindowsSystem32 для использования 64-разрядных версий.", + "terminal.integrated.shell.windows": "Путь оболочки, который используется терминалом в Windows. При работе с оболочкой, поставляемой с Windows (cmd, PowerShell или Bash на Ubuntu), укажите C:\\Windows\\sysnative вместо C:\\Windows\\System32 для использования 64-разрядных версий.", "terminal.integrated.shellArgs.linux": "Аргументы командной строки, которые следует использовать в терминале Linux.", "terminal.integrated.shellArgs.osx": "Аргументы командной строки, которые следует использовать в терминале OS X.", "terminal.integrated.shellArgs.windows": "Аргументы командной строки, используемые в терминале Windows.", diff --git a/i18n/rus/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json b/i18n/rus/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json index 1290f89f61e..fe504334753 100644 --- a/i18n/rus/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json +++ b/i18n/rus/src/vs/workbench/parts/update/electron-browser/update.contribution.i18n.json @@ -4,11 +4,7 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "insiderBuilds": "Ежедневные сборки и выпуски для участников программы предварительной оценки.", - "license": "Прочитать лицензию", - "licenseChanged": "Условия использования лицензии изменились, ознакомьтесь с ними.", - "neverShowAgain": "Больше не показывать", - "read the release notes": "Вас приветствует {0} v{1}! Вы хотите прочитать заметки о выпуске?", - "readmore": "Подробнее", - "release notes": "Заметки о выпуске" + "release notes": "Заметки о выпуске", + "updateChannel": "Настройте канал обновления, по которому вы будете получать обновления. После изменения значения необходим перезапуск.", + "updateConfigurationTitle": "Обновить" } \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/update/electron-browser/update.i18n.json b/i18n/rus/src/vs/workbench/parts/update/electron-browser/update.i18n.json new file mode 100644 index 00000000000..9a044906abc --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/update/electron-browser/update.i18n.json @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "downloadNow": "Скачать сейчас", + "later": "Позже", + "noUpdatesAvailable": "В настоящее время нет доступных обновлений.", + "read the release notes": "Вас приветствует {0} v{1}! Вы хотите прочитать заметки о выпуске?", + "releaseNotes": "Заметки о выпуске", + "showReleaseNotes": "Показать заметки о выпуске", + "thereIsUpdateAvailable": "Доступно обновление.", + "updateAvailable": "{0} будет обновлен после перезапуска.", + "updateNow": "Обновить сейчас" +} \ No newline at end of file diff --git a/src/vs/base/common/jsonSchema.ts b/src/vs/base/common/jsonSchema.ts index ffde9dd6d2d..c81c6c74a41 100644 --- a/src/vs/base/common/jsonSchema.ts +++ b/src/vs/base/common/jsonSchema.ts @@ -42,6 +42,7 @@ export interface IJSONSchema { defaultSnippets?: { label?: string; description?: string; body: any; }[]; // VSCode extension errorMessage?: string; // VSCode extension + deprecatedMessage?: string; // VSCode extension } export interface IJSONSchemaMap { diff --git a/src/vs/base/common/watchDog.ts b/src/vs/base/common/watchDog.ts new file mode 100644 index 00000000000..e7dd61b054e --- /dev/null +++ b/src/vs/base/common/watchDog.ts @@ -0,0 +1,55 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import Event, { Emitter } from 'vs/base/common/event'; + +export class WatchDog { + + private _timeout: number; + private _threshold: number; + private _onAlert = new Emitter(); + + private _handle: number; + private _missed: number; + private _lastSignal: number; + + constructor(timeout: number, threshold: number) { + this._timeout = timeout; + this._threshold = threshold; + } + + dispose(): void { + this.stop(); + } + + get onAlert(): Event { + return this._onAlert.event; + } + + start(): void { + this.reset(); + this._handle = setInterval(this._check.bind(this), this._timeout * 1.5); + } + + stop(): void { + clearInterval(this._handle); + } + + reset(): void { + this._lastSignal = Date.now(); + this._missed = 0; + } + + private _check(): void { + if ((Date.now() - this._lastSignal) > this._timeout) { + this._missed += 1; + if (this._missed > this._threshold) { + this._onAlert.fire(this); + this._missed = 0; + } + } + } +} diff --git a/src/vs/base/node/event.ts b/src/vs/base/node/event.ts index 8955ae2dd82..3cc1c275122 100644 --- a/src/vs/base/node/event.ts +++ b/src/vs/base/node/event.ts @@ -8,7 +8,7 @@ import Event, { Emitter } from 'vs/base/common/event'; import { EventEmitter } from 'events'; -export function fromEventEmitter(emitter: EventEmitter, eventName: string, map: (...args: any[]) => T): Event { +export function fromEventEmitter(emitter: EventEmitter, eventName: string, map: (...args: any[]) => T = id => id): Event { const fn = (...args) => result.fire(map(...args)); const onFirstListenerAdd = () => emitter.on(eventName, fn); const onLastListenerRemove = () => emitter.removeListener(eventName, fn); diff --git a/src/vs/base/parts/ipc/common/ipc.electron.ts b/src/vs/base/parts/ipc/common/ipc.electron.ts index 7f607938f30..f9f773a330c 100644 --- a/src/vs/base/parts/ipc/common/ipc.electron.ts +++ b/src/vs/base/parts/ipc/common/ipc.electron.ts @@ -5,21 +5,13 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; -import Event, { Emitter, chain } from 'vs/base/common/event'; -import { fromEventEmitter } from 'vs/base/node/event'; -import { ChannelServer as IPCServer, ChannelClient as IPCClient, IChannelServer, IChannelClient, IChannel } from 'vs/base/parts/ipc/common/ipc'; - -const Hello = 'ipc:hello'; -const Goodbye = 'ipc:goodbye'; -const Message = 'ipc:message'; +import Event, { Emitter } from 'vs/base/common/event'; export interface Sender { send(channel: string, ...args: any[]): void; } -export interface IPC extends Sender, NodeJS.EventEmitter { } - -class Protocol implements IMessagePassingProtocol { +export class Protocol implements IMessagePassingProtocol { private listener: IDisposable; @@ -33,80 +25,14 @@ class Protocol implements IMessagePassingProtocol { } send(message: any): void { - this.sender.send(Message, message); + try { + this.sender.send('ipc:message', message); + } catch (e) { + // systems are going down + } } dispose(): void { this.listener = dispose(this.listener); } -} - -interface IIPCEvent { - event: any; - message: string; -} - -export class Server implements IChannelServer, IDisposable { - - private channels: { [name: string]: IChannel } = Object.create(null); - - constructor(private ipc: NodeJS.EventEmitter) { - ipc.on(Hello, ({ sender }) => this.onHello(sender)); - } - - registerChannel(channelName: string, channel: IChannel): void { - this.channels[channelName] = channel; - } - - private onHello(sender: any): void { - const senderId = sender.getId(); - const onMessage = this.createScopedEvent(Message, senderId); - const protocol = new Protocol(sender, onMessage); - const ipcServer = new IPCServer(protocol); - - Object.keys(this.channels) - .forEach(name => ipcServer.registerChannel(name, this.channels[name])); - - const onGoodbye = this.createScopedEvent(Goodbye, senderId); - const listener = onGoodbye(() => { - listener.dispose(); - ipcServer.dispose(); - protocol.dispose(); - }); - } - - private createScopedEvent(eventName: string, senderId: string) { - return chain(fromEventEmitter(this.ipc, eventName, (event, message) => ({ event, message }))) - .filter(({ event }) => event.sender.getId() === senderId) - .map(({ message }) => message) - .event; - } - - dispose(): void { - this.channels = null; - } -} - -export class Client implements IChannelClient, IDisposable { - - private protocol: Protocol; - private ipcClient: IPCClient; - - constructor(private ipc: IPC) { - ipc.send(Hello); - - const receiverEvent = fromEventEmitter(ipc, Message, (_, message) => message); - this.protocol = new Protocol(ipc, receiverEvent); - this.ipcClient = new IPCClient(this.protocol); - } - - getChannel(channelName: string): T { - return this.ipcClient.getChannel(channelName) as T; - } - - dispose(): void { - this.ipc.send(Goodbye); - this.ipcClient = dispose(this.ipcClient); - this.protocol = dispose(this.protocol); - } } \ No newline at end of file diff --git a/src/vs/base/parts/ipc/common/ipc.ts b/src/vs/base/parts/ipc/common/ipc.ts index 7458c894afe..d581b2dead1 100644 --- a/src/vs/base/parts/ipc/common/ipc.ts +++ b/src/vs/base/parts/ipc/common/ipc.ts @@ -7,7 +7,7 @@ import { Promise, TPromise } from 'vs/base/common/winjs.base'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import Event, { Emitter } from 'vs/base/common/event'; +import Event, { Emitter, once, filterEvent } from 'vs/base/common/event'; enum MessageType { RequestCommon, @@ -58,35 +58,59 @@ enum State { Idle } +/** + * An `IChannel` is an abstraction over a collection of commands. + * You can `call` several commands on a channel, each taking at + * most one single argument. A `call` always returns a promise + * with at most one single return value. + */ export interface IChannel { call(command: string, arg?: any): TPromise; } +/** + * An `IChannelServer` hosts a collection of channels. You are + * able to register channels onto it, provided a channel name. + */ export interface IChannelServer { registerChannel(channelName: string, channel: IChannel): void; } +/** + * An `IChannelClient` has access to a collection of channels. You + * are able to get those channels, given their channel name. + */ export interface IChannelClient { getChannel(channelName: string): T; } +/** + * An `IClientRouter` is responsible for routing calls to specific + * channels, in scenarios in which there are multiple possible + * channels (each from a separate client) to pick from. + */ export interface IClientRouter { - routeCall(command: string, arg: any): string; + route(command: string, arg: any): string; } +/** + * Similar to the `IChannelClient`, you can get channels from this + * collection of channels. The difference being that in the + * `IRoutingChannelClient`, there are multiple clients providing + * the same channel. You'll need to pass in an `IClientRouter` in + * order to pick the right one. + */ export interface IRoutingChannelClient { getChannel(channelName: string, router: IClientRouter): T; } -export class ChannelServer { +export class ChannelServer implements IChannelServer, IDisposable { - private channels: { [name: string]: IChannel }; - private activeRequests: { [id: number]: IDisposable; }; + private channels: { [name: string]: IChannel } = Object.create(null); + private activeRequests: { [id: number]: IDisposable; } = Object.create(null); private protocolListener: IDisposable; constructor(private protocol: IMessagePassingProtocol) { - this.channels = Object.create(null); - this.activeRequests = Object.create(null); this.protocolListener = this.protocol.onMessage(r => this.onMessage(r)); this.protocol.send({ type: MessageType.ResponseInitialize }); } @@ -313,6 +337,120 @@ export class ChannelClient implements IChannelClient, IDisposable { } } +export interface ClientConnectionEvent { + protocol: IMessagePassingProtocol; + onDidClientDisconnect: Event; +} + +/** + * An `IPCServer` is both a channel server and a routing channel + * client. + * + * As the owner of a protocol, you should extend both this + * and the `IPCClient` classes to get IPC implementations + * for your protocol. + */ +export class IPCServer implements IChannelServer, IRoutingChannelClient, IDisposable { + + private channels: { [name: string]: IChannel } = Object.create(null); + private channelClients: { [id: string]: ChannelClient; } = Object.create(null); + private onClientAdded = new Emitter(); + + constructor(onDidClientConnect: Event) { + onDidClientConnect(({ protocol, onDidClientDisconnect }) => { + const onFirstMessage = once(protocol.onMessage); + + onFirstMessage(id => { + const channelServer = new ChannelServer(protocol); + const channelClient = new ChannelClient(protocol); + + Object.keys(this.channels) + .forEach(name => channelServer.registerChannel(name, this.channels[name])); + + this.channelClients[id] = channelClient; + this.onClientAdded.fire(id); + + onDidClientDisconnect(() => { + channelServer.dispose(); + channelClient.dispose(); + delete this.channelClients[id]; + }); + }); + }); + } + + getChannel(channelName: string, router: IClientRouter): T { + const call = (command: string, arg: any) => { + const id = router.route(command, arg); + + if (!id) { + return TPromise.wrapError('Client id should be provided'); + } + + return this.getClient(id).then(client => client.getChannel(channelName).call(command, arg)); + }; + + return { call } as T; + } + + registerChannel(channelName: string, channel: IChannel): void { + this.channels[channelName] = channel; + } + + private getClient(clientId: string): TPromise { + const client = this.channelClients[clientId]; + + if (client) { + return TPromise.as(client); + } + + return new TPromise(c => { + const onClient = once(filterEvent(this.onClientAdded.event, id => id === clientId)); + onClient(() => c(this.channelClients[clientId])); + }); + } + + dispose(): void { + this.channels = null; + this.channelClients = null; + this.onClientAdded.dispose(); + } +} + +/** + * An `IPCClient` is both a channel client and a channel server. + * + * As the owner of a protocol, you should extend both this + * and the `IPCClient` classes to get IPC implementations + * for your protocol. + */ +export class IPCClient implements IChannelClient, IChannelServer, IDisposable { + + private channelClient: ChannelClient; + private channelServer: ChannelServer; + + constructor(protocol: IMessagePassingProtocol, id: string) { + protocol.send(id); + this.channelClient = new ChannelClient(protocol); + this.channelServer = new ChannelServer(protocol); + } + + getChannel(channelName: string): T { + return this.channelClient.getChannel(channelName) as T; + } + + registerChannel(channelName: string, channel: IChannel): void { + this.channelServer.registerChannel(channelName, channel); + } + + dispose(): void { + this.channelClient.dispose(); + this.channelClient = null; + this.channelServer.dispose(); + this.channelServer = null; + } +} + export function getDelayedChannel(promise: TPromise): T { const call = (command, arg) => promise.then(c => c.call(command, arg)); return { call } as T; diff --git a/src/vs/base/parts/ipc/electron-browser/ipc.electron-browser.ts b/src/vs/base/parts/ipc/electron-browser/ipc.electron-browser.ts new file mode 100644 index 00000000000..599cf277211 --- /dev/null +++ b/src/vs/base/parts/ipc/electron-browser/ipc.electron-browser.ts @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { fromEventEmitter } from 'vs/base/node/event'; +import { IPCClient } from 'vs/base/parts/ipc/common/ipc'; +import { Protocol } from 'vs/base/parts/ipc/common/ipc.electron'; +import { ipcRenderer } from 'electron'; + +export class Client extends IPCClient { + + private static createProtocol(): Protocol { + const onMessage = fromEventEmitter(ipcRenderer, 'ipc:message', (_, message) => message); + ipcRenderer.send('ipc:hello'); + return new Protocol(ipcRenderer, onMessage); + } + + constructor(id: string) { + super(Client.createProtocol(), id); + } +} \ No newline at end of file diff --git a/src/vs/base/parts/ipc/electron-main/ipc.electron-main.ts b/src/vs/base/parts/ipc/electron-main/ipc.electron-main.ts new file mode 100644 index 00000000000..d0295a4937b --- /dev/null +++ b/src/vs/base/parts/ipc/electron-main/ipc.electron-main.ts @@ -0,0 +1,44 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import Event, { filterEvent, mapEvent } from 'vs/base/common/event'; +import { fromEventEmitter } from 'vs/base/node/event'; +import { IPCServer, ClientConnectionEvent } from 'vs/base/parts/ipc/common/ipc'; +import { Protocol } from 'vs/base/parts/ipc/common/ipc.electron'; +import { ipcMain } from 'electron'; + +interface WebContents extends Electron.WebContents { + getId(): number; +} + +interface IIPCEvent { + event: { sender: WebContents; }; + message: string; +} + +function createScopedOnMessageEvent(senderId: number): Event { + const onMessage = fromEventEmitter(ipcMain, 'ipc:message', (event, message) => ({ event, message })); + const onMessageFromSender = filterEvent(onMessage, ({ event }) => event.sender.getId() === senderId); + return mapEvent(onMessageFromSender, ({ message }) => message); +} + +export class Server extends IPCServer { + + private static getOnDidClientConnect(): Event { + const onHello = fromEventEmitter(ipcMain, 'ipc:hello', ({ sender }) => sender); + + return mapEvent(onHello, webContents => { + const onMessage = createScopedOnMessageEvent(webContents.getId()); + const protocol = new Protocol(webContents, onMessage); + const onDidClientDisconnect = fromEventEmitter(webContents, 'destroyed'); + + return { protocol, onDidClientDisconnect }; + }); + } + + constructor() { + super(Server.getOnDidClientConnect()); + } +} diff --git a/src/vs/base/parts/ipc/node/ipc.net.ts b/src/vs/base/parts/ipc/node/ipc.net.ts index 694963fdb8f..fdc6a869a53 100644 --- a/src/vs/base/parts/ipc/node/ipc.net.ts +++ b/src/vs/base/parts/ipc/node/ipc.net.ts @@ -7,10 +7,9 @@ import { Socket, Server as NetServer, createConnection, createServer } from 'net'; import { TPromise } from 'vs/base/common/winjs.base'; -import { IDisposable } from 'vs/base/common/lifecycle'; -import Event, { Emitter, once } from 'vs/base/common/event'; +import Event, { Emitter, once, mapEvent } from 'vs/base/common/event'; import { fromEventEmitter } from 'vs/base/node/event'; -import { ChannelServer, ChannelClient, IMessagePassingProtocol, IChannelServer, IChannelClient, IRoutingChannelClient, IClientRouter, IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { IMessagePassingProtocol, ClientConnectionEvent, IPCServer, IPCClient } from 'vs/base/parts/ipc/common/ipc'; function bufferIndexOf(buffer: Buffer, value: number, start = 0) { while (start < buffer.length && buffer[start] !== value) { @@ -73,141 +72,42 @@ class Protocol implements IMessagePassingProtocol { } } -class RoutingChannelClient implements IRoutingChannelClient, IDisposable { +export class Server extends IPCServer { - private ipcClients: { [id: string]: ChannelClient; }; - private onClientAdded = new Emitter(); + private static toClientConnectionEvent(server: NetServer): Event { + const onConnection = fromEventEmitter(server, 'connection'); - constructor() { - this.ipcClients = Object.create(null); + return mapEvent(onConnection, socket => ({ + protocol: new Protocol(socket), + onDidClientDisconnect: once(fromEventEmitter(socket, 'close')) + })); } - add(id: string, client: ChannelClient): void { - this.ipcClients[id] = client; - this.onClientAdded.fire(); - } - - remove(id: string): void { - delete this.ipcClients[id]; - } - - private getClient(clientId: string): TPromise { - const getClientFn = (clientId: string, c: (client: IChannelClient) => void): boolean => { - let client = this.ipcClients[clientId]; - if (client) { - c(client); - return true; - } - return false; - }; - return new TPromise((c, e) => { - if (!getClientFn(clientId, c)) { - let disposable = this.onClientAdded.event(() => { - if (getClientFn(clientId, c)) { - disposable.dispose(); - } - }); - } - }); - } - - getChannel(channelName: string, router: IClientRouter): T { - const call = (command: string, arg: any) => { - const id = router.routeCall(command, arg); - if (!id) { - return TPromise.wrapError('Client id should be provided'); - } - return this.getClient(id).then(client => client.getChannel(channelName).call(command, arg)); - }; - return { call } as T; - } - - dispose() { - this.ipcClients = null; - this.onClientAdded.dispose(); - } -} - -// TODO@joao: move multi channel implementation down to ipc -export class Server implements IChannelServer, IRoutingChannelClient, IDisposable { - - private channels: { [name: string]: IChannel }; - private router: RoutingChannelClient; - constructor(private server: NetServer) { - this.channels = Object.create(null); - this.router = new RoutingChannelClient(); - - this.server.on('connection', (socket: Socket) => { - const protocol = new Protocol(socket); - const onFirstMessage = once(protocol.onMessage); - - onFirstMessage(id => { - const channelServer = new ChannelServer(protocol); - - Object.keys(this.channels) - .forEach(name => channelServer.registerChannel(name, this.channels[name])); - - const channelClient = new ChannelClient(protocol); - this.router.add(id, channelClient); - - socket.once('close', () => { - channelClient.dispose(); - this.router.remove(id); - channelServer.dispose(); - }); - }); - }); - } - - getChannel(channelName: string, router: IClientRouter): T { - return this.router.getChannel(channelName, router); - } - - registerChannel(channelName: string, channel: IChannel): void { - this.channels[channelName] = channel; + super(Server.toClientConnectionEvent(server)); } dispose(): void { - this.router.dispose(); - this.router = null; - this.channels = null; + super.dispose(); this.server.close(); this.server = null; } } -export class Client implements IChannelClient, IChannelServer, IDisposable { - - private channelClient: ChannelClient; - private channelServer: ChannelServer; +export class Client extends IPCClient { private _onClose = new Emitter(); get onClose(): Event { return this._onClose.event; } constructor(private socket: Socket, id: string) { - const protocol = new Protocol(socket); - protocol.send(id); - - this.channelClient = new ChannelClient(protocol); - this.channelServer = new ChannelServer(protocol); + super(new Protocol(socket), id); socket.once('close', () => this._onClose.fire()); } - getChannel(channelName: string): T { - return this.channelClient.getChannel(channelName) as T; - } - - registerChannel(channelName: string, channel: IChannel): void { - this.channelServer.registerChannel(channelName, channel); - } - dispose(): void { + super.dispose(); this.socket.end(); this.socket = null; - this.channelClient = null; - this.channelServer.dispose(); - this.channelServer = null; } } diff --git a/src/vs/base/test/common/watchDog.test.ts b/src/vs/base/test/common/watchDog.test.ts new file mode 100644 index 00000000000..9a25439c013 --- /dev/null +++ b/src/vs/base/test/common/watchDog.test.ts @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { WatchDog } from 'vs/base/common/watchDog'; + +suite('WatchDog', function () { + + test('start/stop', function (done) { + const dog = new WatchDog(10, 1); + dog.onAlert(e => { + dog.stop(); + assert.ok(e === dog); + done(); + }); + dog.start(); + }); +}); diff --git a/src/vs/base/test/node/config.test.ts b/src/vs/base/test/node/config.test.ts index ba968101d76..08569b6f109 100644 --- a/src/vs/base/test/node/config.test.ts +++ b/src/vs/base/test/node/config.test.ts @@ -13,17 +13,18 @@ import fs = require('fs'); import extfs = require('vs/base/node/extfs'); import uuid = require('vs/base/common/uuid'); import { ConfigWatcher } from 'vs/base/node/config'; +import { onError } from 'vs/test/utils/servicesTestUtils'; suite('Config', () => { - function testFile(callback: (path: string, cleanUp: (callback: () => void) => void) => void): void { + function testFile(callback: (error: Error, path: string, cleanUp: (callback: () => void) => void) => void): void { const id = uuid.generateUuid(); const parentDir = path.join(os.tmpdir(), 'vsctests', id); const newDir = path.join(parentDir, 'config', id); const testFile = path.join(newDir, 'config.json'); - extfs.mkdirp(newDir, 493, (error) => { - callback(testFile, (callback) => extfs.del(parentDir, os.tmpdir(), () => { }, callback)); + extfs.mkdirp(newDir, 493, error => { + callback(error, testFile, (callback) => extfs.del(parentDir, os.tmpdir(), () => { }, callback)); }); } @@ -51,7 +52,11 @@ suite('Config', () => { }); test('getConfig / getValue', function (done: () => void) { - testFile((testFile, cleanUp) => { + testFile((error, testFile, cleanUp) => { + if (error) { + return onError(error, done); + } + fs.writeFileSync(testFile, '// my comment\n{ "foo": "bar" }'); let watcher = new ConfigWatcher<{ foo: string; }>(testFile); @@ -71,7 +76,11 @@ suite('Config', () => { }); test('getConfig / getValue - broken JSON', function (done: () => void) { - testFile((testFile, cleanUp) => { + testFile((error, testFile, cleanUp) => { + if (error) { + return onError(error, done); + } + fs.writeFileSync(testFile, '// my comment\n "foo": "bar ... '); let watcher = new ConfigWatcher<{ foo: string; }>(testFile); @@ -89,7 +98,11 @@ suite('Config', () => { }); test('watching', function (done: () => void) { - testFile((testFile, cleanUp) => { + testFile((error, testFile, cleanUp) => { + if (error) { + return onError(error, done); + } + fs.writeFileSync(testFile, '// my comment\n{ "foo": "bar" }'); let watcher = new ConfigWatcher<{ foo: string; }>(testFile); @@ -111,7 +124,11 @@ suite('Config', () => { }); test('watching also works when file created later', function (done: () => void) { - testFile((testFile, cleanUp) => { + testFile((error, testFile, cleanUp) => { + if (error) { + return onError(error, done); + } + let watcher = new ConfigWatcher<{ foo: string; }>(testFile); watcher.getConfig(); // ensure we are in sync @@ -131,7 +148,11 @@ suite('Config', () => { }); test('watching detects the config file getting deleted', function (done: () => void) { - testFile((testFile, cleanUp) => { + testFile((error, testFile, cleanUp) => { + if (error) { + return onError(error, done); + } + fs.writeFileSync(testFile, '// my comment\n{ "foo": "bar" }'); let watcher = new ConfigWatcher<{ foo: string; }>(testFile); @@ -150,7 +171,11 @@ suite('Config', () => { }); test('reload', function (done: () => void) { - testFile((testFile, cleanUp) => { + testFile((error, testFile, cleanUp) => { + if (error) { + return onError(error, done); + } + fs.writeFileSync(testFile, '// my comment\n{ "foo": "bar" }'); let watcher = new ConfigWatcher<{ foo: string; }>(testFile, { changeBufferDelay: 100 }); diff --git a/src/vs/code/common/windowsIpc.ts b/src/vs/code/common/windowsIpc.ts index 709249d2366..2620656dc23 100644 --- a/src/vs/code/common/windowsIpc.ts +++ b/src/vs/code/common/windowsIpc.ts @@ -28,12 +28,10 @@ export class WindowEventChannel implements IWindowEventChannel { call(command: string, args?: any): any { switch (command) { - case 'event:onNewWindowOpen': - return eventToCall(this.onNewWindowOpen); - case 'event:onWindowFocus': - return eventToCall(this.onWindowFocus); + case 'event:onNewWindowOpen': return eventToCall(this.onNewWindowOpen); + case 'event:onWindowFocus': return eventToCall(this.onWindowFocus); + default: return TPromise.wrapError('invalid command'); } - return TPromise.wrapError('invalid command'); } } @@ -44,12 +42,8 @@ export class WindowEventChannelClient implements IWindowEventService { constructor(private channel: IWindowEventChannel) { } private _onNewWindowOpen: Event = eventFromCall(this.channel, 'event:onNewWindowOpen'); - get onNewWindowOpen(): Event { - return this._onNewWindowOpen; - } + get onNewWindowOpen(): Event { return this._onNewWindowOpen; } private _onWindowFocus: Event = eventFromCall(this.channel, 'event:onWindowFocus'); - get onWindowFocus(): Event { - return this._onWindowFocus; - } + get onWindowFocus(): Event { return this._onWindowFocus; } } \ No newline at end of file diff --git a/src/vs/code/electron-main/launch.ts b/src/vs/code/electron-main/launch.ts index ac17f0d6e66..2c2b01eff49 100644 --- a/src/vs/code/electron-main/launch.ts +++ b/src/vs/code/electron-main/launch.ts @@ -5,7 +5,7 @@ 'use strict'; -import { IWindowsService } from 'vs/code/electron-main/windows'; +import { IWindowsMainService } from 'vs/code/electron-main/windows'; import { VSCodeWindow } from 'vs/code/electron-main/window'; import { TPromise } from 'vs/base/common/winjs.base'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; @@ -63,7 +63,7 @@ export class LaunchService implements ILaunchService { constructor( @ILogService private logService: ILogService, - @IWindowsService private windowsService: IWindowsService, + @IWindowsMainService private windowsService: IWindowsMainService, @IURLService private urlService: IURLService ) { } diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 6ae217638a1..35e0f87c387 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -12,13 +12,15 @@ import * as platform from 'vs/base/common/platform'; import { parseMainProcessArgv, ParsedArgs } from 'vs/platform/environment/node/argv'; import { mkdirp } from 'vs/base/node/pfs'; import { validatePaths } from 'vs/code/electron-main/paths'; -import { IWindowsService, WindowsManager, WindowEventService } from 'vs/code/electron-main/windows'; -import { IWindowEventService } from 'vs/code/common/windows'; +import { IWindowsMainService, WindowsManager } from 'vs/code/electron-main/windows'; +import { IWindowsService } from 'vs/platform/windows/common/windows'; +import { WindowsChannel } from 'vs/platform/windows/common/windowsIpc'; +import { WindowsService } from 'vs/platform/windows/electron-main/windowsService'; import { WindowEventChannel } from 'vs/code/common/windowsIpc'; import { ILifecycleService, LifecycleService } from 'vs/code/electron-main/lifecycle'; import { VSCodeMenu } from 'vs/code/electron-main/menus'; import { IUpdateService, UpdateManager } from 'vs/code/electron-main/update-manager'; -import { Server as ElectronIPCServer } from 'vs/base/parts/ipc/common/ipc.electron'; +import { Server as ElectronIPCServer } from 'vs/base/parts/ipc/electron-main/ipc.electron-main'; import { Server, serve, connect } from 'vs/base/parts/ipc/node/ipc.net'; import { TPromise } from 'vs/base/common/winjs.base'; import { AskpassChannel } from 'vs/workbench/parts/git/common/gitIpc'; @@ -73,12 +75,11 @@ function main(accessor: ServicesAccessor, mainIpcServer: Server, userEnv: platfo const instantiationService = accessor.get(IInstantiationService); const logService = accessor.get(ILogService); const environmentService = accessor.get(IEnvironmentService); - const windowsService = accessor.get(IWindowsService); - const windowEventService = accessor.get(IWindowEventService); + const windowsMainService = accessor.get(IWindowsMainService); const lifecycleService = accessor.get(ILifecycleService); const updateService = accessor.get(IUpdateService); const configurationService = accessor.get(IConfigurationService) as ConfigurationService; - const windowEventChannel = new WindowEventChannel(windowEventService); + const windowEventChannel = new WindowEventChannel(windowsMainService); // We handle uncaught exceptions here to prevent electron from opening a dialog to the user process.on('uncaughtException', (err: any) => { @@ -91,7 +92,7 @@ function main(accessor: ServicesAccessor, mainIpcServer: Server, userEnv: platfo }; // handle on client side - windowsService.sendToFocused('vscode:reportError', JSON.stringify(friendlyError)); + windowsMainService.sendToFocused('vscode:reportError', JSON.stringify(friendlyError)); } console.error('[uncaught exception in main]: ' + err); @@ -125,13 +126,17 @@ function main(accessor: ServicesAccessor, mainIpcServer: Server, userEnv: platfo mainIpcServer.registerChannel('askpass', askpassChannel); // Create Electron IPC Server - const electronIpcServer = new ElectronIPCServer(ipc); + const electronIpcServer = new ElectronIPCServer(); // Register Electron IPC services const urlService = accessor.get(IURLService); const urlChannel = instantiationService.createInstance(URLChannel, urlService); electronIpcServer.registerChannel('url', urlChannel); + const windowsService = accessor.get(IWindowsService); + const windowsChannel = new WindowsChannel(windowsService); + electronIpcServer.registerChannel('windows', windowsChannel); + // Spawn shared process const initData = { args: environmentService.args }; const options = { @@ -140,12 +145,12 @@ function main(accessor: ServicesAccessor, mainIpcServer: Server, userEnv: platfo }; let sharedProcessDisposable; + spawnSharedProcess(initData, options).done(disposable => { sharedProcessDisposable = disposable; - const sharedProcessConnect = connect(environmentService.sharedIPCHandle, 'main'); - sharedProcessConnect.done(client => { - client.registerChannel('windowEvent', windowEventChannel); - }); + + connect(environmentService.sharedIPCHandle, 'main') + .done(client => client.registerChannel('windowEvent', windowEventChannel)); }); // Make sure we associate the program with the app user model id @@ -192,7 +197,7 @@ function main(accessor: ServicesAccessor, mainIpcServer: Server, userEnv: platfo lifecycleService.ready(); // Propagate to clients - windowsService.ready(userEnv); + windowsMainService.ready(userEnv); // Install Menu const menu = instantiationService.createInstance(VSCodeMenu); @@ -219,12 +224,12 @@ function main(accessor: ServicesAccessor, mainIpcServer: Server, userEnv: platfo }); // Recent Folders - const folders = windowsService.getRecentPathsList().folders; + const folders = windowsMainService.getRecentPathsList().folders; if (folders.length > 0) { jumpList.push({ type: 'custom', name: 'Recent Folders', - items: windowsService.getRecentPathsList().folders.slice(0, 7 /* limit number of entries here */).map(folder => { + items: windowsMainService.getRecentPathsList().folders.slice(0, 7 /* limit number of entries here */).map(folder => { return { type: 'task', title: getPathLabel(folder), @@ -255,11 +260,11 @@ function main(accessor: ServicesAccessor, mainIpcServer: Server, userEnv: platfo // Open our first window if (environmentService.args['new-window'] && environmentService.args._.length === 0) { - windowsService.open({ cli: environmentService.args, forceNewWindow: true, forceEmpty: true }); // new window if "-n" was used without paths + windowsMainService.open({ cli: environmentService.args, forceNewWindow: true, forceEmpty: true }); // new window if "-n" was used without paths } else if (global.macOpenFiles && global.macOpenFiles.length && (!environmentService.args._ || !environmentService.args._.length)) { - windowsService.open({ cli: environmentService.args, pathsToOpen: global.macOpenFiles }); // mac: open-file event received on startup + windowsMainService.open({ cli: environmentService.args, pathsToOpen: global.macOpenFiles }); // mac: open-file event received on startup } else { - windowsService.open({ cli: environmentService.args, forceNewWindow: environmentService.args['new-window'], diffMode: environmentService.args.diff }); // default: read paths from cli + windowsMainService.open({ cli: environmentService.args, forceNewWindow: environmentService.args['new-window'], diffMode: environmentService.args.diff }); // default: read paths from cli } } @@ -425,7 +430,7 @@ function getShellEnvironment(): TPromise { } function createPaths(environmentService: IEnvironmentService): TPromise { - const paths = [environmentService.appSettingsHome, environmentService.userHome, environmentService.extensionsPath]; + const paths = [environmentService.appSettingsHome, environmentService.userProductHome, environmentService.extensionsPath]; return TPromise.join(paths.map(p => mkdirp(p))) as TPromise; } @@ -447,8 +452,8 @@ function start(): void { services.set(IEnvironmentService, new SyncDescriptor(EnvironmentService, args, process.execPath)); services.set(ILogService, new SyncDescriptor(MainLogService)); - services.set(IWindowsService, new SyncDescriptor(WindowsManager)); - services.set(IWindowEventService, new SyncDescriptor(WindowEventService)); + services.set(IWindowsMainService, new SyncDescriptor(WindowsManager)); + services.set(IWindowsService, new SyncDescriptor(WindowsService)); services.set(ILifecycleService, new SyncDescriptor(LifecycleService)); services.set(IStorageService, new SyncDescriptor(StorageService)); services.set(IConfigurationService, new SyncDescriptor(ConfigurationService)); diff --git a/src/vs/code/electron-main/menus.ts b/src/vs/code/electron-main/menus.ts index f130651e774..7d97bed307a 100644 --- a/src/vs/code/electron-main/menus.ts +++ b/src/vs/code/electron-main/menus.ts @@ -10,7 +10,7 @@ import * as platform from 'vs/base/common/platform'; import * as arrays from 'vs/base/common/arrays'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ipcMain as ipc, app, shell, dialog, Menu, MenuItem } from 'electron'; -import { IWindowsService } from 'vs/code/electron-main/windows'; +import { IWindowsMainService } from 'vs/code/electron-main/windows'; import { IPath, VSCodeWindow } from 'vs/code/electron-main/window'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IStorageService } from 'vs/code/electron-main/storage'; @@ -57,7 +57,7 @@ export class VSCodeMenu { @IStorageService private storageService: IStorageService, @IUpdateService private updateService: IUpdateService, @IConfigurationService private configurationService: IConfigurationService, - @IWindowsService private windowsService: IWindowsService, + @IWindowsMainService private windowsService: IWindowsMainService, @IEnvironmentService private environmentService: IEnvironmentService ) { this.actionIdKeybindingRequests = []; diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 16da9162265..4530d940889 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -86,9 +86,9 @@ const ReopenFoldersSetting = { NONE: 'none' }; -export const IWindowsService = createDecorator('windowsService'); +export const IWindowsMainService = createDecorator('windowsMainService'); -export interface IWindowsService { +export interface IWindowsMainService { _serviceBrand: any; // TODO make proper events @@ -105,7 +105,7 @@ export interface IWindowsService { open(openConfig: IOpenConfiguration): VSCodeWindow[]; openPluginDevelopmentHostWindow(openConfig: IOpenConfiguration): void; openFileFolderPicker(forceNewWindow?: boolean): void; - openFilePicker(forceNewWindow?: boolean): void; + openFilePicker(forceNewWindow?: boolean, path?: string): void; openFolderPicker(forceNewWindow?: boolean): void; openAccessibilityOptions(): void; focusLastActive(cli: ParsedArgs): VSCodeWindow; @@ -123,22 +123,7 @@ export interface IWindowsService { clearRecentPathsList(): void; } -export class WindowEventService implements IWindowEventService { - - _serviceBrand: any; - - constructor( @IWindowsService private windowsService: IWindowsService) { } - - public get onWindowFocus(): CommonEvent { - return this.windowsService.onWindowFocus; - } - - public get onNewWindowOpen(): CommonEvent { - return this.windowsService.onNewWindowOpen; - } -} - -export class WindowsManager implements IWindowsService { +export class WindowsManager implements IWindowsMainService, IWindowEventService { _serviceBrand: any; @@ -254,24 +239,6 @@ export class WindowsManager implements IWindowsService { } }); - ipc.on('vscode:openFilePicker', (event, forceNewWindow?: boolean, path?: string) => { - this.logService.log('IPC#vscode-openFilePicker'); - - this.openFilePicker(forceNewWindow, path); - }); - - ipc.on('vscode:openFolderPicker', (event, forceNewWindow?: boolean) => { - this.logService.log('IPC#vscode-openFolderPicker'); - - this.openFolderPicker(forceNewWindow); - }); - - ipc.on('vscode:openFileFolderPicker', (event, forceNewWindow?: boolean) => { - this.logService.log('IPC#vscode-openFileFolderPicker'); - - this.openFileFolderPicker(forceNewWindow); - }); - ipc.on('vscode:closeFolder', (event, windowId: number) => { this.logService.log('IPC#vscode-closeFolder'); @@ -287,15 +254,6 @@ export class WindowsManager implements IWindowsService { this.openNewWindow(); }); - ipc.on('vscode:reloadWindow', (event, windowId: number) => { - this.logService.log('IPC#vscode:reloadWindow'); - - const vscodeWindow = this.getWindowById(windowId); - if (vscodeWindow) { - this.reload(vscodeWindow); - } - }); - ipc.on('vscode:toggleFullScreen', (event, windowId: number) => { this.logService.log('IPC#vscode:toggleFullScreen'); diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index dc4b47b0976..46b12590af1 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -160,7 +160,7 @@ export function main(argv: ParsedArgs): TPromise { return instantiationService.invokeFunction(accessor => { const envService = accessor.get(IEnvironmentService); - return TPromise.join([envService.appSettingsHome, envService.userHome, envService.extensionsPath].map(p => mkdirp(p))).then(() => { + return TPromise.join([envService.appSettingsHome, envService.userProductHome, envService.extensionsPath].map(p => mkdirp(p))).then(() => { const { appRoot, extensionsPath, extensionDevelopmentPath, isBuilt } = envService; const services = new ServiceCollection(); diff --git a/src/vs/code/node/sharedProcessMain.ts b/src/vs/code/node/sharedProcessMain.ts index 86313446bfd..e292ec48c35 100644 --- a/src/vs/code/node/sharedProcessMain.ts +++ b/src/vs/code/node/sharedProcessMain.ts @@ -66,17 +66,14 @@ function main(server: Server, initData: ISharedProcessInitData): void { services.set(IConfigurationService, new SyncDescriptor(ConfigurationService)); services.set(IRequestService, new SyncDescriptor(RequestService)); - const windowEventService: IWindowEventService = new WindowEventChannelClient(server.getChannel('windowEvent', { - routeCall: (command: any, arg: any) => { - return 'main'; - } - })); + const windowEventChannel = server.getChannel('windowEvent', { route: () => 'main' }); + const windowEventService: IWindowEventService = new WindowEventChannelClient(windowEventChannel); services.set(IWindowEventService, windowEventService); const activeWindowManager = new ActiveWindowManager(windowEventService); - services.set(IChoiceService, new ChoiceChannelClient(server.getChannel('choice', { - routeCall: () => activeWindowManager.activeClientId - }))); + + const choiceChannel = server.getChannel('choice', { route: () => activeWindowManager.activeClientId }); + services.set(IChoiceService, new ChoiceChannelClient(choiceChannel)); const instantiationService = new InstantiationService(services); diff --git a/src/vs/editor/browser/config/elementSizeObserver.ts b/src/vs/editor/browser/config/elementSizeObserver.ts index 2860d2caca9..ee21f6c21bd 100644 --- a/src/vs/editor/browser/config/elementSizeObserver.ts +++ b/src/vs/editor/browser/config/elementSizeObserver.ts @@ -57,8 +57,8 @@ export class ElementSizeObserver extends Disposable implements IElementSizeObser } private measureReferenceDomElement(callChangeCallback: boolean, dimension?: IDimension): void { - var observedWidth = 0; - var observedHeight = 0; + let observedWidth = 0; + let observedHeight = 0; if (dimension) { observedWidth = dimension.width; observedHeight = dimension.height; diff --git a/src/vs/editor/browser/controller/mouseHandler.ts b/src/vs/editor/browser/controller/mouseHandler.ts index 4816b4a6129..1b09e14de9d 100644 --- a/src/vs/editor/browser/controller/mouseHandler.ts +++ b/src/vs/editor/browser/controller/mouseHandler.ts @@ -287,7 +287,7 @@ export class MouseHandler extends ViewEventHandler implements IDisposable { shouldHandle = false; } - var focus = () => { + let focus = () => { // In IE11, if the focus is in the browser's address bar and // then you click in the editor, calling preventDefault() // will not move focus properly (focus remains the address bar) diff --git a/src/vs/editor/browser/controller/pointerHandler.ts b/src/vs/editor/browser/controller/pointerHandler.ts index 7af7007e0c4..c3cf0812122 100644 --- a/src/vs/editor/browser/controller/pointerHandler.ts +++ b/src/vs/editor/browser/controller/pointerHandler.ts @@ -18,8 +18,8 @@ interface IThrottledGestureEvent { translationY: number; } -var gestureChangeEventMerger = (lastEvent: IThrottledGestureEvent, currentEvent: MSGestureEvent): IThrottledGestureEvent => { - var r = { +function gestureChangeEventMerger(lastEvent: IThrottledGestureEvent, currentEvent: MSGestureEvent): IThrottledGestureEvent { + let r = { translationY: currentEvent.translationY, translationX: currentEvent.translationX }; @@ -49,13 +49,13 @@ class MsPointerHandler extends MouseHandler implements IDisposable { this._installGestureHandlerTimeout = window.setTimeout(() => { this._installGestureHandlerTimeout = -1; if ((window).MSGesture) { - var touchGesture = new MSGesture(); - var penGesture = new MSGesture(); + let touchGesture = new MSGesture(); + let penGesture = new MSGesture(); touchGesture.target = this.viewHelper.linesContentDomNode; penGesture.target = this.viewHelper.linesContentDomNode; this.viewHelper.linesContentDomNode.addEventListener('MSPointerDown', (e: MSPointerEvent) => { // Circumvent IE11 breaking change in e.pointerType & TypeScript's stale definitions - var pointerType = e.pointerType; + let pointerType = e.pointerType; if (pointerType === ((e).MSPOINTER_TYPE_MOUSE || 'mouse')) { this._lastPointerType = 'mouse'; return; @@ -131,12 +131,12 @@ class StandardPointerHandler extends MouseHandler implements IDisposable { // TODO@Alex: replace the usage of MSGesture here with something that works across all browsers if ((window).MSGesture) { - var touchGesture = new MSGesture(); - var penGesture = new MSGesture(); + let touchGesture = new MSGesture(); + let penGesture = new MSGesture(); touchGesture.target = this.viewHelper.linesContentDomNode; penGesture.target = this.viewHelper.linesContentDomNode; this.viewHelper.linesContentDomNode.addEventListener('pointerdown', (e: MSPointerEvent) => { - var pointerType = e.pointerType; + let pointerType = e.pointerType; if (pointerType === 'mouse') { this._lastPointerType = 'mouse'; return; diff --git a/src/vs/editor/browser/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts index 4e6aaf9f7b8..0856aec1e26 100644 --- a/src/vs/editor/browser/editorBrowser.ts +++ b/src/vs/editor/browser/editorBrowser.ts @@ -142,7 +142,7 @@ export interface IViewController { /** * @internal */ -export var ClassNames = { +export const ClassNames = { TEXTAREA_COVER: 'textAreaCover', TEXTAREA: 'inputarea', LINES_CONTENT: 'lines-content', diff --git a/src/vs/editor/browser/services/codeEditorServiceImpl.ts b/src/vs/editor/browser/services/codeEditorServiceImpl.ts index 9784da84372..542fe7d1233 100644 --- a/src/vs/editor/browser/services/codeEditorServiceImpl.ts +++ b/src/vs/editor/browser/services/codeEditorServiceImpl.ts @@ -80,7 +80,7 @@ class DecorationSubTypeOptionsProvider implements IModelDecorationOptionsProvide this._parentTypeKey = parentTypeKey; this.refCount = 0; - var themedOpts = getThemedRenderOptions(options); + let themedOpts = getThemedRenderOptions(options); this._beforeContentClassName = DecorationRenderHelper.createCSSRules( styleSheet, @@ -146,7 +146,7 @@ class DecorationTypeOptionsProvider implements IModelDecorationOptionsProvider { constructor(styleSheet: HTMLStyleElement, key: string, options: IDecorationRenderOptions) { this.refCount = 0; - var themedOpts = getThemedRenderOptions(options); + let themedOpts = getThemedRenderOptions(options); this.className = DecorationRenderHelper.createCSSRules( styleSheet, @@ -446,14 +446,14 @@ interface IResolvedDecorationRenderOptions { } function getThemedRenderOptions(opts: { light?: T, dark?: T }): { light?: T, dark?: T } { // TODO@alex,joh - not really how/what deep clone is being used - // for here but it will break the URI + // for here but it will break the URI TODO@martin - // var light = objects.deepClone(opts); - var light = parse(stringify(opts)); + // let light = objects.deepClone(opts); + let light = parse(stringify(opts)); objects.mixin(light, opts.light); - // var dark = objects.deepClone(opts); - var dark = parse(stringify(opts)); + // let dark = objects.deepClone(opts); + let dark = parse(stringify(opts)); objects.mixin(dark, opts.dark); return { diff --git a/src/vs/editor/browser/standalone/simpleServices.ts b/src/vs/editor/browser/standalone/simpleServices.ts index 09e5a301b1c..6dead3a8db9 100644 --- a/src/vs/editor/browser/standalone/simpleServices.ts +++ b/src/vs/editor/browser/standalone/simpleServices.ts @@ -115,14 +115,14 @@ export class SimpleEditorService implements IEditorService { } private doOpenEditor(editor: editorCommon.ICommonCodeEditor, data: IResourceInput): IEditor { - var model = this.findModel(editor, data); + let model = this.findModel(editor, data); if (!model) { if (data.resource) { if (this.openEditorDelegate) { this.openEditorDelegate(data.resource.toString()); return null; } else { - var schema = data.resource.scheme; + let schema = data.resource.scheme; if (schema === Schemas.http || schema === Schemas.https) { // This is a fully qualified http or https URL window.open(data.resource.toString()); @@ -133,14 +133,13 @@ export class SimpleEditorService implements IEditorService { return null; } - - var selection = data.options.selection; + let selection = data.options.selection; if (selection) { if (typeof selection.endLineNumber === 'number' && typeof selection.endColumn === 'number') { editor.setSelection(selection); editor.revealRangeInCenter(selection); } else { - var pos = { + let pos = { lineNumber: selection.startLineNumber, column: selection.startColumn }; @@ -153,7 +152,7 @@ export class SimpleEditorService implements IEditorService { } private findModel(editor: editorCommon.ICommonCodeEditor, data: IResourceInput): editorCommon.IModel { - var model = editor.getModel(); + let model = editor.getModel(); if (model.uri.toString() !== data.resource.toString()) { return null; } @@ -162,7 +161,7 @@ export class SimpleEditorService implements IEditorService { } public resolveEditorModel(typedData: IResourceInput, refresh?: boolean): TPromise { - var model: editorCommon.IModel; + let model: editorCommon.IModel; model = this.editor.withTypedEditor( (editor) => this.findModel(editor, typedData), @@ -224,7 +223,7 @@ export class SimpleMessageService implements IMessageService { } public confirm(confirmation: IConfirmation): boolean { - var messageText = confirmation.message; + let messageText = confirmation.message; if (confirmation.detail) { messageText = messageText + '\n\n' + confirmation.detail; } @@ -277,7 +276,7 @@ export class StandaloneKeybindingService extends KeybindingService { if (commandId === null) { commandId = 'DYNAMIC_' + (++StandaloneKeybindingService.LAST_GENERATED_ID); } - var parsedContext = IOSupport.readKeybindingWhen(when); + let parsedContext = IOSupport.readKeybindingWhen(when); this._dynamicKeybindings.push({ keybinding: keybinding, command: commandId, diff --git a/src/vs/editor/browser/standalone/standaloneCodeEditor.ts b/src/vs/editor/browser/standalone/standaloneCodeEditor.ts index 473fa1d6c68..0fadbcf9948 100644 --- a/src/vs/editor/browser/standalone/standaloneCodeEditor.ts +++ b/src/vs/editor/browser/standalone/standaloneCodeEditor.ts @@ -135,7 +135,7 @@ export class StandaloneEditor extends CodeEditor implements IStandaloneCodeEdito return null; } if (Array.isArray(descriptor.keybindings)) { - var handler: ICommandHandler = (accessor) => { + let handler: ICommandHandler = (accessor) => { return this.trigger('keyboard', descriptor.id, null); }; descriptor.keybindings.forEach((kb) => { @@ -221,7 +221,7 @@ export class StandaloneDiffEditor extends DiffEditorWidget implements IStandalon return null; } if (Array.isArray(descriptor.keybindings)) { - var handler: ICommandHandler = (ctx) => { + let handler: ICommandHandler = (ctx) => { return this.trigger('keyboard', descriptor.id, null); }; descriptor.keybindings.forEach((kb) => { diff --git a/src/vs/editor/browser/standalone/standaloneLanguages.ts b/src/vs/editor/browser/standalone/standaloneLanguages.ts index f7e14e0d68c..688d49ba25c 100644 --- a/src/vs/editor/browser/standalone/standaloneLanguages.ts +++ b/src/vs/editor/browser/standalone/standaloneLanguages.ts @@ -13,7 +13,7 @@ import { IMonarchLanguage } from 'vs/editor/common/modes/monarch/monarchTypes'; import { ILanguageExtensionPoint } from 'vs/editor/common/services/modeService'; import { StaticServices } from 'vs/editor/browser/standalone/standaloneServices'; import * as modes from 'vs/editor/common/modes'; -import { LanguageConfiguration } from 'vs/editor/common/modes/languageConfigurationRegistry'; +import { LanguageConfiguration, IndentAction } from 'vs/editor/common/modes/languageConfiguration'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; @@ -512,6 +512,6 @@ export function createMonacoLanguagesAPI(): typeof monaco.languages { DocumentHighlightKind: modes.DocumentHighlightKind, CompletionItemKind: CompletionItemKind, SymbolKind: modes.SymbolKind, - IndentAction: modes.IndentAction + IndentAction: IndentAction }; } diff --git a/src/vs/editor/browser/view/viewController.ts b/src/vs/editor/browser/view/viewController.ts index 934c755d5f1..d81a0b5d54b 100644 --- a/src/vs/editor/browser/view/viewController.ts +++ b/src/vs/editor/browser/view/viewController.ts @@ -69,7 +69,7 @@ export class ViewController implements IViewController { } private _validateViewColumn(viewPosition: Position): Position { - var minColumn = this.viewModel.getLineMinColumn(viewPosition.lineNumber); + let minColumn = this.viewModel.getLineMinColumn(viewPosition.lineNumber); if (viewPosition.column < minColumn) { return new Position(viewPosition.lineNumber, minColumn); } diff --git a/src/vs/editor/browser/view/viewImpl.ts b/src/vs/editor/browser/view/viewImpl.ts index c825ec03cca..1fbd5555b60 100644 --- a/src/vs/editor/browser/view/viewImpl.ts +++ b/src/vs/editor/browser/view/viewImpl.ts @@ -98,7 +98,7 @@ export class View extends ViewEventHandler implements editorBrowser.IView, IDisp this._renderAnimationFrame = null; this.outgoingEventBus = new EventEmitter(); - var viewController = new ViewController(model, triggerCursorHandler, this.outgoingEventBus, commandService); + let viewController = new ViewController(model, triggerCursorHandler, this.outgoingEventBus, commandService); this.listenersToRemove = []; this.listenersToDispose = []; @@ -159,7 +159,7 @@ export class View extends ViewEventHandler implements editorBrowser.IView, IDisp } private _flushAnyAccumulatedEvents(): void { - var toEmit = this.accumulatedModelEvents; + let toEmit = this.accumulatedModelEvents; this.accumulatedModelEvents = []; if (toEmit.length > 0) { this.eventDispatcher.emitMany(toEmit); @@ -217,24 +217,24 @@ export class View extends ViewEventHandler implements editorBrowser.IView, IDisp this.viewParts.push(this.viewZones); // Decorations overview ruler - var decorationsOverviewRuler = new DecorationsOverviewRuler( + let decorationsOverviewRuler = new DecorationsOverviewRuler( this._context, this.layoutProvider.getScrollHeight(), (lineNumber: number) => this.layoutProvider.getVerticalOffsetForLineNumber(lineNumber) ); this.viewParts.push(decorationsOverviewRuler); - var scrollDecoration = new ScrollDecorationViewPart(this._context); + let scrollDecoration = new ScrollDecorationViewPart(this._context); this.viewParts.push(scrollDecoration); - var contentViewOverlays = new ContentViewOverlays(this._context, this.layoutProvider); + let contentViewOverlays = new ContentViewOverlays(this._context, this.layoutProvider); this.viewParts.push(contentViewOverlays); contentViewOverlays.addDynamicOverlay(new CurrentLineHighlightOverlay(this._context, this.layoutProvider)); contentViewOverlays.addDynamicOverlay(new SelectionsOverlay(this._context)); contentViewOverlays.addDynamicOverlay(new DecorationsOverlay(this._context)); contentViewOverlays.addDynamicOverlay(new IndentGuidesOverlay(this._context)); - var marginViewOverlays = new MarginViewOverlays(this._context, this.layoutProvider); + let marginViewOverlays = new MarginViewOverlays(this._context, this.layoutProvider); this.viewParts.push(marginViewOverlays); marginViewOverlays.addDynamicOverlay(new GlyphMarginOverlay(this._context)); marginViewOverlays.addDynamicOverlay(new LinesDecorationsOverlay(this._context)); @@ -252,7 +252,7 @@ export class View extends ViewEventHandler implements editorBrowser.IView, IDisp this.overlayWidgets = new ViewOverlayWidgets(this._context); this.viewParts.push(this.overlayWidgets); - var rulers = new Rulers(this._context, this.layoutProvider); + let rulers = new Rulers(this._context, this.layoutProvider); this.viewParts.push(rulers); // -------------- Wire dom nodes up @@ -261,7 +261,7 @@ export class View extends ViewEventHandler implements editorBrowser.IView, IDisp this.linesContentContainer.style.position = 'absolute'; if (decorationsOverviewRuler) { - var overviewRulerData = this.layoutProvider.getOverviewRulerInsertData(); + let overviewRulerData = this.layoutProvider.getOverviewRulerInsertData(); overviewRulerData.parent.insertBefore(decorationsOverviewRuler.getDomNode(), overviewRulerData.insertBefore); } @@ -377,7 +377,7 @@ export class View extends ViewEventHandler implements editorBrowser.IView, IDisp throw new Error('ViewImpl.pointerHandler.visibleRangeForPosition2: View is disposed'); } this._flushAccumulatedAndRenderNow(); - var visibleRanges = this.viewLines.visibleRangesForRange2(new Range(lineNumber, column, lineNumber, column), 0); + let visibleRanges = this.viewLines.visibleRangesForRange2(new Range(lineNumber, column, lineNumber, column), 0); if (!visibleRanges) { return null; } @@ -403,8 +403,8 @@ export class View extends ViewEventHandler implements editorBrowser.IView, IDisp throw new Error('ViewImpl.keyboardHandler.visibleRangeForPositionRelativeToEditor: View is disposed'); } this._flushAccumulatedAndRenderNow(); - var linesViewPortData = this.layoutProvider.getLinesViewportData(); - var visibleRanges = this.viewLines.visibleRangesForRange2(new Range(lineNumber, column, lineNumber, column), linesViewPortData.visibleRangesDeltaTop); + let linesViewPortData = this.layoutProvider.getLinesViewportData(); + let visibleRanges = this.viewLines.visibleRangesForRange2(new Range(lineNumber, column, lineNumber, column), linesViewPortData.visibleRangesDeltaTop); if (!visibleRanges) { return null; } @@ -437,7 +437,7 @@ export class View extends ViewEventHandler implements editorBrowser.IView, IDisp /* tslint:disable:no-unused-variable */ // Access overflowGuardContainer.clientWidth to prevent relayouting bug in Chrome // See Bug 19676: Editor misses a layout event - var clientWidth = this.overflowGuardContainer.clientWidth + 'px'; + let clientWidth = this.overflowGuardContainer.clientWidth + 'px'; /* tslint:enable:no-unused-variable */ } StyleMutator.setWidth(this.domNode, layoutInfo.width); @@ -516,7 +516,7 @@ export class View extends ViewEventHandler implements editorBrowser.IView, IDisp this.viewLines.dispose(); // Destroy IViewPart second - for (var i = 0, len = this.viewParts.length; i < len; i++) { + for (let i = 0, len = this.viewParts.length; i < len; i++) { this.viewParts[i].dispose(); } this.viewParts = []; @@ -567,11 +567,11 @@ export class View extends ViewEventHandler implements editorBrowser.IView, IDisp if (this._isDisposed) { throw new Error('ViewImpl.codeEditorHelper.getVerticalOffsetForPosition: View is disposed'); } - var modelPosition = this._context.model.validateModelPosition({ + let modelPosition = this._context.model.validateModelPosition({ lineNumber: modelLineNumber, column: modelColumn }); - var viewPosition = this._context.model.convertModelPositionToViewPosition(modelPosition.lineNumber, modelPosition.column); + let viewPosition = this._context.model.convertModelPositionToViewPosition(modelPosition.lineNumber, modelPosition.column); return this.layoutProvider.getVerticalOffsetForLineNumber(viewPosition.lineNumber); }, delegateVerticalScrollbarMouseDown: (browserEvent: MouseEvent) => { @@ -584,13 +584,13 @@ export class View extends ViewEventHandler implements editorBrowser.IView, IDisp if (this._isDisposed) { throw new Error('ViewImpl.codeEditorHelper.getOffsetForColumn: View is disposed'); } - var modelPosition = this._context.model.validateModelPosition({ + let modelPosition = this._context.model.validateModelPosition({ lineNumber: modelLineNumber, column: modelColumn }); - var viewPosition = this._context.model.convertModelPositionToViewPosition(modelPosition.lineNumber, modelPosition.column); + let viewPosition = this._context.model.convertModelPositionToViewPosition(modelPosition.lineNumber, modelPosition.column); this._flushAccumulatedAndRenderNow(); - var visibleRanges = this.viewLines.visibleRangesForRange2(new Range(viewPosition.lineNumber, viewPosition.column, viewPosition.lineNumber, viewPosition.column), 0); + let visibleRanges = this.viewLines.visibleRangesForRange2(new Range(viewPosition.lineNumber, viewPosition.column, viewPosition.lineNumber, viewPosition.column), 0); if (!visibleRanges) { return -1; } @@ -605,9 +605,9 @@ export class View extends ViewEventHandler implements editorBrowser.IView, IDisp if (this._isDisposed) { throw new Error('ViewImpl.getCenteredRangeInViewport: View is disposed'); } - var viewLineNumber = this.layoutProvider.getCenteredViewLineNumberInViewport(); - var viewModel = this._context.model; - var currentCenteredViewRange = new Range(viewLineNumber, 1, viewLineNumber, viewModel.getLineMaxColumn(viewLineNumber)); + let viewLineNumber = this.layoutProvider.getCenteredViewLineNumberInViewport(); + let viewModel = this._context.model; + let currentCenteredViewRange = new Range(viewLineNumber, 1, viewLineNumber, viewModel.getLineMaxColumn(viewLineNumber)); return viewModel.convertViewRangeToModelRange(currentCenteredViewRange); } @@ -676,11 +676,11 @@ export class View extends ViewEventHandler implements editorBrowser.IView, IDisp if (this._isDisposed) { throw new Error('ViewImpl.change: View is disposed'); } - var zonesHaveChanged = false; + let zonesHaveChanged = false; this._renderOnce(() => { // Handle events to avoid "adjusting" newly inserted view zones this._flushAnyAccumulatedEvents(); - var changeAccessor: editorBrowser.IViewZoneChangeAccessor = { + let changeAccessor: editorBrowser.IViewZoneChangeAccessor = { addZone: (zone: editorBrowser.IViewZone): number => { zonesHaveChanged = true; return this.viewZones.addZone(zone); @@ -699,7 +699,7 @@ export class View extends ViewEventHandler implements editorBrowser.IView, IDisp } }; - var r: any = safeInvoke1Arg(callback, changeAccessor); + let r: any = safeInvoke1Arg(callback, changeAccessor); // Invalidate changeAccessor changeAccessor.addZone = null; @@ -836,11 +836,11 @@ export class View extends ViewEventHandler implements editorBrowser.IView, IDisp private createRenderingContext(linesViewportData: ViewLinesViewportData): IRenderingContext { - var vInfo = this.layoutProvider.getCurrentViewport(); + let vInfo = this.layoutProvider.getCurrentViewport(); - var deltaTop = linesViewportData.visibleRangesDeltaTop; + let deltaTop = linesViewportData.visibleRangesDeltaTop; - var r: IRenderingContext = { + let r: IRenderingContext = { linesViewportData: linesViewportData, scrollWidth: this.layoutProvider.getScrollWidth(), scrollHeight: this.layoutProvider.getScrollHeight(), @@ -858,8 +858,8 @@ export class View extends ViewEventHandler implements editorBrowser.IView, IDisp }, getViewportVerticalOffsetForLineNumber: (lineNumber: number) => { - var verticalOffset = this.layoutProvider.getVerticalOffsetForLineNumber(lineNumber); - var scrolledTop = this.layoutProvider.getScrolledTopFromAbsoluteTop(verticalOffset); + let verticalOffset = this.layoutProvider.getVerticalOffsetForLineNumber(lineNumber); + let scrolledTop = this.layoutProvider.getScrolledTopFromAbsoluteTop(verticalOffset); return scrolledTop; }, @@ -870,7 +870,7 @@ export class View extends ViewEventHandler implements editorBrowser.IView, IDisp }, visibleRangeForPosition: (position: editorCommon.IPosition) => { - var visibleRanges = this.viewLines.visibleRangesForRange2(new Range(position.lineNumber, position.column, position.lineNumber, position.column), deltaTop); + let visibleRanges = this.viewLines.visibleRangesForRange2(new Range(position.lineNumber, position.column, position.lineNumber, position.column), deltaTop); if (!visibleRanges) { return null; } diff --git a/src/vs/editor/browser/view/viewLayer.ts b/src/vs/editor/browser/view/viewLayer.ts index 8b0db814362..8d030f5cafb 100644 --- a/src/vs/editor/browser/view/viewLayer.ts +++ b/src/vs/editor/browser/view/viewLayer.ts @@ -394,7 +394,7 @@ class ViewLayerRenderer { private _render(inContext: IRendererContext, startLineNumber: number, stopLineNumber: number, deltaTop: number[]): IRendererContext { - var ctx: IRendererContext = { + let ctx: IRendererContext = { domNode: inContext.domNode, rendLineNumberStart: inContext.rendLineNumberStart, lines: inContext.lines.slice(0), @@ -406,9 +406,9 @@ class ViewLayerRenderer { scrollDomNodeIsAbove: inContext.scrollDomNodeIsAbove }; - var canRemoveScrollDomNode = true; + let canRemoveScrollDomNode = true; if (ctx.scrollDomNode) { - var time = this._getScrollDomNodeTime(ctx.scrollDomNode); + let time = this._getScrollDomNodeTime(ctx.scrollDomNode); if ((new Date()).getTime() - time < 1000) { canRemoveScrollDomNode = false; } @@ -419,7 +419,7 @@ class ViewLayerRenderer { ctx.rendLineNumberStart = startLineNumber; ctx.linesLength = stopLineNumber - startLineNumber + 1; ctx.lines = []; - for (var x = startLineNumber; x <= stopLineNumber; x++) { + for (let x = startLineNumber; x <= stopLineNumber; x++) { ctx.lines[x - startLineNumber] = this._createLine(); } this._finishRendering(ctx, true, deltaTop); @@ -436,14 +436,10 @@ class ViewLayerRenderer { startLineNumber ); - var fromLineNumber: number, - toLineNumber: number, - removeCnt: number; - if (ctx.rendLineNumberStart > startLineNumber) { // Insert lines before - fromLineNumber = startLineNumber; - toLineNumber = Math.min(stopLineNumber, ctx.rendLineNumberStart - 1); + let fromLineNumber = startLineNumber; + let toLineNumber = Math.min(stopLineNumber, ctx.rendLineNumberStart - 1); if (fromLineNumber <= toLineNumber) { this._insertLinesBefore(ctx, fromLineNumber, toLineNumber, deltaTop, startLineNumber); ctx.linesLength += toLineNumber - fromLineNumber + 1; @@ -458,7 +454,7 @@ class ViewLayerRenderer { } } else if (ctx.rendLineNumberStart < startLineNumber) { // Remove lines before - removeCnt = Math.min(ctx.linesLength, startLineNumber - ctx.rendLineNumberStart); + let removeCnt = Math.min(ctx.linesLength, startLineNumber - ctx.rendLineNumberStart); if (removeCnt > 0) { this._removeLinesBefore(ctx, removeCnt); ctx.linesLength -= removeCnt; @@ -469,8 +465,8 @@ class ViewLayerRenderer { if (ctx.rendLineNumberStart + ctx.linesLength - 1 < stopLineNumber) { // Insert lines after - fromLineNumber = ctx.rendLineNumberStart + ctx.linesLength; - toLineNumber = stopLineNumber; + let fromLineNumber = ctx.rendLineNumberStart + ctx.linesLength; + let toLineNumber = stopLineNumber; if (fromLineNumber <= toLineNumber) { this._insertLinesAfter(ctx, fromLineNumber, toLineNumber, deltaTop, startLineNumber); @@ -487,9 +483,9 @@ class ViewLayerRenderer { } else if (ctx.rendLineNumberStart + ctx.linesLength - 1 > stopLineNumber) { // Remove lines after - fromLineNumber = Math.max(0, stopLineNumber - ctx.rendLineNumberStart + 1); - toLineNumber = ctx.linesLength - 1; - removeCnt = toLineNumber - fromLineNumber + 1; + let fromLineNumber = Math.max(0, stopLineNumber - ctx.rendLineNumberStart + 1); + let toLineNumber = ctx.linesLength - 1; + let removeCnt = toLineNumber - fromLineNumber + 1; if (removeCnt > 0) { this._removeLinesAfter(ctx, removeCnt); @@ -503,12 +499,9 @@ class ViewLayerRenderer { } private _renderUntouchedLines(ctx: IRendererContext, startIndex: number, endIndex: number, deltaTop: number[], deltaLN: number): void { - var i: number, - lineNumber: number; - - for (i = startIndex; i <= endIndex; i++) { - lineNumber = ctx.rendLineNumberStart + i; - var lineDomNode = ctx.lines[i].getDomNode(); + for (let i = startIndex; i <= endIndex; i++) { + let lineNumber = ctx.rendLineNumberStart + i; + let lineDomNode = ctx.lines[i].getDomNode(); if (lineDomNode) { ctx.lines[i].layoutLine(lineNumber, deltaTop[lineNumber - deltaLN]); } @@ -516,19 +509,16 @@ class ViewLayerRenderer { } private _insertLinesBefore(ctx: IRendererContext, fromLineNumber: number, toLineNumber: number, deltaTop: number[], deltaLN: number): void { - var newLines: T[] = [], - line: T, - lineNumber: number; - - for (lineNumber = fromLineNumber; lineNumber <= toLineNumber; lineNumber++) { - line = this._createLine(); - newLines.push(line); + let newLines: T[] = []; + let newLinesLen = 0; + for (let lineNumber = fromLineNumber; lineNumber <= toLineNumber; lineNumber++) { + newLines[newLinesLen++] = this._createLine(); } ctx.lines = newLines.concat(ctx.lines); } private _getScrollDomNodeTime(domNode: HTMLElement): number { - var lastScrollTime = domNode.getAttribute('last-scroll-time'); + let lastScrollTime = domNode.getAttribute('last-scroll-time'); if (lastScrollTime) { return parseInt(lastScrollTime, 10); } @@ -536,14 +526,14 @@ class ViewLayerRenderer { } private _removeIfNotScrollDomNode(ctx: IRendererContext, domNode: HTMLElement, isAbove: boolean) { - var time = this._getScrollDomNodeTime(domNode); + let time = this._getScrollDomNodeTime(domNode); if (!time) { ctx.domNode.removeChild(domNode); return; } if (ctx.scrollDomNode) { - var otherTime = this._getScrollDomNodeTime(ctx.scrollDomNode); + let otherTime = this._getScrollDomNodeTime(ctx.scrollDomNode); if (otherTime > time) { // The other is the real scroll dom node ctx.domNode.removeChild(domNode); @@ -562,10 +552,8 @@ class ViewLayerRenderer { } private _removeLinesBefore(ctx: IRendererContext, removeCount: number): void { - var i: number; - - for (i = 0; i < removeCount; i++) { - var lineDomNode = ctx.lines[i].getDomNode(); + for (let i = 0; i < removeCount; i++) { + let lineDomNode = ctx.lines[i].getDomNode(); if (lineDomNode) { this._removeIfNotScrollDomNode(ctx, lineDomNode, true); } @@ -574,23 +562,19 @@ class ViewLayerRenderer { } private _insertLinesAfter(ctx: IRendererContext, fromLineNumber: number, toLineNumber: number, deltaTop: number[], deltaLN: number): void { - var newLines: T[] = [], - line: T, - lineNumber: number; - - for (lineNumber = fromLineNumber; lineNumber <= toLineNumber; lineNumber++) { - line = this._createLine(); - newLines.push(line); + let newLines: T[] = []; + let newLinesLen = 0; + for (let lineNumber = fromLineNumber; lineNumber <= toLineNumber; lineNumber++) { + newLines[newLinesLen++] = this._createLine(); } ctx.lines = ctx.lines.concat(newLines); } private _removeLinesAfter(ctx: IRendererContext, removeCount: number): void { - var i: number, - removeIndex = ctx.linesLength - removeCount; + let removeIndex = ctx.linesLength - removeCount; - for (i = 0; i < removeCount; i++) { - var lineDomNode = ctx.lines[removeIndex + i].getDomNode(); + for (let i = 0; i < removeCount; i++) { + let lineDomNode = ctx.lines[removeIndex + i].getDomNode(); if (lineDomNode) { this._removeIfNotScrollDomNode(ctx, lineDomNode, false); } @@ -608,14 +592,14 @@ class ViewLayerRenderer { } private _finishRenderingNewLines(ctx: IRendererContext, domNodeIsEmpty: boolean, newLinesHTML: string[], wasNew: boolean[]): void { - var lastChild = ctx.domNode.lastChild; + let lastChild = ctx.domNode.lastChild; if (domNodeIsEmpty || !lastChild) { ctx.domNode.innerHTML = this._extraDomNodeHTML() + newLinesHTML.join(''); } else { lastChild.insertAdjacentHTML('afterend', newLinesHTML.join('')); } - var currChild = ctx.domNode.lastChild; + let currChild = ctx.domNode.lastChild; for (let i = ctx.linesLength - 1; i >= 0; i--) { let line = ctx.lines[i]; if (wasNew[i]) { @@ -626,17 +610,15 @@ class ViewLayerRenderer { } private _finishRenderingInvalidLines(ctx: IRendererContext, invalidLinesHTML: string[], wasInvalid: boolean[]): void { - var hugeDomNode = document.createElement('div'); + let hugeDomNode = document.createElement('div'); hugeDomNode.innerHTML = invalidLinesHTML.join(''); - var lineDomNode: HTMLElement, - source: HTMLElement; for (let i = 0; i < ctx.linesLength; i++) { let line = ctx.lines[i]; if (wasInvalid[i]) { - source = hugeDomNode.firstChild; - lineDomNode = line.getDomNode(); + let source = hugeDomNode.firstChild; + let lineDomNode = line.getDomNode(); lineDomNode.parentNode.replaceChild(source, lineDomNode); line.setDomNode(source); } @@ -647,23 +629,19 @@ class ViewLayerRenderer { let inlineDecorations = ViewLayerRenderer._resolveInlineDecorations(ctx); - var i: number, - len: number, - line: IVisibleLineData, - lineNumber: number, - hadNewLine = false, - wasNew: boolean[] = [], - newLinesHTML: string[] = [], - hadInvalidLine = false, - wasInvalid: boolean[] = [], - invalidLinesHTML: string[] = []; + let hadNewLine = false; + let wasNew: boolean[] = []; + let newLinesHTML: string[] = []; + let hadInvalidLine = false; + let wasInvalid: boolean[] = []; + let invalidLinesHTML: string[] = []; - for (i = 0, len = ctx.linesLength; i < len; i++) { - line = ctx.lines[i]; - lineNumber = i + ctx.rendLineNumberStart; + for (let i = 0, len = ctx.linesLength; i < len; i++) { + let line = ctx.lines[i]; + let lineNumber = i + ctx.rendLineNumberStart; if (line.shouldUpdateHTML(ctx.rendLineNumberStart, lineNumber, inlineDecorations[i])) { - var lineDomNode = line.getDomNode(); + let lineDomNode = line.getDomNode(); if (!lineDomNode) { // Line is new line.getLineOuterHTML(newLinesHTML, lineNumber, deltaTop[i]); diff --git a/src/vs/editor/browser/view/viewOverlays.ts b/src/vs/editor/browser/view/viewOverlays.ts index 80bf596789b..2d7bb97988d 100644 --- a/src/vs/editor/browser/view/viewOverlays.ts +++ b/src/vs/editor/browser/view/viewOverlays.ts @@ -75,8 +75,7 @@ export class ViewOverlays extends ViewLayer { // ----- end event handlers _createLine(): ViewOverlayLine { - var r = new ViewOverlayLine(this._context, this._dynamicOverlays); - return r; + return new ViewOverlayLine(this._context, this._dynamicOverlays); } @@ -285,18 +284,18 @@ export class MarginViewOverlays extends ViewOverlays { _viewOverlaysRender(ctx: IRestrictedRenderingContext): void { super._viewOverlaysRender(ctx); if (this._canUseTranslate3d) { - var transform = 'translate3d(0px, ' + ctx.linesViewportData.visibleRangesDeltaTop + 'px, 0px)'; + let transform = 'translate3d(0px, ' + ctx.linesViewportData.visibleRangesDeltaTop + 'px, 0px)'; this.domNode.setTransform(transform); this.domNode.setTop(0); } else { this.domNode.setTransform(''); this.domNode.setTop(ctx.linesViewportData.visibleRangesDeltaTop); } - var height = Math.min(this._layoutProvider.getTotalHeight(), 1000000); + let height = Math.min(this._layoutProvider.getTotalHeight(), 1000000); this.domNode.setHeight(height); this.domNode.setWidth(this._contentLeft); - var glyphMargin = this._getGlyphMarginDomNode(); + let glyphMargin = this._getGlyphMarginDomNode(); if (glyphMargin) { StyleMutator.setHeight(glyphMargin, this._scrollHeight); StyleMutator.setLeft(glyphMargin, this._glyphMarginLeft); diff --git a/src/vs/editor/browser/viewLayout/layoutProvider.ts b/src/vs/editor/browser/viewLayout/layoutProvider.ts index b5b8d25b234..5f180552e8a 100644 --- a/src/vs/editor/browser/viewLayout/layoutProvider.ts +++ b/src/vs/editor/browser/viewLayout/layoutProvider.ts @@ -202,7 +202,7 @@ export class LayoutProvider extends ViewEventHandler implements IDisposable, ILa } private _computeScrollWidth(maxLineWidth: number, viewportWidth: number): number { - var isViewportWrapping = this.configuration.editor.wrappingInfo.isViewportWrapping; + let isViewportWrapping = this.configuration.editor.wrappingInfo.isViewportWrapping; if (!isViewportWrapping) { return Math.max(maxLineWidth + LayoutProvider.LINES_HORIZONTAL_EXTRA_PX, viewportWidth); } @@ -210,7 +210,7 @@ export class LayoutProvider extends ViewEventHandler implements IDisposable, ILa } public onMaxLineWidthChanged(maxLineWidth: number): void { - var newScrollWidth = this._computeScrollWidth(maxLineWidth, this.getCurrentViewport().width); + let newScrollWidth = this._computeScrollWidth(maxLineWidth, this.getCurrentViewport().width); this.scrollManager.setScrollWidth(newScrollWidth); // The height might depend on the fact that there is a horizontal scrollbar or not @@ -220,9 +220,9 @@ export class LayoutProvider extends ViewEventHandler implements IDisposable, ILa // ---- view state public saveState(): editorCommon.IViewState { - var scrollTop = this.scrollManager.getScrollTop(); - var firstLineNumberInViewport = this.linesLayout.getLineNumberAtOrAfterVerticalOffset(scrollTop); - var whitespaceAboveFirstLine = this.linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(firstLineNumberInViewport); + let scrollTop = this.scrollManager.getScrollTop(); + let firstLineNumberInViewport = this.linesLayout.getLineNumberAtOrAfterVerticalOffset(scrollTop); + let whitespaceAboveFirstLine = this.linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(firstLineNumberInViewport); return { scrollTop: scrollTop, scrollTopWithoutViewZones: scrollTop - whitespaceAboveFirstLine, @@ -231,7 +231,7 @@ export class LayoutProvider extends ViewEventHandler implements IDisposable, ILa } public restoreState(state: editorCommon.IViewState): void { - var restoreScrollTop = state.scrollTop; + let restoreScrollTop = state.scrollTop; if (typeof state.scrollTopWithoutViewZones === 'number' && !this.linesLayout.hasWhitespace()) { restoreScrollTop = state.scrollTopWithoutViewZones; } @@ -265,7 +265,7 @@ export class LayoutProvider extends ViewEventHandler implements IDisposable, ILa return this.linesLayout.getLineNumberAtOrAfterVerticalOffset(verticalOffset); } public getTotalHeight(): number { - var reserveHorizontalScrollbarHeight = 0; + let reserveHorizontalScrollbarHeight = 0; if (this.scrollManager.getScrollWidth() > this.scrollManager.getWidth()) { reserveHorizontalScrollbarHeight = this.configuration.editor.viewInfo.scrollbar.horizontalScrollbarSize; } @@ -287,7 +287,7 @@ export class LayoutProvider extends ViewEventHandler implements IDisposable, ILa // ---- IScrollingProvider public getOverviewRulerInsertData(): { parent: HTMLElement; insertBefore: HTMLElement; } { - var layoutInfo = this.scrollManager.getOverviewRulerLayoutInfo(); + let layoutInfo = this.scrollManager.getOverviewRulerLayoutInfo(); return { parent: layoutInfo.parent, insertBefore: layoutInfo.insertBefore diff --git a/src/vs/editor/browser/viewLayout/scrollManager.ts b/src/vs/editor/browser/viewLayout/scrollManager.ts index c5c8a75bce8..741b8593846 100644 --- a/src/vs/editor/browser/viewLayout/scrollManager.ts +++ b/src/vs/editor/browser/viewLayout/scrollManager.ts @@ -33,9 +33,9 @@ export class ScrollManager implements IDisposable { this.privateViewEventBus = privateViewEventBus; this.linesContent = linesContent; - var configScrollbarOpts = this.configuration.editor.viewInfo.scrollbar; + let configScrollbarOpts = this.configuration.editor.viewInfo.scrollbar; - var scrollbarOptions: ScrollableElementCreationOptions = { + let scrollbarOptions: ScrollableElementCreationOptions = { canUseTranslate3d: this.configuration.editor.viewInfo.canUseTranslate3d, listenOnDomNode: viewDomNode, vertical: configScrollbarOpts.vertical, @@ -78,11 +78,11 @@ export class ScrollManager implements IDisposable { // the browser will try desperately to reveal that dom node, unexpectedly // changing the .scrollTop of this.linesContent - var onBrowserDesperateReveal = (domNode: HTMLElement, lookAtScrollTop: boolean, lookAtScrollLeft: boolean) => { + let onBrowserDesperateReveal = (domNode: HTMLElement, lookAtScrollTop: boolean, lookAtScrollLeft: boolean) => { let newScrollPosition: INewScrollPosition = {}; if (lookAtScrollTop) { - var deltaTop = domNode.scrollTop; + let deltaTop = domNode.scrollTop; if (deltaTop) { newScrollPosition.scrollTop = this.getScrollTop() + deltaTop; domNode.scrollTop = 0; @@ -90,7 +90,7 @@ export class ScrollManager implements IDisposable { } if (lookAtScrollLeft) { - var deltaLeft = domNode.scrollLeft; + let deltaLeft = domNode.scrollLeft; if (deltaLeft) { newScrollPosition.scrollLeft = this.getScrollLeft() + deltaLeft; domNode.scrollLeft = 0; diff --git a/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.ts b/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.ts index 2f1926abed2..a55fd3dfb23 100644 --- a/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.ts +++ b/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.ts @@ -63,7 +63,7 @@ export class CurrentLineHighlightOverlay extends DynamicViewOverlay { return true; } public onCursorPositionChanged(e: editorCommon.IViewCursorPositionChangedEvent): boolean { - var hasChanged = false; + let hasChanged = false; if (this._primaryCursorIsInEditableRange !== e.isInEditableRange) { this._primaryCursorIsInEditableRange = e.isInEditableRange; hasChanged = true; @@ -75,7 +75,7 @@ export class CurrentLineHighlightOverlay extends DynamicViewOverlay { return hasChanged; } public onCursorSelectionChanged(e: editorCommon.IViewCursorSelectionChangedEvent): boolean { - var isEmpty = e.selection.isEmpty(); + let isEmpty = e.selection.isEmpty(); if (this._selectionIsEmpty !== isEmpty) { this._selectionIsEmpty = isEmpty; return true; diff --git a/src/vs/editor/browser/viewParts/lines/viewLines.ts b/src/vs/editor/browser/viewParts/lines/viewLines.ts index 300d91a5007..1041246cc39 100644 --- a/src/vs/editor/browser/viewParts/lines/viewLines.ts +++ b/src/vs/editor/browser/viewParts/lines/viewLines.ts @@ -108,7 +108,7 @@ export class ViewLines extends ViewLayer { // ---- begin view event handlers public onConfigurationChanged(e: editorCommon.IConfigurationChangedEvent): boolean { - var shouldRender = super.onConfigurationChanged(e); + let shouldRender = super.onConfigurationChanged(e); if (e.wrappingInfo) { this._maxLineWidth = 0; } @@ -133,13 +133,13 @@ export class ViewLines extends ViewLayer { } public onLayoutChanged(layoutInfo: editorCommon.EditorLayoutInfo): boolean { - var shouldRender = super.onLayoutChanged(layoutInfo); + let shouldRender = super.onLayoutChanged(layoutInfo); this._maxLineWidth = 0; return shouldRender; } public onModelFlushed(): boolean { - var shouldRender = super.onModelFlushed(); + let shouldRender = super.onModelFlushed(); this._maxLineWidth = 0; return shouldRender; } @@ -157,7 +157,7 @@ export class ViewLines extends ViewLayer { } public onCursorRevealRange(e: editorCommon.IViewRevealRangeEvent): boolean { - var newScrollTop = this._computeScrollTopToRevealRange(this._layoutProvider.getCurrentViewport(), e.range, e.verticalType); + let newScrollTop = this._computeScrollTopToRevealRange(this._layoutProvider.getCurrentViewport(), e.range, e.verticalType); if (e.revealHorizontal) { this._lastCursorRevealRangeHorizontallyEvent = e; @@ -403,9 +403,9 @@ export class ViewLines extends ViewLayer { this.onDidRender(); // compute new scroll position - var newScrollLeft = this._computeScrollLeftToRevealRange(revealHorizontalRange); + let newScrollLeft = this._computeScrollLeftToRevealRange(revealHorizontalRange); - var isViewportWrapping = this._isViewportWrapping; + let isViewportWrapping = this._isViewportWrapping; if (!isViewportWrapping) { // ensure `scrollWidth` is large enough this._ensureMaxLineWidth(newScrollLeft.maxHorizontalOffset); @@ -444,11 +444,11 @@ export class ViewLines extends ViewLayer { } private _computeScrollTopToRevealRange(viewport: editorCommon.Viewport, range: Range, verticalType: editorCommon.VerticalRevealType): number { - var viewportStartY = viewport.top, - viewportHeight = viewport.height, - viewportEndY = viewportStartY + viewportHeight, - boxStartY: number, - boxEndY: number; + let viewportStartY = viewport.top; + let viewportHeight = viewport.height; + let viewportEndY = viewportStartY + viewportHeight; + let boxStartY: number; + let boxEndY: number; // Have a box that includes one extra line height (for the horizontal scrollbar) boxStartY = this._layoutProvider.getVerticalOffsetForLineNumber(range.startLineNumber); @@ -458,7 +458,7 @@ export class ViewLines extends ViewLayer { boxEndY += this._lineHeight; } - var newScrollTop: number; + let newScrollTop: number; if (verticalType === editorCommon.VerticalRevealType.Center || verticalType === editorCommon.VerticalRevealType.CenterIfOutsideViewport) { if (verticalType === editorCommon.VerticalRevealType.CenterIfOutsideViewport && viewportStartY <= boxStartY && boxEndY <= viewportEndY) { @@ -466,7 +466,7 @@ export class ViewLines extends ViewLayer { newScrollTop = viewportStartY; } else { // Box is outside the viewport... center it - var boxMiddleY = (boxStartY + boxEndY) / 2; + let boxMiddleY = (boxStartY + boxEndY) / 2; newScrollTop = Math.max(0, boxMiddleY - viewportHeight / 2); } } else { @@ -478,7 +478,7 @@ export class ViewLines extends ViewLayer { private _computeScrollLeftToRevealRange(range: Range): { scrollLeft: number; maxHorizontalOffset: number; } { - var maxHorizontalOffset = 0; + let maxHorizontalOffset = 0; if (range.startLineNumber !== range.endLineNumber) { // Two or more lines? => scroll to base (That's how you see most of the two lines) @@ -488,13 +488,13 @@ export class ViewLines extends ViewLayer { }; } - var viewport = this._layoutProvider.getCurrentViewport(), - viewportStartX = viewport.left, - viewportEndX = viewportStartX + viewport.width; + let viewport = this._layoutProvider.getCurrentViewport(); + let viewportStartX = viewport.left; + let viewportEndX = viewportStartX + viewport.width; - var visibleRanges = this.visibleRangesForRange2(range, 0), - boxStartX = Number.MAX_VALUE, - boxEndX = 0; + let visibleRanges = this.visibleRangesForRange2(range, 0); + let boxStartX = Number.MAX_VALUE; + let boxEndX = 0; if (!visibleRanges) { // Unknown @@ -504,11 +504,8 @@ export class ViewLines extends ViewLayer { }; } - var i: number, - visibleRange: VisibleRange; - - for (i = 0; i < visibleRanges.length; i++) { - visibleRange = visibleRanges[i]; + for (let i = 0; i < visibleRanges.length; i++) { + let visibleRange = visibleRanges[i]; if (visibleRange.left < boxStartX) { boxStartX = visibleRange.left; } @@ -522,7 +519,7 @@ export class ViewLines extends ViewLayer { boxStartX = Math.max(0, boxStartX - ViewLines.HORIZONTAL_EXTRA_PX); boxEndX += this._revealHorizontalRightPadding; - var newScrollLeft = this._computeMinimumScrolling(viewportStartX, viewportEndX, boxStartX, boxEndX); + let newScrollLeft = this._computeMinimumScrolling(viewportStartX, viewportEndX, boxStartX, boxEndX); return { scrollLeft: newScrollLeft, maxHorizontalOffset: maxHorizontalOffset diff --git a/src/vs/editor/browser/viewParts/overlayWidgets/overlayWidgets.ts b/src/vs/editor/browser/viewParts/overlayWidgets/overlayWidgets.ts index 16b67497777..d07217245b5 100644 --- a/src/vs/editor/browser/viewParts/overlayWidgets/overlayWidgets.ts +++ b/src/vs/editor/browser/viewParts/overlayWidgets/overlayWidgets.ts @@ -69,7 +69,7 @@ export class ViewOverlayWidgets extends ViewPart { }; // This is sync because a widget wants to be in the dom - var domNode = widget.getDomNode(); + let domNode = widget.getDomNode(); domNode.style.position = 'absolute'; domNode.setAttribute('widgetId', widget.getId()); this.domNode.appendChild(domNode); @@ -78,7 +78,7 @@ export class ViewOverlayWidgets extends ViewPart { } public setWidgetPosition(widget: IOverlayWidget, preference: OverlayWidgetPositionPreference): boolean { - var widgetData = this._widgets[widget.getId()]; + let widgetData = this._widgets[widget.getId()]; if (widgetData.preference === preference) { return false; } @@ -90,10 +90,10 @@ export class ViewOverlayWidgets extends ViewPart { } public removeWidget(widget: IOverlayWidget): void { - var widgetId = widget.getId(); + let widgetId = widget.getId(); if (this._widgets.hasOwnProperty(widgetId)) { - var widgetData = this._widgets[widgetId]; - var domNode = widgetData.widget.getDomNode(); + let widgetData = this._widgets[widgetId]; + let domNode = widgetData.widget.getDomNode(); delete this._widgets[widgetId]; domNode.parentNode.removeChild(domNode); @@ -102,12 +102,12 @@ export class ViewOverlayWidgets extends ViewPart { } private _renderWidget(widgetData: IWidgetData): void { - var _RESTORE_STYLE_TOP = 'data-editor-restoreStyleTop', - domNode = widgetData.widget.getDomNode(); + let _RESTORE_STYLE_TOP = 'data-editor-restoreStyleTop'; + let domNode = widgetData.widget.getDomNode(); if (widgetData.preference === null) { if (domNode.hasAttribute(_RESTORE_STYLE_TOP)) { - var previousTop = domNode.getAttribute(_RESTORE_STYLE_TOP); + let previousTop = domNode.getAttribute(_RESTORE_STYLE_TOP); domNode.removeAttribute(_RESTORE_STYLE_TOP); domNode.style.top = previousTop; } @@ -124,7 +124,7 @@ export class ViewOverlayWidgets extends ViewPart { if (!domNode.hasAttribute(_RESTORE_STYLE_TOP)) { domNode.setAttribute(_RESTORE_STYLE_TOP, domNode.style.top); } - var widgetHeight = domNode.clientHeight; + let widgetHeight = domNode.clientHeight; StyleMutator.setTop(domNode, (this._editorHeight - widgetHeight - 2 * this._horizontalScrollbarHeight)); StyleMutator.setRight(domNode, (2 * this._verticalScrollbarWidth)); } else if (widgetData.preference === OverlayWidgetPositionPreference.TOP_CENTER) { diff --git a/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts b/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts index 3bda51414cb..54d17dfc86d 100644 --- a/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts +++ b/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts @@ -72,10 +72,10 @@ export class DecorationsOverviewRuler extends ViewPart { } public onConfigurationChanged(e: editorCommon.IConfigurationChangedEvent): boolean { - var prevLanesCount = this._overviewRuler.getLanesCount(); - var newLanesCount = this._context.configuration.editor.viewInfo.overviewRulerLanes; + let prevLanesCount = this._overviewRuler.getLanesCount(); + let newLanesCount = this._context.configuration.editor.viewInfo.overviewRulerLanes; - var shouldRender = false; + let shouldRender = false; if (e.lineHeight) { this._overviewRuler.setLineHeight(this._context.configuration.editor.lineHeight, false); @@ -202,17 +202,17 @@ export class DecorationsOverviewRuler extends ViewPart { } } - var allZones: editorCommon.OverviewRulerZone[] = []; + let allZones: editorCommon.OverviewRulerZone[] = []; allZones = allZones.concat(this._zonesFromCursors); allZones = allZones.concat(this._zonesFromDecorations); this._overviewRuler.setZones(allZones, false); } - var hasRendered = this._overviewRuler.render(false); + let hasRendered = this._overviewRuler.render(false); if (hasRendered && OverviewRulerImpl.hasCanvas && this._overviewRuler.getLanesCount() > 0 && (this._zonesFromDecorations.length > 0 || this._zonesFromCursors.length > 0)) { - var ctx2 = this._overviewRuler.getDomNode().getContext('2d'); + let ctx2 = this._overviewRuler.getDomNode().getContext('2d'); ctx2.beginPath(); ctx2.lineWidth = 1; ctx2.strokeStyle = 'rgba(197,197,197,0.8)'; diff --git a/src/vs/editor/browser/viewParts/scrollDecoration/scrollDecoration.ts b/src/vs/editor/browser/viewParts/scrollDecoration/scrollDecoration.ts index f2b927c4682..d72aa1aac1a 100644 --- a/src/vs/editor/browser/viewParts/scrollDecoration/scrollDecoration.ts +++ b/src/vs/editor/browser/viewParts/scrollDecoration/scrollDecoration.ts @@ -33,7 +33,7 @@ export class ScrollDecorationViewPart extends ViewPart { } private _updateShouldShow(): boolean { - var newShouldShow = (this._useShadows && this._scrollTop > 0); + let newShouldShow = (this._useShadows && this._scrollTop > 0); if (this._shouldShow !== newShouldShow) { this._shouldShow = newShouldShow; return true; diff --git a/src/vs/editor/browser/viewParts/selections/selections.ts b/src/vs/editor/browser/viewParts/selections/selections.ts index cb5df9d4687..e32ff141296 100644 --- a/src/vs/editor/browser/viewParts/selections/selections.ts +++ b/src/vs/editor/browser/viewParts/selections/selections.ts @@ -151,12 +151,8 @@ export class SelectionsOverlay extends DynamicViewOverlay { private _visibleRangesHaveGaps(linesVisibleRanges: LineVisibleRangesWithStyle[]): boolean { - var i: number, - len: number, - lineVisibleRanges: LineVisibleRangesWithStyle; - - for (i = 0, len = linesVisibleRanges.length; i < len; i++) { - lineVisibleRanges = linesVisibleRanges[i]; + for (let i = 0, len = linesVisibleRanges.length; i < len; i++) { + let lineVisibleRanges = linesVisibleRanges[i]; if (lineVisibleRanges.ranges.length > 1) { // There are two ranges on the same line @@ -168,32 +164,20 @@ export class SelectionsOverlay extends DynamicViewOverlay { } private _enrichVisibleRangesWithStyle(linesVisibleRanges: LineVisibleRangesWithStyle[], previousFrame: LineVisibleRangesWithStyle[]): void { - var curLineRange: HorizontalRangeWithStyle, - curLeft: number, - curRight: number, - prevLeft: number, - prevRight: number, - nextLeft: number, - nextRight: number, - startStyle: IVisibleRangeEndPointStyle, - endStyle: IVisibleRangeEndPointStyle, - i: number, - len: number; - - var previousFrameTop: HorizontalRangeWithStyle = null, - previousFrameBottom: HorizontalRangeWithStyle = null; + let previousFrameTop: HorizontalRangeWithStyle = null; + let previousFrameBottom: HorizontalRangeWithStyle = null; if (previousFrame && previousFrame.length > 0 && linesVisibleRanges.length > 0) { - var topLineNumber = linesVisibleRanges[0].lineNumber; - for (var i = 0; !previousFrameTop && i < previousFrame.length; i++) { + let topLineNumber = linesVisibleRanges[0].lineNumber; + for (let i = 0; !previousFrameTop && i < previousFrame.length; i++) { if (previousFrame[i].lineNumber === topLineNumber) { previousFrameTop = previousFrame[i].ranges[0]; } } - var bottomLineNumber = linesVisibleRanges[linesVisibleRanges.length - 1].lineNumber; - for (var i = previousFrame.length - 1; !previousFrameBottom && i >= 0; i--) { + let bottomLineNumber = linesVisibleRanges[linesVisibleRanges.length - 1].lineNumber; + for (let i = previousFrame.length - 1; !previousFrameBottom && i >= 0; i--) { if (previousFrame[i].lineNumber === bottomLineNumber) { previousFrameBottom = previousFrame[i].ranges[0]; } @@ -207,26 +191,26 @@ export class SelectionsOverlay extends DynamicViewOverlay { } } - for (i = 0, len = linesVisibleRanges.length; i < len; i++) { + for (let i = 0, len = linesVisibleRanges.length; i < len; i++) { // We know for a fact that there is precisely one range on each line - curLineRange = linesVisibleRanges[i].ranges[0]; - curLeft = curLineRange.left; - curRight = curLineRange.left + curLineRange.width; + let curLineRange = linesVisibleRanges[i].ranges[0]; + let curLeft = curLineRange.left; + let curRight = curLineRange.left + curLineRange.width; - startStyle = { + let startStyle = { top: CornerStyle.EXTERN, bottom: CornerStyle.EXTERN }; - endStyle = { + let endStyle = { top: CornerStyle.EXTERN, bottom: CornerStyle.EXTERN }; if (i > 0) { // Look above - prevLeft = linesVisibleRanges[i - 1].ranges[0].left; - prevRight = linesVisibleRanges[i - 1].ranges[0].left + linesVisibleRanges[i - 1].ranges[0].width; + let prevLeft = linesVisibleRanges[i - 1].ranges[0].left; + let prevRight = linesVisibleRanges[i - 1].ranges[0].left + linesVisibleRanges[i - 1].ranges[0].width; if (curLeft === prevLeft) { startStyle.top = CornerStyle.FLAT; @@ -247,8 +231,8 @@ export class SelectionsOverlay extends DynamicViewOverlay { if (i + 1 < len) { // Look below - nextLeft = linesVisibleRanges[i + 1].ranges[0].left; - nextRight = linesVisibleRanges[i + 1].ranges[0].left + linesVisibleRanges[i + 1].ranges[0].width; + let nextLeft = linesVisibleRanges[i + 1].ranges[0].left; + let nextRight = linesVisibleRanges[i + 1].ranges[0].left + linesVisibleRanges[i + 1].ranges[0].width; if (curLeft === nextLeft) { startStyle.bottom = CornerStyle.FLAT; diff --git a/src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts b/src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts index d6252a24070..737408da875 100644 --- a/src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts +++ b/src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts @@ -123,7 +123,7 @@ export class ViewCursor { } public prepareRender(ctx: IRenderingContext): void { - var visibleRange = ctx.visibleRangeForPosition(this._position); + let visibleRange = ctx.visibleRangeForPosition(this._position); if (visibleRange) { this._positionTop = visibleRange.top; this._positionLeft = visibleRange.left; diff --git a/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts index 66402490518..83838945173 100644 --- a/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts +++ b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts @@ -78,8 +78,8 @@ export class ViewCursors extends ViewPart { public onModelFlushed(): boolean { this._primaryCursor.onModelFlushed(); - for (var i = 0, len = this._secondaryCursors.length; i < len; i++) { - var domNode = this._secondaryCursors[i].getDomNode(); + for (let i = 0, len = this._secondaryCursors.length; i < len; i++) { + let domNode = this._secondaryCursors[i].getDomNode(); domNode.parentNode.removeChild(domNode); } this._secondaryCursors = []; @@ -99,7 +99,7 @@ export class ViewCursors extends ViewPart { return true; } public onModelTokensChanged(e: editorCommon.IViewTokensChangedEvent): boolean { - var shouldRender = (position: editorCommon.IPosition) => { + let shouldRender = (position: editorCommon.IPosition) => { for (let i = 0, len = e.ranges.length; i < len; i++) { if (e.ranges[i].fromLineNumber <= position.lineNumber && position.lineNumber <= e.ranges[i].toLineNumber) { return true; @@ -110,7 +110,7 @@ export class ViewCursors extends ViewPart { if (shouldRender(this._primaryCursor.getPosition())) { return true; } - for (var i = 0; i < this._secondaryCursors.length; i++) { + for (let i = 0; i < this._secondaryCursors.length; i++) { if (shouldRender(this._secondaryCursors[i].getPosition())) { return true; } @@ -123,22 +123,22 @@ export class ViewCursors extends ViewPart { if (this._secondaryCursors.length < e.secondaryPositions.length) { // Create new cursors - var addCnt = e.secondaryPositions.length - this._secondaryCursors.length; - for (var i = 0; i < addCnt; i++) { - var newCursor = new ViewCursor(this._context, true); + let addCnt = e.secondaryPositions.length - this._secondaryCursors.length; + for (let i = 0; i < addCnt; i++) { + let newCursor = new ViewCursor(this._context, true); this._primaryCursor.getDomNode().parentNode.insertBefore(newCursor.getDomNode(), this._primaryCursor.getDomNode().nextSibling); this._secondaryCursors.push(newCursor); } } else if (this._secondaryCursors.length > e.secondaryPositions.length) { // Remove some cursors - var removeCnt = this._secondaryCursors.length - e.secondaryPositions.length; - for (var i = 0; i < removeCnt; i++) { + let removeCnt = this._secondaryCursors.length - e.secondaryPositions.length; + for (let i = 0; i < removeCnt; i++) { this._secondaryCursors[0].getDomNode().parentNode.removeChild(this._secondaryCursors[0].getDomNode()); this._secondaryCursors.splice(0, 1); } } - for (var i = 0; i < e.secondaryPositions.length; i++) { + for (let i = 0; i < e.secondaryPositions.length; i++) { this._secondaryCursors[i].onCursorPositionChanged(e.secondaryPositions[i], e.isInEditableRange); } @@ -164,7 +164,7 @@ export class ViewCursors extends ViewPart { if (e.viewInfo.cursorStyle || e.viewInfo.cursorBlinking) { this._updateDomClassName(); } - for (var i = 0, len = this._secondaryCursors.length; i < len; i++) { + for (let i = 0, len = this._secondaryCursors.length; i < len; i++) { this._secondaryCursors[i].onConfigurationChanged(e); } return true; @@ -288,7 +288,7 @@ export class ViewCursors extends ViewPart { private _show(): void { this._primaryCursor.show(); - for (var i = 0, len = this._secondaryCursors.length; i < len; i++) { + for (let i = 0, len = this._secondaryCursors.length; i < len; i++) { this._secondaryCursors[i].show(); } this._isVisible = true; @@ -296,7 +296,7 @@ export class ViewCursors extends ViewPart { private _hide(): void { this._primaryCursor.hide(); - for (var i = 0, len = this._secondaryCursors.length; i < len; i++) { + for (let i = 0, len = this._secondaryCursors.length; i < len; i++) { this._secondaryCursors[i].hide(); } this._isVisible = false; @@ -310,7 +310,7 @@ export class ViewCursors extends ViewPart { } this._primaryCursor.prepareRender(ctx); - for (var i = 0, len = this._secondaryCursors.length; i < len; i++) { + for (let i = 0, len = this._secondaryCursors.length; i < len; i++) { this._secondaryCursors[i].prepareRender(ctx); } } @@ -318,7 +318,7 @@ export class ViewCursors extends ViewPart { public render(ctx: IRestrictedRenderingContext): void { this._renderData = []; this._renderData.push(this._primaryCursor.render(ctx)); - for (var i = 0, len = this._secondaryCursors.length; i < len; i++) { + for (let i = 0, len = this._secondaryCursors.length; i < len; i++) { this._renderData.push(this._secondaryCursors[i].render(ctx)); } diff --git a/src/vs/editor/browser/viewParts/viewZones/viewZones.ts b/src/vs/editor/browser/viewParts/viewZones/viewZones.ts index ae4821e08d0..45deb9e67f7 100644 --- a/src/vs/editor/browser/viewParts/viewZones/viewZones.ts +++ b/src/vs/editor/browser/viewParts/viewZones/viewZones.ts @@ -200,7 +200,7 @@ export class ViewZones extends ViewPart { public removeZone(id: number): boolean { if (this._zones.hasOwnProperty(id.toString())) { - var zone = this._zones[id.toString()]; + let zone = this._zones[id.toString()]; delete this._zones[id.toString()]; this._whitespaceManager.removeWhitespace(zone.whitespaceId); @@ -234,7 +234,7 @@ export class ViewZones extends ViewPart { public shouldSuppressMouseDownOnViewZone(id: number): boolean { if (this._zones.hasOwnProperty(id.toString())) { - var zone = this._zones[id.toString()]; + let zone = this._zones[id.toString()]; return zone.delegate.suppressMouseDown; } return false; diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index 4548e94f9dd..d0a796b3a1e 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -130,10 +130,10 @@ export abstract class CodeEditorWidget extends CommonCodeEditor implements edito if (!model) { return ''; } - var content = model.getLineContent(lineNumber); - var tokens = model.getLineTokens(lineNumber, false); - var inflatedTokens = tokens.inflate(); - var tabSize = model.getOptions().tabSize; + let content = model.getLineContent(lineNumber); + let tokens = model.getLineTokens(lineNumber, false); + let inflatedTokens = tokens.inflate(); + let tabSize = model.getOptions().tabSize; return Colorizer.colorizeLine(content, inflatedTokens, tabSize); } public getView(): editorBrowser.IView { @@ -247,14 +247,13 @@ export abstract class CodeEditorWidget extends CommonCodeEditor implements edito }; } - public restoreViewState(state: editorCommon.IEditorViewState): void { + public restoreViewState(s: editorCommon.ICodeEditorViewState): void { if (!this.cursor || !this.hasView) { return; } - var s = state; if (s && s.cursorState && s.viewState) { - var codeEditorState = s; - var cursorState = codeEditorState.cursorState; + let codeEditorState = s; + let cursorState = codeEditorState.cursorState; if (Array.isArray(cursorState)) { this.cursor.restoreState(cursorState); } else { @@ -296,7 +295,7 @@ export abstract class CodeEditorWidget extends CommonCodeEditor implements edito } public addContentWidget(widget: editorBrowser.IContentWidget): void { - var widgetData: editorBrowser.IContentWidgetData = { + let widgetData: editorBrowser.IContentWidgetData = { widget: widget, position: widget.getPosition() }; @@ -313,9 +312,9 @@ export abstract class CodeEditorWidget extends CommonCodeEditor implements edito } public layoutContentWidget(widget: editorBrowser.IContentWidget): void { - var widgetId = widget.getId(); + let widgetId = widget.getId(); if (this.contentWidgets.hasOwnProperty(widgetId)) { - var widgetData = this.contentWidgets[widgetId]; + let widgetData = this.contentWidgets[widgetId]; widgetData.position = widget.getPosition(); if (this.hasView) { this._view.layoutContentWidget(widgetData); @@ -324,9 +323,9 @@ export abstract class CodeEditorWidget extends CommonCodeEditor implements edito } public removeContentWidget(widget: editorBrowser.IContentWidget): void { - var widgetId = widget.getId(); + let widgetId = widget.getId(); if (this.contentWidgets.hasOwnProperty(widgetId)) { - var widgetData = this.contentWidgets[widgetId]; + let widgetData = this.contentWidgets[widgetId]; delete this.contentWidgets[widgetId]; if (this.hasView) { this._view.removeContentWidget(widgetData); @@ -335,7 +334,7 @@ export abstract class CodeEditorWidget extends CommonCodeEditor implements edito } public addOverlayWidget(widget: editorBrowser.IOverlayWidget): void { - var widgetData: editorBrowser.IOverlayWidgetData = { + let widgetData: editorBrowser.IOverlayWidgetData = { widget: widget, position: widget.getPosition() }; @@ -352,9 +351,9 @@ export abstract class CodeEditorWidget extends CommonCodeEditor implements edito } public layoutOverlayWidget(widget: editorBrowser.IOverlayWidget): void { - var widgetId = widget.getId(); + let widgetId = widget.getId(); if (this.overlayWidgets.hasOwnProperty(widgetId)) { - var widgetData = this.overlayWidgets[widgetId]; + let widgetData = this.overlayWidgets[widgetId]; widgetData.position = widget.getPosition(); if (this.hasView) { this._view.layoutOverlayWidget(widgetData); @@ -363,9 +362,9 @@ export abstract class CodeEditorWidget extends CommonCodeEditor implements edito } public removeOverlayWidget(widget: editorBrowser.IOverlayWidget): void { - var widgetId = widget.getId(); + let widgetId = widget.getId(); if (this.overlayWidgets.hasOwnProperty(widgetId)) { - var widgetData = this.overlayWidgets[widgetId]; + let widgetData = this.overlayWidgets[widgetId]; delete this.overlayWidgets[widgetId]; if (this.hasView) { this._view.removeOverlayWidget(widgetData); @@ -378,7 +377,7 @@ export abstract class CodeEditorWidget extends CommonCodeEditor implements edito // console.warn('Cannot change view zones on editor that is not attached to a model, since there is no view.'); return; } - var hasChanges = this._view.change(callback); + let hasChanges = this._view.change(callback); if (hasChanges) { this.emit(editorCommon.EventType.ViewZonesChanged); } @@ -410,12 +409,12 @@ export abstract class CodeEditorWidget extends CommonCodeEditor implements edito return null; } - var position = this.model.validatePosition(rawPosition); - var helper = this._view.getCodeEditorHelper(); - var layoutInfo = this._configuration.editor.layoutInfo; + let position = this.model.validatePosition(rawPosition); + let helper = this._view.getCodeEditorHelper(); + let layoutInfo = this._configuration.editor.layoutInfo; - var top = helper.getVerticalOffsetForPosition(position.lineNumber, position.column) - helper.getScrollTop(); - var left = helper.getOffsetForColumn(position.lineNumber, position.column) + layoutInfo.glyphMarginWidth + layoutInfo.lineNumbersWidth + layoutInfo.decorationsWidth - helper.getScrollLeft(); + let top = helper.getVerticalOffsetForPosition(position.lineNumber, position.column) - helper.getScrollTop(); + let left = helper.getOffsetForColumn(position.lineNumber, position.column) + layoutInfo.glyphMarginWidth + layoutInfo.lineNumbersWidth + layoutInfo.decorationsWidth - helper.getScrollLeft(); return { top: top, @@ -506,7 +505,7 @@ export abstract class CodeEditorWidget extends CommonCodeEditor implements edito } protected _detachModel(): editorCommon.IModel { - var removeDomNode: HTMLElement = null; + let removeDomNode: HTMLElement = null; if (this._view) { this._view.dispose(); @@ -634,9 +633,9 @@ export class CommandRunner implements editorCommon.ICommand { }); // Merge operations that touch each other - var resultOps: editorCommon.ISingleEditOperation[] = []; - var previousOp = this._ops[0]; - for (var i = 1; i < this._ops.length; i++) { + let resultOps: editorCommon.ISingleEditOperation[] = []; + let previousOp = this._ops[0]; + for (let i = 1; i < this._ops.length; i++) { if (previousOp.range.endLineNumber === this._ops[i].range.startLineNumber && previousOp.range.endColumn === this._ops[i].range.startColumn) { // These operations are one after another and can be merged previousOp.range = Range.plusRange(previousOp.range, this._ops[i].range); @@ -648,14 +647,14 @@ export class CommandRunner implements editorCommon.ICommand { } resultOps.push(previousOp); - for (var i = 0; i < resultOps.length; i++) { + for (let i = 0; i < resultOps.length; i++) { builder.addEditOperation(Range.lift(resultOps[i].range), resultOps[i].text); } } public computeCursorState(model: editorCommon.ITokenizedModel, helper: editorCommon.ICursorStateComputerData): Selection { - var inverseEditOperations = helper.getInverseEditOperations(); - var srcRange = inverseEditOperations[inverseEditOperations.length - 1].range; + let inverseEditOperations = helper.getInverseEditOperations(); + let srcRange = inverseEditOperations[inverseEditOperations.length - 1].range; return new Selection( srcRange.endLineNumber, srcRange.endColumn, diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index f6f7c2191f0..46630684f54 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -79,7 +79,7 @@ class VisualEditorState { // (1) View zones if (this._zones.length > 0) { editor.changeViewZones((viewChangeAccessor: editorBrowser.IViewZoneChangeAccessor) => { - for (var i = 0, length = this._zones.length; i < length; i++) { + for (let i = 0, length = this._zones.length; i < length; i++) { viewChangeAccessor.removeZone(this._zones[i]); } }); @@ -97,19 +97,16 @@ class VisualEditorState { } public apply(editor: CodeEditor, overviewRuler: editorBrowser.IOverviewRuler, newDecorations: IEditorDiffDecorationsWithZones): void { - var i: number, - length: number; - // view zones editor.changeViewZones((viewChangeAccessor: editorBrowser.IViewZoneChangeAccessor) => { - for (i = 0, length = this._zones.length; i < length; i++) { + for (let i = 0, length = this._zones.length; i < length; i++) { viewChangeAccessor.removeZone(this._zones[i]); } this._zones = []; this._zonesMap = {}; - for (i = 0, length = newDecorations.zones.length; i < length; i++) { + for (let i = 0, length = newDecorations.zones.length; i < length; i++) { newDecorations.zones[i].suppressMouseDown = true; - var zoneId = viewChangeAccessor.addZone(newDecorations.zones[i]); + let zoneId = viewChangeAccessor.addZone(newDecorations.zones[i]); this._zones.push(zoneId); this._zonesMap[String(zoneId)] = true; } @@ -125,7 +122,7 @@ class VisualEditorState { } } -var DIFF_EDITOR_ID = 0; +let DIFF_EDITOR_ID = 0; export class DiffEditorWidget extends EventEmitter implements editorBrowser.IDiffEditor { @@ -313,7 +310,7 @@ export class DiffEditorWidget extends EventEmitter implements editorBrowser.IDif } private static _getClassName(theme: string, renderSideBySide: boolean): string { - var result = 'monaco-diff-editor monaco-editor-background '; + let result = 'monaco-diff-editor monaco-editor-background '; if (renderSideBySide) { result += 'side-by-side '; } @@ -418,7 +415,7 @@ export class DiffEditorWidget extends EventEmitter implements editorBrowser.IDif this._theme = newOptions && newOptions.theme ? newOptions.theme : this._theme; // Handle side by side - var renderSideBySideChanged = false; + let renderSideBySideChanged = false; if (typeof newOptions.renderSideBySide !== 'undefined') { if (this._renderSideBySide !== newOptions.renderSideBySide) { this._renderSideBySide = newOptions.renderSideBySide; @@ -607,18 +604,17 @@ export class DiffEditorWidget extends EventEmitter implements editorBrowser.IDif } public saveViewState(): editorCommon.IDiffEditorViewState { - var originalViewState = this.originalEditor.saveViewState(); - var modifiedViewState = this.modifiedEditor.saveViewState(); + let originalViewState = this.originalEditor.saveViewState(); + let modifiedViewState = this.modifiedEditor.saveViewState(); return { original: originalViewState, modified: modifiedViewState }; } - public restoreViewState(state: editorCommon.IEditorViewState): void { - var s = state; + public restoreViewState(s: editorCommon.IDiffEditorViewState): void { if (s.original && s.original) { - var diffEditorState = s; + let diffEditorState = s; this.originalEditor.restoreViewState(diffEditorState.original); this.modifiedEditor.restoreViewState(diffEditorState.modified); } @@ -690,8 +686,8 @@ export class DiffEditorWidget extends EventEmitter implements editorBrowser.IDif } private _layoutOverviewRulers(): void { - var freeSpace = DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH - 2 * DiffEditorWidget.ONE_OVERVIEW_WIDTH; - var layoutInfo = this.modifiedEditor.getLayoutInfo(); + let freeSpace = DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH - 2 * DiffEditorWidget.ONE_OVERVIEW_WIDTH; + let layoutInfo = this.modifiedEditor.getLayoutInfo(); if (layoutInfo) { this._originalOverviewRuler.setLayout(new editorCommon.OverviewRulerPosition({ top: 0, @@ -711,9 +707,9 @@ export class DiffEditorWidget extends EventEmitter implements editorBrowser.IDif //------------ end layouting methods private _recomputeIfNecessary(events: EmitterEvent[]): void { - var changed = false; - for (var i = 0; !changed && i < events.length; i++) { - var type = events[i].getType(); + let changed = false; + for (let i = 0; !changed && i < events.length; i++) { + let type = events[i].getType(); changed = changed || type === editorCommon.EventType.ModelRawContentChanged || type === editorCommon.EventType.ModelModeChanged; } if (changed && this._isVisible) { @@ -727,7 +723,7 @@ export class DiffEditorWidget extends EventEmitter implements editorBrowser.IDif } private _onOriginalEditorEvents(events: EmitterEvent[]): void { - for (var i = 0; i < events.length; i++) { + for (let i = 0; i < events.length; i++) { let type = events[i].getType(); let data = events[i].getData(); @@ -749,7 +745,7 @@ export class DiffEditorWidget extends EventEmitter implements editorBrowser.IDif } private _onModifiedEditorEvents(events: EmitterEvent[]): void { - for (var i = 0; i < events.length; i++) { + for (let i = 0; i < events.length; i++) { let type = events[i].getType(); let data = events[i].getData(); @@ -795,9 +791,9 @@ export class DiffEditorWidget extends EventEmitter implements editorBrowser.IDif // The best method would be to call cancel on the Promise, but this is not // yet supported, so using tokens for now. this._diffComputationToken++; - var currentToken = this._diffComputationToken; - var currentOriginalModel = this.originalEditor.getModel(); - var currentModifiedModel = this.modifiedEditor.getModel(); + let currentToken = this._diffComputationToken; + let currentOriginalModel = this.originalEditor.getModel(); + let currentModifiedModel = this.modifiedEditor.getModel(); this._editorWorkerService.computeDiff(currentOriginalModel.uri, currentModifiedModel.uri, this._ignoreTrimWhitespace).then((result) => { if (currentToken === this._diffComputationToken @@ -828,12 +824,12 @@ export class DiffEditorWidget extends EventEmitter implements editorBrowser.IDif if (!this.originalEditor.getModel() || !this.modifiedEditor.getModel()) { return; } - var lineChanges = this._lineChanges || []; + let lineChanges = this._lineChanges || []; - var foreignOriginal = this._originalEditorState.getForeignViewZones(this.originalEditor.getWhitespaces()); - var foreignModified = this._modifiedEditorState.getForeignViewZones(this.modifiedEditor.getWhitespaces()); + let foreignOriginal = this._originalEditorState.getForeignViewZones(this.originalEditor.getWhitespaces()); + let foreignModified = this._modifiedEditorState.getForeignViewZones(this.modifiedEditor.getWhitespaces()); - var diffDecorations = this._strategy.getEditorsDiffDecorations(lineChanges, this._ignoreTrimWhitespace, foreignOriginal, foreignModified, this.originalEditor, this.modifiedEditor); + let diffDecorations = this._strategy.getEditorsDiffDecorations(lineChanges, this._ignoreTrimWhitespace, foreignOriginal, foreignModified, this.originalEditor, this.modifiedEditor); try { this._currentlyChangingViewZones = true; @@ -903,7 +899,7 @@ export class DiffEditorWidget extends EventEmitter implements editorBrowser.IDif } private _doLayout(): void { - var splitPoint = this._strategy.layout(); + let splitPoint = this._strategy.layout(); this._originalDomNode.style.width = splitPoint + 'px'; this._originalDomNode.style.left = '0px'; @@ -928,7 +924,7 @@ export class DiffEditorWidget extends EventEmitter implements editorBrowser.IDif } private _layoutOverviewViewport(): void { - var layout = this._computeOverviewViewport(); + let layout = this._computeOverviewViewport(); if (!layout) { StyleMutator.setTop(this._overviewViewportDomElement, 0); StyleMutator.setHeight(this._overviewViewportDomElement, 0); @@ -939,20 +935,20 @@ export class DiffEditorWidget extends EventEmitter implements editorBrowser.IDif } private _computeOverviewViewport(): { height: number; top: number; } { - var layoutInfo = this.modifiedEditor.getLayoutInfo(); + let layoutInfo = this.modifiedEditor.getLayoutInfo(); if (!layoutInfo) { return null; } - var scrollTop = this.modifiedEditor.getScrollTop(); - var scrollHeight = this.modifiedEditor.getScrollHeight(); + let scrollTop = this.modifiedEditor.getScrollTop(); + let scrollHeight = this.modifiedEditor.getScrollHeight(); - var computedAvailableSize = Math.max(0, layoutInfo.contentHeight); - var computedRepresentableSize = Math.max(0, computedAvailableSize - 2 * 0); - var computedRatio = scrollHeight > 0 ? (computedRepresentableSize / scrollHeight) : 0; + let computedAvailableSize = Math.max(0, layoutInfo.contentHeight); + let computedRepresentableSize = Math.max(0, computedAvailableSize - 2 * 0); + let computedRatio = scrollHeight > 0 ? (computedRepresentableSize / scrollHeight) : 0; - var computedSliderSize = Math.max(1, Math.floor(layoutInfo.contentHeight * computedRatio)); - var computedSliderPosition = Math.floor(scrollTop * computedRatio); + let computedSliderSize = Math.max(1, Math.floor(layoutInfo.contentHeight * computedRatio)); + let computedSliderPosition = Math.floor(scrollTop * computedRatio); return { height: computedSliderSize, @@ -1009,11 +1005,11 @@ export class DiffEditorWidget extends EventEmitter implements editorBrowser.IDif return null; } - var min = 0, max = this._lineChanges.length - 1; + let min = 0, max = this._lineChanges.length - 1; while (min < max) { - var mid = Math.floor((min + max) / 2); - var midStart = startLineNumberExtractor(this._lineChanges[mid]); - var midEnd = (mid + 1 <= max ? startLineNumberExtractor(this._lineChanges[mid + 1]) : Number.MAX_VALUE); + let mid = Math.floor((min + max) / 2); + let midStart = startLineNumberExtractor(this._lineChanges[mid]); + let midEnd = (mid + 1 <= max ? startLineNumberExtractor(this._lineChanges[mid + 1]) : Number.MAX_VALUE); if (lineNumber < midStart) { max = mid - 1; @@ -1029,19 +1025,19 @@ export class DiffEditorWidget extends EventEmitter implements editorBrowser.IDif } private _getEquivalentLineForOriginalLineNumber(lineNumber: number): number { - var lineChange = this._getLineChangeAtOrBeforeLineNumber(lineNumber, (lineChange) => lineChange.originalStartLineNumber); + let lineChange = this._getLineChangeAtOrBeforeLineNumber(lineNumber, (lineChange) => lineChange.originalStartLineNumber); if (!lineChange) { return lineNumber; } - var originalEquivalentLineNumber = lineChange.originalStartLineNumber + (lineChange.originalEndLineNumber > 0 ? -1 : 0); - var modifiedEquivalentLineNumber = lineChange.modifiedStartLineNumber + (lineChange.modifiedEndLineNumber > 0 ? -1 : 0); - var lineChangeOriginalLength = (lineChange.originalEndLineNumber > 0 ? (lineChange.originalEndLineNumber - lineChange.originalStartLineNumber + 1) : 0); - var lineChangeModifiedLength = (lineChange.modifiedEndLineNumber > 0 ? (lineChange.modifiedEndLineNumber - lineChange.modifiedStartLineNumber + 1) : 0); + let originalEquivalentLineNumber = lineChange.originalStartLineNumber + (lineChange.originalEndLineNumber > 0 ? -1 : 0); + let modifiedEquivalentLineNumber = lineChange.modifiedStartLineNumber + (lineChange.modifiedEndLineNumber > 0 ? -1 : 0); + let lineChangeOriginalLength = (lineChange.originalEndLineNumber > 0 ? (lineChange.originalEndLineNumber - lineChange.originalStartLineNumber + 1) : 0); + let lineChangeModifiedLength = (lineChange.modifiedEndLineNumber > 0 ? (lineChange.modifiedEndLineNumber - lineChange.modifiedStartLineNumber + 1) : 0); - var delta = lineNumber - originalEquivalentLineNumber; + let delta = lineNumber - originalEquivalentLineNumber; if (delta <= lineChangeOriginalLength) { return modifiedEquivalentLineNumber + Math.min(delta, lineChangeModifiedLength); @@ -1051,19 +1047,19 @@ export class DiffEditorWidget extends EventEmitter implements editorBrowser.IDif } private _getEquivalentLineForModifiedLineNumber(lineNumber: number): number { - var lineChange = this._getLineChangeAtOrBeforeLineNumber(lineNumber, (lineChange) => lineChange.modifiedStartLineNumber); + let lineChange = this._getLineChangeAtOrBeforeLineNumber(lineNumber, (lineChange) => lineChange.modifiedStartLineNumber); if (!lineChange) { return lineNumber; } - var originalEquivalentLineNumber = lineChange.originalStartLineNumber + (lineChange.originalEndLineNumber > 0 ? -1 : 0); - var modifiedEquivalentLineNumber = lineChange.modifiedStartLineNumber + (lineChange.modifiedEndLineNumber > 0 ? -1 : 0); - var lineChangeOriginalLength = (lineChange.originalEndLineNumber > 0 ? (lineChange.originalEndLineNumber - lineChange.originalStartLineNumber + 1) : 0); - var lineChangeModifiedLength = (lineChange.modifiedEndLineNumber > 0 ? (lineChange.modifiedEndLineNumber - lineChange.modifiedStartLineNumber + 1) : 0); + let originalEquivalentLineNumber = lineChange.originalStartLineNumber + (lineChange.originalEndLineNumber > 0 ? -1 : 0); + let modifiedEquivalentLineNumber = lineChange.modifiedStartLineNumber + (lineChange.modifiedEndLineNumber > 0 ? -1 : 0); + let lineChangeOriginalLength = (lineChange.originalEndLineNumber > 0 ? (lineChange.originalEndLineNumber - lineChange.originalStartLineNumber + 1) : 0); + let lineChangeModifiedLength = (lineChange.modifiedEndLineNumber > 0 ? (lineChange.modifiedEndLineNumber - lineChange.modifiedStartLineNumber + 1) : 0); - var delta = lineNumber - modifiedEquivalentLineNumber; + let delta = lineNumber - modifiedEquivalentLineNumber; if (delta <= lineChangeModifiedLength) { return originalEquivalentLineNumber + Math.min(delta, lineChangeOriginalLength); @@ -1119,11 +1115,11 @@ class DiffEditorWidgetStyle { originalWhitespaces = originalWhitespaces.sort((a, b) => { return a.afterLineNumber - b.afterLineNumber; }); - var zones = this._getViewZones(lineChanges, originalWhitespaces, modifiedWhitespaces, originalEditor, modifiedEditor); + let zones = this._getViewZones(lineChanges, originalWhitespaces, modifiedWhitespaces, originalEditor, modifiedEditor); // Get decorations & overview ruler zones - var originalDecorations = this._getOriginalEditorDecorations(lineChanges, ignoreTrimWhitespace, originalEditor, modifiedEditor); - var modifiedDecorations = this._getModifiedEditorDecorations(lineChanges, ignoreTrimWhitespace, originalEditor, modifiedEditor); + let originalDecorations = this._getOriginalEditorDecorations(lineChanges, ignoreTrimWhitespace, originalEditor, modifiedEditor); + let modifiedDecorations = this._getModifiedEditorDecorations(lineChanges, ignoreTrimWhitespace, originalEditor, modifiedEditor); return { original: { @@ -1191,34 +1187,25 @@ abstract class ViewZonesComputer { } public getViewZones(): IEditorsZones { - var result: IEditorsZones = { + let result: IEditorsZones = { original: [], modified: [] }; - var i: number, - length: number, - lineChange: editorCommon.ILineChange; + let lineChangeModifiedLength: number = 0; + let lineChangeOriginalLength: number = 0; + let originalEquivalentLineNumber: number = 0; + let modifiedEquivalentLineNumber: number = 0; + let originalEndEquivalentLineNumber: number = 0; + let modifiedEndEquivalentLineNumber: number = 0; - var stepOriginal: IMyViewZone[], - stepModified: IMyViewZone[], - stepOriginalIndex: number, - stepModifiedIndex: number, - lineChangeModifiedLength: number = 0, - lineChangeOriginalLength: number = 0, - originalEquivalentLineNumber: number = 0, - modifiedEquivalentLineNumber: number = 0, - originalEndEquivalentLineNumber: number = 0, - modifiedEndEquivalentLineNumber: number = 0, - viewZoneLineNumber: number = 0; - - var sortMyViewZones = (a: IMyViewZone, b: IMyViewZone) => { + let sortMyViewZones = (a: IMyViewZone, b: IMyViewZone) => { return a.afterLineNumber - b.afterLineNumber; }; - var addAndCombineIfPossible = (destination: editorBrowser.IViewZone[], item: IMyViewZone) => { + let addAndCombineIfPossible = (destination: editorBrowser.IViewZone[], item: IMyViewZone) => { if (item.domNode === null && destination.length > 0) { - var lastItem = destination[destination.length - 1]; + let lastItem = destination[destination.length - 1]; if (lastItem.afterLineNumber === item.afterLineNumber && lastItem.domNode === null) { lastItem.heightInLines += item.heightInLines; return; @@ -1227,12 +1214,12 @@ abstract class ViewZonesComputer { destination.push(item); }; - var modifiedForeignVZ = new ForeignViewZonesIterator(this.modifiedForeignVZ); - var originalForeignVZ = new ForeignViewZonesIterator(this.originalForeignVZ); + let modifiedForeignVZ = new ForeignViewZonesIterator(this.modifiedForeignVZ); + let originalForeignVZ = new ForeignViewZonesIterator(this.originalForeignVZ); // In order to include foreign view zones after the last line change, the for loop will iterate once more after the end of the `lineChanges` array - for (i = 0, length = this.lineChanges.length; i <= length; i++) { - lineChange = (i < length ? this.lineChanges[i] : null); + for (let i = 0, length = this.lineChanges.length; i <= length; i++) { + let lineChange = (i < length ? this.lineChanges[i] : null); if (lineChange !== null) { originalEquivalentLineNumber = lineChange.originalStartLineNumber + (lineChange.originalEndLineNumber > 0 ? -1 : 0); @@ -1250,13 +1237,14 @@ abstract class ViewZonesComputer { } // Each step produces view zones, and after producing them, we try to cancel them out, to avoid empty-empty view zone cases - stepOriginal = []; - stepModified = []; + let stepOriginal: IMyViewZone[] = []; + let stepModified: IMyViewZone[] = []; // ---------------------------- PRODUCE VIEW ZONES // [PRODUCE] View zone(s) in original-side due to foreign view zone(s) in modified-side while (modifiedForeignVZ.current && modifiedForeignVZ.current.afterLineNumber <= modifiedEndEquivalentLineNumber) { + let viewZoneLineNumber: number; if (modifiedForeignVZ.current.afterLineNumber <= modifiedEquivalentLineNumber) { viewZoneLineNumber = originalEquivalentLineNumber - modifiedEquivalentLineNumber + modifiedForeignVZ.current.afterLineNumber; } else { @@ -1272,6 +1260,7 @@ abstract class ViewZonesComputer { // [PRODUCE] View zone(s) in modified-side due to foreign view zone(s) in original-side while (originalForeignVZ.current && originalForeignVZ.current.afterLineNumber <= originalEndEquivalentLineNumber) { + let viewZoneLineNumber: number; if (originalForeignVZ.current.afterLineNumber <= originalEquivalentLineNumber) { viewZoneLineNumber = modifiedEquivalentLineNumber - originalEquivalentLineNumber + originalForeignVZ.current.afterLineNumber; } else { @@ -1286,14 +1275,14 @@ abstract class ViewZonesComputer { } if (lineChange !== null && isChangeOrInsert(lineChange)) { - var r = this._produceOriginalFromDiff(lineChange, lineChangeOriginalLength, lineChangeModifiedLength); + let r = this._produceOriginalFromDiff(lineChange, lineChangeOriginalLength, lineChangeModifiedLength); if (r) { stepOriginal.push(r); } } if (lineChange !== null && isChangeOrDelete(lineChange)) { - var r = this._produceModifiedFromDiff(lineChange, lineChangeOriginalLength, lineChangeModifiedLength); + let r = this._produceModifiedFromDiff(lineChange, lineChangeOriginalLength, lineChangeModifiedLength); if (r) { stepModified.push(r); } @@ -1305,18 +1294,18 @@ abstract class ViewZonesComputer { // ---------------------------- EMIT MINIMAL VIEW ZONES // [CANCEL & EMIT] Try to cancel view zones out - stepOriginalIndex = 0; - stepModifiedIndex = 0; + let stepOriginalIndex = 0; + let stepModifiedIndex = 0; stepOriginal = stepOriginal.sort(sortMyViewZones); stepModified = stepModified.sort(sortMyViewZones); while (stepOriginalIndex < stepOriginal.length && stepModifiedIndex < stepModified.length) { - var original = stepOriginal[stepOriginalIndex]; - var modified = stepModified[stepModifiedIndex]; + let original = stepOriginal[stepOriginalIndex]; + let modified = stepModified[stepModifiedIndex]; - var originalDelta = original.afterLineNumber - originalEquivalentLineNumber; - var modifiedDelta = modified.afterLineNumber - modifiedEquivalentLineNumber; + let originalDelta = original.afterLineNumber - originalEquivalentLineNumber; + let modifiedDelta = modified.afterLineNumber - modifiedEquivalentLineNumber; if (originalDelta < modifiedDelta) { addAndCombineIfPossible(result.original, original); @@ -1358,7 +1347,7 @@ abstract class ViewZonesComputer { // ---------------------------- END EMIT MINIMAL VIEW ZONES } - var ensureDomNode = (z: IMyViewZone) => { + let ensureDomNode = (z: IMyViewZone) => { if (!z.domNode) { z.domNode = createFakeLinesDiv(); } @@ -1408,7 +1397,7 @@ class DiffEdtorWidgetSideBySide extends DiffEditorWidgetStyle implements IDiffEd } public setEnableSplitViewResizing(enableSplitViewResizing: boolean): void { - var newDisableSash = (enableSplitViewResizing === false); + let newDisableSash = (enableSplitViewResizing === false); if (this._disableSash !== newDisableSash) { this._disableSash = newDisableSash; @@ -1421,11 +1410,11 @@ class DiffEdtorWidgetSideBySide extends DiffEditorWidgetStyle implements IDiffEd } public layout(sashRatio: number = this._sashRatio): number { - var w = this._dataSource.getWidth(); - var contentWidth = w - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH; + let w = this._dataSource.getWidth(); + let contentWidth = w - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH; - var sashPosition = Math.floor((sashRatio || 0.5) * contentWidth); - var midPoint = Math.floor(0.5 * contentWidth); + let sashPosition = Math.floor((sashRatio || 0.5) * contentWidth); + let midPoint = Math.floor(0.5 * contentWidth); sashPosition = this._disableSash ? midPoint : sashPosition || midPoint; @@ -1454,9 +1443,9 @@ class DiffEdtorWidgetSideBySide extends DiffEditorWidgetStyle implements IDiffEd } private onSashDrag(e: ISashEvent): void { - var w = this._dataSource.getWidth(); - var contentWidth = w - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH; - var sashPosition = this.layout((this._startSashPosition + (e.currentX - e.startX)) / contentWidth); + let w = this._dataSource.getWidth(); + let contentWidth = w - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH; + let sashPosition = this.layout((this._startSashPosition + (e.currentX - e.startX)) / contentWidth); this._sashRatio = sashPosition / contentWidth; @@ -1486,29 +1475,21 @@ class DiffEdtorWidgetSideBySide extends DiffEditorWidgetStyle implements IDiffEd } _getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: editorCommon.IEditorWhitespace[], modifiedForeignVZ: editorCommon.IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorsZones { - var c = new SideBySideViewZonesComputer(lineChanges, originalForeignVZ, modifiedForeignVZ); + let c = new SideBySideViewZonesComputer(lineChanges, originalForeignVZ, modifiedForeignVZ); return c.getViewZones(); } _getOriginalEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations { - var result: IEditorDiffDecorations = { + let result: IEditorDiffDecorations = { decorations: [], overviewZones: [] - }, - i: number, - length: number, - j: number, - lengthJ: number, - lineChange: editorCommon.ILineChange, - charChange: editorCommon.ICharChange, - lineNumber: number, - startColumn: number, - endColumn: number, - originalModel = originalEditor.getModel(); + }; - for (i = 0, length = lineChanges.length; i < length; i++) { - lineChange = lineChanges[i]; + let originalModel = originalEditor.getModel(); + + for (let i = 0, length = lineChanges.length; i < length; i++) { + let lineChange = lineChanges[i]; if (isChangeOrDelete(lineChange)) { @@ -1527,11 +1508,13 @@ class DiffEdtorWidgetSideBySide extends DiffEditorWidgetStyle implements IDiffEd )); if (lineChange.charChanges) { - for (j = 0, lengthJ = lineChange.charChanges.length; j < lengthJ; j++) { - charChange = lineChange.charChanges[j]; + for (let j = 0, lengthJ = lineChange.charChanges.length; j < lengthJ; j++) { + let charChange = lineChange.charChanges[j]; if (isChangeOrDelete(charChange)) { if (ignoreTrimWhitespace) { - for (lineNumber = charChange.originalStartLineNumber; lineNumber <= charChange.originalEndLineNumber; lineNumber++) { + for (let lineNumber = charChange.originalStartLineNumber; lineNumber <= charChange.originalEndLineNumber; lineNumber++) { + let startColumn: number; + let endColumn: number; if (lineNumber === charChange.originalStartLineNumber) { startColumn = charChange.originalStartColumn; } else { @@ -1558,23 +1541,15 @@ class DiffEdtorWidgetSideBySide extends DiffEditorWidgetStyle implements IDiffEd _getModifiedEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations { - var result: IEditorDiffDecorations = { + let result: IEditorDiffDecorations = { decorations: [], overviewZones: [] - }, - i: number, - length: number, - j: number, - lengthJ: number, - lineChange: editorCommon.ILineChange, - charChange: editorCommon.ICharChange, - lineNumber: number, - startColumn: number, - endColumn: number, - modifiedModel = modifiedEditor.getModel(); + }; - for (i = 0, length = lineChanges.length; i < length; i++) { - lineChange = lineChanges[i]; + let modifiedModel = modifiedEditor.getModel(); + + for (let i = 0, length = lineChanges.length; i < length; i++) { + let lineChange = lineChanges[i]; if (isChangeOrInsert(lineChange)) { @@ -1592,11 +1567,13 @@ class DiffEdtorWidgetSideBySide extends DiffEditorWidgetStyle implements IDiffEd )); if (lineChange.charChanges) { - for (j = 0, lengthJ = lineChange.charChanges.length; j < lengthJ; j++) { - charChange = lineChange.charChanges[j]; + for (let j = 0, lengthJ = lineChange.charChanges.length; j < lengthJ; j++) { + let charChange = lineChange.charChanges[j]; if (isChangeOrInsert(charChange)) { if (ignoreTrimWhitespace) { - for (lineNumber = charChange.modifiedStartLineNumber; lineNumber <= charChange.modifiedEndLineNumber; lineNumber++) { + for (let lineNumber = charChange.modifiedStartLineNumber; lineNumber <= charChange.modifiedEndLineNumber; lineNumber++) { + let startColumn: number; + let endColumn: number; if (lineNumber === charChange.modifiedStartLineNumber) { startColumn = charChange.modifiedStartColumn; } else { @@ -1679,21 +1656,18 @@ class DiffEdtorWidgetInline extends DiffEditorWidgetStyle implements IDiffEditor } _getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: editorCommon.IEditorWhitespace[], modifiedForeignVZ: editorCommon.IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorsZones { - var computer = new InlineViewZonesComputer(lineChanges, originalForeignVZ, modifiedForeignVZ, originalEditor, modifiedEditor); + let computer = new InlineViewZonesComputer(lineChanges, originalForeignVZ, modifiedForeignVZ, originalEditor, modifiedEditor); return computer.getViewZones(); } _getOriginalEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations { - var result: IEditorDiffDecorations = { + let result: IEditorDiffDecorations = { decorations: [], overviewZones: [] - }, - i: number, - length: number, - lineChange: editorCommon.ILineChange; + }; - for (i = 0, length = lineChanges.length; i < length; i++) { - lineChange = lineChanges[i]; + for (let i = 0, length = lineChanges.length; i < length; i++) { + let lineChange = lineChanges[i]; // Add overview zones in the overview ruler if (isChangeOrDelete(lineChange)) { @@ -1713,23 +1687,15 @@ class DiffEdtorWidgetInline extends DiffEditorWidgetStyle implements IDiffEditor _getModifiedEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations { - var result: IEditorDiffDecorations = { + let result: IEditorDiffDecorations = { decorations: [], overviewZones: [] - }, - i: number, - length: number, - lineChange: editorCommon.ILineChange, - j: number, - lengthJ: number, - charChange: editorCommon.ICharChange, - lineNumber: number, - startColumn: number, - endColumn: number, - modifiedModel = modifiedEditor.getModel(); + }; - for (i = 0, length = lineChanges.length; i < length; i++) { - lineChange = lineChanges[i]; + let modifiedModel = modifiedEditor.getModel(); + + for (let i = 0, length = lineChanges.length; i < length; i++) { + let lineChange = lineChanges[i]; // Add decorations & overview zones if (isChangeOrInsert(lineChange)) { @@ -1745,11 +1711,13 @@ class DiffEdtorWidgetInline extends DiffEditorWidgetStyle implements IDiffEditor )); if (lineChange.charChanges) { - for (j = 0, lengthJ = lineChange.charChanges.length; j < lengthJ; j++) { - charChange = lineChange.charChanges[j]; + for (let j = 0, lengthJ = lineChange.charChanges.length; j < lengthJ; j++) { + let charChange = lineChange.charChanges[j]; if (isChangeOrInsert(charChange)) { if (ignoreTrimWhitespace) { - for (lineNumber = charChange.modifiedStartLineNumber; lineNumber <= charChange.modifiedEndLineNumber; lineNumber++) { + for (let lineNumber = charChange.modifiedStartLineNumber; lineNumber <= charChange.modifiedEndLineNumber; lineNumber++) { + let startColumn: number; + let endColumn: number; if (lineNumber === charChange.modifiedStartLineNumber) { startColumn = charChange.modifiedStartColumn; } else { @@ -1805,14 +1773,11 @@ class InlineViewZonesComputer extends ViewZonesComputer { } _produceModifiedFromDiff(lineChange: editorCommon.ILineChange, lineChangeOriginalLength: number, lineChangeModifiedLength: number): IMyViewZone { - var decorations: InlineDecoration[] = [], - j: number, - lengthJ: number, - charChange: editorCommon.ICharChange; + let decorations: InlineDecoration[] = []; if (lineChange.charChanges) { - for (j = 0, lengthJ = lineChange.charChanges.length; j < lengthJ; j++) { - charChange = lineChange.charChanges[j]; + for (let j = 0, lengthJ = lineChange.charChanges.length; j < lengthJ; j++) { + let charChange = lineChange.charChanges[j]; if (isChangeOrDelete(charChange)) { decorations.push(new InlineDecoration( new Range(charChange.originalStartLineNumber, charChange.originalStartColumn, charChange.originalEndLineNumber, charChange.originalEndColumn), @@ -1822,13 +1787,12 @@ class InlineViewZonesComputer extends ViewZonesComputer { } } - var html: string[] = [], - lineNumber: number; - for (lineNumber = lineChange.originalStartLineNumber; lineNumber <= lineChange.originalEndLineNumber; lineNumber++) { + let html: string[] = []; + for (let lineNumber = lineChange.originalStartLineNumber; lineNumber <= lineChange.originalEndLineNumber; lineNumber++) { html = html.concat(this.renderOriginalLine(lineNumber - lineChange.originalStartLineNumber, this.originalModel, this.modifiedEditorConfiguration, this.modifiedEditorTabSize, lineNumber, decorations)); } - var domNode = document.createElement('div'); + let domNode = document.createElement('div'); domNode.className = 'view-lines line-delete'; domNode.innerHTML = html.join(''); Configuration.applyFontInfoSlow(domNode, this.modifiedEditorConfiguration.fontInfo); @@ -1892,7 +1856,7 @@ function createDecoration(startLineNumber: number, startColumn: number, endLineN } function createFakeLinesDiv(): HTMLElement { - var r = document.createElement('div'); + let r = document.createElement('div'); r.className = 'diagonal-fill'; return r; } diff --git a/src/vs/editor/common/commands/replaceCommand.ts b/src/vs/editor/common/commands/replaceCommand.ts index a31efabe938..e0c8e3ffee2 100644 --- a/src/vs/editor/common/commands/replaceCommand.ts +++ b/src/vs/editor/common/commands/replaceCommand.ts @@ -35,8 +35,8 @@ export class ReplaceCommand implements editorCommon.ICommand { } public computeCursorState(model: editorCommon.ITokenizedModel, helper: editorCommon.ICursorStateComputerData): Selection { - var inverseEditOperations = helper.getInverseEditOperations(); - var srcRange = inverseEditOperations[0].range; + let inverseEditOperations = helper.getInverseEditOperations(); + let srcRange = inverseEditOperations[0].range; return new Selection( srcRange.endLineNumber, srcRange.endColumn, @@ -53,8 +53,8 @@ export class ReplaceCommandWithoutChangingPosition extends ReplaceCommand { } public computeCursorState(model: editorCommon.ITokenizedModel, helper: editorCommon.ICursorStateComputerData): Selection { - var inverseEditOperations = helper.getInverseEditOperations(); - var srcRange = inverseEditOperations[0].range; + let inverseEditOperations = helper.getInverseEditOperations(); + let srcRange = inverseEditOperations[0].range; return new Selection( srcRange.startLineNumber, srcRange.startColumn, @@ -76,8 +76,8 @@ export class ReplaceCommandWithOffsetCursorState extends ReplaceCommand { } public computeCursorState(model: editorCommon.ITokenizedModel, helper: editorCommon.ICursorStateComputerData): Selection { - var inverseEditOperations = helper.getInverseEditOperations(); - var srcRange = inverseEditOperations[0].range; + let inverseEditOperations = helper.getInverseEditOperations(); + let srcRange = inverseEditOperations[0].range; return new Selection( srcRange.endLineNumber + this._lineNumberDeltaOffset, srcRange.endColumn + this._columnDeltaOffset, diff --git a/src/vs/editor/common/commands/shiftCommand.ts b/src/vs/editor/common/commands/shiftCommand.ts index 911f45aad02..bdd732545ab 100644 --- a/src/vs/editor/common/commands/shiftCommand.ts +++ b/src/vs/editor/common/commands/shiftCommand.ts @@ -22,9 +22,9 @@ export class ShiftCommand implements ICommand { public static unshiftIndentCount(line: string, column: number, tabSize: number): number { // Determine the visible column where the content starts - var contentStartVisibleColumn = CursorMoveHelper.visibleColumnFromColumn2(line, column, tabSize); + let contentStartVisibleColumn = CursorMoveHelper.visibleColumnFromColumn2(line, column, tabSize); - var desiredTabStop = CursorMoveHelper.prevTabColumn(contentStartVisibleColumn, tabSize); + let desiredTabStop = CursorMoveHelper.prevTabColumn(contentStartVisibleColumn, tabSize); // The `desiredTabStop` is a multiple of `tabSize` => determine the number of indents return desiredTabStop / tabSize; @@ -32,9 +32,9 @@ export class ShiftCommand implements ICommand { public static shiftIndentCount(line: string, column: number, tabSize: number): number { // Determine the visible column where the content starts - var contentStartVisibleColumn = CursorMoveHelper.visibleColumnFromColumn2(line, column, tabSize); + let contentStartVisibleColumn = CursorMoveHelper.visibleColumnFromColumn2(line, column, tabSize); - var desiredTabStop = CursorMoveHelper.nextTabColumn(contentStartVisibleColumn, tabSize); + let desiredTabStop = CursorMoveHelper.nextTabColumn(contentStartVisibleColumn, tabSize); // The `desiredTabStop` is a multiple of `tabSize` => determine the number of indents return desiredTabStop / tabSize; @@ -154,7 +154,7 @@ export class ShiftCommand implements ICommand { public computeCursorState(model: ITokenizedModel, helper: ICursorStateComputerData): Selection { if (this._useLastEditRangeForCursorEndPosition) { - var lastOp = helper.getInverseEditOperations()[0]; + let lastOp = helper.getInverseEditOperations()[0]; return new Selection(lastOp.range.endLineNumber, lastOp.range.endColumn, lastOp.range.endLineNumber, lastOp.range.endColumn); } return helper.getTrackedSelection(this._selectionId); diff --git a/src/vs/editor/common/commands/surroundSelectionCommand.ts b/src/vs/editor/common/commands/surroundSelectionCommand.ts index 329d7235d78..177e4812a67 100644 --- a/src/vs/editor/common/commands/surroundSelectionCommand.ts +++ b/src/vs/editor/common/commands/surroundSelectionCommand.ts @@ -36,9 +36,9 @@ export class SurroundSelectionCommand implements ICommand { } public computeCursorState(model: ITokenizedModel, helper: ICursorStateComputerData): Selection { - var inverseEditOperations = helper.getInverseEditOperations(); - var firstOperationRange = inverseEditOperations[0].range; - var secondOperationRange = inverseEditOperations[1].range; + let inverseEditOperations = helper.getInverseEditOperations(); + let firstOperationRange = inverseEditOperations[0].range; + let secondOperationRange = inverseEditOperations[1].range; return new Selection( firstOperationRange.endLineNumber, diff --git a/src/vs/editor/common/commands/trimTrailingWhitespaceCommand.ts b/src/vs/editor/common/commands/trimTrailingWhitespaceCommand.ts index 5b412b04c73..5c8026add2b 100644 --- a/src/vs/editor/common/commands/trimTrailingWhitespaceCommand.ts +++ b/src/vs/editor/common/commands/trimTrailingWhitespaceCommand.ts @@ -20,9 +20,9 @@ export class TrimTrailingWhitespaceCommand implements editorCommon.ICommand { } public getEditOperations(model: editorCommon.ITokenizedModel, builder: editorCommon.IEditOperationBuilder): void { - var ops = trimTrailingWhitespace(model, []); - for (var i = 0, len = ops.length; i < len; i++) { - var op = ops[i]; + let ops = trimTrailingWhitespace(model, []); + for (let i = 0, len = ops.length; i < len; i++) { + let op = ops[i]; builder.addEditOperation(op.range, op.text); } @@ -48,29 +48,22 @@ export function trimTrailingWhitespace(model: editorCommon.ITextModel, cursors: }); // Reduce multiple cursors on the same line and only keep the last one on the line - for (var i = cursors.length - 2; i >= 0; i--) { + for (let i = cursors.length - 2; i >= 0; i--) { if (cursors[i].lineNumber === cursors[i + 1].lineNumber) { // Remove cursor at `i` cursors.splice(i, 1); } } - var r: editorCommon.IIdentifiedSingleEditOperation[] = [], - cursorIndex = 0, - cursorLen = cursors.length, - lineNumber: number, - lineCount: number, - lineContent: string, - minEditColumn: number, - maxLineColumn: number, - fromColumn: number, - // toColumn:number, - lastNonWhitespaceIndex: number; + let r: editorCommon.IIdentifiedSingleEditOperation[] = []; + let rLen = 0; + let cursorIndex = 0; + let cursorLen = cursors.length; - for (lineNumber = 1, lineCount = model.getLineCount(); lineNumber <= lineCount; lineNumber++) { - lineContent = model.getLineContent(lineNumber); - maxLineColumn = lineContent.length + 1; - minEditColumn = 0; + for (let lineNumber = 1, lineCount = model.getLineCount(); lineNumber <= lineCount; lineNumber++) { + let lineContent = model.getLineContent(lineNumber); + let maxLineColumn = lineContent.length + 1; + let minEditColumn = 0; if (cursorIndex < cursorLen && cursors[cursorIndex].lineNumber === lineNumber) { minEditColumn = cursors[cursorIndex].column; @@ -85,9 +78,9 @@ export function trimTrailingWhitespace(model: editorCommon.ITextModel, cursors: continue; } - lastNonWhitespaceIndex = strings.lastNonWhitespaceIndex(lineContent); + let lastNonWhitespaceIndex = strings.lastNonWhitespaceIndex(lineContent); - fromColumn = 0; + let fromColumn = 0; if (lastNonWhitespaceIndex === -1) { // Entire line is whitespace fromColumn = 1; @@ -100,11 +93,11 @@ export function trimTrailingWhitespace(model: editorCommon.ITextModel, cursors: } fromColumn = Math.max(minEditColumn, fromColumn); - r.push(EditOperation.delete(new Range( + r[rLen++] = EditOperation.delete(new Range( lineNumber, fromColumn, lineNumber, maxLineColumn - ))); + )); } return r; -} \ No newline at end of file +} diff --git a/src/vs/editor/common/commonCodeEditor.ts b/src/vs/editor/common/commonCodeEditor.ts index 57d9d014c75..48833c7ed4d 100644 --- a/src/vs/editor/common/commonCodeEditor.ts +++ b/src/vs/editor/common/commonCodeEditor.ts @@ -33,7 +33,7 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import EditorContextKeys = editorCommon.EditorContextKeys; -var EDITOR_ID = 0; +let EDITOR_ID = 0; export abstract class CommonCodeEditor extends EventEmitter implements editorCommon.ICommonCodeEditor { diff --git a/src/vs/editor/common/config/commonEditorConfig.ts b/src/vs/editor/common/config/commonEditorConfig.ts index bd486c11379..f7806f161f7 100644 --- a/src/vs/editor/common/config/commonEditorConfig.ts +++ b/src/vs/editor/common/config/commonEditorConfig.ts @@ -338,7 +338,7 @@ class InternalEditorOptionsHelper { private static _sanitizeScrollbarOpts(raw: editorCommon.IEditorScrollbarOptions, mouseWheelScrollSensitivity: number): editorCommon.InternalEditorScrollbarOptions { - var visibilityFromString = (visibility: string) => { + let visibilityFromString = (visibility: string) => { switch (visibility) { case 'hidden': return ScrollbarVisibility.Hidden; diff --git a/src/vs/editor/common/controller/cursor.ts b/src/vs/editor/common/controller/cursor.ts index c480ae8bf5e..569a9a272d3 100644 --- a/src/vs/editor/common/controller/cursor.ts +++ b/src/vs/editor/common/controller/cursor.ts @@ -1052,7 +1052,7 @@ export class Cursor extends EventEmitter { if (sorted) { cursors = cursors.sort((a, b) => { - return Range.compareRangesUsingStarts(a.getSelection(), b.getSelection()); + return Range.compareRangesUsingStarts(a.modelState.selection, b.modelState.selection); }); } @@ -1112,7 +1112,7 @@ export class Cursor extends EventEmitter { private _getColumnSelectToLineNumber(): number { if (!this._columnSelectToLineNumber) { let primaryCursor = this.cursors.getAll()[0]; - let primaryPos = primaryCursor.getViewPosition(); + let primaryPos = primaryCursor.viewState.position; return primaryPos.lineNumber; } return this._columnSelectToLineNumber; @@ -1122,7 +1122,7 @@ export class Cursor extends EventEmitter { private _getColumnSelectToVisualColumn(): number { if (!this._columnSelectToVisualColumn) { let primaryCursor = this.cursors.getAll()[0]; - let primaryPos = primaryCursor.getViewPosition(); + let primaryPos = primaryCursor.viewState.position; return primaryCursor.getViewVisibleColumnFromColumn(primaryPos.lineNumber, primaryPos.column); } return this._columnSelectToVisualColumn; diff --git a/src/vs/editor/common/controller/cursorCollection.ts b/src/vs/editor/common/controller/cursorCollection.ts index 4600518b41d..71870abb0e6 100644 --- a/src/vs/editor/common/controller/cursorCollection.ts +++ b/src/vs/editor/common/controller/cursorCollection.ts @@ -8,7 +8,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { IModeConfiguration, IOneCursorState, IViewModelHelper, OneCursor } from 'vs/editor/common/controller/oneCursor'; import { Selection } from 'vs/editor/common/core/selection'; import { IConfiguration, IModel, ISelection } from 'vs/editor/common/editorCommon'; -import { IAutoClosingPair } from 'vs/editor/common/modes'; +import { IAutoClosingPair } from 'vs/editor/common/modes/languageConfiguration'; import { Position } from 'vs/editor/common/core/position'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; @@ -82,78 +82,71 @@ export class CursorCollection { public getPosition(index: number): Position { if (index === 0) { - return this.primaryCursor.getPosition(); + return this.primaryCursor.modelState.position; } else { - return this.secondaryCursors[index - 1].getPosition(); + return this.secondaryCursors[index - 1].modelState.position; } } public getViewPosition(index: number): Position { if (index === 0) { - return this.primaryCursor.getViewPosition(); + return this.primaryCursor.viewState.position; } else { - return this.secondaryCursors[index - 1].getViewPosition(); + return this.secondaryCursors[index - 1].viewState.position; } } public getPositions(): Position[] { var result: Position[] = []; - result.push(this.primaryCursor.getPosition()); + result.push(this.primaryCursor.modelState.position); for (var i = 0, len = this.secondaryCursors.length; i < len; i++) { - result.push(this.secondaryCursors[i].getPosition()); + result.push(this.secondaryCursors[i].modelState.position); } return result; } public getViewPositions(): Position[] { var result: Position[] = []; - result.push(this.primaryCursor.getViewPosition()); + result.push(this.primaryCursor.viewState.position); for (var i = 0, len = this.secondaryCursors.length; i < len; i++) { - result.push(this.secondaryCursors[i].getViewPosition()); + result.push(this.secondaryCursors[i].viewState.position); } return result; } public getSelection(index: number): Selection { if (index === 0) { - return this.primaryCursor.getSelection(); + return this.primaryCursor.modelState.selection; } else { - return this.secondaryCursors[index - 1].getSelection(); + return this.secondaryCursors[index - 1].modelState.selection; } } public getSelections(): Selection[] { var result: Selection[] = []; - result.push(this.primaryCursor.getSelection()); + result.push(this.primaryCursor.modelState.selection); for (var i = 0, len = this.secondaryCursors.length; i < len; i++) { - result.push(this.secondaryCursors[i].getSelection()); + result.push(this.secondaryCursors[i].modelState.selection); } return result; } public getViewSelections(): Selection[] { var result: Selection[] = []; - result.push(this.primaryCursor.getViewSelection()); + result.push(this.primaryCursor.viewState.selection); for (var i = 0, len = this.secondaryCursors.length; i < len; i++) { - result.push(this.secondaryCursors[i].getViewSelection()); + result.push(this.secondaryCursors[i].viewState.selection); } return result; } public setSelections(selections: ISelection[], viewSelections?: ISelection[]): void { - this.primaryCursor.setSelection(selections[0]); - this._setSecondarySelections(selections.slice(1)); - - if (viewSelections) { - this.primaryCursor.setViewSelection(viewSelections[0]); - for (let i = 0; i < this.secondaryCursors.length; i++) { - this.secondaryCursors[i].setViewSelection(viewSelections[i + 1]); - } - } + this.primaryCursor.setSelection(selections[0], viewSelections ? viewSelections[0] : null); + this._setSecondarySelections(selections.slice(1), viewSelections ? viewSelections.slice(1) : null); } public killSecondaryCursors(): boolean { - return (this._setSecondarySelections([]) > 0); + return (this._setSecondarySelections([], []) > 0); } public normalize(): void { @@ -200,7 +193,7 @@ export class CursorCollection { * - a negative number indicates the number of secondary cursors removed * - 0 indicates that no changes have been done to the secondary cursors list */ - private _setSecondarySelections(secondarySelections: ISelection[]): number { + private _setSecondarySelections(secondarySelections: ISelection[], viewSelections: ISelection[]): number { var secondaryCursorsLength = this.secondaryCursors.length; var secondarySelectionsLength = secondarySelections.length; var returnValue = secondarySelectionsLength - secondaryCursorsLength; @@ -219,7 +212,7 @@ export class CursorCollection { for (var i = 0; i < secondarySelectionsLength; i++) { if (secondarySelections[i]) { - this.secondaryCursors[i].setSelection(secondarySelections[i]); + this.secondaryCursors[i].setSelection(secondarySelections[i], viewSelections ? viewSelections[i] : null); } } @@ -247,8 +240,8 @@ export class CursorCollection { for (var i = 0; i < cursors.length; i++) { sortedCursors.push({ index: i, - selection: cursors[i].getSelection(), - viewSelection: cursors[i].getViewSelection() + selection: cursors[i].modelState.selection, + viewSelection: cursors[i].viewState.selection }); } @@ -328,52 +321,46 @@ export class CursorCollection { }; - let electricCharSupport = LanguageConfigurationRegistry.getElectricCharacterSupport(this.model.getMode().getId()); - if (electricCharSupport) { - let electricChars: string[] = null; - try { - electricChars = electricCharSupport.getElectricCharacters(); - } catch (e) { - onUnexpectedError(e); - electricChars = null; - } - if (electricChars) { - for (i = 0; i < electricChars.length; i++) { - result.electricChars[electricChars[i]] = true; - } + let electricChars: string[] = null; + try { + electricChars = LanguageConfigurationRegistry.getElectricCharacters(this.model.getMode().getId()); + } catch (e) { + onUnexpectedError(e); + electricChars = null; + } + if (electricChars) { + for (i = 0; i < electricChars.length; i++) { + result.electricChars[electricChars[i]] = true; } } - let characterPairSupport = LanguageConfigurationRegistry.getCharacterPairSupport(this.model.getMode().getId()); - if (characterPairSupport) { - let autoClosingPairs: IAutoClosingPair[]; - try { - autoClosingPairs = characterPairSupport.getAutoClosingPairs(); - } catch (e) { - onUnexpectedError(e); - autoClosingPairs = null; - } - if (autoClosingPairs) { - for (i = 0; i < autoClosingPairs.length; i++) { - result.autoClosingPairsOpen[autoClosingPairs[i].open] = autoClosingPairs[i].close; - result.autoClosingPairsClose[autoClosingPairs[i].close] = autoClosingPairs[i].open; - } + let autoClosingPairs: IAutoClosingPair[]; + try { + autoClosingPairs = LanguageConfigurationRegistry.getAutoClosingPairs(this.model.getMode().getId()); + } catch (e) { + onUnexpectedError(e); + autoClosingPairs = null; + } + if (autoClosingPairs) { + for (i = 0; i < autoClosingPairs.length; i++) { + result.autoClosingPairsOpen[autoClosingPairs[i].open] = autoClosingPairs[i].close; + result.autoClosingPairsClose[autoClosingPairs[i].close] = autoClosingPairs[i].open; } + } - let surroundingPairs: IAutoClosingPair[]; - try { - surroundingPairs = characterPairSupport.getSurroundingPairs(); - } catch (e) { - onUnexpectedError(e); - surroundingPairs = null; - } - if (surroundingPairs) { - for (i = 0; i < surroundingPairs.length; i++) { - result.surroundingPairs[surroundingPairs[i].open] = surroundingPairs[i].close; - } + let surroundingPairs: IAutoClosingPair[]; + try { + surroundingPairs = LanguageConfigurationRegistry.getSurroundingPairs(this.model.getMode().getId()); + } catch (e) { + onUnexpectedError(e); + surroundingPairs = null; + } + if (surroundingPairs) { + for (i = 0; i < surroundingPairs.length; i++) { + result.surroundingPairs[surroundingPairs[i].open] = surroundingPairs[i].close; } } return result; } -} \ No newline at end of file +} diff --git a/src/vs/editor/common/controller/cursorMoveHelper.ts b/src/vs/editor/common/controller/cursorMoveHelper.ts index 779a345b759..d0b99bc11f9 100644 --- a/src/vs/editor/common/controller/cursorMoveHelper.ts +++ b/src/vs/editor/common/controller/cursorMoveHelper.ts @@ -4,16 +4,238 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { IPosition } from 'vs/editor/common/editorCommon'; import { Selection } from 'vs/editor/common/core/selection'; +import { CharCode } from 'vs/base/common/charCode'; import * as strings from 'vs/base/common/strings'; -export interface IMoveResult { - lineNumber: number; - column: number; - leftoverVisibleColumns: number; +export class CursorMoveConfiguration { + _cursorMoveConfigurationBrand: void; + + public readonly tabSize: number; + public readonly pageSize: number; + + constructor( + tabSize: number, + pageSize: number + ) { + this.tabSize = tabSize; + this.pageSize = pageSize; + } } +export interface ICursorMoveHelperModel { + getLineCount(): number; + getLineContent(lineNumber: number): string; + getLineMinColumn(lineNumber: number): number; + getLineMaxColumn(lineNumber: number): number; + getLineFirstNonWhitespaceColumn(lineNumber: number): number; + getLineLastNonWhitespaceColumn(lineNumber: number): number; +} + +export class CursorMoveResult { + _cursorMoveResultBrand: void; + + public readonly lineNumber: number; + public readonly column: number; + public readonly leftoverVisibleColumns: number; + + constructor(lineNumber: number, column: number, leftoverVisibleColumns: number) { + this.lineNumber = lineNumber; + this.column = column; + this.leftoverVisibleColumns = leftoverVisibleColumns; + } +} + +/** + * Common operations that work and make sense both on the model and on the view model. + */ +export class CursorMove { + + private static _isLowSurrogate(model: ICursorMoveHelperModel, lineNumber: number, charOffset: number): boolean { + let lineContent = model.getLineContent(lineNumber); + if (charOffset < 0 || charOffset >= lineContent.length) { + return false; + } + return strings.isLowSurrogate(lineContent.charCodeAt(charOffset)); + } + + private static _isHighSurrogate(model: ICursorMoveHelperModel, lineNumber: number, charOffset: number): boolean { + let lineContent = model.getLineContent(lineNumber); + if (charOffset < 0 || charOffset >= lineContent.length) { + return false; + } + return strings.isHighSurrogate(lineContent.charCodeAt(charOffset)); + } + + private static _isInsideSurrogatePair(model: ICursorMoveHelperModel, lineNumber: number, column: number): boolean { + return this._isHighSurrogate(model, lineNumber, column - 2); + } + + public static left(config: CursorMoveConfiguration, model: ICursorMoveHelperModel, lineNumber: number, column: number): CursorMoveResult { + + if (column > model.getLineMinColumn(lineNumber)) { + if (this._isLowSurrogate(model, lineNumber, column - 2)) { + // character before column is a low surrogate + column = column - 2; + } else { + column = column - 1; + } + } else if (lineNumber > 1) { + lineNumber = lineNumber - 1; + column = model.getLineMaxColumn(lineNumber); + } + + return new CursorMoveResult(lineNumber, column, 0); + } + + public static right(config: CursorMoveConfiguration, model: ICursorMoveHelperModel, lineNumber: number, column: number): CursorMoveResult { + + if (column < model.getLineMaxColumn(lineNumber)) { + if (this._isHighSurrogate(model, lineNumber, column - 1)) { + // character after column is a high surrogate + column = column + 2; + } else { + column = column + 1; + } + } else if (lineNumber < model.getLineCount()) { + lineNumber = lineNumber + 1; + column = model.getLineMinColumn(lineNumber); + } + + return new CursorMoveResult(lineNumber, column, 0); + } + + public static up(config: CursorMoveConfiguration, model: ICursorMoveHelperModel, lineNumber: number, column: number, leftoverVisibleColumns: number, count: number, allowMoveOnFirstLine: boolean): CursorMoveResult { + const currentVisibleColumn = this.visibleColumnFromColumn(model.getLineContent(lineNumber), column, config.tabSize) + leftoverVisibleColumns; + + lineNumber = lineNumber - count; + if (lineNumber < 1) { + lineNumber = 1; + if (allowMoveOnFirstLine) { + column = model.getLineMinColumn(lineNumber); + } else { + column = Math.min(model.getLineMaxColumn(lineNumber), column); + if (this._isInsideSurrogatePair(model, lineNumber, column)) { + column = column - 1; + } + } + } else { + column = this.columnFromVisibleColumn(config, model, lineNumber, currentVisibleColumn); + if (this._isInsideSurrogatePair(model, lineNumber, column)) { + column = column - 1; + } + } + + leftoverVisibleColumns = currentVisibleColumn - this.visibleColumnFromColumn(model.getLineContent(lineNumber), column, config.tabSize); + + return new CursorMoveResult(lineNumber, column, leftoverVisibleColumns); + } + + public static down(config: CursorMoveConfiguration, model: ICursorMoveHelperModel, lineNumber: number, column: number, leftoverVisibleColumns: number, count: number, allowMoveOnLastLine: boolean): CursorMoveResult { + const currentVisibleColumn = this.visibleColumnFromColumn(model.getLineContent(lineNumber), column, config.tabSize) + leftoverVisibleColumns; + + lineNumber = lineNumber + count; + var lineCount = model.getLineCount(); + if (lineNumber > lineCount) { + lineNumber = lineCount; + if (allowMoveOnLastLine) { + column = model.getLineMaxColumn(lineNumber); + } else { + column = Math.min(model.getLineMaxColumn(lineNumber), column); + if (this._isInsideSurrogatePair(model, lineNumber, column)) { + column = column - 1; + } + } + } else { + column = this.columnFromVisibleColumn(config, model, lineNumber, currentVisibleColumn); + if (this._isInsideSurrogatePair(model, lineNumber, column)) { + column = column - 1; + } + } + + leftoverVisibleColumns = currentVisibleColumn - this.visibleColumnFromColumn(model.getLineContent(lineNumber), column, config.tabSize); + + return new CursorMoveResult(lineNumber, column, leftoverVisibleColumns); + } + + public static visibleColumnFromColumn(lineContent: string, column: number, tabSize: number): number { + let endOffset = lineContent.length; + if (endOffset > column - 1) { + endOffset = column - 1; + } + + let result = 0; + for (let i = 0; i < endOffset; i++) { + let charCode = lineContent.charCodeAt(i); + if (charCode === CharCode.Tab) { + result = this.nextTabStop(result, tabSize); + } else { + result = result + 1; + } + } + return result; + } + + private static _columnFromVisibleColumn(lineContent: string, visibleColumn: number, tabSize: number): number { + if (visibleColumn <= 0) { + return 1; + } + + const lineLength = lineContent.length; + + let beforeVisibleColumn = 0; + for (let i = 0; i < lineLength; i++) { + let charCode = lineContent.charCodeAt(i); + + let afterVisibleColumn: number; + if (charCode === CharCode.Tab) { + afterVisibleColumn = this.nextTabStop(beforeVisibleColumn, tabSize); + } else { + afterVisibleColumn = beforeVisibleColumn + 1; + } + + if (afterVisibleColumn >= visibleColumn) { + let prevDelta = visibleColumn - beforeVisibleColumn; + let afterDelta = afterVisibleColumn - visibleColumn; + if (afterDelta < prevDelta) { + return i + 2; + } else { + return i + 1; + } + } + + beforeVisibleColumn = afterVisibleColumn; + } + + // walked the entire string + return lineLength + 1; + } + + public static columnFromVisibleColumn(config: CursorMoveConfiguration, model: ICursorMoveHelperModel, lineNumber: number, visibleColumn: number): number { + let result = this._columnFromVisibleColumn(model.getLineContent(lineNumber), visibleColumn, config.tabSize); + + let minColumn = model.getLineMinColumn(lineNumber); + if (result < minColumn) { + return minColumn; + } + + let maxColumn = model.getLineMaxColumn(lineNumber); + if (result > maxColumn) { + return maxColumn; + } + + return result; + } + + /** + * ATTENTION: This works with 0-based columns (as oposed to the regular 1-based columns) + */ + public static nextTabStop(visibleColumn: number, tabSize: number): number { + return visibleColumn + tabSize - visibleColumn % tabSize; + } +} + + export interface IViewColumnSelectResult { viewSelections: Selection[]; reversed: boolean; @@ -24,125 +246,13 @@ export interface IColumnSelectResult extends IViewColumnSelectResult { toVisualColumn: number; } -export interface ICursorMoveHelperModel { - getLineCount(): number; - getLineFirstNonWhitespaceColumn(lineNumber: number): number; - getLineMinColumn(lineNumber: number): number; - getLineMaxColumn(lineNumber: number): number; - getLineLastNonWhitespaceColumn(lineNumber: number): number; - getLineContent(lineNumber: number): string; -} - -/** - * Internal indentation options (computed) for the editor. - */ -export interface IInternalIndentationOptions { - /** - * Tab size in spaces. This is used for rendering and for editing. - */ - tabSize: number; - /** - * Insert spaces instead of tabs when indenting or when auto-indenting. - */ - insertSpaces: boolean; -} - -export interface IConfiguration { - getIndentationOptions(): IInternalIndentationOptions; -} - -function isHighSurrogate(model: ICursorMoveHelperModel, lineNumber: number, column: number) { - return strings.isHighSurrogate(model.getLineContent(lineNumber).charCodeAt(column - 1)); -} - -function isLowSurrogate(model: ICursorMoveHelperModel, lineNumber: number, column: number) { - return strings.isLowSurrogate(model.getLineContent(lineNumber).charCodeAt(column - 1)); -} export class CursorMoveHelper { - private configuration: IConfiguration; + private readonly _config: CursorMoveConfiguration; - constructor(configuration: IConfiguration) { - this.configuration = configuration; - } - - public getLeftOfPosition(model: ICursorMoveHelperModel, lineNumber: number, column: number): IPosition { - - if (column > model.getLineMinColumn(lineNumber)) { - column = column - (isLowSurrogate(model, lineNumber, column - 1) ? 2 : 1); - } else if (lineNumber > 1) { - lineNumber = lineNumber - 1; - column = model.getLineMaxColumn(lineNumber); - } - - return { - lineNumber: lineNumber, - column: column - }; - } - - public getRightOfPosition(model: ICursorMoveHelperModel, lineNumber: number, column: number): IPosition { - - if (column < model.getLineMaxColumn(lineNumber)) { - column = column + (isHighSurrogate(model, lineNumber, column) ? 2 : 1); - } else if (lineNumber < model.getLineCount()) { - lineNumber = lineNumber + 1; - column = model.getLineMinColumn(lineNumber); - } - - return { - lineNumber: lineNumber, - column: column - }; - } - - public getPositionUp(model: ICursorMoveHelperModel, lineNumber: number, column: number, leftoverVisibleColumns: number, count: number, allowMoveOnFirstLine: boolean): IMoveResult { - var currentVisibleColumn = this.visibleColumnFromColumn(model, lineNumber, column) + leftoverVisibleColumns; - - lineNumber = lineNumber - count; - if (lineNumber < 1) { - lineNumber = 1; - if (allowMoveOnFirstLine) { - column = model.getLineMinColumn(lineNumber); - } else { - column = Math.min(model.getLineMaxColumn(lineNumber), column); - } - } else { - column = this.columnFromVisibleColumn(model, lineNumber, currentVisibleColumn); - } - leftoverVisibleColumns = currentVisibleColumn - this.visibleColumnFromColumn(model, lineNumber, column); - - - return { - lineNumber: lineNumber, - column: column, - leftoverVisibleColumns: leftoverVisibleColumns - }; - } - - public getPositionDown(model: ICursorMoveHelperModel, lineNumber: number, column: number, leftoverVisibleColumns: number, count: number, allowMoveOnLastLine: boolean): IMoveResult { - var currentVisibleColumn = this.visibleColumnFromColumn(model, lineNumber, column) + leftoverVisibleColumns; - - lineNumber = lineNumber + count; - var lineCount = model.getLineCount(); - if (lineNumber > lineCount) { - lineNumber = lineCount; - if (allowMoveOnLastLine) { - column = model.getLineMaxColumn(lineNumber); - } else { - column = Math.min(model.getLineMaxColumn(lineNumber), column); - } - } else { - column = this.columnFromVisibleColumn(model, lineNumber, currentVisibleColumn); - } - leftoverVisibleColumns = currentVisibleColumn - this.visibleColumnFromColumn(model, lineNumber, column); - - return { - lineNumber: lineNumber, - column: column, - leftoverVisibleColumns: leftoverVisibleColumns - }; + constructor(config: CursorMoveConfiguration) { + this._config = config; } public columnSelect(model: ICursorMoveHelperModel, fromLineNumber: number, fromVisibleColumn: number, toLineNumber: number, toVisibleColumn: number): IViewColumnSelectResult { @@ -219,7 +329,7 @@ export class CursorMoveHelper { } public visibleColumnFromColumn(model: ICursorMoveHelperModel, lineNumber: number, column: number): number { - return CursorMoveHelper.visibleColumnFromColumn(model, lineNumber, column, this.configuration.getIndentationOptions().tabSize); + return CursorMoveHelper.visibleColumnFromColumn(model, lineNumber, column, this._config.tabSize); } public static visibleColumnFromColumn(model: ICursorMoveHelperModel, lineNumber: number, column: number, tabSize: number): number { @@ -227,47 +337,18 @@ export class CursorMoveHelper { } public static visibleColumnFromColumn2(line: string, column: number, tabSize: number): number { - var result = 0; - for (var i = 0; i < column - 1; i++) { - result = (line.charAt(i) === '\t') ? CursorMoveHelper.nextTabColumn(result, tabSize) : result + 1; - } - return result; + return CursorMove.visibleColumnFromColumn(line, column, tabSize); } public columnFromVisibleColumn(model: ICursorMoveHelperModel, lineNumber: number, visibleColumn: number): number { - var line = model.getLineContent(lineNumber); - - var lastVisibleColumn = -1; - var thisVisibleColumn = 0; - - for (var i = 0; i < line.length && thisVisibleColumn <= visibleColumn; i++) { - lastVisibleColumn = thisVisibleColumn; - thisVisibleColumn = (line.charAt(i) === '\t') ? CursorMoveHelper.nextTabColumn(thisVisibleColumn, this.configuration.getIndentationOptions().tabSize) : thisVisibleColumn + 1; - } - - // Choose the closest - thisVisibleColumn = Math.abs(visibleColumn - thisVisibleColumn); - lastVisibleColumn = Math.abs(visibleColumn - lastVisibleColumn); - - var result: number; - if (thisVisibleColumn < lastVisibleColumn) { - result = i + 1; - } else { - result = i; - } - - var minColumn = model.getLineMinColumn(lineNumber); - if (result < minColumn) { - result = minColumn; - } - return result; + return CursorMove.columnFromVisibleColumn(this._config, model, lineNumber, visibleColumn); } /** * ATTENTION: This works with 0-based columns (as oposed to the regular 1-based columns) */ public static nextTabColumn(column: number, tabSize: number): number { - return column + tabSize - column % tabSize; + return CursorMove.nextTabStop(column, tabSize); } /** diff --git a/src/vs/editor/common/controller/oneCursor.ts b/src/vs/editor/common/controller/oneCursor.ts index 1cdd4a22683..d2ad9252588 100644 --- a/src/vs/editor/common/controller/oneCursor.ts +++ b/src/vs/editor/common/controller/oneCursor.ts @@ -9,15 +9,17 @@ import * as strings from 'vs/base/common/strings'; import { ReplaceCommand, ReplaceCommandWithOffsetCursorState, ReplaceCommandWithoutChangingPosition } from 'vs/editor/common/commands/replaceCommand'; import { ShiftCommand } from 'vs/editor/common/commands/shiftCommand'; import { SurroundSelectionCommand } from 'vs/editor/common/commands/surroundSelectionCommand'; -import { CursorMoveHelper, ICursorMoveHelperModel, IMoveResult, IColumnSelectResult, IViewColumnSelectResult } from 'vs/editor/common/controller/cursorMoveHelper'; +import { CursorMoveHelper, CursorMove, CursorMoveConfiguration, ICursorMoveHelperModel, IColumnSelectResult, IViewColumnSelectResult } from 'vs/editor/common/controller/cursorMoveHelper'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection, SelectionDirection } from 'vs/editor/common/core/selection'; import * as editorCommon from 'vs/editor/common/editorCommon'; -import { IElectricAction, IndentAction } from 'vs/editor/common/modes'; +import { IndentAction } from 'vs/editor/common/modes/languageConfiguration'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { CharCode } from 'vs/base/common/charCode'; import { CharacterClassifier } from 'vs/editor/common/core/characterClassifier'; +import { IElectricAction } from 'vs/editor/common/modes/supports/electricCharacter'; +import { IDisposable } from 'vs/base/common/lifecycle'; export interface IPostOperationRunnable { (ctx: IOneCursorOperationContext): void; @@ -118,167 +120,80 @@ export const enum WordNavigationType { WordEnd = 1 } -export class OneCursor { - - // --- contextual state - private editorId: number; - public model: editorCommon.IModel; - public configuration: editorCommon.IConfiguration; - public modeConfiguration: IModeConfiguration; - private helper: CursorHelper; - private viewModelHelper: IViewModelHelper; +/** + * Represents the cursor state on either the model or on the view model. + */ +export class CursorModelState { + _cursorModelStateBrand: void; // --- selection can start as a range (think double click and drag) - private selectionStart: Range; - private viewSelectionStart: Range; - private selectionStartLeftoverVisibleColumns: number; - - // --- position - private position: Position; - private viewPosition: Position; - private leftoverVisibleColumns: number; - - // --- bracket match decorations - private bracketDecorations: string[]; - - // --- computed properties - private _cachedSelection: Selection; - private _cachedViewSelection: Selection; - private _selStartMarker: string; - private _selEndMarker: string; - private _selDirection: SelectionDirection; + public readonly selectionStart: Range; + public readonly selectionStartLeftoverVisibleColumns: number; + public readonly position: Position; + public readonly leftoverVisibleColumns: number; + public readonly selection: Selection; constructor( - editorId: number, - model: editorCommon.IModel, - configuration: editorCommon.IConfiguration, - modeConfiguration: IModeConfiguration, - viewModelHelper: IViewModelHelper + selectionStart: Range, + selectionStartLeftoverVisibleColumns: number, + position: Position, + leftoverVisibleColumns: number, ) { - this.editorId = editorId; - this.model = model; - this.configuration = configuration; - this.modeConfiguration = modeConfiguration; - this.viewModelHelper = viewModelHelper; - this.helper = new CursorHelper(this.model, this.configuration); - - this.bracketDecorations = []; - - this._set( - new Range(1, 1, 1, 1), 0, - new Position(1, 1), 0, - new Range(1, 1, 1, 1), new Position(1, 1) - ); - } - - private _set( - selectionStart: Range, selectionStartLeftoverVisibleColumns: number, - position: Position, leftoverVisibleColumns: number, - viewSelectionStart: Range, viewPosition: Position - ): void { this.selectionStart = selectionStart; this.selectionStartLeftoverVisibleColumns = selectionStartLeftoverVisibleColumns; - this.position = position; this.leftoverVisibleColumns = leftoverVisibleColumns; - - this.viewSelectionStart = viewSelectionStart; - this.viewPosition = viewPosition; - - this._cachedSelection = OneCursor.computeSelection(this.selectionStart, this.position); - this._cachedViewSelection = OneCursor.computeSelection(this.viewSelectionStart, this.viewPosition); - - this._selStartMarker = this._ensureMarker(this._selStartMarker, this._cachedSelection.startLineNumber, this._cachedSelection.startColumn, true); - this._selEndMarker = this._ensureMarker(this._selEndMarker, this._cachedSelection.endLineNumber, this._cachedSelection.endColumn, false); - this._selDirection = this._cachedSelection.getDirection(); + this.selection = CursorModelState._computeSelection(this.selectionStart, this.position); } - private _ensureMarker(markerId: string, lineNumber: number, column: number, stickToPreviousCharacter: boolean): string { - if (!markerId) { - return this.model._addMarker(lineNumber, column, stickToPreviousCharacter); - } else { - this.model._changeMarker(markerId, lineNumber, column); - this.model._changeMarkerStickiness(markerId, stickToPreviousCharacter); - return markerId; - } - } - - public saveState(): IOneCursorState { - return { - selectionStart: this.selectionStart, - viewSelectionStart: this.viewSelectionStart, - position: this.position, - viewPosition: this.viewPosition, - leftoverVisibleColumns: this.leftoverVisibleColumns, - selectionStartLeftoverVisibleColumns: this.selectionStartLeftoverVisibleColumns - }; - } - - public restoreState(state: IOneCursorState): void { - let position = this.model.validatePosition(state.position); - let selectionStart: Range; - if (state.selectionStart) { - selectionStart = this.model.validateRange(state.selectionStart); - } else { - selectionStart = new Range(position.lineNumber, position.column, position.lineNumber, position.column); - } - - let viewPosition = this.viewModelHelper.validateViewPosition(state.viewPosition.lineNumber, state.viewPosition.column, position); - let viewSelectionStart: Range; - if (state.viewSelectionStart) { - viewSelectionStart = this.viewModelHelper.validateViewRange(state.viewSelectionStart.startLineNumber, state.viewSelectionStart.startColumn, state.viewSelectionStart.endLineNumber, state.viewSelectionStart.endColumn, selectionStart); - } else { - viewSelectionStart = this.viewModelHelper.convertModelRangeToViewRange(selectionStart); - } - - this._set( - selectionStart, state.selectionStartLeftoverVisibleColumns, - position, state.leftoverVisibleColumns, - viewSelectionStart, viewPosition + public withSelectionStartLeftoverVisibleColumns(selectionStartLeftoverVisibleColumns: number): CursorModelState { + return new CursorModelState( + this.selectionStart, + selectionStartLeftoverVisibleColumns, + this.position, + this.leftoverVisibleColumns ); } - public updateModeConfiguration(modeConfiguration: IModeConfiguration): void { - this.modeConfiguration = modeConfiguration; - } - - public duplicate(): OneCursor { - let result = new OneCursor(this.editorId, this.model, this.configuration, this.modeConfiguration, this.viewModelHelper); - result._set( - this.selectionStart, this.selectionStartLeftoverVisibleColumns, - this.position, this.leftoverVisibleColumns, - this.viewSelectionStart, this.viewPosition + public withSelectionStart(selectionStart: Range): CursorModelState { + return new CursorModelState( + selectionStart, + 0, + this.position, + this.leftoverVisibleColumns ); - return result; } - public dispose(): void { - this.model._removeMarker(this._selStartMarker); - this.model._removeMarker(this._selEndMarker); - this.bracketDecorations = this.model.deltaDecorations(this.bracketDecorations, [], this.editorId); + public collapse(): CursorModelState { + return new CursorModelState( + new Range(this.position.lineNumber, this.position.column, this.position.lineNumber, this.position.column), + 0, + this.position, + 0 + ); } - public adjustBracketDecorations(): void { - let bracketMatch: [Range, Range] = null; - let selection = this.getSelection(); - if (selection.isEmpty()) { - bracketMatch = this.model.matchBracket(this.position); + public move(inSelectionMode: boolean, position: Position, leftoverVisibleColumns: number): CursorModelState { + if (inSelectionMode) { + // move just position + return new CursorModelState( + this.selectionStart, + this.selectionStartLeftoverVisibleColumns, + position, + leftoverVisibleColumns + ); + } else { + // move everything + return new CursorModelState( + new Range(position.lineNumber, position.column, position.lineNumber, position.column), + leftoverVisibleColumns, + position, + leftoverVisibleColumns + ); } - - let newDecorations: editorCommon.IModelDeltaDecoration[] = []; - if (bracketMatch) { - let options: editorCommon.IModelDecorationOptions = { - stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, - className: 'bracket-match' - }; - newDecorations.push({ range: bracketMatch[0], options: options }); - newDecorations.push({ range: bracketMatch[1], options: options }); - } - - this.bracketDecorations = this.model.deltaDecorations(this.bracketDecorations, newDecorations, this.editorId); } - private static computeSelection(selectionStart: Range, position: Position): Selection { + private static _computeSelection(selectionStart: Range, position: Position): Selection { let startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number; if (selectionStart.isEmpty()) { startLineNumber = selectionStart.startLineNumber; @@ -305,56 +220,216 @@ export class OneCursor { endColumn ); } +} - public setSelection(desiredSelection: editorCommon.ISelection): void { - let position = this.model.validatePosition({ - lineNumber: desiredSelection.positionLineNumber, - column: desiredSelection.positionColumn +export interface IOneCursor { + readonly modelState: CursorModelState; + readonly viewState: CursorModelState; + readonly config: CursorMoveConfiguration; +} + +export class OneCursor implements IOneCursor { + + // --- contextual state + private readonly editorId: number; + public readonly model: editorCommon.IModel; + public readonly viewModel: ICursorMoveHelperModel; + public readonly configuration: editorCommon.IConfiguration; + private readonly viewModelHelper: IViewModelHelper; + + private readonly _modelOptionsListener: IDisposable; + private readonly _configChangeListener: IDisposable; + + public modeConfiguration: IModeConfiguration; + private helper: CursorHelper; + public config: CursorMoveConfiguration; + + public modelState: CursorModelState; + public viewState: CursorModelState; + + // --- bracket match decorations + private bracketDecorations: string[]; + + // --- computed properties + private _selStartMarker: string; + private _selEndMarker: string; + + constructor( + editorId: number, + model: editorCommon.IModel, + configuration: editorCommon.IConfiguration, + modeConfiguration: IModeConfiguration, + viewModelHelper: IViewModelHelper + ) { + this.editorId = editorId; + this.model = model; + this.configuration = configuration; + this.modeConfiguration = modeConfiguration; + this.viewModelHelper = viewModelHelper; + this.viewModel = viewModelHelper.viewModel; + + this._recreateCursorHelper(); + + this._modelOptionsListener = model.onDidChangeOptions(() => this._recreateCursorHelper()); + + this._configChangeListener = this.configuration.onDidChange((e) => { + if (e.layoutInfo) { + // due to pageSize + this._recreateCursorHelper(); + } }); - let selectionStartPosition = this.model.validatePosition({ - lineNumber: desiredSelection.selectionStartLineNumber, - column: desiredSelection.selectionStartColumn - }); - let selectionStart = new Range(selectionStartPosition.lineNumber, selectionStartPosition.column, selectionStartPosition.lineNumber, selectionStartPosition.column); - let viewPosition = this.viewModelHelper.convertModelPositionToViewPosition(position.lineNumber, position.column); - let viewSelectionStart = this.viewModelHelper.convertModelRangeToViewRange(selectionStart); + this.bracketDecorations = []; - this._set( - selectionStart, 0, - position, 0, - viewSelectionStart, viewPosition + this._setState( + new CursorModelState(new Range(1, 1, 1, 1), 0, new Position(1, 1), 0), + new CursorModelState(new Range(1, 1, 1, 1), 0, new Position(1, 1), 0) ); } - public setViewSelection(desiredViewSel: editorCommon.ISelection): void { - let viewSelectionStart = this.viewModelHelper.validateViewRange(desiredViewSel.selectionStartLineNumber, desiredViewSel.selectionStartColumn, desiredViewSel.selectionStartLineNumber, desiredViewSel.selectionStartColumn, this.selectionStart); - let viewPosition = this.viewModelHelper.validateViewPosition(desiredViewSel.positionLineNumber, desiredViewSel.positionColumn, this.position); + private _recreateCursorHelper(): void { + this.config = new CursorMoveConfiguration( + this.model.getOptions().tabSize, + this.getPageSize() + ); + this.helper = new CursorHelper(this.model, this.configuration, this.config); + } - this._set( - this.selectionStart, 0, - this.position, 0, - viewSelectionStart, viewPosition + private _setState(modelState: CursorModelState, viewState: CursorModelState): void { + this.modelState = modelState; + this.viewState = viewState; + + this._selStartMarker = this._ensureMarker(this._selStartMarker, this.modelState.selection.startLineNumber, this.modelState.selection.startColumn, true); + this._selEndMarker = this._ensureMarker(this._selEndMarker, this.modelState.selection.endLineNumber, this.modelState.selection.endColumn, false); + } + + private _ensureMarker(markerId: string, lineNumber: number, column: number, stickToPreviousCharacter: boolean): string { + if (!markerId) { + return this.model._addMarker(lineNumber, column, stickToPreviousCharacter); + } else { + this.model._changeMarker(markerId, lineNumber, column); + this.model._changeMarkerStickiness(markerId, stickToPreviousCharacter); + return markerId; + } + } + + public saveState(): IOneCursorState { + return { + selectionStart: this.modelState.selectionStart, + viewSelectionStart: this.viewState.selectionStart, + position: this.modelState.position, + viewPosition: this.viewState.position, + leftoverVisibleColumns: this.modelState.leftoverVisibleColumns, + selectionStartLeftoverVisibleColumns: this.modelState.selectionStartLeftoverVisibleColumns + }; + } + + public restoreState(state: IOneCursorState): void { + let position = this.model.validatePosition(state.position); + let selectionStart: Range; + if (state.selectionStart) { + selectionStart = this.model.validateRange(state.selectionStart); + } else { + selectionStart = new Range(position.lineNumber, position.column, position.lineNumber, position.column); + } + + let viewPosition = this.viewModelHelper.validateViewPosition(state.viewPosition.lineNumber, state.viewPosition.column, position); + let viewSelectionStart: Range; + if (state.viewSelectionStart) { + viewSelectionStart = this.viewModelHelper.validateViewRange(state.viewSelectionStart.startLineNumber, state.viewSelectionStart.startColumn, state.viewSelectionStart.endLineNumber, state.viewSelectionStart.endColumn, selectionStart); + } else { + viewSelectionStart = this.viewModelHelper.convertModelRangeToViewRange(selectionStart); + } + + this._setState( + new CursorModelState(selectionStart, state.selectionStartLeftoverVisibleColumns, position, state.leftoverVisibleColumns), + new CursorModelState(viewSelectionStart, state.selectionStartLeftoverVisibleColumns, viewPosition, state.leftoverVisibleColumns) + ); + } + + public updateModeConfiguration(modeConfiguration: IModeConfiguration): void { + this.modeConfiguration = modeConfiguration; + } + + public duplicate(): OneCursor { + let result = new OneCursor(this.editorId, this.model, this.configuration, this.modeConfiguration, this.viewModelHelper); + result._setState( + this.modelState, + this.viewState + ); + return result; + } + + public dispose(): void { + this._modelOptionsListener.dispose(); + this._configChangeListener.dispose(); + this.model._removeMarker(this._selStartMarker); + this.model._removeMarker(this._selEndMarker); + this.bracketDecorations = this.model.deltaDecorations(this.bracketDecorations, [], this.editorId); + } + + public adjustBracketDecorations(): void { + let bracketMatch: [Range, Range] = null; + let selection = this.modelState.selection; + if (selection.isEmpty()) { + bracketMatch = this.model.matchBracket(this.modelState.position); + } + + let newDecorations: editorCommon.IModelDeltaDecoration[] = []; + if (bracketMatch) { + let options: editorCommon.IModelDecorationOptions = { + stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + className: 'bracket-match' + }; + newDecorations.push({ range: bracketMatch[0], options: options }); + newDecorations.push({ range: bracketMatch[1], options: options }); + } + + this.bracketDecorations = this.model.deltaDecorations(this.bracketDecorations, newDecorations, this.editorId); + } + + + + public setSelection(selection: editorCommon.ISelection, viewSelection: editorCommon.ISelection = null): void { + let position = this.model.validatePosition({ + lineNumber: selection.positionLineNumber, + column: selection.positionColumn + }); + let selectionStart = this.model.validatePosition({ + lineNumber: selection.selectionStartLineNumber, + column: selection.selectionStartColumn + }); + + let viewPosition: Position; + let viewSelectionStart: Position; + + if (viewSelection) { + viewPosition = this.viewModelHelper.validateViewPosition(viewSelection.positionLineNumber, viewSelection.positionColumn, position); + viewSelectionStart = this.viewModelHelper.validateViewPosition(viewSelection.selectionStartLineNumber, viewSelection.selectionStartColumn, selectionStart); + } else { + viewPosition = this.viewModelHelper.convertModelPositionToViewPosition(position.lineNumber, position.column); + viewSelectionStart = this.viewModelHelper.convertModelPositionToViewPosition(selectionStart.lineNumber, selectionStart.column); + } + + this._setState( + new CursorModelState(new Range(selectionStart.lineNumber, selectionStart.column, selectionStart.lineNumber, selectionStart.column), 0, position, 0), + new CursorModelState(new Range(viewSelectionStart.lineNumber, viewSelectionStart.column, viewSelectionStart.lineNumber, viewSelectionStart.column), 0, viewPosition, 0) ); } // -------------------- START modifications - public setSelectionStart(rng: Range, viewRng: Range): void { - this._set( - rng, this.selectionStartLeftoverVisibleColumns, - this.position, this.leftoverVisibleColumns, - viewRng, this.viewPosition + public setSelectionStart(range: Range): void { + this._setState( + this.modelState.withSelectionStart(range), + this.viewState.withSelectionStart(this.viewModelHelper.convertModelRangeToViewRange(range)) ); } public collapseSelection(): void { - let selectionStart = new Range(this.position.lineNumber, this.position.column, this.position.lineNumber, this.position.column); - let viewSelectionStart = new Range(this.viewPosition.lineNumber, this.viewPosition.column, this.viewPosition.lineNumber, this.viewPosition.column); - this._set( - selectionStart, 0, - this.position, this.leftoverVisibleColumns, - viewSelectionStart, this.viewPosition + this._setState( + this.modelState.collapse(), + this.viewState.collapse() ); } @@ -390,34 +465,17 @@ export class OneCursor { } } - this._actualMove(inSelectionMode, new Position(lineNumber, column), new Position(viewLineNumber, viewColumn), leftoverVisibleColumns); - } - - private _actualMove(inSelectionMode: boolean, position: Position, viewPosition: Position, leftoverVisibleColumns: number): void { - if (inSelectionMode) { - // move just position - this._set( - this.selectionStart, this.selectionStartLeftoverVisibleColumns, - position, leftoverVisibleColumns, - this.viewSelectionStart, viewPosition - ); - } else { - // move everything - let selectionStart = new Range(position.lineNumber, position.column, position.lineNumber, position.column); - let viewSelectionStart = new Range(viewPosition.lineNumber, viewPosition.column, viewPosition.lineNumber, viewPosition.column); - this._set( - selectionStart, leftoverVisibleColumns, - position, leftoverVisibleColumns, - viewSelectionStart, viewPosition - ); - } + this._setState( + this.modelState.move(inSelectionMode, new Position(lineNumber, column), leftoverVisibleColumns), + this.viewState.move(inSelectionMode, new Position(viewLineNumber, viewColumn), leftoverVisibleColumns) + ); } private _recoverSelectionFromMarkers(): Selection { let start = this.model._getMarker(this._selStartMarker); let end = this.model._getMarker(this._selEndMarker); - if (this._selDirection === SelectionDirection.LTR) { + if (this.modelState.selection.getDirection() === SelectionDirection.LTR) { return new Selection(start.lineNumber, start.column, end.lineNumber, end.column); } @@ -439,10 +497,9 @@ export class OneCursor { let viewSelectionStart = this.viewModelHelper.convertModelRangeToViewRange(selectionStart); let viewPosition = this.viewModelHelper.convertViewToModelPosition(position.lineNumber, position.column); - this._set( - selectionStart, 0, - position, 0, - viewSelectionStart, viewPosition + this._setState( + new CursorModelState(selectionStart, 0, position, 0), + new CursorModelState(viewSelectionStart, 0, viewPosition, 0) ); return true; @@ -457,40 +514,21 @@ export class OneCursor { return Math.floor(c.layoutInfo.height / c.fontInfo.lineHeight) - 2; } - public getSelectionStart(): Range { - return this.selectionStart; - } - public getPosition(): Position { - return this.position; - } - public getSelection(): Selection { - return this._cachedSelection; - } - - public getViewPosition(): Position { - return this.viewPosition; - } - public getViewSelection(): Selection { - return this._cachedViewSelection; - } public getValidViewPosition(): Position { - return this.viewModelHelper.validateViewPosition(this.viewPosition.lineNumber, this.viewPosition.column, this.position); + return this.viewModelHelper.validateViewPosition(this.viewState.position.lineNumber, this.viewState.position.column, this.modelState.position); } public hasSelection(): boolean { - return (!this.getSelection().isEmpty() || !this.selectionStart.isEmpty()); + return (!this.modelState.selection.isEmpty() || !this.modelState.selectionStart.isEmpty()); } public getBracketsDecorations(): string[] { return this.bracketDecorations; } - public getLeftoverVisibleColumns(): number { - return this.leftoverVisibleColumns; - } - public getSelectionStartLeftoverVisibleColumns(): number { - return this.selectionStartLeftoverVisibleColumns; - } public setSelectionStartLeftoverVisibleColumns(value: number): void { - this.selectionStartLeftoverVisibleColumns = value; + this._setState( + this.modelState.withSelectionStartLeftoverVisibleColumns(value), + this.viewState.withSelectionStartLeftoverVisibleColumns(value) + ); } // -- utils @@ -558,36 +596,18 @@ export class OneCursor { let visibleLineNumber = visibleRange.endLineNumber - (lineFromBottom - 1); return visibleLineNumber > visibleRange.startLineNumber ? visibleLineNumber : this.getLineFromViewPortTop(); } - public getLineContent(lineNumber: number): string { - return this.model.getLineContent(lineNumber); - } public findPreviousWordOnLine(position: Position): IFindWordResult { return this.helper.findPreviousWordOnLine(position); } public findNextWordOnLine(position: Position): IFindWordResult { return this.helper.findNextWordOnLine(position); } - public getLeftOfPosition(lineNumber: number, column: number): editorCommon.IPosition { - return this.helper.getLeftOfPosition(this.model, lineNumber, column); - } - public getRightOfPosition(lineNumber: number, column: number): editorCommon.IPosition { - return this.helper.getRightOfPosition(this.model, lineNumber, column); - } - public getPositionUp(lineNumber: number, column: number, leftoverVisibleColumns: number, count: number, allowMoveOnFirstLine: boolean): IMoveResult { - return this.helper.getPositionUp(this.model, lineNumber, column, leftoverVisibleColumns, count, allowMoveOnFirstLine); - } - public getPositionDown(lineNumber: number, column: number, leftoverVisibleColumns: number, count: number, allowMoveOnLastLine: boolean): IMoveResult { - return this.helper.getPositionDown(this.model, lineNumber, column, leftoverVisibleColumns, count, allowMoveOnLastLine); - } public getColumnAtEndOfLine(lineNumber: number, column: number): number { return this.helper.getColumnAtEndOfLine(this.model, lineNumber, column); } public getVisibleColumnFromColumn(lineNumber: number, column: number): number { return this.helper.visibleColumnFromColumn(this.model, lineNumber, column); } - public getColumnFromVisibleColumn(lineNumber: number, column: number): number { - return this.helper.columnFromVisibleColumn(this.model, lineNumber, column); - } public getViewVisibleColumnFromColumn(viewLineNumber: number, viewColumn: number): number { return this.helper.visibleColumnFromColumn(this.viewModelHelper.viewModel, viewLineNumber, viewColumn); } @@ -635,18 +655,6 @@ export class OneCursor { public getViewLineLastNonWhiteSpaceColumn(lineNumber: number): number { return this.viewModelHelper.viewModel.getLineLastNonWhitespaceColumn(lineNumber); } - public getLeftOfViewPosition(lineNumber: number, column: number): editorCommon.IPosition { - return this.helper.getLeftOfPosition(this.viewModelHelper.viewModel, lineNumber, column); - } - public getRightOfViewPosition(lineNumber: number, column: number): editorCommon.IPosition { - return this.helper.getRightOfPosition(this.viewModelHelper.viewModel, lineNumber, column); - } - public getViewPositionUp(lineNumber: number, column: number, leftoverVisibleColumns: number, count: number, allowMoveOnFirstLine: boolean): IMoveResult { - return this.helper.getPositionUp(this.viewModelHelper.viewModel, lineNumber, column, leftoverVisibleColumns, count, allowMoveOnFirstLine); - } - public getViewPositionDown(lineNumber: number, column: number, leftoverVisibleColumns: number, count: number, allowMoveOnLastLine: boolean): IMoveResult { - return this.helper.getPositionDown(this.viewModelHelper.viewModel, lineNumber, column, leftoverVisibleColumns, count, allowMoveOnLastLine); - } public getColumnAtBeginningOfViewLine(lineNumber: number, column: number): number { return this.helper.getColumnAtBeginningOfLine(this.viewModelHelper.viewModel, lineNumber, column); } @@ -664,7 +672,7 @@ export class OneCursor { }; } public getNearestRevealViewPositionInViewport(): Position { - const position = this.getViewPosition(); + const position = this.viewState.position; const revealRange = this.getRevealViewLinesRangeInViewport(); if (position.lineNumber < revealRange.startLineNumber) { @@ -693,7 +701,7 @@ export class OneCursorOp { let firstBracket = cursor.model.getDecorationRange(bracketDecorations[0]); let secondBracket = cursor.model.getDecorationRange(bracketDecorations[1]); - let position = cursor.getPosition(); + let position = cursor.modelState.position; if (Utils.isPositionAtRangeEdges(position, firstBracket) || Utils.isPositionInsideRange(position, firstBracket)) { cursor.moveModelPosition(false, secondBracket.endLineNumber, secondBracket.endColumn, 0, false); @@ -787,7 +795,7 @@ export class OneCursorOp { } private static _columnSelectOp(cursor: OneCursor, toViewLineNumber: number, toViewVisualColumn: number): IColumnSelectResult { - let viewStartSelection = cursor.getViewSelection(); + let viewStartSelection = cursor.viewState.selection; let fromVisibleColumn = cursor.getViewVisibleColumnFromColumn(viewStartSelection.selectionStartLineNumber, viewStartSelection.selectionStartColumn); return cursor.columnSelect(viewStartSelection.selectionStartLineNumber, fromVisibleColumn, toViewLineNumber, toViewVisualColumn); @@ -816,8 +824,8 @@ export class OneCursorOp { public static columnSelectRight(cursor: OneCursor, toViewLineNumber: number, toViewVisualColumn: number): IColumnSelectResult { let maxVisualViewColumn = 0; - let minViewLineNumber = Math.min(cursor.getViewPosition().lineNumber, toViewLineNumber); - let maxViewLineNumber = Math.max(cursor.getViewPosition().lineNumber, toViewLineNumber); + let minViewLineNumber = Math.min(cursor.viewState.position.lineNumber, toViewLineNumber); + let maxViewLineNumber = Math.max(cursor.viewState.position.lineNumber, toViewLineNumber); for (let lineNumber = minViewLineNumber; lineNumber <= maxViewLineNumber; lineNumber++) { let lineMaxViewColumn = cursor.getViewLineMaxColumn(lineNumber); let lineMaxVisualViewColumn = cursor.getViewVisibleColumnFromColumn(lineNumber, lineMaxViewColumn); @@ -859,13 +867,13 @@ export class OneCursorOp { if (cursor.hasSelection() && !inSelectionMode) { // If we are in selection mode, move left without selection cancels selection and puts cursor at the beginning of the selection - let viewSelection = cursor.getViewSelection(); - let viewSelectionStart = cursor.validateViewPosition(viewSelection.startLineNumber, viewSelection.startColumn, cursor.getSelection().getStartPosition()); + let viewSelection = cursor.viewState.selection; + let viewSelectionStart = cursor.validateViewPosition(viewSelection.startLineNumber, viewSelection.startColumn, cursor.modelState.selection.getStartPosition()); viewLineNumber = viewSelectionStart.lineNumber; viewColumn = viewSelectionStart.column; } else { let validatedViewPosition = cursor.getValidViewPosition(); - let r = cursor.getLeftOfViewPosition(validatedViewPosition.lineNumber, validatedViewPosition.column - (noOfColumns - 1)); + let r = CursorMove.left(cursor.config, cursor.viewModel, validatedViewPosition.lineNumber, validatedViewPosition.column - (noOfColumns - 1)); viewLineNumber = r.lineNumber; viewColumn = r.column; } @@ -876,7 +884,7 @@ export class OneCursorOp { } public static moveWordLeft(cursor: OneCursor, inSelectionMode: boolean, wordNavigationType: WordNavigationType, ctx: IOneCursorOperationContext): boolean { - let position = cursor.getPosition(); + let position = cursor.modelState.position; let lineNumber = position.lineNumber; let column = position.column; @@ -917,13 +925,13 @@ export class OneCursorOp { if (cursor.hasSelection() && !inSelectionMode) { // If we are in selection mode, move right without selection cancels selection and puts cursor at the end of the selection - let viewSelection = cursor.getViewSelection(); - let viewSelectionEnd = cursor.validateViewPosition(viewSelection.endLineNumber, viewSelection.endColumn, cursor.getSelection().getEndPosition()); + let viewSelection = cursor.viewState.selection; + let viewSelectionEnd = cursor.validateViewPosition(viewSelection.endLineNumber, viewSelection.endColumn, cursor.modelState.selection.getEndPosition()); viewLineNumber = viewSelectionEnd.lineNumber; viewColumn = viewSelectionEnd.column; } else { let validatedViewPosition = cursor.getValidViewPosition(); - let r = cursor.getRightOfViewPosition(validatedViewPosition.lineNumber, validatedViewPosition.column + (noOfColumns - 1)); + let r = CursorMove.right(cursor.config, cursor.viewModel, validatedViewPosition.lineNumber, validatedViewPosition.column + (noOfColumns - 1)); viewLineNumber = r.lineNumber; viewColumn = r.column; } @@ -934,7 +942,7 @@ export class OneCursorOp { } public static moveWordRight(cursor: OneCursor, inSelectionMode: boolean, wordNavigationType: WordNavigationType, ctx: IOneCursorOperationContext): boolean { - let position = cursor.getPosition(); + let position = cursor.modelState.position; let lineNumber = position.lineNumber; let column = position.column; @@ -983,8 +991,8 @@ export class OneCursorOp { if (cursor.hasSelection() && !inSelectionMode) { // If we are in selection mode, move down acts relative to the end of selection - let viewSelection = cursor.getViewSelection(); - let viewSelectionEnd = cursor.validateViewPosition(viewSelection.endLineNumber, viewSelection.endColumn, cursor.getSelection().getEndPosition()); + let viewSelection = cursor.viewState.selection; + let viewSelectionEnd = cursor.validateViewPosition(viewSelection.endLineNumber, viewSelection.endColumn, cursor.modelState.selection.getEndPosition()); viewLineNumber = viewSelectionEnd.lineNumber; viewColumn = viewSelectionEnd.column; } else { @@ -993,7 +1001,7 @@ export class OneCursorOp { viewColumn = validatedViewPosition.column; } - let r = cursor.getViewPositionDown(viewLineNumber, viewColumn, cursor.getLeftoverVisibleColumns(), linesCount, true); + let r = CursorMove.down(cursor.config, cursor.viewModel, viewLineNumber, viewColumn, cursor.viewState.leftoverVisibleColumns, linesCount, true); ctx.cursorPositionChangeReason = editorCommon.CursorChangeReason.Explicit; cursor.moveViewPosition(inSelectionMode, r.lineNumber, r.column, r.leftoverVisibleColumns, true); return true; @@ -1005,16 +1013,16 @@ export class OneCursorOp { if (cursor.hasSelection() && !inSelectionMode) { // If we are in selection mode, move down acts relative to the end of selection - let selection = cursor.getSelection(); + let selection = cursor.modelState.selection; lineNumber = selection.endLineNumber; column = selection.endColumn; } else { - let position = cursor.getPosition(); + let position = cursor.modelState.position; lineNumber = position.lineNumber; column = position.column; } - let r = cursor.getPositionDown(lineNumber, column, cursor.getLeftoverVisibleColumns(), linesCount, true); + let r = CursorMove.down(cursor.config, cursor.model, lineNumber, column, cursor.modelState.leftoverVisibleColumns, linesCount, true); ctx.cursorPositionChangeReason = editorCommon.CursorChangeReason.Explicit; cursor.moveModelPosition(inSelectionMode, r.lineNumber, r.column, r.leftoverVisibleColumns, true); return true; @@ -1022,13 +1030,13 @@ export class OneCursorOp { public static translateDown(cursor: OneCursor, ctx: IOneCursorOperationContext): boolean { - let selection = cursor.getViewSelection(); + let selection = cursor.viewState.selection; - let selectionStart = cursor.getViewPositionDown(selection.selectionStartLineNumber, selection.selectionStartColumn, cursor.getSelectionStartLeftoverVisibleColumns(), 1, false); + let selectionStart = CursorMove.down(cursor.config, cursor.viewModel, selection.selectionStartLineNumber, selection.selectionStartColumn, cursor.viewState.selectionStartLeftoverVisibleColumns, 1, false); ctx.cursorPositionChangeReason = editorCommon.CursorChangeReason.Explicit; - cursor.moveViewPosition(false, selectionStart.lineNumber, selectionStart.column, cursor.getLeftoverVisibleColumns(), true); + cursor.moveViewPosition(false, selectionStart.lineNumber, selectionStart.column, cursor.viewState.leftoverVisibleColumns, true); - let position = cursor.getViewPositionDown(selection.positionLineNumber, selection.positionColumn, cursor.getLeftoverVisibleColumns(), 1, false); + let position = CursorMove.down(cursor.config, cursor.viewModel, selection.positionLineNumber, selection.positionColumn, cursor.viewState.leftoverVisibleColumns, 1, false); ctx.cursorPositionChangeReason = editorCommon.CursorChangeReason.Explicit; cursor.moveViewPosition(true, position.lineNumber, position.column, position.leftoverVisibleColumns, true); @@ -1052,8 +1060,8 @@ export class OneCursorOp { if (cursor.hasSelection() && !inSelectionMode) { // If we are in selection mode, move up acts relative to the beginning of selection - let viewSelection = cursor.getViewSelection(); - let viewSelectionStart = cursor.validateViewPosition(viewSelection.startLineNumber, viewSelection.startColumn, cursor.getSelection().getStartPosition()); + let viewSelection = cursor.viewState.selection; + let viewSelectionStart = cursor.validateViewPosition(viewSelection.startLineNumber, viewSelection.startColumn, cursor.modelState.selection.getStartPosition()); viewLineNumber = viewSelectionStart.lineNumber; viewColumn = viewSelectionStart.column; } else { @@ -1062,7 +1070,7 @@ export class OneCursorOp { viewColumn = validatedViewPosition.column; } - let r = cursor.getViewPositionUp(viewLineNumber, viewColumn, cursor.getLeftoverVisibleColumns(), linesCount, true); + let r = CursorMove.up(cursor.config, cursor.viewModel, viewLineNumber, viewColumn, cursor.viewState.leftoverVisibleColumns, linesCount, true); ctx.cursorPositionChangeReason = editorCommon.CursorChangeReason.Explicit; cursor.moveViewPosition(inSelectionMode, r.lineNumber, r.column, r.leftoverVisibleColumns, true); @@ -1075,16 +1083,16 @@ export class OneCursorOp { if (cursor.hasSelection() && !inSelectionMode) { // If we are in selection mode, move up acts relative to the beginning of selection - let selection = cursor.getSelection(); + let selection = cursor.modelState.selection; lineNumber = selection.startLineNumber; column = selection.startColumn; } else { - let position = cursor.getPosition(); + let position = cursor.modelState.position; lineNumber = position.lineNumber; column = position.column; } - let r = cursor.getPositionUp(lineNumber, column, cursor.getLeftoverVisibleColumns(), linesCount, true); + let r = CursorMove.up(cursor.config, cursor.model, lineNumber, column, cursor.modelState.leftoverVisibleColumns, linesCount, true); ctx.cursorPositionChangeReason = editorCommon.CursorChangeReason.Explicit; cursor.moveModelPosition(inSelectionMode, r.lineNumber, r.column, r.leftoverVisibleColumns, true); @@ -1093,13 +1101,13 @@ export class OneCursorOp { public static translateUp(cursor: OneCursor, ctx: IOneCursorOperationContext): boolean { - let selection = cursor.getViewSelection(); + let selection = cursor.viewState.selection; - let selectionStart = cursor.getViewPositionUp(selection.selectionStartLineNumber, selection.selectionStartColumn, cursor.getSelectionStartLeftoverVisibleColumns(), 1, false); + let selectionStart = CursorMove.up(cursor.config, cursor.viewModel, selection.selectionStartLineNumber, selection.selectionStartColumn, cursor.viewState.selectionStartLeftoverVisibleColumns, 1, false); ctx.cursorPositionChangeReason = editorCommon.CursorChangeReason.Explicit; - cursor.moveViewPosition(false, selectionStart.lineNumber, selectionStart.column, cursor.getLeftoverVisibleColumns(), true); + cursor.moveViewPosition(false, selectionStart.lineNumber, selectionStart.column, cursor.viewState.leftoverVisibleColumns, true); - let position = cursor.getViewPositionUp(selection.positionLineNumber, selection.positionColumn, cursor.getLeftoverVisibleColumns(), 1, false); + let position = CursorMove.up(cursor.config, cursor.viewModel, selection.positionLineNumber, selection.positionColumn, cursor.viewState.leftoverVisibleColumns, 1, false); ctx.cursorPositionChangeReason = editorCommon.CursorChangeReason.Explicit; cursor.moveViewPosition(true, position.lineNumber, position.column, position.leftoverVisibleColumns, true); @@ -1132,7 +1140,7 @@ export class OneCursorOp { public static expandLineSelection(cursor: OneCursor, ctx: IOneCursorOperationContext): boolean { ctx.cursorPositionChangeReason = editorCommon.CursorChangeReason.Explicit; - let viewSel = cursor.getViewSelection(); + let viewSel = cursor.viewState.selection; let viewStartLineNumber = viewSel.startLineNumber; let viewStartColumn = viewSel.startColumn; @@ -1145,7 +1153,7 @@ export class OneCursorOp { viewEndColumn = viewEndMaxColumn; } else { // Expand selection with one more line down - let moveResult = cursor.getViewPositionDown(viewEndLineNumber, viewEndColumn, 0, 1, true); + let moveResult = CursorMove.down(cursor.config, cursor.viewModel, viewEndLineNumber, viewEndColumn, 0, 1, true); viewEndLineNumber = moveResult.lineNumber; viewEndColumn = cursor.getViewLineMaxColumn(viewEndLineNumber); } @@ -1182,7 +1190,7 @@ export class OneCursorOp { // Toggle between selecting editable range and selecting the entire buffer let editableRange = cursor.model.getEditableRange(); - let selection = cursor.getSelection(); + let selection = cursor.modelState.selection; if (!selection.equalsRange(editableRange)) { // Selection is not editable range => select editable range @@ -1233,15 +1241,13 @@ export class OneCursorOp { } let selectionStartRange = new Range(position.lineNumber, 1, selectToLineNumber, selectToColumn); - let r1 = cursor.convertModelPositionToViewPosition(position.lineNumber, 1); - let r2 = cursor.convertModelPositionToViewPosition(selectToLineNumber, selectToColumn); - cursor.setSelectionStart(selectionStartRange, new Range(r1.lineNumber, r1.column, r2.lineNumber, r2.column)); + cursor.setSelectionStart(selectionStartRange); cursor.moveModelPosition(cursor.hasSelection(), selectionStartRange.endLineNumber, selectionStartRange.endColumn, 0, false); return true; } else { // Continuing line selection - let enteringLineNumber = cursor.getSelectionStart().getStartPosition().lineNumber; + let enteringLineNumber = cursor.modelState.selectionStart.getStartPosition().lineNumber; if (position.lineNumber < enteringLineNumber) { @@ -1259,7 +1265,7 @@ export class OneCursorOp { } else { - let endPositionOfSelectionStart = cursor.getSelectionStart().getEndPosition(); + let endPositionOfSelectionStart = cursor.modelState.selectionStart.getEndPosition(); cursor.moveModelPosition(cursor.hasSelection(), endPositionOfSelectionStart.lineNumber, endPositionOfSelectionStart.column, 0, false); } @@ -1306,9 +1312,7 @@ export class OneCursorOp { } let selectionStartRange = new Range(validatedPosition.lineNumber, startColumn, validatedPosition.lineNumber, endColumn); - let r1 = cursor.convertModelPositionToViewPosition(validatedPosition.lineNumber, startColumn); - let r2 = cursor.convertModelPositionToViewPosition(validatedPosition.lineNumber, endColumn); - cursor.setSelectionStart(selectionStartRange, new Range(r1.lineNumber, r1.column, r2.lineNumber, r2.column)); + cursor.setSelectionStart(selectionStartRange); lineNumber = selectionStartRange.endLineNumber; column = selectionStartRange.endColumn; } else { @@ -1328,17 +1332,17 @@ export class OneCursorOp { } lineNumber = validatedPosition.lineNumber; - if (validatedPosition.isBeforeOrEqual(cursor.getSelectionStart().getStartPosition())) { + if (validatedPosition.isBeforeOrEqual(cursor.modelState.selectionStart.getStartPosition())) { column = startColumn; let possiblePosition = new Position(lineNumber, column); - if (cursor.getSelectionStart().containsPosition(possiblePosition)) { - column = cursor.getSelectionStart().endColumn; + if (cursor.modelState.selectionStart.containsPosition(possiblePosition)) { + column = cursor.modelState.selectionStart.endColumn; } } else { column = endColumn; let possiblePosition = new Position(lineNumber, column); - if (cursor.getSelectionStart().containsPosition(possiblePosition)) { - column = cursor.getSelectionStart().startColumn; + if (cursor.modelState.selectionStart.containsPosition(possiblePosition)) { + column = cursor.modelState.selectionStart.startColumn; } } } @@ -1368,11 +1372,11 @@ export class OneCursorOp { return false; } - return this._enter(cursor, false, ctx, cursor.getPosition(), cursor.getSelection()); + return this._enter(cursor, false, ctx, cursor.modelState.position, cursor.modelState.selection); } public static lineInsertBefore(cursor: OneCursor, ctx: IOneCursorOperationContext): boolean { - let lineNumber = cursor.getPosition().lineNumber; + let lineNumber = cursor.modelState.position.lineNumber; if (lineNumber === 1) { ctx.executeCommand = new ReplaceCommandWithoutChangingPosition(new Range(1, 1, 1, 1), '\n'); @@ -1386,13 +1390,13 @@ export class OneCursorOp { } public static lineInsertAfter(cursor: OneCursor, ctx: IOneCursorOperationContext): boolean { - let position = cursor.getPosition(); + let position = cursor.modelState.position; let column = cursor.model.getLineMaxColumn(position.lineNumber); return this._enter(cursor, false, ctx, new Position(position.lineNumber, column), new Range(position.lineNumber, column, position.lineNumber, column)); } public static lineBreakInsert(cursor: OneCursor, ctx: IOneCursorOperationContext): boolean { - return this._enter(cursor, true, ctx, cursor.getPosition(), cursor.getSelection()); + return this._enter(cursor, true, ctx, cursor.modelState.position, cursor.modelState.selection); } private static _enter(cursor: OneCursor, keepPosition: boolean, ctx: IOneCursorOperationContext, position: Position, range: Range): boolean { @@ -1440,13 +1444,13 @@ export class OneCursorOp { return false; } - let selection = cursor.getSelection(); + let selection = cursor.modelState.selection; if (!selection.isEmpty() || !cursor.modeConfiguration.autoClosingPairsClose.hasOwnProperty(ch)) { return false; } - let position = cursor.getPosition(); + let position = cursor.modelState.position; let lineText = cursor.model.getLineContent(position.lineNumber); let beforeCharacter = lineText[position.column - 1]; @@ -1465,19 +1469,13 @@ export class OneCursorOp { return false; } - let selection = cursor.getSelection(); + let selection = cursor.modelState.selection; if (!selection.isEmpty() || !cursor.modeConfiguration.autoClosingPairsOpen.hasOwnProperty(ch)) { return false; } - let characterPairSupport = LanguageConfigurationRegistry.getCharacterPairSupport(cursor.model.getMode().getId()); - - if (!characterPairSupport) { - return false; - } - - let position = cursor.getPosition(); + let position = cursor.modelState.position; let lineText = cursor.model.getLineContent(position.lineNumber); let beforeCharacter = lineText[position.column - 1]; @@ -1495,11 +1493,11 @@ export class OneCursorOp { } } - let lineContext = cursor.model.getLineContext(position.lineNumber); + let lineTokens = cursor.model.getLineTokens(position.lineNumber, false); let shouldAutoClosePair = false; try { - shouldAutoClosePair = characterPairSupport.shouldAutoClosePair(ch, lineContext, position.column - 1); + shouldAutoClosePair = LanguageConfigurationRegistry.shouldAutoClosePair(ch, lineTokens, position.column - 1); } catch (e) { onUnexpectedError(e); } @@ -1519,7 +1517,7 @@ export class OneCursorOp { return false; } - let selection = cursor.getSelection(); + let selection = cursor.modelState.selection; if (selection.isEmpty() || !cursor.modeConfiguration.surroundingPairs.hasOwnProperty(ch)) { return false; @@ -1569,18 +1567,15 @@ export class OneCursorOp { private static _typeInterceptorElectricCharRunnable(cursor: OneCursor, ctx: IOneCursorOperationContext): void { - let position = cursor.getPosition(); + let position = cursor.modelState.position; let lineText = cursor.model.getLineContent(position.lineNumber); - let lineContext = cursor.model.getLineContext(position.lineNumber); + let lineTokens = cursor.model.getLineTokens(position.lineNumber, false); let electricAction: IElectricAction; - let electricCharSupport = LanguageConfigurationRegistry.getElectricCharacterSupport(cursor.model.getMode().getId()); - if (electricCharSupport) { - try { - electricAction = electricCharSupport.onElectricCharacter(lineContext, position.column - 2); - } catch (e) { - onUnexpectedError(e); - } + try { + electricAction = LanguageConfigurationRegistry.onElectricCharacter(lineTokens, position.column - 2); + } catch (e) { + onUnexpectedError(e); } if (electricAction) { @@ -1615,14 +1610,14 @@ export class OneCursorOp { columnDeltaOffset += electricAction.advanceCount; } ctx.shouldPushStackElementAfter = true; - ctx.executeCommand = new ReplaceCommandWithOffsetCursorState(cursor.getSelection(), appendText, 0, columnDeltaOffset); + ctx.executeCommand = new ReplaceCommandWithOffsetCursorState(cursor.modelState.selection, appendText, 0, columnDeltaOffset); } } } public static actualType(cursor: OneCursor, text: string, keepPosition: boolean, ctx: IOneCursorOperationContext, range?: Range): boolean { if (typeof range === 'undefined') { - range = cursor.getSelection(); + range = cursor.modelState.selection; } if (keepPosition) { ctx.executeCommand = new ReplaceCommandWithoutChangingPosition(range, text); @@ -1658,7 +1653,7 @@ export class OneCursorOp { } public static replacePreviousChar(cursor: OneCursor, txt: string, replaceCharCnt: number, ctx: IOneCursorOperationContext): boolean { - let pos = cursor.getPosition(); + let pos = cursor.modelState.position; let range: Range; let startColumn = Math.max(1, pos.column - replaceCharCnt); range = new Range(pos.lineNumber, startColumn, pos.lineNumber, pos.column); @@ -1725,7 +1720,7 @@ export class OneCursorOp { } public static tab(cursor: OneCursor, ctx: IOneCursorOperationContext): boolean { - let selection = cursor.getSelection(); + let selection = cursor.modelState.selection; if (selection.isEmpty()) { @@ -1758,7 +1753,7 @@ export class OneCursorOp { } public static indent(cursor: OneCursor, ctx: IOneCursorOperationContext): boolean { - let selection = cursor.getSelection(); + let selection = cursor.modelState.selection; ctx.shouldPushStackElementBefore = true; ctx.shouldPushStackElementAfter = true; @@ -1773,7 +1768,7 @@ export class OneCursorOp { } public static outdent(cursor: OneCursor, ctx: IOneCursorOperationContext): boolean { - let selection = cursor.getSelection(); + let selection = cursor.modelState.selection; ctx.shouldPushStackElementBefore = true; ctx.shouldPushStackElementAfter = true; @@ -1788,8 +1783,8 @@ export class OneCursorOp { } public static paste(cursor: OneCursor, text: string, pasteOnNewLine: boolean, ctx: IOneCursorOperationContext): boolean { - let position = cursor.getPosition(); - let selection = cursor.getSelection(); + let position = cursor.modelState.position; + let selection = cursor.modelState.selection; ctx.cursorPositionChangeReason = editorCommon.CursorChangeReason.Paste; @@ -1825,11 +1820,11 @@ export class OneCursorOp { return false; } - if (!cursor.getSelection().isEmpty()) { + if (!cursor.modelState.selection.isEmpty()) { return false; } - let position = cursor.getPosition(); + let position = cursor.modelState.position; let lineText = cursor.model.getLineContent(position.lineNumber); let character = lineText[position.column - 2]; @@ -1862,13 +1857,13 @@ export class OneCursorOp { return true; } - let deleteSelection: Range = cursor.getSelection(); + let deleteSelection: Range = cursor.modelState.selection; if (deleteSelection.isEmpty()) { - let position = cursor.getPosition(); + let position = cursor.modelState.position; if (cursor.configuration.editor.useTabStops && position.column > 1) { - let lineContent = cursor.getLineContent(position.lineNumber); + let lineContent = cursor.model.getLineContent(position.lineNumber); let firstNonWhitespaceIndex = strings.firstNonWhitespaceIndex(lineContent); let lastIndentationColumn = ( @@ -1880,13 +1875,13 @@ export class OneCursorOp { if (position.column <= lastIndentationColumn) { let fromVisibleColumn = cursor.getVisibleColumnFromColumn(position.lineNumber, position.column); let toVisibleColumn = CursorMoveHelper.prevTabColumn(fromVisibleColumn, cursor.model.getOptions().tabSize); - let toColumn = cursor.getColumnFromVisibleColumn(position.lineNumber, toVisibleColumn); + let toColumn = CursorMove.columnFromVisibleColumn(cursor.config, cursor.model, position.lineNumber, toVisibleColumn); deleteSelection = new Range(position.lineNumber, toColumn, position.lineNumber, position.column); } else { deleteSelection = new Range(position.lineNumber, position.column - 1, position.lineNumber, position.column); } } else { - let leftOfPosition = cursor.getLeftOfPosition(position.lineNumber, position.column); + let leftOfPosition = CursorMove.left(cursor.config, cursor.model, position.lineNumber, position.column); deleteSelection = new Range( leftOfPosition.lineNumber, leftOfPosition.column, @@ -1910,8 +1905,8 @@ export class OneCursorOp { } private static deleteWordLeftWhitespace(cursor: OneCursor, ctx: IOneCursorOperationContext): boolean { - let position = cursor.getPosition(); - let lineContent = cursor.getLineContent(position.lineNumber); + let position = cursor.modelState.position; + let lineContent = cursor.model.getLineContent(position.lineNumber); let startIndex = position.column - 2; let lastNonWhitespace = strings.lastNonWhitespaceIndex(lineContent, startIndex); if (lastNonWhitespace + 1 < startIndex) { @@ -1928,10 +1923,10 @@ export class OneCursorOp { return true; } - let selection = cursor.getSelection(); + let selection = cursor.modelState.selection; if (selection.isEmpty()) { - let position = cursor.getPosition(); + let position = cursor.modelState.position; let lineNumber = position.lineNumber; let column = position.column; @@ -1976,11 +1971,11 @@ export class OneCursorOp { public static deleteRight(cursor: OneCursor, ctx: IOneCursorOperationContext): boolean { - let deleteSelection: Range = cursor.getSelection(); + let deleteSelection: Range = cursor.modelState.selection; if (deleteSelection.isEmpty()) { - let position = cursor.getPosition(); - let rightOfPosition = cursor.getRightOfPosition(position.lineNumber, position.column); + let position = cursor.modelState.position; + let rightOfPosition = CursorMove.right(cursor.config, cursor.model, position.lineNumber, position.column); deleteSelection = new Range( rightOfPosition.lineNumber, rightOfPosition.column, @@ -2014,8 +2009,8 @@ export class OneCursorOp { } private static deleteWordRightWhitespace(cursor: OneCursor, ctx: IOneCursorOperationContext): boolean { - let position = cursor.getPosition(); - let lineContent = cursor.getLineContent(position.lineNumber); + let position = cursor.modelState.position; + let lineContent = cursor.model.getLineContent(position.lineNumber); let startIndex = position.column - 1; let firstNonWhitespace = this._findFirstNonWhitespaceChar(lineContent, startIndex); if (startIndex + 1 < firstNonWhitespace) { @@ -2028,10 +2023,10 @@ export class OneCursorOp { public static deleteWordRight(cursor: OneCursor, whitespaceHeuristics: boolean, wordNavigationType: WordNavigationType, ctx: IOneCursorOperationContext): boolean { - let selection = cursor.getSelection(); + let selection = cursor.modelState.selection; if (selection.isEmpty()) { - let position = cursor.getPosition(); + let position = cursor.modelState.position; let lineNumber = position.lineNumber; let column = position.column; @@ -2102,10 +2097,10 @@ export class OneCursorOp { return true; } - let selection = cursor.getSelection(); + let selection = cursor.modelState.selection; if (selection.isEmpty()) { - let position = cursor.getPosition(); + let position = cursor.modelState.position; let lineNumber = position.lineNumber; let column = position.column; @@ -2125,10 +2120,10 @@ export class OneCursorOp { } public static deleteAllRight(cursor: OneCursor, ctx: IOneCursorOperationContext): boolean { - let selection = cursor.getSelection(); + let selection = cursor.modelState.selection; if (selection.isEmpty()) { - let position = cursor.getPosition(); + let position = cursor.modelState.position; let lineNumber = position.lineNumber; let column = position.column; let maxColumn = cursor.model.getLineMaxColumn(lineNumber); @@ -2149,13 +2144,13 @@ export class OneCursorOp { } public static cut(cursor: OneCursor, enableEmptySelectionClipboard: boolean, ctx: IOneCursorOperationContext): boolean { - let selection = cursor.getSelection(); + let selection = cursor.modelState.selection; if (selection.isEmpty()) { if (enableEmptySelectionClipboard) { // This is a full line cut - let position = cursor.getPosition(); + let position = cursor.modelState.position; let startLineNumber: number, startColumn: number, @@ -2211,30 +2206,10 @@ class CursorHelper { private configuration: editorCommon.IConfiguration; private moveHelper: CursorMoveHelper; - constructor(model: editorCommon.IModel, configuration: editorCommon.IConfiguration) { + constructor(model: editorCommon.IModel, configuration: editorCommon.IConfiguration, config: CursorMoveConfiguration) { this.model = model; this.configuration = configuration; - this.moveHelper = new CursorMoveHelper({ - getIndentationOptions: () => { - return this.model.getOptions(); - } - }); - } - - public getLeftOfPosition(model: ICursorMoveHelperModel, lineNumber: number, column: number): editorCommon.IPosition { - return this.moveHelper.getLeftOfPosition(model, lineNumber, column); - } - - public getRightOfPosition(model: ICursorMoveHelperModel, lineNumber: number, column: number): editorCommon.IPosition { - return this.moveHelper.getRightOfPosition(model, lineNumber, column); - } - - public getPositionUp(model: ICursorMoveHelperModel, lineNumber: number, column: number, leftoverVisibleColumns: number, count: number, allowMoveOnFirstLine: boolean): IMoveResult { - return this.moveHelper.getPositionUp(model, lineNumber, column, leftoverVisibleColumns, count, allowMoveOnFirstLine); - } - - public getPositionDown(model: ICursorMoveHelperModel, lineNumber: number, column: number, leftoverVisibleColumns: number, count: number, allowMoveOnLastLine: boolean): IMoveResult { - return this.moveHelper.getPositionDown(model, lineNumber, column, leftoverVisibleColumns, count, allowMoveOnLastLine); + this.moveHelper = new CursorMoveHelper(config); } public getColumnAtBeginningOfLine(model: ICursorMoveHelperModel, lineNumber: number, column: number): number { @@ -2253,10 +2228,6 @@ class CursorHelper { return this.moveHelper.visibleColumnFromColumn(model, lineNumber, column); } - public columnFromVisibleColumn(model: ICursorMoveHelperModel, lineNumber: number, column: number): number { - return this.moveHelper.columnFromVisibleColumn(model, lineNumber, column); - } - private static _createWord(lineContent: string, wordType: WordType, start: number, end: number): IFindWordResult { // console.log('WORD ==> ' + start + ' => ' + end + ':::: <<<' + lineContent.substring(start, end) + '>>>'); return { start: start, end: end, wordType: wordType }; diff --git a/src/vs/editor/common/core/lineTokens.ts b/src/vs/editor/common/core/lineTokens.ts index 35d568c3c91..ff7f049978b 100644 --- a/src/vs/editor/common/core/lineTokens.ts +++ b/src/vs/editor/common/core/lineTokens.ts @@ -8,6 +8,33 @@ import { TokensBinaryEncoding, TokensInflatorMap } from 'vs/editor/common/model/ import { ModeTransition } from 'vs/editor/common/core/modeTransition'; import { ViewLineToken } from 'vs/editor/common/core/viewLineToken'; +/** + * A standard token type. Values are 2^x such that a bit mask can be used. + */ +export const enum StandardTokenType { + Other = 0, + Comment = 1, + String = 2, + RegEx = 4 +} + +const STANDARD_TOKEN_TYPE_REGEXP = /\b(comment|string|regex)\b/; +function toStandardTokenType(tokenType: string): StandardTokenType { + let m = tokenType.match(STANDARD_TOKEN_TYPE_REGEXP); + if (!m) { + return StandardTokenType.Other; + } + switch (m[1]) { + case 'comment': + return StandardTokenType.Comment; + case 'string': + return StandardTokenType.String; + case 'regex': + return StandardTokenType.RegEx; + } + throw new Error('Unexpected match for standard token type!'); +} + export class LineToken { _lineTokenBrand: void; @@ -17,7 +44,7 @@ export class LineToken { public readonly startOffset: number; public readonly endOffset: number; - public readonly type: string; + public readonly standardType: StandardTokenType; public readonly modeId: string; public readonly hasPrev: boolean; public readonly hasNext: boolean; @@ -29,7 +56,7 @@ export class LineToken { this.startOffset = this._source.getTokenStartOffset(this._tokenIndex); this.endOffset = this._source.getTokenEndOffset(this._tokenIndex); - this.type = this._source.getTokenType(this._tokenIndex); + this.standardType = this._source.getStandardTokenType(this._tokenIndex); this.modeId = this._source.modeTransitions[this._modeIndex].modeId; this.hasPrev = (this._tokenIndex > 0); this.hasNext = (this._tokenIndex + 1 < this._source.getTokenCount()); @@ -75,23 +102,29 @@ export class LineToken { export class LineTokens { _lineTokensBrand: void; - private _map: TokensInflatorMap; - private _tokens: number[]; - private _textLength: number; + private readonly _map: TokensInflatorMap; + private readonly _tokens: number[]; + private readonly _text: string; + private readonly _textLength: number; readonly modeTransitions: ModeTransition[]; - constructor(map: TokensInflatorMap, tokens: number[], modeTransitions: ModeTransition[], textLength: number) { + constructor(map: TokensInflatorMap, tokens: number[], modeTransitions: ModeTransition[], text: string) { this._map = map; this._tokens = tokens; this.modeTransitions = modeTransitions; - this._textLength = textLength; + this._text = text; + this._textLength = this._text.length; } public getTokenCount(): number { return this._tokens.length; } + public getLineContent(): string { + return this._text; + } + public getTokenStartOffset(tokenIndex: number): number { return TokensBinaryEncoding.getStartIndex(this._tokens[tokenIndex]); } @@ -100,6 +133,10 @@ export class LineTokens { return TokensBinaryEncoding.getType(this._map, this._tokens[tokenIndex]); } + public getStandardTokenType(tokenIndex: number): StandardTokenType { + return toStandardTokenType(this.getTokenType(tokenIndex)); + } + public getTokenEndOffset(tokenIndex: number): number { if (tokenIndex + 1 < this._tokens.length) { return TokensBinaryEncoding.getStartIndex(this._tokens[tokenIndex + 1]); @@ -107,26 +144,15 @@ export class LineTokens { return this._textLength; } - public equals(other: LineTokens): boolean { - if (other instanceof LineTokens) { - if (this._map !== other._map) { - return false; - } - if (this._tokens.length !== other._tokens.length) { - return false; - } - for (let i = 0, len = this._tokens.length; i < len; i++) { - if (this._tokens[i] !== other._tokens[i]) { - return false; - } - } - return true; - } - if (!(other instanceof LineTokens)) { - return false; - } - } - + /** + * Find the token containing offset `offset`. + * For example, with the following tokens [0, 5), [5, 9), [9, infinity) + * Searching for 0, 1, 2, 3 or 4 will return 0. + * Searching for 5, 6, 7 or 8 will return 1. + * Searching for 9, 10, 11, ... will return 2. + * @param offset The search offset + * @return The index of the token containing the offset. + */ public findTokenIndexAtOffset(offset: number): number { return TokensBinaryEncoding.findIndexOfOffset(this._tokens, offset); } diff --git a/src/vs/editor/common/editorCommon.ts b/src/vs/editor/common/editorCommon.ts index 0074cbe6e8d..1b041b73321 100644 --- a/src/vs/editor/common/editorCommon.ts +++ b/src/vs/editor/common/editorCommon.ts @@ -10,8 +10,8 @@ import * as types from 'vs/base/common/types'; import URI from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; import { ServicesAccessor, IConstructorSignature1 } from 'vs/platform/instantiation/common/instantiation'; -import { ILineContext, IMode } from 'vs/editor/common/modes'; -import { LineTokens } from 'vs/editor/common/core/lineTokens'; +import { IMode } from 'vs/editor/common/modes'; +import { LineTokens, StandardTokenType } from 'vs/editor/common/core/lineTokens'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { IDisposable } from 'vs/base/common/lifecycle'; import { Position } from 'vs/editor/common/core/position'; @@ -1295,7 +1295,7 @@ export interface IWordRange { * @internal */ export interface ITokenInfo { - readonly type: string; + readonly standardType: StandardTokenType; readonly lineNumber: number; readonly startColumn: number; readonly endColumn: number; @@ -1848,17 +1848,6 @@ export interface IReadOnlyModel extends ITextModel { getWordUntilPosition(position: IPosition): IWordAtPosition; } -/** - * @internal - */ -export interface IRichEditBracket { - modeId: string; - open: string; - close: string; - forwardRegex: RegExp; - reversedRegex: RegExp; -} - /** * @internal */ @@ -1882,12 +1871,6 @@ export interface ITokenizedModel extends ITextModel { */ getLineTokens(lineNumber: number, inaccurateTokensAcceptable?: boolean): LineTokens; - /** - * Tokenize if necessary and get the tokenization result for the line `lineNumber`, as returned by the language mode. - * @internal - */ - getLineContext(lineNumber: number): ILineContext; - /** * Get the current language mode associated with the model. */ diff --git a/src/vs/editor/common/model/modelLine.ts b/src/vs/editor/common/model/modelLine.ts index 2b1ce5aef7e..6bb832b77ba 100644 --- a/src/vs/editor/common/model/modelLine.ts +++ b/src/vs/editor/common/model/modelLine.ts @@ -195,8 +195,6 @@ export class ModelLine { } public getTokens(map: TokensInflatorMap): LineTokens { - const textLength = this._text.length; - let lineTokens = this._lineTokens; if (!lineTokens) { if (this._text.length === 0) { @@ -206,7 +204,7 @@ export class ModelLine { } } - return new LineTokens(map, lineTokens, this.getModeTransitions(map.topLevelModeId), textLength); + return new LineTokens(map, lineTokens, this.getModeTransitions(map.topLevelModeId), this._text); } // --- END TOKENS diff --git a/src/vs/editor/common/model/textModelWithTokens.ts b/src/vs/editor/common/model/textModelWithTokens.ts index 873cec88f39..643efba0876 100644 --- a/src/vs/editor/common/model/textModelWithTokens.ts +++ b/src/vs/editor/common/model/textModelWithTokens.ts @@ -11,20 +11,19 @@ import { StopWatch } from 'vs/base/common/stopwatch'; import * as timer from 'vs/base/common/timer'; import { Range } from 'vs/editor/common/core/range'; import * as editorCommon from 'vs/editor/common/editorCommon'; -import { ModelLine } from 'vs/editor/common/model/modelLine'; import { TextModel } from 'vs/editor/common/model/textModel'; -import { WordHelper } from 'vs/editor/common/model/textModelWithTokensHelpers'; import { TokenIterator } from 'vs/editor/common/model/tokenIterator'; -import { ITokenizationSupport, ILineContext, ILineTokens, IMode, IState, TokenizationRegistry, IRichEditBrackets } from 'vs/editor/common/modes'; +import { ITokenizationSupport, ILineTokens, IMode, IState, TokenizationRegistry } from 'vs/editor/common/modes'; import { NULL_MODE_ID, nullTokenize } from 'vs/editor/common/modes/nullMode'; import { ignoreBracketsInToken } from 'vs/editor/common/modes/supports'; -import { BracketsUtils } from 'vs/editor/common/modes/supports/richEditBrackets'; +import { BracketsUtils, RichEditBrackets, RichEditBracket } from 'vs/editor/common/modes/supports/richEditBrackets'; import { ModeTransition } from 'vs/editor/common/core/modeTransition'; import { TokensInflatorMap } from 'vs/editor/common/model/tokensBinaryEncoding'; import { Position } from 'vs/editor/common/core/position'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { Token } from 'vs/editor/common/core/token'; import { LineTokens, LineToken } from 'vs/editor/common/core/lineTokens'; +import { getWordAtText } from 'vs/editor/common/model/wordHelper'; class Mode implements IMode { @@ -74,42 +73,6 @@ class ModelTokensChangedEventBuilder { } } -/** - * TODO@Alex: remove this wrapper - */ -class LineContext implements ILineContext { - - public modeTransitions: ModeTransition[]; - private _text: string; - private _lineTokens: LineTokens; - - constructor(topLevelModeId: string, line: ModelLine, map: TokensInflatorMap) { - this.modeTransitions = line.getModeTransitions(topLevelModeId); - this._text = line.text; - this._lineTokens = line.getTokens(map); - } - - public getLineContent(): string { - return this._text; - } - - public getTokenCount(): number { - return this._lineTokens.getTokenCount(); - } - - public getTokenStartOffset(tokenIndex: number): number { - return this._lineTokens.getTokenStartOffset(tokenIndex); - } - - public getTokenType(tokenIndex: number): string { - return this._lineTokens.getTokenType(tokenIndex); - } - - public findIndexOfOffset(offset: number): number { - return this._lineTokens.findTokenIndexAtOffset(offset); - } -} - export class TextModelWithTokens extends TextModel implements editorCommon.ITokenizedModel { private static MODE_TOKENIZATION_FAILED_MSG = nls.localize('mode.tokenizationSupportFailed', "The mode has failed while tokenizing the input."); @@ -239,18 +202,6 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke return this._lines[lineNumber - 1].getTokens(this._tokensInflatorMap); } - public getLineContext(lineNumber: number): ILineContext { - if (lineNumber < 1 || lineNumber > this.getLineCount()) { - throw new Error('Illegal value ' + lineNumber + ' for `lineNumber`'); - } - - this._withModelTokensChangedEventBuilder((eventBuilder) => { - this._updateTokensUntilLine(eventBuilder, lineNumber, true); - }); - - return new LineContext(this.getModeId(), this._lines[lineNumber - 1], this._tokensInflatorMap); - } - public getMode(): IMode { return new Mode(this._languageId); } @@ -489,10 +440,6 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke // Having tokens allows implementing additional helper methods - protected _getWordDefinition(): RegExp { - return WordHelper.massageWordDefinitionOf(this.getModeId()); - } - public getWordAtPosition(_position: editorCommon.IPosition): editorCommon.IWordAtPosition { this._assertNotDisposed(); let position = this.validatePosition(_position); @@ -500,11 +447,44 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke if (this._invalidLineStartIndex <= position.lineNumber) { // this line is not tokenized - return WordHelper.getWordAtPosition(lineContent, position.column, this.getModeId(), null); + return getWordAtText( + position.column, + LanguageConfigurationRegistry.getWordDefinition(this.getModeId()), + lineContent, + 0 + ); } let modeTransitions = this._lines[position.lineNumber - 1].getModeTransitions(this.getModeId()); - return WordHelper.getWordAtPosition(lineContent, position.column, this.getModeId(), modeTransitions); + let offset = position.column - 1; + let modeIndex = ModeTransition.findIndexInSegmentsArray(modeTransitions, offset); + + let modeStartOffset = modeTransitions[modeIndex].startIndex; + let modeEndOffset = (modeIndex + 1 < modeTransitions.length ? modeTransitions[modeIndex + 1].startIndex : lineContent.length); + let modeId = modeTransitions[modeIndex].modeId; + + let result = getWordAtText( + position.column, + LanguageConfigurationRegistry.getWordDefinition(modeId), + lineContent.substring(modeStartOffset, modeEndOffset), + modeStartOffset + ); + + if (!result && modeIndex > 0 && modeTransitions[modeIndex].startIndex === offset) { + // The position is right at the beginning of `modeIndex`, so try looking at `modeIndex` - 1 too + + let prevModeStartOffset = modeTransitions[modeIndex - 1].startIndex; + let prevModeId = modeTransitions[modeIndex - 1].modeId; + + result = getWordAtText( + position.column, + LanguageConfigurationRegistry.getWordDefinition(prevModeId), + lineContent.substring(prevModeStartOffset, modeStartOffset), + prevModeStartOffset + ); + } + + return result; } public getWordUntilPosition(position: editorCommon.IPosition): editorCommon.IWordAtPosition { @@ -568,7 +548,7 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke const currentModeBrackets = LanguageConfigurationRegistry.getBracketsSupport(currentToken.modeId); // check that the token is not to be ignored - if (currentModeBrackets && !ignoreBracketsInToken(currentToken.type)) { + if (currentModeBrackets && !ignoreBracketsInToken(currentToken.standardType)) { // limit search to not go before `maxBracketLength` let searchStartOffset = Math.max(currentToken.startOffset, position.column - 1 - currentModeBrackets.maxBracketLength); // limit search to not go after `maxBracketLength` @@ -619,7 +599,7 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke const prevModeBrackets = LanguageConfigurationRegistry.getBracketsSupport(prevToken.modeId); // check that previous token is not to be ignored - if (prevModeBrackets && !ignoreBracketsInToken(prevToken.type)) { + if (prevModeBrackets && !ignoreBracketsInToken(prevToken.standardType)) { // limit search in case previous token is very large, there's no need to go beyond `maxBracketLength` const searchStartOffset = Math.max(prevToken.startOffset, position.column - 1 - prevModeBrackets.maxBracketLength); const searchEndOffset = currentToken.startOffset; @@ -643,7 +623,7 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke return null; } - private _matchFoundBracket(foundBracket: Range, data: editorCommon.IRichEditBracket, isOpen: boolean): [Range, Range] { + private _matchFoundBracket(foundBracket: Range, data: RichEditBracket, isOpen: boolean): [Range, Range] { if (isOpen) { let matched = this._findMatchingBracketDown(data, foundBracket.getEndPosition()); if (matched) { @@ -659,7 +639,7 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke return null; } - private _findMatchingBracketUp(bracket: editorCommon.IRichEditBracket, position: Position): Range { + private _findMatchingBracketUp(bracket: RichEditBracket, position: Position): Range { // console.log('_findMatchingBracketUp: ', 'bracket: ', JSON.stringify(bracket), 'startPosition: ', String(position)); const modeId = bracket.modeId; @@ -683,7 +663,7 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke } while (currentToken) { - if (currentToken.modeId === modeId && !ignoreBracketsInToken(currentToken.type)) { + if (currentToken.modeId === modeId && !ignoreBracketsInToken(currentToken.standardType)) { while (true) { let r = BracketsUtils.findPrevBracketInToken(reversedBracketRegex, lineNumber, lineText, currentToken.startOffset, searchStopOffset); @@ -718,7 +698,7 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke return null; } - private _findMatchingBracketDown(bracket: editorCommon.IRichEditBracket, position: Position): Range { + private _findMatchingBracketDown(bracket: RichEditBracket, position: Position): Range { // console.log('_findMatchingBracketDown: ', 'bracket: ', JSON.stringify(bracket), 'startPosition: ', String(position)); const modeId = bracket.modeId; @@ -742,7 +722,7 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke } while (currentToken) { - if (currentToken.modeId === modeId && !ignoreBracketsInToken(currentToken.type)) { + if (currentToken.modeId === modeId && !ignoreBracketsInToken(currentToken.standardType)) { while (true) { let r = BracketsUtils.findNextBracketInToken(bracketRegex, lineNumber, lineText, searchStartOffset, currentToken.endOffset); if (!r) { @@ -780,7 +760,7 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke const position = this.validatePosition(_position); let modeId: string = null; - let modeBrackets: IRichEditBrackets; + let modeBrackets: RichEditBrackets; for (let lineNumber = position.lineNumber; lineNumber >= 1; lineNumber--) { const lineTokens = this._lines[lineNumber - 1].getTokens(this._tokensInflatorMap); const lineText = this._lines[lineNumber - 1].text; @@ -802,7 +782,7 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke modeId = currentToken.modeId; modeBrackets = LanguageConfigurationRegistry.getBracketsSupport(modeId); } - if (modeBrackets && !ignoreBracketsInToken(currentToken.type)) { + if (modeBrackets && !ignoreBracketsInToken(currentToken.standardType)) { let r = BracketsUtils.findPrevBracketInToken(modeBrackets.reversedRegex, lineNumber, lineText, currentToken.startOffset, searchStopOffset); if (r) { return this._toFoundBracket(modeBrackets, r); @@ -823,7 +803,7 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke const position = this.validatePosition(_position); let modeId: string = null; - let modeBrackets: IRichEditBrackets; + let modeBrackets: RichEditBrackets; for (let lineNumber = position.lineNumber, lineCount = this.getLineCount(); lineNumber <= lineCount; lineNumber++) { const lineTokens = this._lines[lineNumber - 1].getTokens(this._tokensInflatorMap); const lineText = this._lines[lineNumber - 1].text; @@ -845,7 +825,7 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke modeId = currentToken.modeId; modeBrackets = LanguageConfigurationRegistry.getBracketsSupport(modeId); } - if (modeBrackets && !ignoreBracketsInToken(currentToken.type)) { + if (modeBrackets && !ignoreBracketsInToken(currentToken.standardType)) { let r = BracketsUtils.findNextBracketInToken(modeBrackets.forwardRegex, lineNumber, lineText, searchStartOffset, currentToken.endOffset); if (r) { return this._toFoundBracket(modeBrackets, r); @@ -862,7 +842,7 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke return null; } - private _toFoundBracket(modeBrackets: IRichEditBrackets, r: Range): editorCommon.IFoundBracket { + private _toFoundBracket(modeBrackets: RichEditBrackets, r: Range): editorCommon.IFoundBracket { if (!r) { return null; } diff --git a/src/vs/editor/common/model/textModelWithTokensHelpers.ts b/src/vs/editor/common/model/textModelWithTokensHelpers.ts deleted file mode 100644 index 0c88eb17de3..00000000000 --- a/src/vs/editor/common/model/textModelWithTokensHelpers.ts +++ /dev/null @@ -1,49 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; - -import { IWordAtPosition } from 'vs/editor/common/editorCommon'; -import { ModeTransition } from 'vs/editor/common/core/modeTransition'; -import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; -import { getWordAtText, ensureValidWordDefinition } from 'vs/editor/common/model/wordHelper'; - -export class WordHelper { - - public static massageWordDefinitionOf(modeId: string): RegExp { - let wordDefinition = LanguageConfigurationRegistry.getWordDefinition(modeId); - return ensureValidWordDefinition(wordDefinition); - } - - private static _getWordAtColumn(txt: string, column: number, modeIndex: number, modeTransitions: ModeTransition[]): IWordAtPosition { - let modeStartIndex = modeTransitions[modeIndex].startIndex; - let modeEndIndex = (modeIndex + 1 < modeTransitions.length ? modeTransitions[modeIndex + 1].startIndex : txt.length); - let modeId = modeTransitions[modeIndex].modeId; - - return getWordAtText( - column, WordHelper.massageWordDefinitionOf(modeId), - txt.substring(modeStartIndex, modeEndIndex), modeStartIndex - ); - } - - public static getWordAtPosition(lineContent: string, column: number, topModeId: string, modeTransitions: ModeTransition[]): IWordAtPosition { - - if (!modeTransitions || modeTransitions.length === 0) { - return getWordAtText(column, WordHelper.massageWordDefinitionOf(topModeId), lineContent, 0); - } - - let result: IWordAtPosition = null; - let columnIndex = column - 1; - let modeIndex = ModeTransition.findIndexInSegmentsArray(modeTransitions, columnIndex); - - result = WordHelper._getWordAtColumn(lineContent, column, modeIndex, modeTransitions); - - if (!result && modeIndex > 0 && modeTransitions[modeIndex].startIndex === columnIndex) { - // The position is right at the beginning of `modeIndex`, so try looking at `modeIndex` - 1 too - result = WordHelper._getWordAtColumn(lineContent, column, modeIndex - 1, modeTransitions); - } - - return result; - } -} diff --git a/src/vs/editor/common/model/tokenIterator.ts b/src/vs/editor/common/model/tokenIterator.ts index f37dfb66e4e..11026ec137c 100644 --- a/src/vs/editor/common/model/tokenIterator.ts +++ b/src/vs/editor/common/model/tokenIterator.ts @@ -5,24 +5,24 @@ 'use strict'; import * as editorCommon from 'vs/editor/common/editorCommon'; -import { LineToken } from 'vs/editor/common/core/lineTokens'; +import { LineToken, StandardTokenType } from 'vs/editor/common/core/lineTokens'; import { Position } from 'vs/editor/common/core/position'; class TokenInfo implements editorCommon.ITokenInfo { _tokenInfoBrand: void; - _actual: LineToken; - public lineNumber: number; - public startColumn: number; - public endColumn: number; - public type: string; + readonly _actual: LineToken; + public readonly lineNumber: number; + public readonly startColumn: number; + public readonly endColumn: number; + public readonly standardType: StandardTokenType; constructor(actual: LineToken, lineNumber: number) { this._actual = actual; this.lineNumber = lineNumber; this.startColumn = this._actual.startOffset + 1; this.endColumn = this._actual.endOffset + 1; - this.type = this._actual.type; + this.standardType = this._actual.standardType; } } diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index cfa97be48bc..13e4a00d227 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -36,47 +36,6 @@ export interface IModeDescriptor { id: string; } -/** - * @internal - */ -export interface ILineContext { - /** - * Get the content of the line. - */ - getLineContent(): string; - - /** - * The mode transitions on this line. - */ - modeTransitions: ModeTransition[]; - - /** - * Get the number of tokens on this line. - */ - getTokenCount(): number; - - /** - * Get the start offset of the token at `tokenIndex`. - */ - getTokenStartOffset(tokenIndex: number): number; - - /** - * Get the type of the token at `tokenIndex`. - */ - getTokenType(tokenIndex: number): string; - - /** - * Find the token containing offset `offset`. - * For example, with the following tokens [0, 5), [5, 9), [9, infinity) - * Searching for 0, 1, 2, 3 or 4 will return 0. - * Searching for 5, 6, 7 or 8 will return 1. - * Searching for 9, 10, 11, ... will return 2. - * @param offset The search offset - * @return The index of the token containing the offset. - */ - findIndexOfOffset(offset: number): number; -} - /** * A mode. Will soon be obsolete. */ @@ -702,129 +661,6 @@ export interface CodeLensProvider { resolveCodeLens?(model: editorCommon.IReadOnlyModel, codeLens: ICodeLensSymbol, token: CancellationToken): ICodeLensSymbol | Thenable; } -/** - * A tuple of two characters, like a pair of - * opening and closing brackets. - */ -export type CharacterPair = [string, string]; - -export interface IAutoClosingPairConditional extends IAutoClosingPair { - notIn?: string[]; - -} - -/** - * Interface used to support electric characters - * @internal - */ -export interface IElectricAction { - // Only one of the following properties should be defined: - - // The line will be indented at the same level of the line - // which contains the matching given bracket type. - matchOpenBracket?: string; - - // The text will be appended after the electric character. - appendText?: string; - - // The number of characters to advance the cursor, useful with appendText - advanceCount?: number; -} - -/** - * Describes what to do with the indentation when pressing Enter. - */ -export enum IndentAction { - /** - * Insert new line and copy the previous line's indentation. - */ - None = 0, - /** - * Insert new line and indent once (relative to the previous line's indentation). - */ - Indent = 1, - /** - * Insert two new lines: - * - the first one indented which will hold the cursor - * - the second one at the same indentation level - */ - IndentOutdent = 2, - /** - * Insert new line and outdent once (relative to the previous line's indentation). - */ - Outdent = 3 -} - -/** - * Describes what to do when pressing Enter. - */ -export interface EnterAction { - /** - * Describe what to do with the indentation. - */ - indentAction: IndentAction; - /** - * Describes text to be appended after the new line and after the indentation. - */ - appendText?: string; - /** - * Describes the number of characters to remove from the new line's indentation. - */ - removeText?: number; -} - -/** - * @internal - */ -export interface IRichEditElectricCharacter { - getElectricCharacters(): string[]; - // Should return opening bracket type to match indentation with - onElectricCharacter(context: ILineContext, offset: number): IElectricAction; -} - -/** - * @internal - */ -export interface IRichEditOnEnter { - onEnter(model: editorCommon.ITokenizedModel, position: editorCommon.IPosition): EnterAction; -} - -/** - * Interface used to support insertion of mode specific comments. - * @internal - */ -export interface ICommentsConfiguration { - lineCommentToken?: string; - blockCommentStartToken?: string; - blockCommentEndToken?: string; -} - -export interface IAutoClosingPair { - open: string; - close: string; -} - -/** - * @internal - */ -export interface IRichEditCharacterPair { - getAutoClosingPairs(): IAutoClosingPairConditional[]; - shouldAutoClosePair(character: string, context: ILineContext, offset: number): boolean; - getSurroundingPairs(): IAutoClosingPair[]; -} - -/** - * @internal - */ -export interface IRichEditBrackets { - maxBracketLength: number; - forwardRegex: RegExp; - reversedRegex: RegExp; - brackets: editorCommon.IRichEditBracket[]; - textIsBracket: { [text: string]: editorCommon.IRichEditBracket; }; - textIsOpenBracket: { [text: string]: boolean; }; -} - // --- feature registries ------ /** diff --git a/src/vs/editor/common/modes/languageConfiguration.ts b/src/vs/editor/common/modes/languageConfiguration.ts new file mode 100644 index 00000000000..a0afe5f5a0f --- /dev/null +++ b/src/vs/editor/common/modes/languageConfiguration.ts @@ -0,0 +1,225 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { StandardTokenType } from 'vs/editor/common/core/lineTokens'; + +/** + * Describes how comments for a language work. + */ +export interface CommentRule { + /** + * The line comment token, like `// this is a comment` + */ + lineComment?: string; + /** + * The block comment character pair, like `/* block comment */` + */ + blockComment?: CharacterPair; +} + +/** + * The language configuration interface defines the contract between extensions and + * various editor features, like automatic bracket insertion, automatic indentation etc. + */ +export interface LanguageConfiguration { + /** + * The language's comment settings. + */ + comments?: CommentRule; + /** + * The language's brackets. + * This configuration implicitly affects pressing Enter around these brackets. + */ + brackets?: CharacterPair[]; + /** + * The language's word definition. + * If the language supports Unicode identifiers (e.g. JavaScript), it is preferable + * to provide a word definition that uses exclusion of known separators. + * e.g.: A regex that matches anything except known separators (and dot is allowed to occur in a floating point number): + * /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g + */ + wordPattern?: RegExp; + /** + * The language's indentation settings. + */ + indentationRules?: IndentationRule; + /** + * The language's rules to be evaluated when pressing Enter. + */ + onEnterRules?: OnEnterRule[]; + /** + * The language's auto closing pairs. The 'close' character is automatically inserted with the + * 'open' character is typed. If not set, the configured brackets will be used. + */ + autoClosingPairs?: IAutoClosingPairConditional[]; + /** + * The language's surrounding pairs. When the 'open' character is typed on a selection, the + * selected string is surrounded by the open and close characters. If not set, the autoclosing pairs + * settings will be used. + */ + surroundingPairs?: IAutoClosingPair[]; + /** + * **Deprecated** Do not use. + * + * @deprecated Will be replaced by a better API soon. + */ + __electricCharacterSupport?: IBracketElectricCharacterContribution; +} + +/** + * Describes indentation rules for a language. + */ +export interface IndentationRule { + /** + * If a line matches this pattern, then all the lines after it should be unindendented once (until another rule matches). + */ + decreaseIndentPattern: RegExp; + /** + * If a line matches this pattern, then all the lines after it should be indented once (until another rule matches). + */ + increaseIndentPattern: RegExp; + /** + * If a line matches this pattern, then **only the next line** after it should be indented once. + */ + indentNextLinePattern?: RegExp; + /** + * If a line matches this pattern, then its indentation should not be changed and it should not be evaluated against the other rules. + */ + unIndentedLinePattern?: RegExp; +} + +/** + * Describes a rule to be evaluated when pressing Enter. + */ +export interface OnEnterRule { + /** + * This rule will only execute if the text before the cursor matches this regular expression. + */ + beforeText: RegExp; + /** + * This rule will only execute if the text after the cursor matches this regular expression. + */ + afterText?: RegExp; + /** + * The action to execute. + */ + action: EnterAction; +} + +export interface IBracketElectricCharacterContribution { + docComment?: IDocComment; +} + +/** + * Definition of documentation comments (e.g. Javadoc/JSdoc) + */ +export interface IDocComment { + /** + * The string that starts a doc comment (e.g. '/**') + */ + open: string; + /** + * The string that appears on the last line and closes the doc comment (e.g. ' * /'). + */ + close: string; +} + +/** + * A tuple of two characters, like a pair of + * opening and closing brackets. + */ +export type CharacterPair = [string, string]; + +export interface IAutoClosingPair { + open: string; + close: string; +} + +export interface IAutoClosingPairConditional extends IAutoClosingPair { + notIn?: string[]; +} + +/** + * Describes what to do with the indentation when pressing Enter. + */ +export enum IndentAction { + /** + * Insert new line and copy the previous line's indentation. + */ + None = 0, + /** + * Insert new line and indent once (relative to the previous line's indentation). + */ + Indent = 1, + /** + * Insert two new lines: + * - the first one indented which will hold the cursor + * - the second one at the same indentation level + */ + IndentOutdent = 2, + /** + * Insert new line and outdent once (relative to the previous line's indentation). + */ + Outdent = 3 +} + +/** + * Describes what to do when pressing Enter. + */ +export interface EnterAction { + /** + * Describe what to do with the indentation. + */ + indentAction: IndentAction; + /** + * Describes text to be appended after the new line and after the indentation. + */ + appendText?: string; + /** + * Describes the number of characters to remove from the new line's indentation. + */ + removeText?: number; +} + +/** + * @internal + */ +export class StandardAutoClosingPairConditional { + _standardAutoClosingPairConditionalBrand: void; + + readonly open: string; + readonly close: string; + private readonly _standardTokenMask: number; + + constructor(source: IAutoClosingPairConditional) { + this.open = source.open; + this.close = source.close; + + // initially allowed in all tokens + this._standardTokenMask = 0; + + if (Array.isArray(source.notIn)) { + for (let i = 0, len = source.notIn.length; i < len; i++) { + let notIn = source.notIn[i]; + switch (notIn) { + case 'string': + this._standardTokenMask |= StandardTokenType.String; + break; + case 'comment': + this._standardTokenMask |= StandardTokenType.Comment; + break; + case 'regex': + this._standardTokenMask |= StandardTokenType.RegEx; + break; + } + } + } + } + + public isOK(standardToken: StandardTokenType): boolean { + return (this._standardTokenMask & standardToken) === 0; + } +} diff --git a/src/vs/editor/common/modes/languageConfigurationRegistry.ts b/src/vs/editor/common/modes/languageConfigurationRegistry.ts index 3153f9563c2..37c78d0f801 100644 --- a/src/vs/editor/common/modes/languageConfigurationRegistry.ts +++ b/src/vs/editor/common/modes/languageConfigurationRegistry.ts @@ -4,84 +4,27 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { - ICommentsConfiguration, IRichEditBrackets, IRichEditCharacterPair, IAutoClosingPair, - IAutoClosingPairConditional, IRichEditOnEnter, CharacterPair, - IRichEditElectricCharacter, EnterAction, IndentAction -} from 'vs/editor/common/modes'; import { CharacterPairSupport } from 'vs/editor/common/modes/supports/characterPair'; -import { BracketElectricCharacterSupport, IBracketElectricCharacterContribution } from 'vs/editor/common/modes/supports/electricCharacter'; -import { IndentationRule, OnEnterRule, IOnEnterSupportOptions, OnEnterSupport } from 'vs/editor/common/modes/supports/onEnter'; +import { BracketElectricCharacterSupport, IElectricAction } from 'vs/editor/common/modes/supports/electricCharacter'; +import { IOnEnterSupportOptions, OnEnterSupport } from 'vs/editor/common/modes/supports/onEnter'; import { RichEditBrackets } from 'vs/editor/common/modes/supports/richEditBrackets'; import Event, { Emitter } from 'vs/base/common/event'; import { ITokenizedModel } from 'vs/editor/common/editorCommon'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { Position } from 'vs/editor/common/core/position'; import * as strings from 'vs/base/common/strings'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { DEFAULT_WORD_REGEXP } from 'vs/editor/common/model/wordHelper'; +import { DEFAULT_WORD_REGEXP, ensureValidWordDefinition } from 'vs/editor/common/model/wordHelper'; +import { createScopedLineTokens } from 'vs/editor/common/modes/supports'; +import { LineTokens } from 'vs/editor/common/core/lineTokens'; +import { IndentAction, EnterAction, IAutoClosingPair, LanguageConfiguration } from 'vs/editor/common/modes/languageConfiguration'; /** - * Describes how comments for a language work. + * Interface used to support insertion of mode specific comments. */ -export interface CommentRule { - /** - * The line comment token, like `// this is a comment` - */ - lineComment?: string; - /** - * The block comment character pair, like `/* block comment */` - */ - blockComment?: CharacterPair; -} - -/** - * The language configuration interface defines the contract between extensions and - * various editor features, like automatic bracket insertion, automatic indentation etc. - */ -export interface LanguageConfiguration { - /** - * The language's comment settings. - */ - comments?: CommentRule; - /** - * The language's brackets. - * This configuration implicitly affects pressing Enter around these brackets. - */ - brackets?: CharacterPair[]; - /** - * The language's word definition. - * If the language supports Unicode identifiers (e.g. JavaScript), it is preferable - * to provide a word definition that uses exclusion of known separators. - * e.g.: A regex that matches anything except known separators (and dot is allowed to occur in a floating point number): - * /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g - */ - wordPattern?: RegExp; - /** - * The language's indentation settings. - */ - indentationRules?: IndentationRule; - /** - * The language's rules to be evaluated when pressing Enter. - */ - onEnterRules?: OnEnterRule[]; - /** - * The language's auto closing pairs. The 'close' character is automatically inserted with the - * 'open' character is typed. If not set, the configured brackets will be used. - */ - autoClosingPairs?: IAutoClosingPairConditional[]; - /** - * The language's surrounding pairs. When the 'open' character is typed on a selection, the - * selected string is surrounded by the open and close characters. If not set, the autoclosing pairs - * settings will be used. - */ - surroundingPairs?: IAutoClosingPair[]; - /** - * **Deprecated** Do not use. - * - * @deprecated Will be replaced by a better API soon. - */ - __electricCharacterSupport?: IBracketElectricCharacterContribution; +export interface ICommentsConfiguration { + lineCommentToken?: string; + blockCommentStartToken?: string; + blockCommentEndToken?: string; } export class RichEditSupport { @@ -90,10 +33,10 @@ export class RichEditSupport { public electricCharacter: BracketElectricCharacterSupport; public comments: ICommentsConfiguration; - public characterPair: IRichEditCharacterPair; + public characterPair: CharacterPairSupport; public wordDefinition: RegExp; - public onEnter: IRichEditOnEnter; - public brackets: IRichEditBrackets; + public onEnter: OnEnterSupport; + public brackets: RichEditBrackets; constructor(modeId: string, previous: RichEditSupport, rawConf: LanguageConfiguration) { @@ -112,8 +55,8 @@ export class RichEditSupport { this._handleComments(modeId, this._conf); - this.characterPair = new CharacterPairSupport(LanguageConfigurationRegistry, modeId, this._conf); - this.electricCharacter = new BracketElectricCharacterSupport(LanguageConfigurationRegistry, modeId, this.brackets, this.characterPair.getAutoClosingPairs(), this._conf.__electricCharacterSupport); + this.characterPair = new CharacterPairSupport(this._conf); + this.electricCharacter = new BracketElectricCharacterSupport(this.brackets, this.characterPair.getAutoClosingPairs(), this._conf.__electricCharacterSupport); this.wordDefinition = this._conf.wordPattern || DEFAULT_WORD_REGEXP; } @@ -150,7 +93,7 @@ export class RichEditSupport { } if (!empty) { - this.onEnter = new OnEnterSupport(LanguageConfigurationRegistry, modeId, onEnter); + this.onEnter = new OnEnterSupport(onEnter); } } @@ -198,7 +141,9 @@ export class LanguageConfigurationRegistryImpl { return this._entries[modeId]; } - public getElectricCharacterSupport(modeId: string): IRichEditElectricCharacter { + // begin electricCharacter + + private _getElectricCharacterSupport(modeId: string): BracketElectricCharacterSupport { let value = this._getRichEditSupport(modeId); if (!value) { return null; @@ -206,6 +151,28 @@ export class LanguageConfigurationRegistryImpl { return value.electricCharacter || null; } + public getElectricCharacters(modeId: string): string[] { + let electricCharacterSupport = this._getElectricCharacterSupport(modeId); + if (!electricCharacterSupport) { + return []; + } + return electricCharacterSupport.getElectricCharacters(); + } + + /** + * Should return opening bracket type to match indentation with + */ + public onElectricCharacter(context: LineTokens, offset: number): IElectricAction { + let scopedLineTokens = createScopedLineTokens(context, offset); + let electricCharacterSupport = this._getElectricCharacterSupport(scopedLineTokens.modeId); + if (!electricCharacterSupport) { + return null; + } + return electricCharacterSupport.onElectricCharacter(scopedLineTokens, offset - scopedLineTokens.firstCharOffset); + } + + // end electricCharacter + public getComments(modeId: string): ICommentsConfiguration { let value = this._getRichEditSupport(modeId); if (!value) { @@ -214,7 +181,9 @@ export class LanguageConfigurationRegistryImpl { return value.comments || null; } - public getCharacterPairSupport(modeId: string): IRichEditCharacterPair { + // begin characterPair + + private _getCharacterPairSupport(modeId: string): CharacterPairSupport { let value = this._getRichEditSupport(modeId); if (!value) { return null; @@ -222,15 +191,44 @@ export class LanguageConfigurationRegistryImpl { return value.characterPair || null; } + public getAutoClosingPairs(modeId: string): IAutoClosingPair[] { + let characterPairSupport = this._getCharacterPairSupport(modeId); + if (!characterPairSupport) { + return []; + } + return characterPairSupport.getAutoClosingPairs(); + } + + public getSurroundingPairs(modeId: string): IAutoClosingPair[] { + let characterPairSupport = this._getCharacterPairSupport(modeId); + if (!characterPairSupport) { + return []; + } + return characterPairSupport.getSurroundingPairs(); + } + + public shouldAutoClosePair(character: string, context: LineTokens, offset: number): boolean { + let scopedLineTokens = createScopedLineTokens(context, offset); + let characterPairSupport = this._getCharacterPairSupport(scopedLineTokens.modeId); + if (!characterPairSupport) { + return false; + } + return characterPairSupport.shouldAutoClosePair(character, scopedLineTokens, offset - scopedLineTokens.firstCharOffset); + } + + // end characterPair + public getWordDefinition(modeId: string): RegExp { let value = this._getRichEditSupport(modeId); if (!value) { - return null; + return ensureValidWordDefinition(null); } - return value.wordDefinition || null; + return ensureValidWordDefinition(value.wordDefinition || null); } - public getOnEnterSupport(modeId: string): IRichEditOnEnter { + // begin onEnter + + private _getOnEnterSupport(modeId: string): OnEnterSupport { let value = this._getRichEditSupport(modeId); if (!value) { return null; @@ -239,18 +237,35 @@ export class LanguageConfigurationRegistryImpl { } public getRawEnterActionAtPosition(model: ITokenizedModel, lineNumber: number, column: number): EnterAction { - let result: EnterAction; + let lineTokens = model.getLineTokens(lineNumber, false); + let scopedLineTokens = createScopedLineTokens(lineTokens, column - 1); + let onEnterSupport = this._getOnEnterSupport(scopedLineTokens.modeId); + if (!onEnterSupport) { + return null; + } - let onEnterSupport = this.getOnEnterSupport(model.getMode().getId()); + let scopedLineText = scopedLineTokens.getLineContent(); + let beforeEnterText = scopedLineText.substr(0, column - 1 - scopedLineTokens.firstCharOffset); + let afterEnterText = scopedLineText.substr(column - 1 - scopedLineTokens.firstCharOffset); - if (onEnterSupport) { - try { - result = onEnterSupport.onEnter(model, new Position(lineNumber, column)); - } catch (e) { - onUnexpectedError(e); + let oneLineAboveText = ''; + if (lineNumber > 1 && scopedLineTokens.firstCharOffset === 0) { + // This is not the first line and the entire line belongs to this mode + let oneLineAboveLineTokens = model.getLineTokens(lineNumber - 1, false); + let oneLineAboveMaxColumn = model.getLineMaxColumn(lineNumber - 1); + let oneLineAboveScopedLineTokens = createScopedLineTokens(oneLineAboveLineTokens, oneLineAboveMaxColumn - 1); + if (oneLineAboveScopedLineTokens.modeId === scopedLineTokens.modeId) { + // The line above ends with text belonging to the same mode + oneLineAboveText = oneLineAboveScopedLineTokens.getLineContent(); } } + let result: EnterAction = null; + try { + result = onEnterSupport.onEnter(oneLineAboveText, beforeEnterText, afterEnterText); + } catch (e) { + onUnexpectedError(e); + } return result; } @@ -290,7 +305,9 @@ export class LanguageConfigurationRegistryImpl { }; } - public getBracketsSupport(modeId: string): IRichEditBrackets { + // end onEnter + + public getBracketsSupport(modeId: string): RichEditBrackets { let value = this._getRichEditSupport(modeId); if (!value) { return null; diff --git a/src/vs/editor/common/modes/supports.ts b/src/vs/editor/common/modes/supports.ts index 36d10ab5867..436433a81e4 100644 --- a/src/vs/editor/common/modes/supports.ts +++ b/src/vs/editor/common/modes/supports.ts @@ -8,8 +8,9 @@ import { TPromise } from 'vs/base/common/winjs.base'; import * as modes from 'vs/editor/common/modes'; import { ModeTransition } from 'vs/editor/common/core/modeTransition'; import { Token } from 'vs/editor/common/core/token'; +import { LineTokens, StandardTokenType } from 'vs/editor/common/core/lineTokens'; -export class LineTokens implements modes.ILineTokens { +export class RawLineTokens implements modes.ILineTokens { _lineTokensBrand: void; tokens: Token[]; @@ -27,77 +28,83 @@ export class LineTokens implements modes.ILineTokens { } } -export function handleEvent(context: modes.ILineContext, offset: number, runner: (modeId: string, newContext: modes.ILineContext, offset: number) => T): T { +export function createScopedLineTokens(context: LineTokens, offset: number): ScopedLineTokens { let modeTransitions = context.modeTransitions; if (modeTransitions.length === 1) { - return runner(modeTransitions[0].modeId, context, offset); + return new ScopedLineTokens(context, modeTransitions[0].modeId, 0, context.getTokenCount(), 0, context.getLineContent().length); } let modeIndex = ModeTransition.findIndexInSegmentsArray(modeTransitions, offset); let nestedModeId = modeTransitions[modeIndex].modeId; let modeStartIndex = modeTransitions[modeIndex].startIndex; - let firstTokenInModeIndex = context.findIndexOfOffset(modeStartIndex); - let nextCharacterAfterModeIndex = -1; - let nextTokenAfterMode = -1; + let firstTokenIndex = context.findTokenIndexAtOffset(modeStartIndex); + let lastCharOffset = -1; + let lastTokenIndex = -1; if (modeIndex + 1 < modeTransitions.length) { - nextTokenAfterMode = context.findIndexOfOffset(modeTransitions[modeIndex + 1].startIndex); - nextCharacterAfterModeIndex = context.getTokenStartOffset(nextTokenAfterMode); + lastTokenIndex = context.findTokenIndexAtOffset(modeTransitions[modeIndex + 1].startIndex); + lastCharOffset = context.getTokenStartOffset(lastTokenIndex); } else { - nextTokenAfterMode = context.getTokenCount(); - nextCharacterAfterModeIndex = context.getLineContent().length; + lastTokenIndex = context.getTokenCount(); + lastCharOffset = context.getLineContent().length; } - let firstTokenCharacterOffset = context.getTokenStartOffset(firstTokenInModeIndex); - let newCtx = new FilteredLineContext(context, nestedModeId, firstTokenInModeIndex, nextTokenAfterMode, firstTokenCharacterOffset, nextCharacterAfterModeIndex); - return runner(nestedModeId, newCtx, offset - firstTokenCharacterOffset); + let firstCharOffset = context.getTokenStartOffset(firstTokenIndex); + return new ScopedLineTokens(context, nestedModeId, firstTokenIndex, lastTokenIndex, firstCharOffset, lastCharOffset); } -export class FilteredLineContext implements modes.ILineContext { +export class ScopedLineTokens { + _scopedLineTokensBrand: void; - public modeTransitions: ModeTransition[]; + public readonly modeId: string; + private readonly _actual: LineTokens; + private readonly _firstTokenIndex: number; + private readonly _lastTokenIndex: number; + public readonly firstCharOffset: number; + private readonly _lastCharOffset: number; - private _actual: modes.ILineContext; - private _firstTokenInModeIndex: number; - private _nextTokenAfterMode: number; - private _firstTokenCharacterOffset: number; - private _nextCharacterAfterModeIndex: number; - - constructor(actual: modes.ILineContext, modeId: string, - firstTokenInModeIndex: number, nextTokenAfterMode: number, - firstTokenCharacterOffset: number, nextCharacterAfterModeIndex: number) { - - this.modeTransitions = [new ModeTransition(0, modeId)]; + constructor( + actual: LineTokens, + modeId: string, + firstTokenIndex: number, + lastTokenIndex: number, + firstCharOffset: number, + lastCharOffset: number + ) { this._actual = actual; - this._firstTokenInModeIndex = firstTokenInModeIndex; - this._nextTokenAfterMode = nextTokenAfterMode; - this._firstTokenCharacterOffset = firstTokenCharacterOffset; - this._nextCharacterAfterModeIndex = nextCharacterAfterModeIndex; + this.modeId = modeId; + this._firstTokenIndex = firstTokenIndex; + this._lastTokenIndex = lastTokenIndex; + this.firstCharOffset = firstCharOffset; + this._lastCharOffset = lastCharOffset; } public getLineContent(): string { var actualLineContent = this._actual.getLineContent(); - return actualLineContent.substring(this._firstTokenCharacterOffset, this._nextCharacterAfterModeIndex); + return actualLineContent.substring(this.firstCharOffset, this._lastCharOffset); } public getTokenCount(): number { - return this._nextTokenAfterMode - this._firstTokenInModeIndex; + return this._lastTokenIndex - this._firstTokenIndex; } - public findIndexOfOffset(offset: number): number { - return this._actual.findIndexOfOffset(offset + this._firstTokenCharacterOffset) - this._firstTokenInModeIndex; + public findTokenIndexAtOffset(offset: number): number { + return this._actual.findTokenIndexAtOffset(offset + this.firstCharOffset) - this._firstTokenIndex; } public getTokenStartOffset(tokenIndex: number): number { - return this._actual.getTokenStartOffset(tokenIndex + this._firstTokenInModeIndex) - this._firstTokenCharacterOffset; + return this._actual.getTokenStartOffset(tokenIndex + this._firstTokenIndex) - this.firstCharOffset; } - public getTokenType(tokenIndex: number): string { - return this._actual.getTokenType(tokenIndex + this._firstTokenInModeIndex); + public getStandardTokenType(tokenIndex: number): StandardTokenType { + return this._actual.getStandardTokenType(tokenIndex + this._firstTokenIndex); } } -const IGNORE_IN_TOKENS = /\b(comment|string|regex)\b/; -export function ignoreBracketsInToken(tokenType: string): boolean { - return IGNORE_IN_TOKENS.test(tokenType); +const enum IgnoreBracketsInTokens { + value = StandardTokenType.Comment | StandardTokenType.String | StandardTokenType.RegEx +} + +export function ignoreBracketsInToken(standardTokenType: StandardTokenType): boolean { + return (standardTokenType & IgnoreBracketsInTokens.value) !== 0; } diff --git a/src/vs/editor/common/modes/supports/characterPair.ts b/src/vs/editor/common/modes/supports/characterPair.ts index 998f8fef5ad..d4f72526ae8 100644 --- a/src/vs/editor/common/modes/supports/characterPair.ts +++ b/src/vs/editor/common/modes/supports/characterPair.ts @@ -4,24 +4,23 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { IAutoClosingPair, IAutoClosingPairConditional, ILineContext, IRichEditCharacterPair, CharacterPair } from 'vs/editor/common/modes'; -import { handleEvent } from 'vs/editor/common/modes/supports'; -import { LanguageConfigurationRegistryImpl } from 'vs/editor/common/modes/languageConfigurationRegistry'; +import { ScopedLineTokens } from 'vs/editor/common/modes/supports'; +import { CharacterPair, IAutoClosingPair, IAutoClosingPairConditional, StandardAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration'; -export class CharacterPairSupport implements IRichEditCharacterPair { +export class CharacterPairSupport { - private _registry: LanguageConfigurationRegistryImpl; - private _modeId: string; - private _autoClosingPairs: IAutoClosingPairConditional[]; - private _surroundingPairs: IAutoClosingPair[]; + private readonly _autoClosingPairs: StandardAutoClosingPairConditional[]; + private readonly _surroundingPairs: IAutoClosingPair[]; - constructor(registry: LanguageConfigurationRegistryImpl, modeId: string, config: { brackets?: CharacterPair[]; autoClosingPairs?: IAutoClosingPairConditional[], surroundingPairs?: IAutoClosingPair[] }) { - this._registry = registry; - this._modeId = modeId; - this._autoClosingPairs = config.autoClosingPairs; - if (!this._autoClosingPairs) { - this._autoClosingPairs = config.brackets ? config.brackets.map(b => ({ open: b[0], close: b[1] })) : []; + constructor(config: { brackets?: CharacterPair[]; autoClosingPairs?: IAutoClosingPairConditional[], surroundingPairs?: IAutoClosingPair[] }) { + if (config.autoClosingPairs) { + this._autoClosingPairs = config.autoClosingPairs.map(el => new StandardAutoClosingPairConditional(el)); + } else if (config.brackets) { + this._autoClosingPairs = config.brackets.map(b => new StandardAutoClosingPairConditional({ open: b[0], close: b[1] })); + } else { + this._autoClosingPairs = []; } + this._surroundingPairs = config.surroundingPairs || this._autoClosingPairs; } @@ -29,41 +28,24 @@ export class CharacterPairSupport implements IRichEditCharacterPair { return this._autoClosingPairs; } - public shouldAutoClosePair(character: string, context: ILineContext, offset: number): boolean { - return handleEvent(context, offset, (nestedModeId: string, context: ILineContext, offset: number) => { - if (this._modeId === nestedModeId) { + public shouldAutoClosePair(character: string, context: ScopedLineTokens, offset: number): boolean { + // Always complete on empty line + if (context.getTokenCount() === 0) { + return true; + } - // Always complete on empty line - if (context.getTokenCount() === 0) { - return true; - } + let tokenIndex = context.findTokenIndexAtOffset(offset - 1); + let standardTokenType = context.getStandardTokenType(tokenIndex); - var tokenIndex = context.findIndexOfOffset(offset - 1); - var tokenType = context.getTokenType(tokenIndex); + for (let i = 0; i < this._autoClosingPairs.length; ++i) { + let autoClosingPair = this._autoClosingPairs[i]; - for (var i = 0; i < this._autoClosingPairs.length; ++i) { - if (this._autoClosingPairs[i].open === character) { - if (this._autoClosingPairs[i].notIn) { - for (var notInIndex = 0; notInIndex < this._autoClosingPairs[i].notIn.length; ++notInIndex) { - if (tokenType.indexOf(this._autoClosingPairs[i].notIn[notInIndex]) > -1) { - return false; - } - } - } - break; - } - } - - return true; + if (autoClosingPair.open === character) { + return autoClosingPair.isOK(standardTokenType); } + } - let characterPairSupport = this._registry.getCharacterPairSupport(nestedModeId); - if (characterPairSupport) { - return characterPairSupport.shouldAutoClosePair(character, context, offset); - } - - return null; - }); + return true; } public getSurroundingPairs(): IAutoClosingPair[] { diff --git a/src/vs/editor/common/modes/supports/electricCharacter.ts b/src/vs/editor/common/modes/supports/electricCharacter.ts index b0a0be8b3a9..7e13ce5a656 100644 --- a/src/vs/editor/common/modes/supports/electricCharacter.ts +++ b/src/vs/editor/common/modes/supports/electricCharacter.ts @@ -5,76 +5,41 @@ 'use strict'; import * as strings from 'vs/base/common/strings'; -import * as modes from 'vs/editor/common/modes'; -import { handleEvent, ignoreBracketsInToken } from 'vs/editor/common/modes/supports'; +import { ScopedLineTokens, ignoreBracketsInToken } from 'vs/editor/common/modes/supports'; import { BracketsUtils } from 'vs/editor/common/modes/supports/richEditBrackets'; -import { LanguageConfigurationRegistryImpl } from 'vs/editor/common/modes/languageConfigurationRegistry'; +import { RichEditBrackets } from 'vs/editor/common/modes/supports/richEditBrackets'; +import { IAutoClosingPairConditional, IBracketElectricCharacterContribution, StandardAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration'; /** - * Definition of documentation comments (e.g. Javadoc/JSdoc) + * Interface used to support electric characters + * @internal */ -export interface IDocComment { - scope: string; // What tokens should be used to detect a doc comment (e.g. 'comment.documentation'). - open: string; // The string that starts a doc comment (e.g. '/**') - lineStart: string; // The string that appears at the start of each line, except the first and last (e.g. ' * '). - close?: string; // The string that appears on the last line and closes the doc comment (e.g. ' */'). +export interface IElectricAction { + // Only one of the following properties should be defined: + + // The line will be indented at the same level of the line + // which contains the matching given bracket type. + matchOpenBracket?: string; + + // The text will be appended after the electric character. + appendText?: string; + + // The number of characters to advance the cursor, useful with appendText + advanceCount?: number; } -export interface IBracketElectricCharacterContribution { - docComment?: IDocComment; - embeddedElectricCharacters?: string[]; -} +export class BracketElectricCharacterSupport { -export class BracketElectricCharacterSupport implements modes.IRichEditElectricCharacter { + private readonly _richEditBrackets: RichEditBrackets; + private readonly _complexAutoClosePairs: StandardAutoClosingPairConditional[]; - private _registry: LanguageConfigurationRegistryImpl; - private _modeId: string; - private contribution: IBracketElectricCharacterContribution; - private brackets: Brackets; - - constructor(registry: LanguageConfigurationRegistryImpl, modeId: string, brackets: modes.IRichEditBrackets, autoClosePairs: modes.IAutoClosingPairConditional[], contribution: IBracketElectricCharacterContribution) { - this._registry = registry; - this._modeId = modeId; - this.contribution = contribution || {}; - this.brackets = new Brackets(modeId, brackets, autoClosePairs, this.contribution.docComment); - } - - public getElectricCharacters(): string[] { - if (Array.isArray(this.contribution.embeddedElectricCharacters)) { - return this.contribution.embeddedElectricCharacters.concat(this.brackets.getElectricCharacters()); - } - return this.brackets.getElectricCharacters(); - } - - public onElectricCharacter(context: modes.ILineContext, offset: number): modes.IElectricAction { - return handleEvent(context, offset, (nestedModeId: string, context: modes.ILineContext, offset: number) => { - if (this._modeId === nestedModeId) { - return this.brackets.onElectricCharacter(context, offset); - } - let electricCharacterSupport = this._registry.getElectricCharacterSupport(nestedModeId); - if (electricCharacterSupport) { - return electricCharacterSupport.onElectricCharacter(context, offset); - } - return null; - }); - } -} - - - -export class Brackets { - - private _modeId: string; - private _richEditBrackets: modes.IRichEditBrackets; - private _complexAutoClosePairs: modes.IAutoClosingPairConditional[]; - - constructor(modeId: string, richEditBrackets: modes.IRichEditBrackets, autoClosePairs: modes.IAutoClosingPairConditional[], docComment?: IDocComment) { - this._modeId = modeId; + constructor(richEditBrackets: RichEditBrackets, autoClosePairs: IAutoClosingPairConditional[], contribution: IBracketElectricCharacterContribution) { + contribution = contribution || {}; this._richEditBrackets = richEditBrackets; - this._complexAutoClosePairs = autoClosePairs.filter(pair => pair.open.length > 1 && !!pair.close); - if (docComment) { + this._complexAutoClosePairs = autoClosePairs.filter(pair => pair.open.length > 1 && !!pair.close).map(el => new StandardAutoClosingPairConditional(el)); + if (contribution.docComment) { // IDocComment is legacy, only partially supported - this._complexAutoClosePairs.push({ open: docComment.open, close: docComment.close }); + this._complexAutoClosePairs.push(new StandardAutoClosingPairConditional({ open: contribution.docComment.open, close: contribution.docComment.close })); } } @@ -102,7 +67,7 @@ export class Brackets { return result; } - public onElectricCharacter(context: modes.ILineContext, offset: number): modes.IElectricAction { + public onElectricCharacter(context: ScopedLineTokens, offset: number): IElectricAction { if (context.getTokenCount() === 0) { return null; } @@ -111,17 +76,7 @@ export class Brackets { this._onElectricAutoIndent(context, offset)); } - private containsTokenTypes(fullTokenSpec: string, tokensToLookFor: string): boolean { - var array = tokensToLookFor.split('.'); - for (var i = 0; i < array.length; ++i) { - if (fullTokenSpec.indexOf(array[i]) < 0) { - return false; - } - } - return true; - } - - private _onElectricAutoIndent(context: modes.ILineContext, offset: number): modes.IElectricAction { + private _onElectricAutoIndent(context: ScopedLineTokens, offset: number): IElectricAction { if (!this._richEditBrackets || this._richEditBrackets.brackets.length === 0) { return null; @@ -130,7 +85,7 @@ export class Brackets { let reversedBracketRegex = this._richEditBrackets.reversedRegex; let lineText = context.getLineContent(); - let tokenIndex = context.findIndexOfOffset(offset); + let tokenIndex = context.findTokenIndexAtOffset(offset); let tokenStart = context.getTokenStartOffset(tokenIndex); let tokenEnd = offset + 1; @@ -139,7 +94,7 @@ export class Brackets { return null; } - if (!ignoreBracketsInToken(context.getTokenType(tokenIndex))) { + if (!ignoreBracketsInToken(context.getStandardTokenType(tokenIndex))) { let r = BracketsUtils.findPrevBracketInToken(reversedBracketRegex, 1, lineText, tokenStart, tokenEnd); if (r) { let text = lineText.substring(r.startColumn - 1, r.endColumn - 1); @@ -157,7 +112,7 @@ export class Brackets { return null; } - private _onElectricAutoClose(context: modes.ILineContext, offset: number): modes.IElectricAction { + private _onElectricAutoClose(context: ScopedLineTokens, offset: number): IElectricAction { if (!this._complexAutoClosePairs.length) { return null; @@ -180,17 +135,14 @@ export class Brackets { } // check if the full open bracket matches - let lastTokenIndex = context.findIndexOfOffset(offset); + let lastTokenIndex = context.findTokenIndexAtOffset(offset); if (line.substring(context.getTokenStartOffset(lastTokenIndex), offset + 1/* include electric char*/) !== pair.open) { continue; } - // If we're in a scope listen in 'notIn', do nothing - if (pair.notIn) { - let tokenType = context.getTokenType(lastTokenIndex); - if (pair.notIn.some(scope => this.containsTokenTypes(tokenType, scope))) { - continue; - } + // If we're in a scope listed in 'notIn', do nothing + if (!pair.isOK(context.getStandardTokenType(lastTokenIndex))) { + continue; } return { appendText: pair.close }; diff --git a/src/vs/editor/common/modes/supports/onEnter.ts b/src/vs/editor/common/modes/supports/onEnter.ts index 159ee91886c..2cd37261c60 100644 --- a/src/vs/editor/common/modes/supports/onEnter.ts +++ b/src/vs/editor/common/modes/supports/onEnter.ts @@ -6,50 +6,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import * as strings from 'vs/base/common/strings'; -import { IPosition, ITextModel, ITokenizedModel } from 'vs/editor/common/editorCommon'; -import { EnterAction, ILineContext, IRichEditOnEnter, IndentAction, CharacterPair } from 'vs/editor/common/modes'; -import { handleEvent } from 'vs/editor/common/modes/supports'; -import { LanguageConfigurationRegistryImpl } from 'vs/editor/common/modes/languageConfigurationRegistry'; - -/** - * Describes indentation rules for a language. - */ -export interface IndentationRule { - /** - * If a line matches this pattern, then all the lines after it should be unindendented once (until another rule matches). - */ - decreaseIndentPattern: RegExp; - /** - * If a line matches this pattern, then all the lines after it should be indented once (until another rule matches). - */ - increaseIndentPattern: RegExp; - /** - * If a line matches this pattern, then **only the next line** after it should be indented once. - */ - indentNextLinePattern?: RegExp; - /** - * If a line matches this pattern, then its indentation should not be changed and it should not be evaluated against the other rules. - */ - unIndentedLinePattern?: RegExp; -} - -/** - * Describes a rule to be evaluated when pressing Enter. - */ -export interface OnEnterRule { - /** - * This rule will only execute if the text before the cursor matches this regular expression. - */ - beforeText: RegExp; - /** - * This rule will only execute if the text after the cursor matches this regular expression. - */ - afterText?: RegExp; - /** - * The action to execute. - */ - action: EnterAction; -} +import { CharacterPair, IndentationRule, IndentAction, EnterAction, OnEnterRule } from 'vs/editor/common/modes/languageConfiguration'; export interface IOnEnterSupportOptions { brackets?: CharacterPair[]; @@ -64,20 +21,17 @@ interface IProcessedBracketPair { closeRegExp: RegExp; } -export class OnEnterSupport implements IRichEditOnEnter { +export class OnEnterSupport { private static _INDENT: EnterAction = { indentAction: IndentAction.Indent }; private static _INDENT_OUTDENT: EnterAction = { indentAction: IndentAction.IndentOutdent }; private static _OUTDENT: EnterAction = { indentAction: IndentAction.Outdent }; - private _registry: LanguageConfigurationRegistryImpl; - private _modeId: string; - private _brackets: IProcessedBracketPair[]; - private _indentationRules: IndentationRule; - private _regExpRules: OnEnterRule[]; + private readonly _brackets: IProcessedBracketPair[]; + private readonly _indentationRules: IndentationRule; + private readonly _regExpRules: OnEnterRule[]; - constructor(registry: LanguageConfigurationRegistryImpl, modeId: string, opts?: IOnEnterSupportOptions) { - this._registry = registry; + constructor(opts?: IOnEnterSupportOptions) { opts = opts || {}; opts.brackets = opts.brackets || [ ['(', ')'], @@ -85,7 +39,6 @@ export class OnEnterSupport implements IRichEditOnEnter { ['[', ']'] ]; - this._modeId = modeId; this._brackets = opts.brackets.map((bracket) => { return { open: bracket[0], @@ -98,34 +51,7 @@ export class OnEnterSupport implements IRichEditOnEnter { this._indentationRules = opts.indentationRules; } - public onEnter(model: ITokenizedModel, position: IPosition): EnterAction { - var context = model.getLineContext(position.lineNumber); - - return handleEvent(context, position.column - 1, (nestedModeId: string, context: ILineContext, offset: number) => { - if (this._modeId === nestedModeId) { - return this._onEnter(model, position); - } - - let onEnterSupport = this._registry.getOnEnterSupport(nestedModeId); - if (onEnterSupport) { - return onEnterSupport.onEnter(model, position); - } - - return null; - }); - } - - private _onEnter(model: ITextModel, position: IPosition): EnterAction { - let lineText = model.getLineContent(position.lineNumber); - let beforeEnterText = lineText.substr(0, position.column - 1); - let afterEnterText = lineText.substr(position.column - 1); - - let oneLineAboveText = position.lineNumber === 1 ? '' : model.getLineContent(position.lineNumber - 1); - - return this._actualOnEnter(oneLineAboveText, beforeEnterText, afterEnterText); - } - - _actualOnEnter(oneLineAboveText: string, beforeEnterText: string, afterEnterText: string): EnterAction { + public onEnter(oneLineAboveText: string, beforeEnterText: string, afterEnterText: string): EnterAction { // (1): `regExpRules` for (let i = 0, len = this._regExpRules.length; i < len; i++) { let rule = this._regExpRules[i]; diff --git a/src/vs/editor/common/modes/supports/richEditBrackets.ts b/src/vs/editor/common/modes/supports/richEditBrackets.ts index 13d5efcd312..e88dceb376e 100644 --- a/src/vs/editor/common/modes/supports/richEditBrackets.ts +++ b/src/vs/editor/common/modes/supports/richEditBrackets.ts @@ -6,47 +6,67 @@ import * as strings from 'vs/base/common/strings'; import { Range } from 'vs/editor/common/core/range'; -import { IRichEditBracket } from 'vs/editor/common/editorCommon'; -import { IRichEditBrackets, CharacterPair } from 'vs/editor/common/modes'; +import { CharacterPair } from 'vs/editor/common/modes/languageConfiguration'; interface ISimpleInternalBracket { open: string; close: string; } -export class RichEditBrackets implements IRichEditBrackets { +export class RichEditBracket { + _richEditBracketBrand: void; - public brackets: IRichEditBracket[]; - public forwardRegex: RegExp; - public reversedRegex: RegExp; - public maxBracketLength: number; - public textIsBracket: { [text: string]: IRichEditBracket; }; - public textIsOpenBracket: { [text: string]: boolean; }; + readonly modeId: string; + readonly open: string; + readonly close: string; + readonly forwardRegex: RegExp; + readonly reversedRegex: RegExp; + + constructor(modeId: string, open: string, close: string, forwardRegex: RegExp, reversedRegex: RegExp) { + this.modeId = modeId; + this.open = open; + this.close = close; + this.forwardRegex = forwardRegex; + this.reversedRegex = reversedRegex; + } +} + +export class RichEditBrackets { + _richEditBracketsBrand: void; + + public readonly brackets: RichEditBracket[]; + public readonly forwardRegex: RegExp; + public readonly reversedRegex: RegExp; + public readonly maxBracketLength: number; + public readonly textIsBracket: { [text: string]: RichEditBracket; }; + public readonly textIsOpenBracket: { [text: string]: boolean; }; constructor(modeId: string, brackets: CharacterPair[]) { this.brackets = brackets.map((b) => { - return { - modeId: modeId, - open: b[0], - close: b[1], - forwardRegex: getRegexForBracketPair({ open: b[0], close: b[1] }), - reversedRegex: getReversedRegexForBracketPair({ open: b[0], close: b[1] }) - }; + return new RichEditBracket( + modeId, + b[0], + b[1], + getRegexForBracketPair({ open: b[0], close: b[1] }), + getReversedRegexForBracketPair({ open: b[0], close: b[1] }) + ); }); this.forwardRegex = getRegexForBrackets(this.brackets); this.reversedRegex = getReversedRegexForBrackets(this.brackets); this.textIsBracket = {}; this.textIsOpenBracket = {}; - this.maxBracketLength = 0; + + let maxBracketLength = 0; this.brackets.forEach((b) => { this.textIsBracket[b.open.toLowerCase()] = b; this.textIsBracket[b.close.toLowerCase()] = b; this.textIsOpenBracket[b.open.toLowerCase()] = true; this.textIsOpenBracket[b.close.toLowerCase()] = false; - this.maxBracketLength = Math.max(this.maxBracketLength, b.open.length); - this.maxBracketLength = Math.max(this.maxBracketLength, b.close.length); + maxBracketLength = Math.max(maxBracketLength, b.open.length); + maxBracketLength = Math.max(maxBracketLength, b.close.length); }); + this.maxBracketLength = maxBracketLength; } } diff --git a/src/vs/editor/common/services/editorWorkerServiceImpl.ts b/src/vs/editor/common/services/editorWorkerServiceImpl.ts index f96156d6b35..651e40ab772 100644 --- a/src/vs/editor/common/services/editorWorkerServiceImpl.ts +++ b/src/vs/editor/common/services/editorWorkerServiceImpl.ts @@ -11,11 +11,11 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { SimpleWorkerClient, logOnceWebWorkerWarning } from 'vs/base/common/worker/simpleWorker'; import { DefaultWorkerFactory } from 'vs/base/worker/defaultWorkerFactory'; import * as editorCommon from 'vs/editor/common/editorCommon'; -import { WordHelper } from 'vs/editor/common/model/textModelWithTokensHelpers'; import { IInplaceReplaceSupportResult, ILink, ISuggestResult, LinkProviderRegistry, LinkProvider } from 'vs/editor/common/modes'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { EditorSimpleWorkerImpl } from 'vs/editor/common/services/editorSimpleWorker'; +import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; /** * Stop syncing a model to the worker if it was not needed for 1 min. @@ -321,7 +321,7 @@ export class EditorWorkerClient extends Disposable { if (!model) { return null; } - let wordDefRegExp = WordHelper.massageWordDefinitionOf(model.getModeId()); + let wordDefRegExp = LanguageConfigurationRegistry.getWordDefinition(model.getModeId()); let wordDef = wordDefRegExp.source; let wordDefFlags = (wordDefRegExp.global ? 'g' : '') + (wordDefRegExp.ignoreCase ? 'i' : '') + (wordDefRegExp.multiline ? 'm' : ''); return proxy.textualSuggest(resource.toString(), position, wordDef, wordDefFlags); @@ -334,7 +334,7 @@ export class EditorWorkerClient extends Disposable { if (!model) { return null; } - let wordDefRegExp = WordHelper.massageWordDefinitionOf(model.getModeId()); + let wordDefRegExp = LanguageConfigurationRegistry.getWordDefinition(model.getModeId()); let wordDef = wordDefRegExp.source; let wordDefFlags = (wordDefRegExp.global ? 'g' : '') + (wordDefRegExp.ignoreCase ? 'i' : '') + (wordDefRegExp.multiline ? 'm' : ''); return proxy.navigateValueSet(resource.toString(), range, up, wordDef, wordDefFlags); diff --git a/src/vs/editor/contrib/comment/common/blockCommentCommand.ts b/src/vs/editor/contrib/comment/common/blockCommentCommand.ts index 044a379ab2f..b21f9c3fa67 100644 --- a/src/vs/editor/contrib/comment/common/blockCommentCommand.ts +++ b/src/vs/editor/contrib/comment/common/blockCommentCommand.ts @@ -9,8 +9,7 @@ import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import * as editorCommon from 'vs/editor/common/editorCommon'; -import { ICommentsConfiguration } from 'vs/editor/common/modes'; -import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; +import { ICommentsConfiguration, LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; export class BlockCommentCommand implements editorCommon.ICommand { diff --git a/src/vs/editor/contrib/comment/common/lineCommentCommand.ts b/src/vs/editor/contrib/comment/common/lineCommentCommand.ts index 4778eed2dab..7b0853ab141 100644 --- a/src/vs/editor/contrib/comment/common/lineCommentCommand.ts +++ b/src/vs/editor/contrib/comment/common/lineCommentCommand.ts @@ -10,9 +10,8 @@ import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import * as editorCommon from 'vs/editor/common/editorCommon'; -import { ICommentsConfiguration } from 'vs/editor/common/modes'; import { BlockCommentCommand } from './blockCommentCommand'; -import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; +import { ICommentsConfiguration, LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { CharCode } from 'vs/base/common/charCode'; export interface IInsertionPoint { diff --git a/src/vs/editor/contrib/folding/browser/folding.ts b/src/vs/editor/contrib/folding/browser/folding.ts index a6351e310ed..53c4988b6a5 100644 --- a/src/vs/editor/contrib/folding/browser/folding.ts +++ b/src/vs/editor/contrib/folding/browser/folding.ts @@ -316,7 +316,7 @@ export class FoldingController implements IFoldingController { for (let i = 0; i < this.decorations.length; i++) { let dec = this.decorations[i]; let decRange = dec.getDecorationRange(model); - if (decRange.startLineNumber === lineNumber) { + if (decRange && decRange.startLineNumber === lineNumber) { if (iconClicked || dec.isCollapsed) { dec.setCollapsed(!dec.isCollapsed, changeAccessor); this.updateHiddenAreas(lineNumber); @@ -334,6 +334,9 @@ export class FoldingController implements IFoldingController { let hiddenAreas: editorCommon.IRange[] = []; this.decorations.filter(dec => dec.isCollapsed).forEach(dec => { let decRange = dec.getDecorationRange(model); + if (!decRange) { + return; + } hiddenAreas.push({ startLineNumber: decRange.startLineNumber + 1, startColumn: 1, @@ -574,7 +577,7 @@ class UnFoldRecursivelyAction extends FoldingAction { constructor() { super({ - id: 'editor.unFoldRecursively', + id: 'editor.unfoldRecursively', label: nls.localize('unFoldRecursivelyAction.label', "Unfold Recursively"), alias: 'Unfold Recursively', precondition: null, diff --git a/src/vs/editor/contrib/smartSelect/common/tokenTree.ts b/src/vs/editor/contrib/smartSelect/common/tokenTree.ts index 6a19a70b5e0..1e4404fc480 100644 --- a/src/vs/editor/contrib/smartSelect/common/tokenTree.ts +++ b/src/vs/editor/contrib/smartSelect/common/tokenTree.ts @@ -7,10 +7,9 @@ import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { IModel, IPosition } from 'vs/editor/common/editorCommon'; -import { LineToken } from 'vs/editor/common/core/lineTokens'; -import { IRichEditBrackets } from 'vs/editor/common/modes'; +import { LineToken, StandardTokenType } from 'vs/editor/common/core/lineTokens'; import { ignoreBracketsInToken } from 'vs/editor/common/modes/supports'; -import { BracketsUtils } from 'vs/editor/common/modes/supports/richEditBrackets'; +import { BracketsUtils, RichEditBrackets } from 'vs/editor/common/modes/supports/richEditBrackets'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; export const enum TokenTreeBracket { @@ -104,14 +103,14 @@ export class Block extends Node { class Token { _tokenBrand: void; - range: Range; - type: string; - bracket: TokenTreeBracket; + readonly range: Range; + readonly bracket: TokenTreeBracket; + readonly bracketType: string; - constructor(range: Range, type: string, bracket: TokenTreeBracket) { + constructor(range: Range, bracket: TokenTreeBracket, bracketType: string) { this.range = range; - this.type = type; this.bracket = bracket; + this.bracketType = bracketType; } } @@ -129,7 +128,7 @@ class RawToken { public lineText: string; public startOffset: number; public endOffset: number; - public type: string; + public standardType: StandardTokenType; public modeId: string; constructor(source: LineToken, lineNumber: number, lineText: string) { @@ -137,7 +136,7 @@ class RawToken { this.lineText = lineText; this.startOffset = source.startOffset; this.endOffset = source.endOffset; - this.type = source.type; + this.standardType = source.standardType; this.modeId = source.modeId; } } @@ -189,7 +188,7 @@ class TokenScanner { private _rawTokenScanner: ModelRawTokenScanner; private _nextBuff: Token[]; - private _cachedModeBrackets: IRichEditBrackets; + private _cachedModeBrackets: RichEditBrackets; private _cachedModeId: string; constructor(model: IModel) { @@ -210,7 +209,7 @@ class TokenScanner { } const lineNumber = token.lineNumber; const lineText = token.lineText; - const tokenType = token.type; + const standardTokenType = token.standardType; let startOffset = token.startOffset; const endOffset = token.endOffset; @@ -220,11 +219,11 @@ class TokenScanner { } const modeBrackets = this._cachedModeBrackets; - if (!modeBrackets || ignoreBracketsInToken(tokenType)) { + if (!modeBrackets || ignoreBracketsInToken(standardTokenType)) { return new Token( new Range(lineNumber, startOffset + 1, lineNumber, endOffset + 1), - tokenType, - TokenTreeBracket.None + TokenTreeBracket.None, + null ); } @@ -239,8 +238,8 @@ class TokenScanner { // there is some text before this bracket in this token this._nextBuff.push(new Token( new Range(lineNumber, startOffset + 1, lineNumber, foundBracketStartOffset + 1), - tokenType, - TokenTreeBracket.None + TokenTreeBracket.None, + null )); } @@ -252,8 +251,8 @@ class TokenScanner { this._nextBuff.push(new Token( new Range(lineNumber, foundBracketStartOffset + 1, lineNumber, foundBracketEndOffset + 1), - `${bracketData.modeId};${bracketData.open};${bracketData.close}`, - bracketIsOpen ? TokenTreeBracket.Open : TokenTreeBracket.Close + bracketIsOpen ? TokenTreeBracket.Open : TokenTreeBracket.Close, + `${bracketData.modeId};${bracketData.open};${bracketData.close}` )); startOffset = foundBracketEndOffset; @@ -264,8 +263,8 @@ class TokenScanner { // there is some remaining none-bracket text in this token this._nextBuff.push(new Token( new Range(lineNumber, startOffset + 1, lineNumber, endOffset + 1), - tokenType, - TokenTreeBracket.None + TokenTreeBracket.None, + null )); } @@ -354,7 +353,7 @@ class TokenTreeBuilder { accepted: boolean; accepted = this._accept(token => { - bracketType = token.type; + bracketType = token.bracketType; return token.bracket === TokenTreeBracket.Open; }); if (!accepted) { @@ -367,7 +366,7 @@ class TokenTreeBuilder { // inside brackets } - if (!this._accept(token => token.bracket === TokenTreeBracket.Close && token.type === bracketType)) { + if (!this._accept(token => token.bracket === TokenTreeBracket.Close && token.bracketType === bracketType)) { // missing closing bracket -> return just a node list var nodelist = new NodeList(); nodelist.append(bracket.open); diff --git a/src/vs/editor/contrib/smartSelect/test/common/tokenSelectionSupport.test.ts b/src/vs/editor/contrib/smartSelect/test/common/tokenSelectionSupport.test.ts index 49cd28be2eb..ad8a30c2766 100644 --- a/src/vs/editor/contrib/smartSelect/test/common/tokenSelectionSupport.test.ts +++ b/src/vs/editor/contrib/smartSelect/test/common/tokenSelectionSupport.test.ts @@ -7,7 +7,8 @@ import * as assert from 'assert'; import URI from 'vs/base/common/uri'; import { Range } from 'vs/editor/common/core/range'; -import { IMode, IndentAction } from 'vs/editor/common/modes'; +import { IMode } from 'vs/editor/common/modes'; +import { IndentAction } from 'vs/editor/common/modes/languageConfiguration'; import { TokenSelectionSupport } from 'vs/editor/contrib/smartSelect/common/tokenSelectionSupport'; import { MockTokenizingMode } from 'vs/editor/test/common/mocks/mockMode'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; diff --git a/src/vs/editor/node/languageConfiguration.ts b/src/vs/editor/node/languageConfigurationExtensionPoint.ts similarity index 95% rename from src/vs/editor/node/languageConfiguration.ts rename to src/vs/editor/node/languageConfigurationExtensionPoint.ts index 02e20dc20da..61f8f204da1 100644 --- a/src/vs/editor/node/languageConfiguration.ts +++ b/src/vs/editor/node/languageConfigurationExtensionPoint.ts @@ -7,24 +7,16 @@ import * as nls from 'vs/nls'; import { parse } from 'vs/base/common/json'; import { readFile } from 'vs/base/node/pfs'; -import { LanguageConfiguration } from 'vs/editor/common/modes/languageConfigurationRegistry'; +import { CharacterPair, LanguageConfiguration, IAutoClosingPair, IAutoClosingPairConditional, CommentRule } from 'vs/editor/common/modes/languageConfiguration'; import { IModeService } from 'vs/editor/common/services/modeService'; -import { IAutoClosingPair, IAutoClosingPairConditional } from 'vs/editor/common/modes'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { Extensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { Registry } from 'vs/platform/platform'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { MainProcessTextMateSyntax } from 'vs/editor/node/textMate/TMSyntax'; -type CharacterPair = [string, string]; - -interface ICommentRule { - lineComment?: string; - blockComment?: CharacterPair; -} - interface ILanguageConfiguration { - comments?: ICommentRule; + comments?: CommentRule; brackets?: CharacterPair[]; autoClosingPairs?: (CharacterPair | IAutoClosingPairConditional)[]; surroundingPairs?: (CharacterPair | IAutoClosingPair)[]; diff --git a/src/vs/editor/node/textMate/TMSyntax.ts b/src/vs/editor/node/textMate/TMSyntax.ts index 1e0ef0e988f..99b84653116 100644 --- a/src/vs/editor/node/textMate/TMSyntax.ts +++ b/src/vs/editor/node/textMate/TMSyntax.ts @@ -13,7 +13,7 @@ import Event, { Emitter } from 'vs/base/common/event'; import { IExtensionPoint, ExtensionMessageCollector, ExtensionsRegistry } from 'vs/platform/extensions/common/extensionsRegistry'; import { ILineTokens, ITokenizationSupport, TokenizationRegistry } from 'vs/editor/common/modes'; import { TMState } from 'vs/editor/common/modes/TMState'; -import { LineTokens } from 'vs/editor/common/modes/supports'; +import { RawLineTokens } from 'vs/editor/common/modes/supports'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IGrammar, Registry, StackElement, IToken } from 'vscode-textmate'; import { ModeTransition } from 'vs/editor/common/core/modeTransition'; @@ -430,7 +430,7 @@ class Tokenizer { // Do not attempt to tokenize if a line has over 20k // or if the rule stack contains more than 100 rules (indicator of broken grammar that forgets to pop rules) if (line.length >= 20000 || depth(state.getRuleStack()) > 100) { - return new LineTokens( + return new RawLineTokens( [new Token(offsetDelta, '')], [new ModeTransition(offsetDelta, state.getModeId())], offsetDelta, @@ -445,7 +445,7 @@ class Tokenizer { } } -export function decodeTextMateTokens(line: string, offsetDelta: number, decodeMap: DecodeMap, resultTokens: IToken[], resultState: TMState): LineTokens { +export function decodeTextMateTokens(line: string, offsetDelta: number, decodeMap: DecodeMap, resultTokens: IToken[], resultState: TMState): RawLineTokens { const topLevelModeId = resultState.getModeId(); // Create the result early and fill in the tokens later @@ -481,7 +481,7 @@ export function decodeTextMateTokens(line: string, offsetDelta: number, decodeMa } } - return new LineTokens( + return new RawLineTokens( tokens, modeTransitions, offsetDelta + line.length, diff --git a/src/vs/editor/test/common/commands/shiftCommand.test.ts b/src/vs/editor/test/common/commands/shiftCommand.test.ts index a2a744a099a..a85e9a05742 100644 --- a/src/vs/editor/test/common/commands/shiftCommand.test.ts +++ b/src/vs/editor/test/common/commands/shiftCommand.test.ts @@ -8,7 +8,7 @@ import * as assert from 'assert'; import { ShiftCommand } from 'vs/editor/common/commands/shiftCommand'; import { Selection } from 'vs/editor/common/core/selection'; import { IIdentifiedSingleEditOperation } from 'vs/editor/common/editorCommon'; -import { IndentAction } from 'vs/editor/common/modes'; +import { IndentAction } from 'vs/editor/common/modes/languageConfiguration'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { createSingleEditOp, getEditOperation, testCommand } from 'vs/editor/test/common/commands/commandTestUtils'; import { withEditorModel } from 'vs/editor/test/common/editorTestUtils'; diff --git a/src/vs/editor/test/common/controller/cursor.test.ts b/src/vs/editor/test/common/controller/cursor.test.ts index 2a09eca9f0f..67b78a3f9ab 100644 --- a/src/vs/editor/test/common/controller/cursor.test.ts +++ b/src/vs/editor/test/common/controller/cursor.test.ts @@ -17,7 +17,7 @@ import { ICursorPositionChangedEvent, ICursorSelectionChangedEvent } from 'vs/editor/common/editorCommon'; import { Model } from 'vs/editor/common/model/model'; -import { IMode, IndentAction } from 'vs/editor/common/modes'; +import { IndentAction } from 'vs/editor/common/modes/languageConfiguration'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { MockConfiguration } from 'vs/editor/test/common/mocks/mockConfiguration'; import { MockMode } from 'vs/editor/test/common/mocks/mockMode'; @@ -1148,7 +1148,7 @@ suite('Editor Controller - Regression tests', () => { modeId: new BracketMode().getId() }, (model, cursor) => { // ensure is tokenized - model.getLineContext(1); + model.getLineTokens(1, false); moveTo(cursor, 1, 20); diff --git a/src/vs/editor/test/common/controller/cursorMoveHelper.test.ts b/src/vs/editor/test/common/controller/cursorMoveHelper.test.ts new file mode 100644 index 00000000000..8ff1e854911 --- /dev/null +++ b/src/vs/editor/test/common/controller/cursorMoveHelper.test.ts @@ -0,0 +1,224 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import * as strings from 'vs/base/common/strings'; +import { CursorMoveHelper, ICursorMoveHelperModel, CursorMoveConfiguration } from 'vs/editor/common/controller/cursorMoveHelper'; + +suite('CursorMove', () => { + + test('nextTabStop', () => { + assert.equal(CursorMoveHelper.nextTabColumn(0, 4), 4); + assert.equal(CursorMoveHelper.nextTabColumn(1, 4), 4); + assert.equal(CursorMoveHelper.nextTabColumn(2, 4), 4); + assert.equal(CursorMoveHelper.nextTabColumn(3, 4), 4); + assert.equal(CursorMoveHelper.nextTabColumn(4, 4), 8); + assert.equal(CursorMoveHelper.nextTabColumn(5, 4), 8); + assert.equal(CursorMoveHelper.nextTabColumn(6, 4), 8); + assert.equal(CursorMoveHelper.nextTabColumn(7, 4), 8); + assert.equal(CursorMoveHelper.nextTabColumn(8, 4), 12); + + assert.equal(CursorMoveHelper.nextTabColumn(0, 2), 2); + assert.equal(CursorMoveHelper.nextTabColumn(1, 2), 2); + assert.equal(CursorMoveHelper.nextTabColumn(2, 2), 4); + assert.equal(CursorMoveHelper.nextTabColumn(3, 2), 4); + assert.equal(CursorMoveHelper.nextTabColumn(4, 2), 6); + assert.equal(CursorMoveHelper.nextTabColumn(5, 2), 6); + assert.equal(CursorMoveHelper.nextTabColumn(6, 2), 8); + assert.equal(CursorMoveHelper.nextTabColumn(7, 2), 8); + assert.equal(CursorMoveHelper.nextTabColumn(8, 2), 10); + + assert.equal(CursorMoveHelper.nextTabColumn(0, 1), 1); + assert.equal(CursorMoveHelper.nextTabColumn(1, 1), 2); + assert.equal(CursorMoveHelper.nextTabColumn(2, 1), 3); + assert.equal(CursorMoveHelper.nextTabColumn(3, 1), 4); + assert.equal(CursorMoveHelper.nextTabColumn(4, 1), 5); + assert.equal(CursorMoveHelper.nextTabColumn(5, 1), 6); + assert.equal(CursorMoveHelper.nextTabColumn(6, 1), 7); + assert.equal(CursorMoveHelper.nextTabColumn(7, 1), 8); + assert.equal(CursorMoveHelper.nextTabColumn(8, 1), 9); + }); + + class OneLineModel implements ICursorMoveHelperModel { + private _line: string; + + constructor(line: string) { + this._line = line; + } + + getLineCount(): number { + return 1; + } + + getLineContent(lineNumber: number): string { + return this._line; + } + + getLineMinColumn(lineNumber: number): number { + return 1; + } + + getLineMaxColumn(lineNumber: number): number { + return this._line.length + 1; + } + + getLineFirstNonWhitespaceColumn(lineNumber: number): number { + let result = strings.firstNonWhitespaceIndex(this._line); + if (result === -1) { + return 0; + } + return result + 1; + } + + getLineLastNonWhitespaceColumn(lineNumber: number): number { + let result = strings.lastNonWhitespaceIndex(this._line); + if (result === -1) { + return 0; + } + return result + 2; + } + + } + + test('visibleColumnFromColumn', () => { + + function testVisibleColumnFromColumn(text:string, tabSize:number, column:number, expected:number): void { + let helper = new CursorMoveHelper(new CursorMoveConfiguration(tabSize, 13)); + let model = new OneLineModel(text); + assert.equal(helper.visibleColumnFromColumn(model, 1, column), expected); + } + + testVisibleColumnFromColumn('\t\tvar x = 3;', 4, 1, 0); + testVisibleColumnFromColumn('\t\tvar x = 3;', 4, 2, 4); + testVisibleColumnFromColumn('\t\tvar x = 3;', 4, 3, 8); + testVisibleColumnFromColumn('\t\tvar x = 3;', 4, 4, 9); + testVisibleColumnFromColumn('\t\tvar x = 3;', 4, 5, 10); + testVisibleColumnFromColumn('\t\tvar x = 3;', 4, 6, 11); + testVisibleColumnFromColumn('\t\tvar x = 3;', 4, 7, 12); + testVisibleColumnFromColumn('\t\tvar x = 3;', 4, 8, 13); + testVisibleColumnFromColumn('\t\tvar x = 3;', 4, 9, 14); + testVisibleColumnFromColumn('\t\tvar x = 3;', 4, 10, 15); + testVisibleColumnFromColumn('\t\tvar x = 3;', 4, 11, 16); + testVisibleColumnFromColumn('\t\tvar x = 3;', 4, 12, 17); + testVisibleColumnFromColumn('\t\tvar x = 3;', 4, 13, 18); + + testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 1, 0); + testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 2, 4); + testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 3, 5); + testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 4, 8); + testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 5, 9); + testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 6, 10); + testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 7, 11); + testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 8, 12); + testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 9, 13); + testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 10, 14); + testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 11, 15); + testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 12, 16); + testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 13, 17); + testVisibleColumnFromColumn('\t \tvar x = 3;', 4, 14, 18); + + testVisibleColumnFromColumn('\t \tx\t', 4, -1, 0); + testVisibleColumnFromColumn('\t \tx\t', 4, 0, 0); + testVisibleColumnFromColumn('\t \tx\t', 4, 1, 0); + testVisibleColumnFromColumn('\t \tx\t', 4, 2, 4); + testVisibleColumnFromColumn('\t \tx\t', 4, 3, 5); + testVisibleColumnFromColumn('\t \tx\t', 4, 4, 6); + testVisibleColumnFromColumn('\t \tx\t', 4, 5, 8); + testVisibleColumnFromColumn('\t \tx\t', 4, 6, 9); + testVisibleColumnFromColumn('\t \tx\t', 4, 7, 12); + testVisibleColumnFromColumn('\t \tx\t', 4, 8, 12); + testVisibleColumnFromColumn('\t \tx\t', 4, 9, 12); + + testVisibleColumnFromColumn('baz', 4, 1, 0); + testVisibleColumnFromColumn('baz', 4, 2, 1); + testVisibleColumnFromColumn('baz', 4, 3, 2); + testVisibleColumnFromColumn('baz', 4, 4, 3); + + testVisibleColumnFromColumn('📚az', 4, 1, 0); + testVisibleColumnFromColumn('📚az', 4, 2, 1); + testVisibleColumnFromColumn('📚az', 4, 3, 2); + testVisibleColumnFromColumn('📚az', 4, 4, 3); + testVisibleColumnFromColumn('📚az', 4, 5, 4); + }); + + test('columnFromVisibleColumn', () => { + + function testColumnFromVisibleColumn(text:string, tabSize:number, visibleColumn:number, expected:number): void { + let helper = new CursorMoveHelper(new CursorMoveConfiguration(tabSize, 13)); + let model = new OneLineModel(text); + assert.equal(helper.columnFromVisibleColumn(model, 1, visibleColumn), expected); + } + + // testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 0, 1); + testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 1, 1); + testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 2, 1); + testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 3, 2); + testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 4, 2); + testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 5, 2); + testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 6, 2); + testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 7, 3); + testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 8, 3); + testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 9, 4); + testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 10, 5); + testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 11, 6); + testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 12, 7); + testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 13, 8); + testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 14, 9); + testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 15, 10); + testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 16, 11); + testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 17, 12); + testColumnFromVisibleColumn('\t\tvar x = 3;', 4, 18, 13); + + testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 0, 1); + testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 1, 1); + testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 2, 1); + testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 3, 2); + testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 4, 2); + testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 5, 3); + testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 6, 3); + testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 7, 4); + testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 8, 4); + testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 9, 5); + testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 10, 6); + testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 11, 7); + testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 12, 8); + testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 13, 9); + testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 14, 10); + testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 15, 11); + testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 16, 12); + testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 17, 13); + testColumnFromVisibleColumn('\t \tvar x = 3;', 4, 18, 14); + + testColumnFromVisibleColumn('\t \tx\t', 4, -2, 1); + testColumnFromVisibleColumn('\t \tx\t', 4, -1, 1); + testColumnFromVisibleColumn('\t \tx\t', 4, 0, 1); + testColumnFromVisibleColumn('\t \tx\t', 4, 1, 1); + testColumnFromVisibleColumn('\t \tx\t', 4, 2, 1); + testColumnFromVisibleColumn('\t \tx\t', 4, 3, 2); + testColumnFromVisibleColumn('\t \tx\t', 4, 4, 2); + testColumnFromVisibleColumn('\t \tx\t', 4, 5, 3); + testColumnFromVisibleColumn('\t \tx\t', 4, 6, 4); + testColumnFromVisibleColumn('\t \tx\t', 4, 7, 4); + testColumnFromVisibleColumn('\t \tx\t', 4, 8, 5); + testColumnFromVisibleColumn('\t \tx\t', 4, 9, 6); + testColumnFromVisibleColumn('\t \tx\t', 4, 10, 6); + testColumnFromVisibleColumn('\t \tx\t', 4, 11, 7); + testColumnFromVisibleColumn('\t \tx\t', 4, 12, 7); + testColumnFromVisibleColumn('\t \tx\t', 4, 13, 7); + testColumnFromVisibleColumn('\t \tx\t', 4, 14, 7); + + testColumnFromVisibleColumn('baz', 4, 0, 1); + testColumnFromVisibleColumn('baz', 4, 1, 2); + testColumnFromVisibleColumn('baz', 4, 2, 3); + testColumnFromVisibleColumn('baz', 4, 3, 4); + + testColumnFromVisibleColumn('📚az', 4, 0, 1); + testColumnFromVisibleColumn('📚az', 4, 1, 2); + testColumnFromVisibleColumn('📚az', 4, 2, 3); + testColumnFromVisibleColumn('📚az', 4, 3, 4); + testColumnFromVisibleColumn('📚az', 4, 4, 5); + }); +}); \ No newline at end of file diff --git a/src/vs/editor/test/common/model/textModelWithTokens.test.ts b/src/vs/editor/test/common/model/textModelWithTokens.test.ts index b07c86f1b3e..8e94e5e4c1f 100644 --- a/src/vs/editor/test/common/model/textModelWithTokens.test.ts +++ b/src/vs/editor/test/common/model/textModelWithTokens.test.ts @@ -7,7 +7,8 @@ import * as assert from 'assert'; import { Model } from 'vs/editor/common/model/model'; import { ViewLineToken } from 'vs/editor/common/core/viewLineToken'; -import { TokenizationRegistry, CharacterPair } from 'vs/editor/common/modes'; +import { TokenizationRegistry } from 'vs/editor/common/modes'; +import { CharacterPair } from 'vs/editor/common/modes/languageConfiguration'; import { MockMode } from 'vs/editor/test/common/mocks/mockMode'; import { Token } from 'vs/editor/common/core/token'; import { Range } from 'vs/editor/common/core/range'; diff --git a/src/vs/editor/test/common/modes/autoIndentation.test.ts b/src/vs/editor/test/common/modes/autoIndentation.test.ts deleted file mode 100644 index 8f5c00f979a..00000000000 --- a/src/vs/editor/test/common/modes/autoIndentation.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; - -import * as assert from 'assert'; -import { Brackets } from 'vs/editor/common/modes/supports/electricCharacter'; -import { createLineContextFromTokenText } from 'vs/editor/test/common/modesTestUtils'; - -suite('Editor Modes - Auto Indentation', () => { - test('Doc comments', () => { - var brackets = new Brackets('test', null, [{ open: '/**', close: ' */' }]); - - assert.equal(brackets.onElectricCharacter(createLineContextFromTokenText([ - { text: '/**', type: 'doc' }, - ]), 2).appendText, ' */'); - assert.equal(brackets.onElectricCharacter(createLineContextFromTokenText([ - { text: '/**', type: 'doc' }, - { text: ' ', type: 'doc' }, - { text: '*/', type: 'doc' }, - ]), 2), null); - }); -}); diff --git a/src/vs/editor/test/common/modes/languageConfiguration.test.ts b/src/vs/editor/test/common/modes/languageConfiguration.test.ts new file mode 100644 index 00000000000..45bb72ca8ab --- /dev/null +++ b/src/vs/editor/test/common/modes/languageConfiguration.test.ts @@ -0,0 +1,93 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import { StandardAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration'; +import { StandardTokenType } from 'vs/editor/common/core/lineTokens'; + +suite('StandardAutoClosingPairConditional', () => { + + test('Missing notIn', () => { + let v = new StandardAutoClosingPairConditional({ open: '{', close: '}' }); + assert.equal(v.isOK(StandardTokenType.Other), true); + assert.equal(v.isOK(StandardTokenType.Comment), true); + assert.equal(v.isOK(StandardTokenType.String), true); + assert.equal(v.isOK(StandardTokenType.RegEx), true); + }); + + test('Empty notIn', () => { + let v = new StandardAutoClosingPairConditional({ open: '{', close: '}', notIn: [] }); + assert.equal(v.isOK(StandardTokenType.Other), true); + assert.equal(v.isOK(StandardTokenType.Comment), true); + assert.equal(v.isOK(StandardTokenType.String), true); + assert.equal(v.isOK(StandardTokenType.RegEx), true); + }); + + test('Invalid notIn', () => { + let v = new StandardAutoClosingPairConditional({ open: '{', close: '}', notIn: [ 'bla' ] }); + assert.equal(v.isOK(StandardTokenType.Other), true); + assert.equal(v.isOK(StandardTokenType.Comment), true); + assert.equal(v.isOK(StandardTokenType.String), true); + assert.equal(v.isOK(StandardTokenType.RegEx), true); + }); + + test('notIn in strings', () => { + let v = new StandardAutoClosingPairConditional({ open: '{', close: '}', notIn: [ 'string' ] }); + assert.equal(v.isOK(StandardTokenType.Other), true); + assert.equal(v.isOK(StandardTokenType.Comment), true); + assert.equal(v.isOK(StandardTokenType.String), false); + assert.equal(v.isOK(StandardTokenType.RegEx), true); + }); + + test('notIn in comments', () => { + let v = new StandardAutoClosingPairConditional({ open: '{', close: '}', notIn: [ 'comment' ] }); + assert.equal(v.isOK(StandardTokenType.Other), true); + assert.equal(v.isOK(StandardTokenType.Comment), false); + assert.equal(v.isOK(StandardTokenType.String), true); + assert.equal(v.isOK(StandardTokenType.RegEx), true); + }); + + test('notIn in regex', () => { + let v = new StandardAutoClosingPairConditional({ open: '{', close: '}', notIn: [ 'regex' ] }); + assert.equal(v.isOK(StandardTokenType.Other), true); + assert.equal(v.isOK(StandardTokenType.Comment), true); + assert.equal(v.isOK(StandardTokenType.String), true); + assert.equal(v.isOK(StandardTokenType.RegEx), false); + }); + + test('notIn in strings nor comments', () => { + let v = new StandardAutoClosingPairConditional({ open: '{', close: '}', notIn: [ 'string', 'comment' ] }); + assert.equal(v.isOK(StandardTokenType.Other), true); + assert.equal(v.isOK(StandardTokenType.Comment), false); + assert.equal(v.isOK(StandardTokenType.String), false); + assert.equal(v.isOK(StandardTokenType.RegEx), true); + }); + + test('notIn in strings nor regex', () => { + let v = new StandardAutoClosingPairConditional({ open: '{', close: '}', notIn: [ 'string', 'regex' ] }); + assert.equal(v.isOK(StandardTokenType.Other), true); + assert.equal(v.isOK(StandardTokenType.Comment), true); + assert.equal(v.isOK(StandardTokenType.String), false); + assert.equal(v.isOK(StandardTokenType.RegEx), false); + }); + + test('notIn in comments nor regex', () => { + let v = new StandardAutoClosingPairConditional({ open: '{', close: '}', notIn: [ 'comment', 'regex' ] }); + assert.equal(v.isOK(StandardTokenType.Other), true); + assert.equal(v.isOK(StandardTokenType.Comment), false); + assert.equal(v.isOK(StandardTokenType.String), true); + assert.equal(v.isOK(StandardTokenType.RegEx), false); + }); + + test('notIn in strings, comments nor regex', () => { + let v = new StandardAutoClosingPairConditional({ open: '{', close: '}', notIn: [ 'string', 'comment', 'regex' ] }); + assert.equal(v.isOK(StandardTokenType.Other), true); + assert.equal(v.isOK(StandardTokenType.Comment), false); + assert.equal(v.isOK(StandardTokenType.String), false); + assert.equal(v.isOK(StandardTokenType.RegEx), false); + }); +}); diff --git a/src/vs/editor/test/common/modes/supports/characterPair.test.ts b/src/vs/editor/test/common/modes/supports/characterPair.test.ts new file mode 100644 index 00000000000..85ebdaecd37 --- /dev/null +++ b/src/vs/editor/test/common/modes/supports/characterPair.test.ts @@ -0,0 +1,121 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { CharacterPairSupport } from 'vs/editor/common/modes/supports/characterPair'; +import { TokenText, createFakeScopedLineTokens } from 'vs/editor/test/common/modesTestUtils'; + +suite('CharacterPairSupport', () => { + + test('only autoClosingPairs', () => { + let characaterPairSupport = new CharacterPairSupport({ autoClosingPairs: [{ open: 'a', close: 'b' }] }); + assert.deepEqual(characaterPairSupport.getAutoClosingPairs(), [{ open: 'a', close: 'b', _standardTokenMask: 0 }]); + assert.deepEqual(characaterPairSupport.getSurroundingPairs(), [{ open: 'a', close: 'b', _standardTokenMask: 0 }]); + }); + + test('only empty autoClosingPairs', () => { + let characaterPairSupport = new CharacterPairSupport({ autoClosingPairs: [] }); + assert.deepEqual(characaterPairSupport.getAutoClosingPairs(), []); + assert.deepEqual(characaterPairSupport.getSurroundingPairs(), []); + }); + + test('only brackets', () => { + let characaterPairSupport = new CharacterPairSupport({ brackets: [['a', 'b']] }); + assert.deepEqual(characaterPairSupport.getAutoClosingPairs(), [{ open: 'a', close: 'b', _standardTokenMask: 0 }]); + assert.deepEqual(characaterPairSupport.getSurroundingPairs(), [{ open: 'a', close: 'b', _standardTokenMask: 0 }]); + }); + + test('only empty brackets', () => { + let characaterPairSupport = new CharacterPairSupport({ brackets: [] }); + assert.deepEqual(characaterPairSupport.getAutoClosingPairs(), []); + assert.deepEqual(characaterPairSupport.getSurroundingPairs(), []); + }); + + test('only surroundingPairs', () => { + let characaterPairSupport = new CharacterPairSupport({ surroundingPairs: [{ open: 'a', close: 'b' }] }); + assert.deepEqual(characaterPairSupport.getAutoClosingPairs(), []); + assert.deepEqual(characaterPairSupport.getSurroundingPairs(), [{ open: 'a', close: 'b' }]); + }); + + test('only empty surroundingPairs', () => { + let characaterPairSupport = new CharacterPairSupport({ surroundingPairs: [] }); + assert.deepEqual(characaterPairSupport.getAutoClosingPairs(), []); + assert.deepEqual(characaterPairSupport.getSurroundingPairs(), []); + }); + + test('brackets is ignored when having autoClosingPairs', () => { + let characaterPairSupport = new CharacterPairSupport({ autoClosingPairs: [], brackets: [['a', 'b']] }); + assert.deepEqual(characaterPairSupport.getAutoClosingPairs(), []); + assert.deepEqual(characaterPairSupport.getSurroundingPairs(), []); + }); + + function testShouldAutoClose(characterPairSupport: CharacterPairSupport, line: TokenText[], character: string, offset: number): boolean { + return characterPairSupport.shouldAutoClosePair(character, createFakeScopedLineTokens('test', line), offset); + } + + test('shouldAutoClosePair in empty line', () => { + let sup = new CharacterPairSupport({ autoClosingPairs: [{ open: '{', close: '}', notIn: ['string', 'comment'] }] }); + assert.equal(testShouldAutoClose(sup, [], 'a', 0), true); + assert.equal(testShouldAutoClose(sup, [], '{', 0), true); + }); + + test('shouldAutoClosePair in not interesting line 1', () => { + let sup = new CharacterPairSupport({ autoClosingPairs: [{ open: '{', close: '}', notIn: ['string', 'comment'] }] }); + assert.equal(testShouldAutoClose(sup, [{ text: 'do', type: 'keyword' }], '{', 2), true); + assert.equal(testShouldAutoClose(sup, [{ text: 'do', type: 'keyword' }], 'a', 2), true); + }); + + test('shouldAutoClosePair in not interesting line 2', () => { + let sup = new CharacterPairSupport({ autoClosingPairs: [{ open: '{', close: '}' }] }); + assert.equal(testShouldAutoClose(sup, [{ text: 'do', type: 'string' }], '{', 2), true); + assert.equal(testShouldAutoClose(sup, [{ text: 'do', type: 'string' }], 'a', 2), true); + }); + + test('shouldAutoClosePair in interesting line 1', () => { + let sup = new CharacterPairSupport({ autoClosingPairs: [{ open: '{', close: '}', notIn: ['string', 'comment'] }] }); + assert.equal(testShouldAutoClose(sup, [{ text: '"a"', type: 'string' }], '{', 0), false); + assert.equal(testShouldAutoClose(sup, [{ text: '"a"', type: 'string' }], 'a', 0), true); + assert.equal(testShouldAutoClose(sup, [{ text: '"a"', type: 'string' }], '{', 1), false); + assert.equal(testShouldAutoClose(sup, [{ text: '"a"', type: 'string' }], 'a', 1), true); + assert.equal(testShouldAutoClose(sup, [{ text: '"a"', type: 'string' }], '{', 2), false); + assert.equal(testShouldAutoClose(sup, [{ text: '"a"', type: 'string' }], 'a', 2), true); + assert.equal(testShouldAutoClose(sup, [{ text: '"a"', type: 'string' }], '{', 3), false); + assert.equal(testShouldAutoClose(sup, [{ text: '"a"', type: 'string' }], 'a', 3), true); + }); + + test('shouldAutoClosePair in interesting line 2', () => { + let sup = new CharacterPairSupport({ autoClosingPairs: [{ open: '{', close: '}', notIn: ['string', 'comment'] }] }); + assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: 'op' }, { text: '"a"', type: 'string' }, { text: ';', type: 'punct' }], '{', 0), true); + assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: 'op' }, { text: '"a"', type: 'string' }, { text: ';', type: 'punct' }], 'a', 0), true); + assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: 'op' }, { text: '"a"', type: 'string' }, { text: ';', type: 'punct' }], '{', 1), true); + assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: 'op' }, { text: '"a"', type: 'string' }, { text: ';', type: 'punct' }], 'a', 1), true); + assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: 'op' }, { text: '"a"', type: 'string' }, { text: ';', type: 'punct' }], '{', 2), true); + assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: 'op' }, { text: '"a"', type: 'string' }, { text: ';', type: 'punct' }], 'a', 2), true); + assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: 'op' }, { text: '"a"', type: 'string' }, { text: ';', type: 'punct' }], '{', 3), false); + assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: 'op' }, { text: '"a"', type: 'string' }, { text: ';', type: 'punct' }], 'a', 3), true); + assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: 'op' }, { text: '"a"', type: 'string' }, { text: ';', type: 'punct' }], '{', 4), false); + assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: 'op' }, { text: '"a"', type: 'string' }, { text: ';', type: 'punct' }], 'a', 4), true); + assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: 'op' }, { text: '"a"', type: 'string' }, { text: ';', type: 'punct' }], '{', 5), false); + assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: 'op' }, { text: '"a"', type: 'string' }, { text: ';', type: 'punct' }], 'a', 5), true); + assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: 'op' }, { text: '"a"', type: 'string' }, { text: ';', type: 'punct' }], '{', 6), true); + assert.equal(testShouldAutoClose(sup, [{ text: 'x=', type: 'op' }, { text: '"a"', type: 'string' }, { text: ';', type: 'punct' }], 'a', 6), true); + }); + + test('shouldAutoClosePair in interesting line 3', () => { + let sup = new CharacterPairSupport({ autoClosingPairs: [{ open: '{', close: '}', notIn: ['string', 'comment'] }] }); + assert.equal(testShouldAutoClose(sup, [{ text: ' ', type: '' }, { text: '//a', type: 'comment' }], '{', 0), true); + assert.equal(testShouldAutoClose(sup, [{ text: ' ', type: '' }, { text: '//a', type: 'comment' }], 'a', 0), true); + assert.equal(testShouldAutoClose(sup, [{ text: ' ', type: '' }, { text: '//a', type: 'comment' }], '{', 1), true); + assert.equal(testShouldAutoClose(sup, [{ text: ' ', type: '' }, { text: '//a', type: 'comment' }], 'a', 1), true); + assert.equal(testShouldAutoClose(sup, [{ text: ' ', type: '' }, { text: '//a', type: 'comment' }], '{', 2), false); + assert.equal(testShouldAutoClose(sup, [{ text: ' ', type: '' }, { text: '//a', type: 'comment' }], 'a', 2), true); + assert.equal(testShouldAutoClose(sup, [{ text: ' ', type: '' }, { text: '//a', type: 'comment' }], '{', 3), false); + assert.equal(testShouldAutoClose(sup, [{ text: ' ', type: '' }, { text: '//a', type: 'comment' }], 'a', 3), true); + assert.equal(testShouldAutoClose(sup, [{ text: ' ', type: '' }, { text: '//a', type: 'comment' }], '{', 4), false); + assert.equal(testShouldAutoClose(sup, [{ text: ' ', type: '' }, { text: '//a', type: 'comment' }], 'a', 4), true); + }); + +}); diff --git a/src/vs/editor/test/common/modes/supports/electricCharacter.test.ts b/src/vs/editor/test/common/modes/supports/electricCharacter.test.ts new file mode 100644 index 00000000000..1dcb2ffcae7 --- /dev/null +++ b/src/vs/editor/test/common/modes/supports/electricCharacter.test.ts @@ -0,0 +1,128 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { BracketElectricCharacterSupport, IElectricAction } from 'vs/editor/common/modes/supports/electricCharacter'; +import { createFakeScopedLineTokens, TokenText } from 'vs/editor/test/common/modesTestUtils'; +import { RichEditBrackets } from 'vs/editor/common/modes/supports/richEditBrackets'; + +suite('Editor Modes - Auto Indentation', () => { + function _testOnElectricCharacter(electricCharacterSupport: BracketElectricCharacterSupport, line: TokenText[], offset: number): IElectricAction { + return electricCharacterSupport.onElectricCharacter(createFakeScopedLineTokens('test', line), offset); + } + + function testDoesNothing(electricCharacterSupport: BracketElectricCharacterSupport, line: TokenText[], offset: number): void { + let actual = _testOnElectricCharacter(electricCharacterSupport, line, offset); + assert.deepEqual(actual, null); + } + + function testAppends(electricCharacterSupport: BracketElectricCharacterSupport, line: TokenText[], offset: number, appendText: string): void { + let actual = _testOnElectricCharacter(electricCharacterSupport, line, offset); + assert.deepEqual(actual, { appendText: appendText }); + } + + function testMatchBracket(electricCharacterSupport: BracketElectricCharacterSupport, line: TokenText[], offset: number, matchOpenBracket: string): void { + let actual = _testOnElectricCharacter(electricCharacterSupport, line, offset); + assert.deepEqual(actual, { matchOpenBracket: matchOpenBracket }); + } + + test('Doc comments', () => { + var brackets = new BracketElectricCharacterSupport(null, [{ open: '/**', close: ' */' }], null); + + testAppends(brackets, [ + { text: '/**', type: 'doc' }, + ], 2, ' */'); + + testDoesNothing(brackets, [ + { text: '/**', type: 'doc' }, + { text: ' ', type: 'doc' }, + { text: '*/', type: 'doc' }, + ], 2); + }); + + test('getElectricCharacters uses all sources and dedups', () => { + var sup = new BracketElectricCharacterSupport( + new RichEditBrackets('test', [ + ['{', '}'], + ['(', ')'] + ]), [ + { open: '{', close: '}', notIn: ['string', 'comment'] }, + { open: '"', close: '"', notIn: ['string', 'comment'] }, + { open: 'begin', close: 'end', notIn: ['string'] } + ], + { docComment: { open: '/**', close: ' */' } } + ); + + assert.deepEqual(sup.getElectricCharacters(), ['}', ')', 'n', '*']); + }); + + test('auto-close', () => { + var sup = new BracketElectricCharacterSupport( + new RichEditBrackets('test', [ + ['{', '}'], + ['(', ')'] + ]), [ + { open: '{', close: '}', notIn: ['string', 'comment'] }, + { open: '"', close: '"', notIn: ['string', 'comment'] }, + { open: 'begin', close: 'end', notIn: ['string'] } + ], + { docComment: { open: '/**', close: ' */' } } + ); + + testDoesNothing(sup, [], 0); + + testDoesNothing(sup, [{ text: 'begi', type: '' }], 0); + testDoesNothing(sup, [{ text: 'begi', type: '' }], 1); + testDoesNothing(sup, [{ text: 'begi', type: '' }], 2); + testDoesNothing(sup, [{ text: 'begi', type: '' }], 3); + testDoesNothing(sup, [{ text: 'begi', type: '' }], 4); + + testDoesNothing(sup, [{ text: 'begin', type: '' }], 0); + testDoesNothing(sup, [{ text: 'begin', type: '' }], 1); + testDoesNothing(sup, [{ text: 'begin', type: '' }], 2); + testDoesNothing(sup, [{ text: 'begin', type: '' }], 3); + testAppends(sup, [{ text: 'begin', type: '' }], 4, 'end'); + testDoesNothing(sup, [{ text: 'begin', type: '' }], 5); + + testDoesNothing(sup, [{ text: 'b3gin', type: '' }], 0); + testDoesNothing(sup, [{ text: 'b3gin', type: '' }], 1); + testDoesNothing(sup, [{ text: 'b3gin', type: '' }], 2); + testDoesNothing(sup, [{ text: 'b3gin', type: '' }], 3); + testDoesNothing(sup, [{ text: 'b3gin', type: '' }], 4); + testDoesNothing(sup, [{ text: 'b3gin', type: '' }], 5); + + testDoesNothing(sup, [{ text: 'begin', type: 'string' }], 4); + + testAppends(sup, [{ text: '"', type: 'string' }, { text: 'begin', type: '' }], 5, 'end'); + testDoesNothing(sup, [{ text: '"', type: 'string' }, { text: 'begin', type: 'string' }], 5); + + testAppends(sup, [{ text: '/**', type: 'string' }], 2, ' */'); + + testDoesNothing(sup, [{ text: 'begin', type: '' }, { text: 'end', type: '' }], 4); + }); + + test('matchOpenBracket', () => { + var sup = new BracketElectricCharacterSupport( + new RichEditBrackets('test', [ + ['{', '}'], + ['(', ')'] + ]), [ + { open: '{', close: '}', notIn: ['string', 'comment'] }, + { open: '"', close: '"', notIn: ['string', 'comment'] }, + { open: 'begin', close: 'end', notIn: ['string'] } + ], + { docComment: { open: '/**', close: ' */' } } + ); + + testDoesNothing(sup, [{ text: '\t\t{', type: '' }], 0); + testDoesNothing(sup, [{ text: '\t\t{', type: '' }], 1); + testDoesNothing(sup, [{ text: '\t\t{', type: '' }], 2); + + testDoesNothing(sup, [{ text: '\t\t}', type: '' }], 0); + testDoesNothing(sup, [{ text: '\t\t}', type: '' }], 1); + testMatchBracket(sup, [{ text: '\t\t}', type: '' }], 2, '}'); + }); +}); diff --git a/src/vs/editor/test/common/modes/supports/onEnter.test.ts b/src/vs/editor/test/common/modes/supports/onEnter.test.ts index 93678319639..075f4cfb14d 100644 --- a/src/vs/editor/test/common/modes/supports/onEnter.test.ts +++ b/src/vs/editor/test/common/modes/supports/onEnter.test.ts @@ -5,13 +5,13 @@ 'use strict'; import * as assert from 'assert'; -import { IndentAction, CharacterPair } from 'vs/editor/common/modes'; +import { CharacterPair, IndentAction } from 'vs/editor/common/modes/languageConfiguration'; import { OnEnterSupport } from 'vs/editor/common/modes/supports/onEnter'; suite('OnEnter', () => { test('uses indentationRules', () => { - var support = new OnEnterSupport(null, null, { + var support = new OnEnterSupport({ indentationRules: { decreaseIndentPattern: /^\s*((?!\S.*\/[*]).*[*]\/\s*)?[})\]]|^\s*(case\b.*|default):\s*(\/\/.*|\/[*].*[*]\/\s*)?$/, increaseIndentPattern: /(\{[^}"']*|\([^)"']*|\[[^\]"']*|^\s*(\{\}|\(\)|\[\]|(case\b.*|default):))\s*(\/\/.*|\/[*].*[*]\/\s*)?$/, @@ -21,7 +21,7 @@ suite('OnEnter', () => { }); var testIndentAction = (oneLineAboveText: string, beforeText: string, afterText: string, expected: IndentAction) => { - var actual = support._actualOnEnter(oneLineAboveText, beforeText, afterText); + var actual = support.onEnter(oneLineAboveText, beforeText, afterText); if (expected === IndentAction.None) { assert.equal(actual, null); } else { @@ -42,11 +42,11 @@ suite('OnEnter', () => { ['(', ')'], ['begin', 'end'] ]; - var support = new OnEnterSupport(null, null, { + var support = new OnEnterSupport({ brackets: brackets }); var testIndentAction = (beforeText: string, afterText: string, expected: IndentAction) => { - var actual = support._actualOnEnter('', beforeText, afterText); + var actual = support.onEnter('', beforeText, afterText); if (expected === IndentAction.None) { assert.equal(actual, null); } else { @@ -75,7 +75,7 @@ suite('OnEnter', () => { }); test('uses regExpRules', () => { - var support = new OnEnterSupport(null, null, { + var support = new OnEnterSupport({ regExpRules: [ { beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/, @@ -101,7 +101,7 @@ suite('OnEnter', () => { ] }); var testIndentAction = (beforeText: string, afterText: string, expectedIndentAction: IndentAction, expectedAppendText: string, removeText: number = 0) => { - var actual = support._actualOnEnter('', beforeText, afterText); + var actual = support.onEnter('', beforeText, afterText); if (expectedIndentAction === null) { assert.equal(actual, null, 'isNull:' + beforeText); } else { diff --git a/src/vs/editor/test/common/modes/tokenization.test.ts b/src/vs/editor/test/common/modes/tokenization.test.ts index 1d6cf4cd04c..4b1e073f78b 100644 --- a/src/vs/editor/test/common/modes/tokenization.test.ts +++ b/src/vs/editor/test/common/modes/tokenization.test.ts @@ -7,9 +7,9 @@ import * as assert from 'assert'; import * as modes from 'vs/editor/common/modes'; import { AbstractState, ITokenizationResult } from 'vs/editor/common/modes/abstractState'; -import { handleEvent } from 'vs/editor/common/modes/supports'; +import { createScopedLineTokens } from 'vs/editor/common/modes/supports'; import { IModeLocator, ILeavingNestedModeData, TokenizationSupport } from 'vs/editor/common/modes/supports/tokenizationSupport'; -import { createMockLineContext } from 'vs/editor/test/common/modesTestUtils'; +import { createFakeLineTokens } from 'vs/editor/test/common/modesTestUtils'; import { MockMode } from 'vs/editor/test/common/mocks/mockMode'; import { ModeTransition } from 'vs/editor/common/core/modeTransition'; import { Token } from 'vs/editor/common/core/token'; @@ -315,27 +315,21 @@ suite('Editor Modes - Tokenization', () => { { startIndex: 5, id: 'B' } ]); - handleEvent(createMockLineContext('abc (def', lineTokens), 0, (modeId: string, context: modes.ILineContext, offset: number) => { - assert.deepEqual(modeId, 'A'); - assert.equal(context.getTokenCount(), 3); - assert.equal(context.getTokenStartOffset(0), 0); - assert.equal(context.getTokenType(0), 'A.abc'); - assert.equal(context.getTokenStartOffset(1), 3); - assert.equal(context.getTokenType(1), ''); - assert.equal(context.getTokenStartOffset(2), 4); - assert.equal(context.getTokenType(2), 'A.('); - assert.deepEqual(offset, 0); - assert.equal(context.getLineContent(), 'abc ('); - }); + let scopedLineTokens1 = createScopedLineTokens(createFakeLineTokens('abc (def', switchingMode.getId(), lineTokens.tokens, lineTokens.modeTransitions), 0); + assert.deepEqual(scopedLineTokens1.modeId, 'A'); + assert.equal(scopedLineTokens1.getTokenCount(), 3); + assert.equal(scopedLineTokens1.getTokenStartOffset(0), 0); + assert.equal(scopedLineTokens1.getTokenStartOffset(1), 3); + assert.equal(scopedLineTokens1.getTokenStartOffset(2), 4); + assert.deepEqual(scopedLineTokens1.firstCharOffset, 0); + assert.equal(scopedLineTokens1.getLineContent(), 'abc ('); - handleEvent(createMockLineContext('abc (def', lineTokens), 6, (modeId: string, context: modes.ILineContext, offset: number) => { - assert.deepEqual(modeId, 'B'); - assert.equal(context.getTokenCount(), 1); - assert.equal(context.getTokenStartOffset(0), 0); - assert.equal(context.getTokenType(0), 'B.def'); - assert.deepEqual(offset, 1); - assert.equal(context.getLineContent(), 'def'); - }); + let scopedLineTokens2 = createScopedLineTokens(createFakeLineTokens('abc (def', switchingMode.getId(), lineTokens.tokens, lineTokens.modeTransitions), 6); + assert.deepEqual(scopedLineTokens2.modeId, 'B'); + assert.equal(scopedLineTokens2.getTokenCount(), 1); + assert.equal(scopedLineTokens2.getTokenStartOffset(0), 0); + assert.deepEqual(scopedLineTokens2.firstCharOffset, 5); + assert.equal(scopedLineTokens2.getLineContent(), 'def'); lineTokens = switchingModeTokenize('ghi jkl', lineTokens.endState); assertTokens(lineTokens.tokens, [ diff --git a/src/vs/editor/test/common/modesTestUtils.ts b/src/vs/editor/test/common/modesTestUtils.ts index f810d241106..2b3701fb005 100644 --- a/src/vs/editor/test/common/modesTestUtils.ts +++ b/src/vs/editor/test/common/modesTestUtils.ts @@ -4,17 +4,28 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { Arrays } from 'vs/editor/common/core/arrays'; -import * as modes from 'vs/editor/common/modes'; import { ModeTransition } from 'vs/editor/common/core/modeTransition'; import { Token } from 'vs/editor/common/core/token'; +import { LineTokens } from 'vs/editor/common/core/lineTokens'; +import { TokensBinaryEncoding, TokensInflatorMap } from 'vs/editor/common/model/tokensBinaryEncoding'; +import { createScopedLineTokens, ScopedLineTokens } from 'vs/editor/common/modes/supports'; + +export function createFakeLineTokens(line: string, modeId: string, tokens: Token[], modeTransitions: ModeTransition[]): LineTokens { + let map = new TokensInflatorMap(modeId); + let deflatedTokens = TokensBinaryEncoding.deflateArr(map, tokens); + return new LineTokens(map, deflatedTokens, modeTransitions, line); +} export interface TokenText { text: string; type: string; } -export function createLineContextFromTokenText(tokens: TokenText[]): modes.ILineContext { +export function createFakeScopedLineTokens(modeId: string, tokens: TokenText[]): ScopedLineTokens { + return createScopedLineTokens(createLineContextFromTokenText(modeId, tokens), 0); +} + +function createLineContextFromTokenText(modeId: string, tokens: TokenText[]): LineTokens { let line = ''; let processedTokens: Token[] = []; @@ -25,42 +36,5 @@ export function createLineContextFromTokenText(tokens: TokenText[]): modes.ILine indexSoFar += tokens[i].text.length; } - return new TestLineContext(line, processedTokens, null); + return createFakeLineTokens(line, modeId, processedTokens, [new ModeTransition(0, modeId)]); } - -export function createMockLineContext(line: string, tokens: modes.ILineTokens): modes.ILineContext { - return new TestLineContext(line, tokens.tokens, tokens.modeTransitions); -} - -class TestLineContext implements modes.ILineContext { - - public modeTransitions: ModeTransition[]; - private _line: string; - private _tokens: Token[]; - - constructor(line: string, tokens: Token[], modeTransitions: ModeTransition[]) { - this.modeTransitions = modeTransitions; - this._line = line; - this._tokens = tokens; - } - - public getLineContent(): string { - return this._line; - } - - public getTokenCount(): number { - return this._tokens.length; - } - - public getTokenStartOffset(tokenIndex: number): number { - return this._tokens[tokenIndex].startIndex; - } - - public getTokenType(tokenIndex: number): string { - return this._tokens[tokenIndex].type; - } - - public findIndexOfOffset(offset: number): number { - return Arrays.findIndexInSegmentsArray(this._tokens, offset); - } -} \ No newline at end of file diff --git a/src/vs/editor/test/common/testModes.ts b/src/vs/editor/test/common/testModes.ts index 4e3c144111e..eeda213c85e 100644 --- a/src/vs/editor/test/common/testModes.ts +++ b/src/vs/editor/test/common/testModes.ts @@ -4,7 +4,8 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { LanguageConfigurationRegistry, CommentRule } from 'vs/editor/common/modes/languageConfigurationRegistry'; +import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; +import { CommentRule } from 'vs/editor/common/modes/languageConfiguration'; import { MockMode } from 'vs/editor/test/common/mocks/mockMode'; export class CommentMode extends MockMode { diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 0329b1eb5c0..2e5f97c1a6a 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -4173,17 +4173,77 @@ declare module monaco.languages { export interface IBracketElectricCharacterContribution { docComment?: IDocComment; - embeddedElectricCharacters?: string[]; } /** * Definition of documentation comments (e.g. Javadoc/JSdoc) */ export interface IDocComment { - scope: string; + /** + * The string that starts a doc comment (e.g. '/**') + */ open: string; - lineStart: string; - close?: string; + /** + * The string that appears on the last line and closes the doc comment (e.g. ' * /'). + */ + close: string; + } + + /** + * A tuple of two characters, like a pair of + * opening and closing brackets. + */ + export type CharacterPair = [string, string]; + + export interface IAutoClosingPair { + open: string; + close: string; + } + + export interface IAutoClosingPairConditional extends IAutoClosingPair { + notIn?: string[]; + } + + /** + * Describes what to do with the indentation when pressing Enter. + */ + export enum IndentAction { + /** + * Insert new line and copy the previous line's indentation. + */ + None = 0, + /** + * Insert new line and indent once (relative to the previous line's indentation). + */ + Indent = 1, + /** + * Insert two new lines: + * - the first one indented which will hold the cursor + * - the second one at the same indentation level + */ + IndentOutdent = 2, + /** + * Insert new line and outdent once (relative to the previous line's indentation). + */ + Outdent = 3, + } + + /** + * Describes what to do when pressing Enter. + */ + export interface EnterAction { + /** + * Describe what to do with the indentation. + */ + indentAction: IndentAction; + /** + * Describes text to be appended after the new line and after the indentation. + */ + appendText?: string; + /** + * Describes the number of characters to remove from the new line's indentation. + */ + removeText?: number; } /** @@ -4620,63 +4680,6 @@ declare module monaco.languages { resolveCodeLens?(model: editor.IReadOnlyModel, codeLens: ICodeLensSymbol, token: CancellationToken): ICodeLensSymbol | Thenable; } - /** - * A tuple of two characters, like a pair of - * opening and closing brackets. - */ - export type CharacterPair = [string, string]; - - export interface IAutoClosingPairConditional extends IAutoClosingPair { - notIn?: string[]; - } - - /** - * Describes what to do with the indentation when pressing Enter. - */ - export enum IndentAction { - /** - * Insert new line and copy the previous line's indentation. - */ - None = 0, - /** - * Insert new line and indent once (relative to the previous line's indentation). - */ - Indent = 1, - /** - * Insert two new lines: - * - the first one indented which will hold the cursor - * - the second one at the same indentation level - */ - IndentOutdent = 2, - /** - * Insert new line and outdent once (relative to the previous line's indentation). - */ - Outdent = 3, - } - - /** - * Describes what to do when pressing Enter. - */ - export interface EnterAction { - /** - * Describe what to do with the indentation. - */ - indentAction: IndentAction; - /** - * Describes text to be appended after the new line and after the indentation. - */ - appendText?: string; - /** - * Describes the number of characters to remove from the new line's indentation. - */ - removeText?: number; - } - - export interface IAutoClosingPair { - open: string; - close: string; - } - export interface ILanguageExtensionPoint { id: string; extensions?: string[]; diff --git a/src/vs/platform/commands/test/commandService.test.ts b/src/vs/platform/commands/test/commandService.test.ts index 3ef879a5d8f..7496b53b4a2 100644 --- a/src/vs/platform/commands/test/commandService.test.ts +++ b/src/vs/platform/commands/test/commandService.test.ts @@ -9,7 +9,7 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { TPromise } from 'vs/base/common/winjs.base'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { CommandService } from 'vs/platform/commands/common/commandService'; -import { IExtensionService, ExtensionPointContribution } from 'vs/platform/extensions/common/extensions'; +import { IExtensionService, ExtensionPointContribution, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; import { IExtensionPoint } from 'vs/platform/extensions/common/extensionsRegistry'; @@ -27,6 +27,9 @@ class SimpleExtensionService implements IExtensionService { getExtensionsStatus() { return undefined; } + getExtensions() : TPromise { + return TPromise.wrap([]); + } } suite('CommandService', function () { diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index 23998095f4f..386e7cb10e0 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -17,6 +17,7 @@ export interface IEnvironmentService { appRoot: string; userHome: string; + userProductHome: string; userDataPath: string; appSettingsHome: string; diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts index 990d047edb1..d656bf4c3b2 100644 --- a/src/vs/platform/environment/node/environmentService.ts +++ b/src/vs/platform/environment/node/environmentService.ts @@ -63,7 +63,10 @@ export class EnvironmentService implements IEnvironmentService { get execPath(): string { return this._execPath; } @memoize - get userHome(): string { return path.join(os.homedir(), product.dataFolderName); } + get userHome(): string { return os.homedir(); } + + @memoize + get userProductHome(): string { return path.join(this.userHome, product.dataFolderName); } @memoize get userDataPath(): string { return parseUserDataDir(this._args, process); } @@ -78,7 +81,7 @@ export class EnvironmentService implements IEnvironmentService { get appKeybindingsPath(): string { return path.join(this.appSettingsHome, 'keybindings.json'); } @memoize - get extensionsPath(): string { return path.normalize(this._args['extensions-dir'] || path.join(this.userHome, 'extensions')); } + get extensionsPath(): string { return path.normalize(this._args['extensions-dir'] || path.join(this.userProductHome, 'extensions')); } @memoize get extensionDevelopmentPath(): string { return this._args.extensionDevelopmentPath ? path.normalize(this._args.extensionDevelopmentPath) : this._args.extensionDevelopmentPath; } diff --git a/src/vs/platform/extensionManagement/common/extensionEnablementService.ts b/src/vs/platform/extensionManagement/common/extensionEnablementService.ts new file mode 100644 index 00000000000..c5969b330cb --- /dev/null +++ b/src/vs/platform/extensionManagement/common/extensionEnablementService.ts @@ -0,0 +1,148 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { localize } from 'vs/nls'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { distinct } from 'vs/base/common/arrays'; +import Event, { Emitter } from 'vs/base/common/event'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IExtensionManagementService, DidUninstallExtensionEvent, IExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IWorkspaceContextService, IWorkspace } from 'vs/platform/workspace/common/workspace'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; + +const DISABLED_EXTENSIONS_STORAGE_PATH = 'extensions/disabled'; + +export class ExtensionEnablementService implements IExtensionEnablementService { + + _serviceBrand: any; + + private workspace: IWorkspace; + private disposables: IDisposable[] = []; + + private _onEnablementChanged = new Emitter(); + public onEnablementChanged: Event = this._onEnablementChanged.event; + + constructor( + @IStorageService private storageService: IStorageService, + @IWorkspaceContextService contextService: IWorkspaceContextService, + @IMessageService private messageService: IMessageService, + @IEnvironmentService private environmentService: IEnvironmentService, + @IExtensionManagementService private extensionManagementService: IExtensionManagementService + ) { + this.workspace = contextService.getWorkspace(); + extensionManagementService.onDidUninstallExtension(this.onDidUninstallExtension, this, this.disposables); + } + + public getGloballyDisabledExtensions(): string[] { + return this.getDisabledExtensionsFromStorage(StorageScope.GLOBAL); + } + + public getWorkspaceDisabledExtensions(): string[] { + return this.getDisabledExtensionsFromStorage(StorageScope.WORKSPACE); + } + + public canEnable(identifier: string): boolean { + return !this.environmentService.disableExtensions && this.isDisabled(identifier); + } + + public setEnablement(identifier: string, enable: boolean, workspace: boolean = false): TPromise { + if (workspace && !this.workspace) { + return TPromise.wrapError(localize('noWorkspace', "No workspace.")); + } + + if (this.environmentService.disableExtensions) { + return TPromise.wrap(false); + } + + if (this.isDisabled(identifier) === !enable) { + return TPromise.wrap(false); + } + + if (enable) { + if (workspace) { + this.enableExtension(identifier, StorageScope.WORKSPACE); + } else { + this.enableExtension(identifier, StorageScope.GLOBAL); + } + } else { + if (workspace) { + this.disableExtension(identifier, StorageScope.WORKSPACE); + } else { + this.disableExtension(identifier, StorageScope.GLOBAL); + } + } + + return TPromise.wrap(true); + } + + private getDisabledExtensions(): string[] { + const globalDisabledExtensions = this.getGloballyDisabledExtensions(); + const workspaceDisabledExtensions = this.getWorkspaceDisabledExtensions(); + return distinct([...workspaceDisabledExtensions, ...globalDisabledExtensions]); + } + + private isDisabled(identifier: string): boolean { + return this.getDisabledExtensions().indexOf(identifier) !== -1; + } + + private getDisabledExtensionsFromStorage(scope?: StorageScope): string[] { + if (scope !== void 0) { + return this._getDisabledExtensions(scope); + } + + const globallyDisabled = this._getDisabledExtensions(StorageScope.GLOBAL); + const workspaceDisabled = this._getDisabledExtensions(StorageScope.WORKSPACE); + return [...globallyDisabled, ...workspaceDisabled]; + } + + private disableExtension(identifier: string, scope: StorageScope): TPromise { + let disabledExtensions = this._getDisabledExtensions(scope); + disabledExtensions.push(identifier); + this._setDisabledExtensions(disabledExtensions, scope, identifier); + return TPromise.wrap(true); + } + + private enableExtension(identifier: string, scope: StorageScope): TPromise { + let disabledExtensions = this._getDisabledExtensions(scope); + const index = disabledExtensions.indexOf(identifier); + if (index !== -1) { + disabledExtensions.splice(index, 1); + this._setDisabledExtensions(disabledExtensions, scope, identifier); + } + return TPromise.wrap(true); + } + + private _getDisabledExtensions(scope: StorageScope): string[] { + const value = this.storageService.get(DISABLED_EXTENSIONS_STORAGE_PATH, scope, ''); + return value ? distinct(value.split(',')) : []; + } + + private _setDisabledExtensions(disabledExtensions: string[], scope: StorageScope, extension: string): void { + if (disabledExtensions.length) { + this.storageService.store(DISABLED_EXTENSIONS_STORAGE_PATH, disabledExtensions.join(','), scope); + } else { + this.storageService.remove(DISABLED_EXTENSIONS_STORAGE_PATH, scope); + } + this._onEnablementChanged.fire(extension); + } + + private onDidUninstallExtension({id, error}: DidUninstallExtensionEvent): void { + if (!error) { + id = stripVersion(id); + this.enableExtension(id, StorageScope.WORKSPACE); + this.enableExtension(id, StorageScope.GLOBAL); + } + } + + dispose(): void { + this.disposables = dispose(this.disposables); + } +} + +function stripVersion(id: string): string { + return id.replace(/-\d+\.\d+\.\d+$/, ''); +} \ No newline at end of file diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index af76fd3bad0..414e7c597fe 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -240,6 +240,46 @@ export interface IExtensionManagementService { getInstalled(type?: LocalExtensionType): TPromise; } +export const IExtensionEnablementService = createDecorator('extensionEnablementService'); + +// TODO: @sandy: Merge this into IExtensionManagementService when we have a storage service available in Shared process +export interface IExtensionEnablementService { + _serviceBrand: any; + + /** + * Event to listen on for extension enablement changes + */ + onEnablementChanged: Event; + + /** + * Returns all globally disabled extension identifiers. + * Returns an empty array if none exist. + */ + getGloballyDisabledExtensions(): string[]; + + /** + * Returns all workspace disabled extension identifiers. + * Returns an empty array if none exist or workspace does not exist. + */ + getWorkspaceDisabledExtensions(): string[]; + + /** + * Returns `true` if given extension can be enabled by calling `setEnablement`, otherwise false`. + */ + canEnable(identifier: string): boolean; + + /** + * Enable or disable the given extension. + * if `workspace` is `true` then enablement is done for workspace, otherwise globally. + * + * Returns a promise that resolves to boolean value. + * if resolves to `true` then requires restart for the change to take effect. + * + * Throws error if enablement is requested for workspace and there is no workspace + */ + setEnablement(identifier: string, enable: boolean, workspace?: boolean): TPromise; +} + export const IExtensionTipsService = createDecorator('extensionTipsService'); export interface IExtensionTipsService { diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 1454d13f0e3..1e5d0033a8c 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -345,7 +345,7 @@ export class ExtensionManagementService implements IExtensionManagementService { nls.localize('uninstallAll', "All"), nls.localize('cancel', "Cancel") ]; - return this.choiceService.choose(Severity.Info, message, options) + return this.choiceService.choose(Severity.Info, message, options, true) .then(value => { if (value === 0) { return this.uninstallWithDependencies(extension, [], installed); @@ -364,7 +364,7 @@ export class ExtensionManagementService implements IExtensionManagementService { nls.localize('ok', "Ok"), nls.localize('cancel', "Cancel") ]; - return this.choiceService.choose(Severity.Info, message, options) + return this.choiceService.choose(Severity.Info, message, options, true) .then(value => { if (value === 0) { return this.uninstallWithDependencies(extension, [], installed); diff --git a/src/vs/platform/extensions/common/abstractExtensionService.ts b/src/vs/platform/extensions/common/abstractExtensionService.ts index b6b9f4fca70..f0214d5ef1f 100644 --- a/src/vs/platform/extensions/common/abstractExtensionService.ts +++ b/src/vs/platform/extensions/common/abstractExtensionService.ts @@ -78,7 +78,7 @@ export abstract class AbstractExtensionService imp }); } - public readExtensions(): TPromise { + public getExtensions(): TPromise { return this.onReady().then(() => { return this._registry.getAllExtensionDescriptions(); }); diff --git a/src/vs/platform/extensions/common/extensions.ts b/src/vs/platform/extensions/common/extensions.ts index e3b96a680ed..119cc706626 100644 --- a/src/vs/platform/extensions/common/extensions.ts +++ b/src/vs/platform/extensions/common/extensions.ts @@ -9,6 +9,8 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IExtensionPoint } from 'vs/platform/extensions/common/extensionsRegistry'; +export const ExtensionProperties = ['id', 'name', 'version', 'publisher', 'isBuiltin', 'extensionFolderPath', 'extensionDependencies', 'activationEvents', 'engines', 'main', 'contributes', 'enableProposedApi']; + export interface IExtensionDescription { readonly id: string; readonly name: string; @@ -23,7 +25,7 @@ export interface IExtensionDescription { }; readonly main?: string; readonly contributes?: { [point: string]: any; }; - readonly enableProposedApi: boolean; + readonly enableProposedApi?: boolean; } export const IExtensionService = createDecorator('extensionService'); @@ -61,6 +63,11 @@ export interface IExtensionService { */ onReady(): TPromise; + /** + * Return all registered extensions + */ + getExtensions(): TPromise; + /** * Read all contributions to an extension point. */ @@ -70,42 +77,4 @@ export interface IExtensionService { * Get information about extensions status. */ getExtensionsStatus(): { [id: string]: IExtensionsStatus }; -} - -export const IExtensionsRuntimeService = createDecorator('extensionsRuntimeService'); - -export interface IExtensionsRuntimeService { - _serviceBrand: any; - - /** - * Scans and returns only enabled extensions. - * **NOTE**: This call returns different results based on `setEnablement` calls! - */ - getExtensions(): TPromise; - - /** - * Returns `true` if given extension is disabled, otherwise `false`. - */ - isDisabled(identifier: string): boolean; - - /** - * Returns `true` if given extension is disabled always, otherwise `false`. - */ - isDisabledAlways(identifier: string): boolean; - - /** - * Returns `true` if given extension can be enabled by calling `setEnablement`, otherwise false`. - */ - canEnable(identifier: string): boolean; - - /** - * Enable or disable the given extension. - * if `workspace` is `true` then enablement is done for workspace, otherwise globally. - * - * Returns a promise that resolves to boolean value. - * if resolves to `true` then requires restart for the change to take effect. - * - * Throws error if enablement is requested for workspace and there is no workspace - */ - setEnablement(identifier: string, enable: boolean, workspace?: boolean): TPromise; -} +} \ No newline at end of file diff --git a/src/vs/platform/extensions/common/extensionsRegistry.ts b/src/vs/platform/extensions/common/extensionsRegistry.ts index a5d19f5bc6a..703c4e08057 100644 --- a/src/vs/platform/extensions/common/extensionsRegistry.ts +++ b/src/vs/platform/extensions/common/extensionsRegistry.ts @@ -148,7 +148,7 @@ const schema: IJSONSchema = { uniqueItems: true, items: { type: 'string', - enum: ['Languages', 'Snippets', 'Linters', 'Themes', 'Debuggers', 'Productivity', 'Other', 'Keymaps', 'Formatters'] + enum: ['Languages', 'Snippets', 'Linters', 'Themes', 'Debuggers', 'Other', 'Keymaps', 'Formatters'] } }, galleryBanner: { diff --git a/src/vs/platform/files/common/files.ts b/src/vs/platform/files/common/files.ts index dfa97b21361..f444305e28e 100644 --- a/src/vs/platform/files/common/files.ts +++ b/src/vs/platform/files/common/files.ts @@ -9,6 +9,7 @@ import paths = require('vs/base/common/paths'); import URI from 'vs/base/common/uri'; import glob = require('vs/base/common/glob'); import events = require('vs/base/common/events'); +import { isLinux } from 'vs/base/common/platform'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; export const IFileService = createDecorator('fileService'); @@ -191,19 +192,6 @@ export class FileChangesEvent extends events.Event { return false; } - return this.containsAny([resource], type); - } - - /** - * Returns true if this change event contains any of the provided files with the given change type. In case of - * type DELETED, this method will also return true if a folder got deleted that is the parent of any of the - * provided file paths. - */ - public containsAny(resources: URI[], type: FileChangeType): boolean { - if (!resources || !resources.length) { - return false; - } - return this._changes.some((change) => { if (change.type !== type) { return false; @@ -211,22 +199,10 @@ export class FileChangesEvent extends events.Event { // For deleted also return true when deleted folder is parent of target path if (type === FileChangeType.DELETED) { - return resources.some((a: URI) => { - if (!a) { - return false; - } - - return paths.isEqualOrParent(a.fsPath, change.resource.fsPath); - }); + return isEqual(resource.fsPath, change.resource.fsPath) || isParent(resource.fsPath, change.resource.fsPath); } - return resources.some((a: URI) => { - if (!a) { - return false; - } - - return a.fsPath === change.resource.fsPath; - }); + return isEqual(resource.fsPath, change.resource.fsPath); }); } @@ -283,6 +259,24 @@ export class FileChangesEvent extends events.Event { } } +export function isEqual(path1: string, path2: string) { + const identityEquals = (path1 === path2); + if (isLinux || identityEquals) { + return identityEquals; + } + + return path1.toLowerCase() === path2.toLowerCase(); +} + +export function isParent(path: string, candidate: string): boolean { + if (!isLinux) { + path = path.toLowerCase(); + candidate = candidate.toLowerCase(); + } + + return path.indexOf(candidate + paths.nativeSep) === 0; +} + export interface IBaseStat { /** diff --git a/src/vs/platform/files/test/events.test.ts b/src/vs/platform/files/test/events.test.ts index b2f265fbffb..8069aa2e645 100644 --- a/src/vs/platform/files/test/events.test.ts +++ b/src/vs/platform/files/test/events.test.ts @@ -39,24 +39,24 @@ suite('Workbench Events', () => { test('File Changes Event', function () { let changes = [ - { resource: URI.file(Paths.join('C:\\', '/foo/updated.txt')), type: 0 }, - { resource: URI.file(Paths.join('C:\\', '/foo/otherupdated.txt')), type: 0 }, - { resource: URI.file(Paths.join('C:\\', '/added.txt')), type: 1 }, - { resource: URI.file(Paths.join('C:\\', '/bar/deleted.txt')), type: 2 }, - { resource: URI.file(Paths.join('C:\\', '/bar/folder')), type: 2 } + { resource: URI.file(Paths.join('C:\\', '/foo/updated.txt')), type: Files.FileChangeType.UPDATED }, + { resource: URI.file(Paths.join('C:\\', '/foo/otherupdated.txt')), type: Files.FileChangeType.UPDATED }, + { resource: URI.file(Paths.join('C:\\', '/added.txt')), type: Files.FileChangeType.ADDED }, + { resource: URI.file(Paths.join('C:\\', '/bar/deleted.txt')), type: Files.FileChangeType.DELETED }, + { resource: URI.file(Paths.join('C:\\', '/bar/folder')), type: Files.FileChangeType.DELETED } ]; let r1 = new FileChangesEvent(changes); - assert(!r1.contains(toResource('/foo'), 0)); - assert(r1.contains(toResource('/foo/updated.txt'), 0)); - assert(!r1.contains(toResource('/foo/updated.txt'), 1)); - assert(!r1.contains(toResource('/foo/updated.txt'), 2)); + assert(!r1.contains(toResource('/foo'), Files.FileChangeType.UPDATED)); + assert(r1.contains(toResource('/foo/updated.txt'), Files.FileChangeType.UPDATED)); + assert(!r1.contains(toResource('/foo/updated.txt'), Files.FileChangeType.ADDED)); + assert(!r1.contains(toResource('/foo/updated.txt'), Files.FileChangeType.DELETED)); - assert(r1.contains(toResource('/bar/folder'), 2)); - assert(r1.contains(toResource('/bar/folder/somefile'), 2)); - assert(r1.contains(toResource('/bar/folder/somefile/test.txt'), 2)); - assert(!r1.contains(toResource('/bar/folder2/somefile'), 2)); + assert(r1.contains(toResource('/bar/folder'), Files.FileChangeType.DELETED)); + assert(r1.contains(toResource('/bar/folder/somefile'), Files.FileChangeType.DELETED)); + assert(r1.contains(toResource('/bar/folder/somefile/test.txt'), Files.FileChangeType.DELETED)); + assert(!r1.contains(toResource('/bar/folder2/somefile'), Files.FileChangeType.DELETED)); assert.strictEqual(5, r1.changes.length); assert.strictEqual(1, r1.getAdded().length); diff --git a/src/vs/platform/keybinding/browser/keybindingServiceImpl.ts b/src/vs/platform/keybinding/browser/keybindingServiceImpl.ts index da145c71d04..bc68d738e71 100644 --- a/src/vs/platform/keybinding/browser/keybindingServiceImpl.ts +++ b/src/vs/platform/keybinding/browser/keybindingServiceImpl.ts @@ -182,7 +182,7 @@ export abstract class KeybindingService implements IKeybindingService { e.preventDefault(); } let commandId = resolveResult.commandId.replace(/^\^/, ''); - this._commandService.executeCommand(commandId, {}).done(undefined, err => { + this._commandService.executeCommand(commandId, resolveResult.commandArgs || {}).done(undefined, err => { this._messageService.show(Severity.Warning, err); }); } diff --git a/src/vs/platform/keybinding/common/keybinding.ts b/src/vs/platform/keybinding/common/keybinding.ts index b80dfedc9b7..5b133882eb9 100644 --- a/src/vs/platform/keybinding/common/keybinding.ts +++ b/src/vs/platform/keybinding/common/keybinding.ts @@ -13,6 +13,7 @@ import Event from 'vs/base/common/event'; export interface IUserFriendlyKeybinding { key: string; command: string; + args?: any; when?: string; } @@ -36,6 +37,7 @@ export interface IKeybindings { export interface IKeybindingItem { keybinding: number; command: string; + commandArgs?: any; when: ContextKeyExpr; weight1: number; weight2: number; diff --git a/src/vs/platform/keybinding/common/keybindingResolver.ts b/src/vs/platform/keybinding/common/keybindingResolver.ts index 1f63e8e33d3..bea2bd18c42 100644 --- a/src/vs/platform/keybinding/common/keybindingResolver.ts +++ b/src/vs/platform/keybinding/common/keybindingResolver.ts @@ -13,6 +13,7 @@ import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; export interface IResolveResult { enterChord: number; commandId: string; + commandArgs: any; } export interface IBoundCommands { @@ -31,11 +32,13 @@ interface ICommandEntry { when: ContextKeyExpr; keybinding: number; commandId: string; + commandArgs: any; } export class NormalizedKeybindingItem { keybinding: number; command: string; + commandArgs: any; when: ContextKeyExpr; isDefault: boolean; actualCommand: string; @@ -45,12 +48,13 @@ export class NormalizedKeybindingItem { if (source.when) { when = source.when.normalize(); } - return new NormalizedKeybindingItem(source.keybinding, source.command, when, isDefault); + return new NormalizedKeybindingItem(source.keybinding, source.command, source.commandArgs, when, isDefault); } - constructor(keybinding: number, command: string, when: ContextKeyExpr, isDefault: boolean) { + constructor(keybinding: number, command: string, commandArgs: any, when: ContextKeyExpr, isDefault: boolean) { this.keybinding = keybinding; this.command = command; + this.commandArgs = commandArgs; this.actualCommand = this.command ? this.command.replace(/^\^/, '') : this.command; this.when = when; this.isDefault = isDefault; @@ -97,7 +101,8 @@ export class KeybindingResolver { let entry: ICommandEntry = { when: k.when, keybinding: k.keybinding, - commandId: k.command + commandId: k.command, + commandArgs: k.commandArgs }; if (BinaryKeybindings.hasChord(k.keybinding)) { @@ -313,13 +318,15 @@ export class KeybindingResolver { if (currentChord === 0 && BinaryKeybindings.hasChord(result.keybinding)) { return { enterChord: keypress, - commandId: null + commandId: null, + commandArgs: null }; } return { enterChord: 0, - commandId: result.commandId + commandId: result.commandId, + commandArgs: result.commandArgs }; } @@ -424,9 +431,15 @@ export class IOSupport { command = input.command; } + let commandArgs: any = null; + if (typeof input.args !== 'undefined') { + commandArgs = input.args; + } + return { keybinding: key, command: command, + commandArgs: commandArgs, when: when, weight1: 1000, weight2: index diff --git a/src/vs/platform/keybinding/common/keybindingsRegistry.ts b/src/vs/platform/keybinding/common/keybindingsRegistry.ts index 72eeecec833..02d64285d52 100644 --- a/src/vs/platform/keybinding/common/keybindingsRegistry.ts +++ b/src/vs/platform/keybinding/common/keybindingsRegistry.ts @@ -113,6 +113,7 @@ class KeybindingsRegistryImpl implements IKeybindingsRegistry { this._keybindings.push({ keybinding: keybinding, command: commandId, + commandArgs: null, when: when, weight1: weight1, weight2: weight2 diff --git a/src/vs/platform/keybinding/test/common/keybindingIO.test.ts b/src/vs/platform/keybinding/test/common/keybindingIO.test.ts index e5d746a40e9..5eec3a14f0c 100644 --- a/src/vs/platform/keybinding/test/common/keybindingIO.test.ts +++ b/src/vs/platform/keybinding/test/common/keybindingIO.test.ts @@ -136,4 +136,12 @@ suite('Keybinding IO', () => { let normalizedKeybindingItem = NormalizedKeybindingItem.fromKeybindingItem(keybindingItem, false); assert.equal(normalizedKeybindingItem.keybinding, 0); }); + + test('test commands args', () => { + let strJSON = `[{ "key": "ctrl+k ctrl+f", "command": "firstcommand", "when": [], "args": { "text": "theText" } }]`; + let userKeybinding = JSON.parse(strJSON)[0]; + let keybindingItem = IOSupport.readKeybindingItem(userKeybinding, 0); + let normalizedKeybindingItem = NormalizedKeybindingItem.fromKeybindingItem(keybindingItem, false); + assert.equal(normalizedKeybindingItem.commandArgs.text, 'theText'); + }); }); diff --git a/src/vs/platform/keybinding/test/common/keybindingService.test.ts b/src/vs/platform/keybinding/test/common/keybindingService.test.ts index 22e4191d7cd..5b7cd0f0d3b 100644 --- a/src/vs/platform/keybinding/test/common/keybindingService.test.ts +++ b/src/vs/platform/keybinding/test/common/keybindingService.test.ts @@ -31,6 +31,23 @@ suite('Keybinding Service', () => { assert.equal(resolver.resolve({ bar: 'bz' }, 0, keybinding), null); }); + test('resolve key with arguments', function () { + let commandArgs = { text: 'no' }; + let keybinding = KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z; + let contextRules = ContextKeyExpr.equals('bar', 'baz'); + let keybindingItem: IKeybindingItem = { + command: 'yes', + commandArgs: commandArgs, + when: contextRules, + keybinding: keybinding, + weight1: 0, + weight2: 0 + }; + + let resolver = new KeybindingResolver([keybindingItem], []); + assert.equal(resolver.resolve({ bar: 'baz' }, 0, keybinding).commandArgs, commandArgs); + }); + test('KbAndExpression.equals', function () { let a = ContextKeyExpr.and( ContextKeyExpr.has('a1'), @@ -74,8 +91,8 @@ suite('Keybinding Service', () => { }]; let actual = KeybindingResolver.combine(defaults, overrides); assert.deepEqual(actual, [ - new NormalizedKeybindingItem(KeyCode.KEY_A, 'yes1', ContextKeyExpr.equals('1', 'a'), true), - new NormalizedKeybindingItem(KeyCode.KEY_B, 'yes2', ContextKeyExpr.equals('2', 'b'), false), + new NormalizedKeybindingItem(KeyCode.KEY_A, 'yes1', null, ContextKeyExpr.equals('1', 'a'), true), + new NormalizedKeybindingItem(KeyCode.KEY_B, 'yes2', null, ContextKeyExpr.equals('2', 'b'), false), ]); }); @@ -102,9 +119,9 @@ suite('Keybinding Service', () => { }]; let actual = KeybindingResolver.combine(defaults, overrides); assert.deepEqual(actual, [ - new NormalizedKeybindingItem(KeyCode.KEY_A, 'yes1', ContextKeyExpr.equals('1', 'a'), true), - new NormalizedKeybindingItem(KeyCode.KEY_B, 'yes2', ContextKeyExpr.equals('2', 'b'), true), - new NormalizedKeybindingItem(KeyCode.KEY_C, 'yes3', ContextKeyExpr.equals('3', 'c'), false), + new NormalizedKeybindingItem(KeyCode.KEY_A, 'yes1', null, ContextKeyExpr.equals('1', 'a'), true), + new NormalizedKeybindingItem(KeyCode.KEY_B, 'yes2', null, ContextKeyExpr.equals('2', 'b'), true), + new NormalizedKeybindingItem(KeyCode.KEY_C, 'yes3', null, ContextKeyExpr.equals('3', 'c'), false), ]); }); @@ -131,8 +148,8 @@ suite('Keybinding Service', () => { }]; let actual = KeybindingResolver.combine(defaults, overrides); assert.deepEqual(actual, [ - new NormalizedKeybindingItem(KeyCode.KEY_A, 'yes1', ContextKeyExpr.equals('1', 'a'), true), - new NormalizedKeybindingItem(KeyCode.KEY_B, 'yes2', ContextKeyExpr.equals('2', 'b'), true) + new NormalizedKeybindingItem(KeyCode.KEY_A, 'yes1', null, ContextKeyExpr.equals('1', 'a'), true), + new NormalizedKeybindingItem(KeyCode.KEY_B, 'yes2', null, ContextKeyExpr.equals('2', 'b'), true) ]); }); @@ -159,8 +176,8 @@ suite('Keybinding Service', () => { }]; let actual = KeybindingResolver.combine(defaults, overrides); assert.deepEqual(actual, [ - new NormalizedKeybindingItem(KeyCode.KEY_A, 'yes1', ContextKeyExpr.equals('1', 'a'), true), - new NormalizedKeybindingItem(KeyCode.KEY_B, 'yes2', ContextKeyExpr.equals('2', 'b'), true) + new NormalizedKeybindingItem(KeyCode.KEY_A, 'yes1', null, ContextKeyExpr.equals('1', 'a'), true), + new NormalizedKeybindingItem(KeyCode.KEY_B, 'yes2', null, ContextKeyExpr.equals('2', 'b'), true) ]); }); @@ -187,7 +204,7 @@ suite('Keybinding Service', () => { }]; let actual = KeybindingResolver.combine(defaults, overrides); assert.deepEqual(actual, [ - new NormalizedKeybindingItem(KeyCode.KEY_B, 'yes2', ContextKeyExpr.equals('2', 'b'), true) + new NormalizedKeybindingItem(KeyCode.KEY_B, 'yes2', null, ContextKeyExpr.equals('2', 'b'), true) ]); }); @@ -214,7 +231,7 @@ suite('Keybinding Service', () => { }]; let actual = KeybindingResolver.combine(defaults, overrides); assert.deepEqual(actual, [ - new NormalizedKeybindingItem(KeyCode.KEY_B, 'yes2', ContextKeyExpr.equals('2', 'b'), true) + new NormalizedKeybindingItem(KeyCode.KEY_B, 'yes2', null, ContextKeyExpr.equals('2', 'b'), true) ]); }); @@ -241,7 +258,7 @@ suite('Keybinding Service', () => { }]; let actual = KeybindingResolver.combine(defaults, overrides); assert.deepEqual(actual, [ - new NormalizedKeybindingItem(KeyCode.KEY_B, 'yes2', ContextKeyExpr.equals('2', 'b'), true) + new NormalizedKeybindingItem(KeyCode.KEY_B, 'yes2', null, ContextKeyExpr.equals('2', 'b'), true) ]); }); @@ -268,7 +285,7 @@ suite('Keybinding Service', () => { }]; let actual = KeybindingResolver.combine(defaults, overrides); assert.deepEqual(actual, [ - new NormalizedKeybindingItem(KeyCode.KEY_B, 'yes2', ContextKeyExpr.equals('2', 'b'), true) + new NormalizedKeybindingItem(KeyCode.KEY_B, 'yes2', null, ContextKeyExpr.equals('2', 'b'), true) ]); }); @@ -295,7 +312,7 @@ suite('Keybinding Service', () => { }]; let actual = KeybindingResolver.combine(defaults, overrides); assert.deepEqual(actual, [ - new NormalizedKeybindingItem(KeyCode.KEY_B, 'yes2', ContextKeyExpr.equals('2', 'b'), true) + new NormalizedKeybindingItem(KeyCode.KEY_B, 'yes2', null, ContextKeyExpr.equals('2', 'b'), true) ]); }); @@ -471,8 +488,6 @@ suite('Keybinding Service', () => { let resolver = new KeybindingResolver(items, [], false); - - let testKey = (commandId: string, expectedKeys: number[]) => { // Test lookup let lookupResult = resolver.lookupKeybinding(commandId); diff --git a/src/vs/platform/message/common/message.ts b/src/vs/platform/message/common/message.ts index eb58084df3b..ed5da01eb0f 100644 --- a/src/vs/platform/message/common/message.ts +++ b/src/vs/platform/message/common/message.ts @@ -64,11 +64,17 @@ export interface IChoiceService { /** * Prompt the user for a choice between multiple options. * + * @param when `modal` is true, this will block the user until chooses. + * * @returns A promise with the selected choice index. The promise is cancellable * which hides the message. The promise can return an error, meaning that * the user refused to choose. + * + * When `modal` is true and user refused to choose, then promise with index of + * `Cancel` option is returned. If there is no such option then promise with + * `0` index is returned. */ - choose(severity: Severity, message: string, options: string[]): TPromise; + choose(severity: Severity, message: string, options: string[], modal?: boolean): TPromise; } export import Severity = Severity; \ No newline at end of file diff --git a/src/vs/platform/message/common/messageIpc.ts b/src/vs/platform/message/common/messageIpc.ts index e7e305efdfa..619183f2de2 100644 --- a/src/vs/platform/message/common/messageIpc.ts +++ b/src/vs/platform/message/common/messageIpc.ts @@ -21,7 +21,7 @@ export class ChoiceChannel implements IChoiceChannel { call(command: string, args?: any): TPromise { switch (command) { - case 'choose': return this.service.choose(args[0], args[1], args[2]); + case 'choose': return this.service.choose(args[0], args[1], args[2], args[3]); } return TPromise.wrapError('invalid command'); } @@ -33,8 +33,8 @@ export class ChoiceChannelClient implements IChoiceService { constructor(private channel: IChoiceChannel) { } - choose(severity: Severity, message: string, options: string[]): TPromise { - return this.channel.call('choose', [severity, message, options]); + choose(severity: Severity, message: string, options: string[], modal?: boolean): TPromise { + return this.channel.call('choose', [severity, message, options, modal]); } } \ No newline at end of file diff --git a/src/vs/platform/url/common/urlIpc.ts b/src/vs/platform/url/common/urlIpc.ts index 95e3a657b96..52cb4b8d386 100644 --- a/src/vs/platform/url/common/urlIpc.ts +++ b/src/vs/platform/url/common/urlIpc.ts @@ -9,7 +9,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { IChannel, eventToCall, eventFromCall, Serializer, Deserializer } from 'vs/base/parts/ipc/common/ipc'; import { IURLService } from './url'; import Event, { filterEvent } from 'vs/base/common/event'; -import { IWindowsService } from 'vs/code/electron-main/windows'; +import { IWindowsMainService } from 'vs/code/electron-main/windows'; import URI from 'vs/base/common/uri'; const URISerializer: Serializer = uri => uri.toJSON(); @@ -24,7 +24,7 @@ export class URLChannel implements IURLChannel { constructor( private service: IURLService, - @IWindowsService private windowsService: IWindowsService + @IWindowsMainService private windowsService: IWindowsMainService ) { } call(command: string, arg?: any): TPromise { diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts new file mode 100644 index 00000000000..9e81bbfecca --- /dev/null +++ b/src/vs/platform/windows/common/windows.ts @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +export const IWindowsService = createDecorator('windowsService'); + +export interface IWindowsService { + + _serviceBrand: any; + + openFileFolderPicker(windowId: number, forceNewWindow?: boolean): TPromise; + openFilePicker(windowId: number, forceNewWindow?: boolean, path?: string): TPromise; + openFolderPicker(windowId: number, forceNewWindow?: boolean): TPromise; + reloadWindow(windowId: number): TPromise; +} + +export const IWindowService = createDecorator('windowService'); + +export interface IWindowService { + + _serviceBrand: any; + + openFileFolderPicker(forceNewWindow?: boolean): TPromise; + openFilePicker(forceNewWindow?: boolean, path?: string): TPromise; + openFolderPicker(forceNewWindow?: boolean): TPromise; + reloadWindow(): TPromise; +} \ No newline at end of file diff --git a/src/vs/platform/windows/common/windowsIpc.ts b/src/vs/platform/windows/common/windowsIpc.ts new file mode 100644 index 00000000000..a83ac7f6783 --- /dev/null +++ b/src/vs/platform/windows/common/windowsIpc.ts @@ -0,0 +1,55 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { IWindowsService } from './windows'; + +export interface IWindowsChannel extends IChannel { + call(command: 'openFileFolderPicker', args: [number, boolean]): TPromise; + call(command: 'openFilePicker', args: [number, boolean, string]): TPromise; + call(command: 'openFolderPicker', args: [number, boolean]): TPromise; + call(command: 'reloadWindow', arg: number): TPromise; + call(command: string, arg?: any): TPromise; +} + +export class WindowsChannel implements IWindowsChannel { + + constructor(private service: IWindowsService) { } + + call(command: string, arg?: any): TPromise { + switch (command) { + case 'openFileFolderPicker': return this.service.openFileFolderPicker(arg[0], arg[1]); + case 'openFilePicker': return this.service.openFilePicker(arg[0], arg[1], arg[2]); + case 'openFolderPicker': return this.service.openFolderPicker(arg[0], arg[1]); + case 'reloadWindow': return this.service.reloadWindow(arg); + } + } +} + +export class WindowsChannelClient implements IWindowsService { + + _serviceBrand: any; + + constructor(private channel: IWindowsChannel) { } + + openFileFolderPicker(windowId: number, forceNewWindow?: boolean): TPromise { + return this.channel.call('openFileFolderPicker', [windowId, forceNewWindow]); + } + + openFilePicker(windowId: number, forceNewWindow?: boolean, path?: string): TPromise { + return this.channel.call('openFilePicker', [windowId, forceNewWindow, path]); + } + + openFolderPicker(windowId: number, forceNewWindow?: boolean): TPromise { + return this.channel.call('openFolderPicker', [windowId, forceNewWindow]); + } + + reloadWindow(windowId: number): TPromise { + return this.channel.call('reloadWindow', windowId); + } +} \ No newline at end of file diff --git a/src/vs/platform/windows/electron-browser/windowService.ts b/src/vs/platform/windows/electron-browser/windowService.ts new file mode 100644 index 00000000000..aee56786b0e --- /dev/null +++ b/src/vs/platform/windows/electron-browser/windowService.ts @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; + +export class WindowService implements IWindowService { + + _serviceBrand: any; + + constructor( + private windowId: number, + @IWindowsService private windowsService: IWindowsService + ) { } + + openFileFolderPicker(forceNewWindow?: boolean): TPromise { + return this.windowsService.openFileFolderPicker(this.windowId, forceNewWindow); + } + + openFilePicker(forceNewWindow?: boolean, path?: string): TPromise { + return this.windowsService.openFilePicker(this.windowId, forceNewWindow, path); + } + + openFolderPicker(forceNewWindow?: boolean): TPromise { + return this.windowsService.openFolderPicker(this.windowId, forceNewWindow); + } + + reloadWindow(): TPromise { + return this.windowsService.reloadWindow(this.windowId); + } +} \ No newline at end of file diff --git a/src/vs/platform/windows/electron-main/windowsService.ts b/src/vs/platform/windows/electron-main/windowsService.ts new file mode 100644 index 00000000000..72407812187 --- /dev/null +++ b/src/vs/platform/windows/electron-main/windowsService.ts @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { IWindowsService } from 'vs/platform/windows/common/windows'; + +// TODO@Joao: remove this dependency, move all implementation to this class +import { IWindowsMainService } from 'vs/code/electron-main/windows'; + +export class WindowsService implements IWindowsService { + + _serviceBrand: any; + + constructor( + @IWindowsMainService private windowsMainService: IWindowsMainService + ) { } + + openFileFolderPicker(windowId: number, forceNewWindow?: boolean): TPromise { + this.windowsMainService.openFileFolderPicker(forceNewWindow); + return TPromise.as(null); + } + + openFilePicker(windowId: number, forceNewWindow?: boolean, path?: string): TPromise { + this.windowsMainService.openFilePicker(forceNewWindow, path); + return TPromise.as(null); + } + + openFolderPicker(windowId: number, forceNewWindow?: boolean): TPromise { + this.windowsMainService.openFolderPicker(forceNewWindow); + return TPromise.as(null); + } + + reloadWindow(windowId: number): TPromise { + const vscodeWindow = this.windowsMainService.getWindowById(windowId); + + if (vscodeWindow) { + this.windowsMainService.reload(vscodeWindow); + } + + return TPromise.as(null); + } +} \ No newline at end of file diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index aef4f716268..875e267580e 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -31,7 +31,6 @@ import { ExtHostLanguages } from 'vs/workbench/api/node/extHostLanguages'; import { ExtHostLanguageFeatures } from 'vs/workbench/api/node/extHostLanguageFeatures'; import { ExtHostApiCommands } from 'vs/workbench/api/node/extHostApiCommands'; import * as extHostTypes from 'vs/workbench/api/node/extHostTypes'; -import Modes = require('vs/editor/common/modes'); import URI from 'vs/base/common/uri'; import Severity from 'vs/base/common/severity'; import EditorCommon = require('vs/editor/common/editorCommon'); @@ -45,14 +44,15 @@ import * as paths from 'vs/base/common/paths'; import { realpathSync } from 'fs'; import { ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry'; import { MainContext, ExtHostContext, InstanceCollection, IInitConfiguration } from './extHost.protocol'; +import * as languageConfiguration from 'vs/editor/common/modes/languageConfiguration'; export interface IExtensionApiFactory { - (extension: IExtensionDescription): typeof vscode; + (extension?: IExtensionDescription): typeof vscode; } function proposedApiFunction(extension: IExtensionDescription, fn: T): T { - if (extension.enableProposedApi) { + if (extension && extension.enableProposedApi) { return fn; } else { return (() => { @@ -94,9 +94,9 @@ export function createApiFactory(initDataConfiguration: IInitConfiguration, init // Register API-ish commands ExtHostApiCommands.register(extHostCommands); - return function (extension: IExtensionDescription): typeof vscode { + return function (extension?: IExtensionDescription): typeof vscode { - if (extension.enableProposedApi) { + if (extension && extension.enableProposedApi) { console.warn(`${extension.name} (${extension.id}) uses PROPOSED API which is subject to change and removal without notice`); } @@ -390,7 +390,7 @@ export function createApiFactory(initDataConfiguration: IInitConfiguration, init DocumentLink: extHostTypes.DocumentLink, ViewColumn: extHostTypes.ViewColumn, StatusBarAlignment: extHostTypes.StatusBarAlignment, - IndentAction: Modes.IndentAction, + IndentAction: languageConfiguration.IndentAction, OverviewRulerLane: EditorCommon.OverviewRulerLane, TextEditorRevealType: extHostTypes.TextEditorRevealType, EndOfLine: extHostTypes.EndOfLine, @@ -440,7 +440,7 @@ export function defineAPI(factory: IExtensionApiFactory, extensionService: ExtHo const trie = new TrieMap(TrieMap.PathSplitter); const extensions = extensionService.getAllExtensionDescriptions(); for (const ext of extensions) { - if (ext.name) { + if (ext.main) { const path = realpathSync(ext.extensionFolderPath); trie.insert(path, ext); } diff --git a/src/vs/workbench/api/node/extHost.contribution.ts b/src/vs/workbench/api/node/extHost.contribution.ts index 8d98fa079b3..bc1b43beebe 100644 --- a/src/vs/workbench/api/node/extHost.contribution.ts +++ b/src/vs/workbench/api/node/extHost.contribution.ts @@ -38,7 +38,7 @@ import { MainThreadFileSystemEventService } from './mainThreadFileSystemEventSer import { MainProcessTextMateSyntax } from 'vs/editor/node/textMate/TMSyntax'; import { MainProcessTextMateSnippet } from 'vs/editor/node/textMate/TMSnippets'; import { JSONValidationExtensionPoint } from 'vs/platform/jsonschemas/common/jsonValidationExtensionPoint'; -import { LanguageConfigurationFileHandler } from 'vs/editor/node/languageConfiguration'; +import { LanguageConfigurationFileHandler } from 'vs/editor/node/languageConfigurationExtensionPoint'; import { SaveParticipant } from './mainThreadSaveParticipant'; // --- registers itself as service diff --git a/src/vs/workbench/api/node/extHostApiCommands.ts b/src/vs/workbench/api/node/extHostApiCommands.ts index 56796b4f7c0..c270f87afd0 100644 --- a/src/vs/workbench/api/node/extHostApiCommands.ts +++ b/src/vs/workbench/api/node/extHostApiCommands.ts @@ -183,12 +183,12 @@ export class ExtHostApiCommands { ] }); - this._register('vscode.openFolder', (uri?: URI, newWindow?: boolean) => { + this._register('vscode.openFolder', (uri?: URI, forceNewWindow?: boolean) => { if (!uri) { - return this._commands.executeCommand('_workbench.ipc', 'vscode:openFolderPicker', [newWindow]); + return this._commands.executeCommand('_files.openFolderPicker', forceNewWindow); } - return this._commands.executeCommand('_workbench.ipc', 'vscode:windowOpen', [[uri.fsPath], newWindow]); + return this._commands.executeCommand('_workbench.ipc', 'vscode:windowOpen', [[uri.fsPath], forceNewWindow]); }, { description: 'Open a folder in the current window or new window depending on the newWindow argument. Note that opening in the same window will shutdown the current extension host process and start a new one on the given folder unless the newWindow parameter is set to true.', args: [ diff --git a/src/vs/workbench/api/node/mainThreadExtensionService.ts b/src/vs/workbench/api/node/mainThreadExtensionService.ts index 436d492117e..c1039bcd5ed 100644 --- a/src/vs/workbench/api/node/mainThreadExtensionService.ts +++ b/src/vs/workbench/api/node/mainThreadExtensionService.ts @@ -6,14 +6,22 @@ import Severity from 'vs/base/common/severity'; import { TPromise } from 'vs/base/common/winjs.base'; +import pkg from 'vs/platform/package'; +import { localize } from 'vs/nls'; +import * as path from 'path'; +import URI from 'vs/base/common/uri'; import { AbstractExtensionService, ActivatedExtension } from 'vs/platform/extensions/common/abstractExtensionService'; -import { IExtensionsRuntimeService, IMessage, IExtensionDescription, IExtensionsStatus } from 'vs/platform/extensions/common/extensions'; +import { IMessage, IExtensionDescription, IExtensionsStatus } from 'vs/platform/extensions/common/extensions'; +import { IExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionsRegistry, ExtensionPoint, IExtensionPointUser, ExtensionMessageCollector } from 'vs/platform/extensions/common/extensionsRegistry'; +import { ExtensionScanner, MessagesCollector } from 'vs/workbench/node/extensionPoints'; import { IMessageService } from 'vs/platform/message/common/message'; import { IThreadService } from 'vs/workbench/services/thread/common/threadService'; import { ExtHostContext, ExtHostExtensionServiceShape } from './extHost.protocol'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +const SystemExtensionsRoot = path.normalize(path.join(URI.parse(require.toUrl('')).fsPath, '..', 'extensions')); + /** * Represents a failed extension in the ext host. */ @@ -54,7 +62,7 @@ export class MainProcessExtensionService extends AbstractExtensionService this._onExtensionDescriptions(extensionDescriptions)); + const disabledExtensions = [...extensionEnablementService.getGloballyDisabledExtensions(), ...extensionEnablementService.getWorkspaceDisabledExtensions()]; + this.scanExtensions().done(extensionDescriptions => { + this._onExtensionDescriptions(disabledExtensions.length ? extensionDescriptions.filter(e => disabledExtensions.indexOf(`${e.publisher}.${e.name}`) === -1) : extensionDescriptions); + }); } private _handleMessage(msg: IMessage) { @@ -166,4 +177,44 @@ export class MainProcessExtensionService extends AbstractExtensionService { + const collector = new MessagesCollector(); + const version = pkg.version; + const builtinExtensions = ExtensionScanner.scanExtensions(version, collector, SystemExtensionsRoot, true); + const userExtensions = this.environmentService.disableExtensions || !this.environmentService.extensionsPath ? TPromise.as([]) : ExtensionScanner.scanExtensions(version, collector, this.environmentService.extensionsPath, false); + const developedExtensions = this.environmentService.disableExtensions || !this.environmentService.extensionDevelopmentPath ? TPromise.as([]) : ExtensionScanner.scanOneOrMultipleExtensions(version, collector, this.environmentService.extensionDevelopmentPath, false); + + return TPromise.join([builtinExtensions, userExtensions, developedExtensions]).then((extensionDescriptions: IExtensionDescription[][]) => { + let builtinExtensions = extensionDescriptions[0]; + let userExtensions = extensionDescriptions[1]; + let developedExtensions = extensionDescriptions[2]; + + let result: { [extensionId: string]: IExtensionDescription; } = {}; + builtinExtensions.forEach((builtinExtension) => { + result[builtinExtension.id] = builtinExtension; + }); + userExtensions.forEach((userExtension) => { + if (result.hasOwnProperty(userExtension.id)) { + collector.warn(userExtension.extensionFolderPath, localize('overwritingExtension', "Overwriting extension {0} with {1}.", result[userExtension.id].extensionFolderPath, userExtension.extensionFolderPath)); + } + result[userExtension.id] = userExtension; + }); + developedExtensions.forEach(developedExtension => { + collector.info('', localize('extensionUnderDevelopment', "Loading development extension at {0}", developedExtension.extensionFolderPath)); + if (result.hasOwnProperty(developedExtension.id)) { + collector.warn(developedExtension.extensionFolderPath, localize('overwritingExtension', "Overwriting extension {0} with {1}.", result[developedExtension.id].extensionFolderPath, developedExtension.extensionFolderPath)); + } + result[developedExtension.id] = developedExtension; + }); + + return Object.keys(result).map(name => result[name]); + }).then(null, err => { + collector.error('', err); + return []; + }).then(extensions => { + collector.getMessages().forEach(entry => this.$localShowMessage(entry.type, this._isDev ? (entry.source ? '[' + entry.source + ']: ' : '') + entry.message : entry.message)); + return extensions; + }); + } } diff --git a/src/vs/workbench/api/node/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/node/mainThreadLanguageFeatures.ts index cf924f2f9fe..275a9350c95 100644 --- a/src/vs/workbench/api/node/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/node/mainThreadLanguageFeatures.ts @@ -16,7 +16,8 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { Position as EditorPosition } from 'vs/editor/common/core/position'; import { Range as EditorRange } from 'vs/editor/common/core/range'; import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape } from './extHost.protocol'; -import { LanguageConfigurationRegistry, LanguageConfiguration } from 'vs/editor/common/modes/languageConfigurationRegistry'; +import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; +import { LanguageConfiguration } from 'vs/editor/common/modes/languageConfiguration'; import { IHeapService } from './mainThreadHeapService'; export class MainThreadLanguageFeatures extends MainThreadLanguageFeaturesShape { @@ -226,13 +227,35 @@ export class MainThreadLanguageFeatures extends MainThreadLanguageFeaturesShape // --- configuration - $setLanguageConfiguration(handle: number, languageId: string, configuration: vscode.LanguageConfiguration): TPromise { + $setLanguageConfiguration(handle: number, languageId: string, _configuration: vscode.LanguageConfiguration): TPromise { - if (configuration.__characterPairSupport) { - (configuration).autoClosingPairs = configuration.__characterPairSupport.autoClosingPairs; + let configuration: LanguageConfiguration = { + comments: _configuration.comments, + brackets: _configuration.brackets, + wordPattern: _configuration.wordPattern, + indentationRules: _configuration.indentationRules, + onEnterRules: _configuration.onEnterRules, + + autoClosingPairs: null, + surroundingPairs: null, + __electricCharacterSupport: null + }; + + if (_configuration.__characterPairSupport) { + // backwards compatibility + configuration.autoClosingPairs = _configuration.__characterPairSupport.autoClosingPairs; } - this._registrations[handle] = LanguageConfigurationRegistry.register(languageId, configuration); + if (_configuration.__electricCharacterSupport && _configuration.__electricCharacterSupport.docComment) { + configuration.__electricCharacterSupport = { + docComment: { + open: _configuration.__electricCharacterSupport.docComment.open, + close: _configuration.__electricCharacterSupport.docComment.close + } + }; + } + + this._registrations[handle] = LanguageConfigurationRegistry.register(languageId, configuration); return undefined; } diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index 43f7df3257b..d6b42f0e142 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -7,6 +7,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import nls = require('vs/nls'); import { Action } from 'vs/base/common/actions'; +import { mixin } from 'vs/base/common/objects'; import { EditorInput, getUntitledOrFileResource, TextEditorOptions, EditorOptions, IEditorIdentifier, IEditorContext, ActiveEditorMoveArguments, ActiveEditorMovePositioning, EditorCommands } from 'vs/workbench/common/editor'; import { QuickOpenEntryGroup } from 'vs/base/parts/quickopen/browser/quickOpenModel'; import { EditorQuickOpenEntry, EditorQuickOpenEntryGroup, IEditorQuickOpenEntry, QuickOpenAction } from 'vs/workbench/browser/quickopen'; @@ -433,15 +434,15 @@ export class OpenToSideAction extends Action { public run(context: any): TPromise { let entry = toEditorQuickOpenEntry(context); if (entry) { - let typedInputPromise: TPromise; const input = entry.getInput(); if (input instanceof EditorInput) { - typedInputPromise = TPromise.as(input); - } else { - typedInputPromise = this.editorService.createInput(input); + return this.editorService.openEditor(input, entry.getOptions(), true); } - return typedInputPromise.then(typedInput => this.editorService.openEditor(typedInput, entry.getOptions(), true)); + const resourceInput = input as IResourceInput; + resourceInput.options = mixin(resourceInput.options, entry.getOptions()); + + return this.editorService.openEditor(resourceInput, true); } return TPromise.as(false); @@ -466,6 +467,49 @@ export function toEditorQuickOpenEntry(element: any): IEditorQuickOpenEntry { return null; } +export class SaveEditorAction extends Action { + + public static ID = 'workbench.action.files.save'; + public static LABEL = nls.localize('saveEditor', "Save Editor"); + + constructor( + id: string, + label: string, + @IEditorGroupService private editorGroupService: IEditorGroupService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService + ) { + super(id, label, 'save-editor-action'); + } + + public run(context?: IEditorContext): TPromise { + const position = context ? this.editorGroupService.getStacksModel().positionOfGroup(context.group) : null; + + // Save Active Editor + if (typeof position !== 'number') { + const activeEditor = this.editorService.getActiveEditorInput(); + if (activeEditor instanceof EditorInput) { + return activeEditor.save(); + } + } + + let input = context ? context.editor : null; + if (!input) { + + // Get Editor at Position + const visibleEditors = this.editorService.getVisibleEditors(); + if (visibleEditors[position]) { + input = visibleEditors[position].input; + } + } + + if (input instanceof EditorInput) { + return input.save(); + } + + return TPromise.as(false); + } +} + export class CloseEditorAction extends Action { public static ID = 'workbench.action.closeActiveEditor'; diff --git a/src/vs/workbench/browser/parts/editor/media/tabstitle.css b/src/vs/workbench/browser/parts/editor/media/tabstitle.css index 7ba8f69e7e1..a066a6b1029 100644 --- a/src/vs/workbench/browser/parts/editor/media/tabstitle.css +++ b/src/vs/workbench/browser/parts/editor/media/tabstitle.css @@ -72,6 +72,14 @@ padding-left: 10px; } +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.no-close-button { + padding-right: 28px; /* make room for dirty indication when we are running without close button */ +} + +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.no-close-button.dirty { + padding-right: 0; /* dirty tabs always show the dirty indicator, so we can clear the padding now */ +} + .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.active { z-index: 2; /* on top of the horizontal border of the title */ } @@ -171,6 +179,14 @@ width: 28px; } +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.no-close-button > .tab-close { + display: none; /* hide the close action bar when we are configured to hide it */ +} + +.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.no-close-button.dirty > .tab-close { + display: block; /* show the close action bar when the tab gets dirty */ +} + .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.active .tabs-container > .tab.active > .tab-close .action-label, /* always show it for active tab */ .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.active .tabs-container > .tab > .tab-close .action-label:focus, /* always show it on focus */ .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.active .tabs-container > .tab:hover > .tab-close .action-label, /* always show it on hover */ @@ -199,12 +215,15 @@ margin-right: 0.5em; } -.vs .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dirty .close-editor-action { +.vs .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dirty .close-editor-action, +.vs .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dirty .save-editor-action { background: url('close-dirty.svg') center center no-repeat; } .vs-dark .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dirty .close-editor-action, -.hc-black .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dirty .close-editor-action { +.hc-black .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dirty .close-editor-action, +.vs-dark .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dirty .save-editor-action, +.hc-black .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dirty .save-editor-action { background: url('close-dirty-inverse.svg') center center no-repeat; } diff --git a/src/vs/workbench/browser/parts/editor/sideBySideEditorControl.ts b/src/vs/workbench/browser/parts/editor/sideBySideEditorControl.ts index 9ba56be69d3..fc51439a6f2 100644 --- a/src/vs/workbench/browser/parts/editor/sideBySideEditorControl.ts +++ b/src/vs/workbench/browser/parts/editor/sideBySideEditorControl.ts @@ -110,6 +110,7 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti private layoutVertically: boolean; private showTabs: boolean; + private showTabCloseButton: boolean; private showIcons: boolean; private silos: Builder[]; @@ -216,11 +217,15 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti } private onConfigurationUpdated(config: IWorkbenchEditorConfiguration, refresh?: boolean): void { + const showTabCloseButton = this.showTabCloseButton; + if (config.workbench && config.workbench.editor) { this.showTabs = config.workbench.editor.showTabs; + this.showTabCloseButton = config.workbench.editor.showTabCloseButton; this.showIcons = config.workbench.editor.showIcons; } else { this.showTabs = true; + this.showTabCloseButton = true; this.showIcons = false; } @@ -259,7 +264,7 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti } // Refresh title when icons change - else if (showingIcons !== this.showIcons) { + else if (showingIcons !== this.showIcons || showTabCloseButton !== this.showTabCloseButton) { titleControl.refresh(true); } } diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index 492eecb1ff1..07d66a8d951 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -337,6 +337,12 @@ export class TabsTitleControl extends TitleControl { DOM.addClass(tabContainer, 'tab monaco-editor-background'); tabContainers.push(tabContainer); + if (!this.showTabCloseButton) { + DOM.addClass(tabContainer, 'no-close-button'); + } else { + DOM.removeClass(tabContainer, 'no-close-button'); + } + // Tab Editor Label const editorLabel = this.instantiationService.createInstance(EditorLabel, tabContainer, void 0); this.editorLabels.push(editorLabel); @@ -347,7 +353,8 @@ export class TabsTitleControl extends TitleControl { tabContainer.appendChild(tabCloseContainer); const bar = new ActionBar(tabCloseContainer, { context: { editor, group }, ariaLabel: nls.localize('araLabelTabActions', "Tab actions") }); - bar.push(this.closeEditorAction, { icon: true, label: false, keybinding: this.getKeybindingLabel(this.closeEditorAction) }); + const action = this.showTabCloseButton ? this.closeEditorAction : this.saveEditorAction; + bar.push(action, { icon: true, label: false, keybinding: this.getKeybindingLabel(action) }); this.tabDisposeables.push(bar); @@ -537,6 +544,8 @@ export class TabsTitleControl extends TitleControl { } private onDrop(e: DragEvent, group: IEditorGroup, targetPosition: Position, targetIndex: number): void { + DOM.removeClass(this.tabsContainer, 'dropfeedback'); + DOM.removeClass(this.tabsContainer, 'scroll'); // Local DND const draggedEditor = TabsTitleControl.getDraggedEditor(); diff --git a/src/vs/workbench/browser/parts/editor/titleControl.ts b/src/vs/workbench/browser/parts/editor/titleControl.ts index baae7dad1b6..e9a069932dd 100644 --- a/src/vs/workbench/browser/parts/editor/titleControl.ts +++ b/src/vs/workbench/browser/parts/editor/titleControl.ts @@ -33,7 +33,7 @@ import { IQuickOpenService } from 'vs/workbench/services/quickopen/common/quickO import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { Keybinding } from 'vs/base/common/keybinding'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { CloseEditorsInGroupAction, SplitEditorAction, CloseEditorAction, KeepEditorAction, CloseOtherEditorsInGroupAction, CloseRightEditorsInGroupAction, ShowEditorsInGroupAction } from 'vs/workbench/browser/parts/editor/editorActions'; +import { CloseEditorsInGroupAction, SplitEditorAction, SaveEditorAction, CloseEditorAction, KeepEditorAction, CloseOtherEditorsInGroupAction, CloseRightEditorsInGroupAction, ShowEditorsInGroupAction } from 'vs/workbench/browser/parts/editor/editorActions'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { createActionItem, fillInActions } from 'vs/platform/actions/browser/menuItemActionItem'; import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions'; @@ -68,6 +68,7 @@ export abstract class TitleControl implements ITitleAreaControl { protected dragged: boolean; protected closeEditorAction: CloseEditorAction; + protected saveEditorAction: SaveEditorAction; protected pinEditorAction: KeepEditorAction; protected closeOtherEditorsAction: CloseOtherEditorsInGroupAction; protected closeRightEditorsAction: CloseRightEditorsInGroupAction; @@ -79,6 +80,7 @@ export abstract class TitleControl implements ITitleAreaControl { private previewEditors: boolean; private showTabs: boolean; + protected showTabCloseButton: boolean; private currentPrimaryEditorActionIds: string[] = []; private currentSecondaryEditorActionIds: string[] = []; @@ -154,6 +156,7 @@ export abstract class TitleControl implements ITitleAreaControl { private onConfigurationUpdated(config: IWorkbenchEditorConfiguration): void { this.previewEditors = config.workbench && config.workbench.editor && config.workbench.editor.enablePreview; this.showTabs = config.workbench && config.workbench.editor && config.workbench.editor.showTabs; + this.showTabCloseButton = config.workbench && config.workbench.editor && config.workbench.editor.showTabCloseButton; } private updateSplitActionEnablement(): void { @@ -229,6 +232,7 @@ export abstract class TitleControl implements ITitleAreaControl { private initActions(): void { this.closeEditorAction = this.instantiationService.createInstance(CloseEditorAction, CloseEditorAction.ID, nls.localize('close', "Close")); + this.saveEditorAction = this.instantiationService.createInstance(SaveEditorAction, SaveEditorAction.ID, nls.localize('save', "Save")); this.closeOtherEditorsAction = this.instantiationService.createInstance(CloseOtherEditorsInGroupAction, CloseOtherEditorsInGroupAction.ID, nls.localize('closeOthers', "Close Others")); this.closeRightEditorsAction = this.instantiationService.createInstance(CloseRightEditorsInGroupAction, CloseRightEditorsInGroupAction.ID, nls.localize('closeRight', "Close to the Right")); this.closeEditorsInGroupAction = this.instantiationService.createInstance(CloseEditorsInGroupAction, CloseEditorsInGroupAction.ID, nls.localize('closeAll', "Close All")); @@ -491,6 +495,7 @@ export abstract class TitleControl implements ITitleAreaControl { this.splitEditorAction, this.showEditorsInGroupAction, this.closeEditorAction, + this.saveEditorAction, this.closeRightEditorsAction, this.closeOtherEditorsAction, this.closeEditorsInGroupAction, diff --git a/src/vs/workbench/browser/quickopen.ts b/src/vs/workbench/browser/quickopen.ts index fae478e3de5..9bc3f88cd3e 100644 --- a/src/vs/workbench/browser/quickopen.ts +++ b/src/vs/workbench/browser/quickopen.ts @@ -223,7 +223,7 @@ export interface IEditorQuickOpenEntry { /** * The editor options used for this entry when opening. */ - getOptions(): EditorOptions; + getOptions(): IEditorOptions; } /** @@ -243,7 +243,7 @@ export class EditorQuickOpenEntry extends QuickOpenEntry implements IEditorQuick return null; } - public getOptions(): EditorOptions { + public getOptions(): IEditorOptions { return null; } @@ -262,7 +262,7 @@ export class EditorQuickOpenEntry extends QuickOpenEntry implements IEditorQuick if (input instanceof EditorInput) { let opts = this.getOptions(); if (opts) { - opts.mixin(openInBackgroundOptions); + opts = objects.mixin(opts, openInBackgroundOptions, true); } else if (openInBackgroundOptions) { opts = EditorOptions.create(openInBackgroundOptions); } @@ -292,7 +292,7 @@ export class EditorQuickOpenEntryGroup extends QuickOpenEntryGroup implements IE return null; } - public getOptions(): EditorOptions { + public getOptions(): IEditorOptions { return null; } } diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 316e19103d2..e6953eea5a0 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -857,6 +857,7 @@ export interface IWorkbenchEditorConfiguration { workbench: { editor: { showTabs: boolean; + showTabCloseButton: boolean; showIcons: boolean; enablePreview: boolean; enablePreviewFromQuickOpen: boolean; diff --git a/src/vs/workbench/common/editor/untitledEditorInput.ts b/src/vs/workbench/common/editor/untitledEditorInput.ts index 7761d5135a5..5f6d0c9771b 100644 --- a/src/vs/workbench/common/editor/untitledEditorInput.ts +++ b/src/vs/workbench/common/editor/untitledEditorInput.ts @@ -75,7 +75,11 @@ export class UntitledEditorInput extends AbstractUntitledEditorInput { } public isDirty(): boolean { - return this.cachedModel && this.cachedModel.isDirty(); + if (this.cachedModel) { + return this.cachedModel.isDirty(); + } + + return this.hasAssociatedFilePath; // untitled files with associated path are always dirty } public confirmSave(): ConfirmResult { diff --git a/src/vs/workbench/common/editor/untitledEditorModel.ts b/src/vs/workbench/common/editor/untitledEditorModel.ts index ee8d1203866..fb4c699ce39 100644 --- a/src/vs/workbench/common/editor/untitledEditorModel.ts +++ b/src/vs/workbench/common/editor/untitledEditorModel.ts @@ -127,13 +127,6 @@ export class UntitledEditorModel extends StringEditorModel implements IEncodingS // Listen to content changes this.textModelChangeListener = this.textEditorModel.onDidChangeContent(e => this.onModelContentChanged()); - // Emit initial dirty event if we are - if (this.dirty) { - setTimeout(() => { - this._onDidChangeDirty.fire(); - }, 0 /* prevent race condition between creating model and emitting dirty event */); - } - return model; }); } diff --git a/src/vs/workbench/electron-browser/actions.ts b/src/vs/workbench/electron-browser/actions.ts index 2dd461675f8..c37f2a181ea 100644 --- a/src/vs/workbench/electron-browser/actions.ts +++ b/src/vs/workbench/electron-browser/actions.ts @@ -9,7 +9,8 @@ import URI from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; import timer = require('vs/base/common/timer'); import { Action } from 'vs/base/common/actions'; -import { IWindowService } from 'vs/workbench/services/window/electron-browser/windowService'; +import { IWindowIPCService } from 'vs/workbench/services/window/electron-browser/windowService'; +import { IWindowService } from 'vs/platform/windows/common/windows'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { EditorInput } from 'vs/workbench/common/editor'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; @@ -66,7 +67,7 @@ export class CloseWindowAction extends Action { public static ID = 'workbench.action.closeWindow'; public static LABEL = nls.localize('closeWindow', "Close Window"); - constructor(id: string, label: string, @IWindowService private windowService: IWindowService) { + constructor(id: string, label: string, @IWindowIPCService private windowService: IWindowIPCService) { super(id, label); } @@ -85,7 +86,7 @@ export class SwitchWindow extends Action { constructor( id: string, label: string, - @IWindowService private windowService: IWindowService, + @IWindowIPCService private windowService: IWindowIPCService, @IQuickOpenService private quickOpenService: IQuickOpenService ) { super(id, label); @@ -121,7 +122,7 @@ export class CloseFolderAction extends Action { label: string, @IWorkspaceContextService private contextService: IWorkspaceContextService, @IMessageService private messageService: IMessageService, - @IWindowService private windowService: IWindowService + @IWindowIPCService private windowService: IWindowIPCService ) { super(id, label); } @@ -145,7 +146,7 @@ export class NewWindowAction extends Action { constructor( id: string, label: string, - @IWindowService private windowService: IWindowService + @IWindowIPCService private windowService: IWindowIPCService ) { super(id, label); } @@ -162,7 +163,7 @@ export class ToggleFullScreenAction extends Action { public static ID = 'workbench.action.toggleFullScreen'; public static LABEL = nls.localize('toggleFullScreen', "Toggle Full Screen"); - constructor(id: string, label: string, @IWindowService private windowService: IWindowService) { + constructor(id: string, label: string, @IWindowIPCService private windowService: IWindowIPCService) { super(id, label); } @@ -178,7 +179,7 @@ export class ToggleMenuBarAction extends Action { public static ID = 'workbench.action.toggleMenuBar'; public static LABEL = nls.localize('toggleMenuBar', "Toggle Menu Bar"); - constructor(id: string, label: string, @IWindowService private windowService: IWindowService) { + constructor(id: string, label: string, @IWindowIPCService private windowService: IWindowIPCService) { super(id, label); } @@ -194,7 +195,7 @@ export class ToggleDevToolsAction extends Action { public static ID = 'workbench.action.toggleDevTools'; public static LABEL = nls.localize('toggleDevTools', "Toggle Developer Tools"); - constructor(id: string, label: string, @IWindowService private windowService: IWindowService) { + constructor(id: string, label: string, @IWindowIPCService private windowService: IWindowIPCService) { super(id, label); } @@ -329,7 +330,7 @@ export class ShowStartupPerformance extends Action { constructor( id: string, label: string, - @IWindowService private windowService: IWindowService, + @IWindowIPCService private windowService: IWindowIPCService, @IEnvironmentService environmentService: IEnvironmentService ) { super(id, label); @@ -420,8 +421,8 @@ export class ShowStartupPerformance extends Action { export class ReloadWindowAction extends Action { - public static ID = 'workbench.action.reloadWindow'; - public static LABEL = nls.localize('reloadWindow', "Reload Window"); + static ID = 'workbench.action.reloadWindow'; + static LABEL = nls.localize('reloadWindow', "Reload Window"); constructor( id: string, @@ -432,11 +433,9 @@ export class ReloadWindowAction extends Action { super(id, label); } - public run(): TPromise { + run(): TPromise { this.partService.setRestoreSidebar(); // we want the same sidebar after a reload restored - this.windowService.getWindow().reload(); - - return TPromise.as(true); + return this.windowService.reloadWindow().then(() => true); } } @@ -448,7 +447,7 @@ export class OpenRecentAction extends Action { constructor( id: string, label: string, - @IWindowService private windowService: IWindowService, + @IWindowIPCService private windowService: IWindowIPCService, @IQuickOpenService private quickOpenService: IQuickOpenService, @IWorkspaceContextService private contextService: IWorkspaceContextService ) { diff --git a/src/vs/workbench/electron-browser/bootstrap/index.js b/src/vs/workbench/electron-browser/bootstrap/index.js index 9595b257339..7c66ad35cf6 100644 --- a/src/vs/workbench/electron-browser/bootstrap/index.js +++ b/src/vs/workbench/electron-browser/bootstrap/index.js @@ -81,8 +81,9 @@ function registerListeners(enableDeveloperTools) { const key = extractKey(e); if (key === TOGGLE_DEV_TOOLS_KB) { ipc.send('vscode:toggleDevTools', windowId); + // remote.getCurrentWebContents().toggleDevTools(); } else if (key === RELOAD_KB) { - ipc.send('vscode:reloadWindow', windowId); + remote.getCurrentWindow().reload(); } }); } diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/electron-browser/extensionHost.ts similarity index 92% rename from src/vs/workbench/services/extensions/electron-browser/extensionHost.ts rename to src/vs/workbench/electron-browser/extensionHost.ts index 650ccd8cb60..d48a8687bc8 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/electron-browser/extensionHost.ts @@ -17,7 +17,7 @@ import { IMessageService, Severity } from 'vs/platform/message/common/message'; import { ILifecycleService, ShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IWindowService } from 'vs/workbench/services/window/electron-browser/windowService'; +import { IWindowIPCService } from 'vs/workbench/services/window/electron-browser/windowService'; import { ChildProcess, fork } from 'child_process'; import { ipcRenderer as ipc } from 'electron'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -25,6 +25,7 @@ import { ReloadWindowAction } from 'vs/workbench/electron-browser/actions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; import Event, { Emitter } from 'vs/base/common/event'; +import { WatchDog } from 'vs/base/common/watchDog'; import { createQueuedSender, IQueuedSender } from 'vs/base/node/processes'; import { IInitData, IInitConfiguration } from 'vs/workbench/api/node/extHost.protocol'; import { MainProcessExtensionService } from 'vs/workbench/api/node/mainThreadExtensionService'; @@ -55,6 +56,8 @@ export class ExtensionHostProcessWorker { private isExtensionDevelopmentTestFromCli: boolean; private isExtensionDevelopmentDebugging: boolean; + private extHostWatchDog = new WatchDog(250, 4); + private _onMessage = new Emitter(); public get onMessage(): Event { return this._onMessage.event; @@ -65,7 +68,7 @@ export class ExtensionHostProcessWorker { constructor( @IWorkspaceContextService private contextService: IWorkspaceContextService, @IMessageService private messageService: IMessageService, - @IWindowService private windowService: IWindowService, + @IWindowIPCService private windowService: IWindowIPCService, @ILifecycleService lifecycleService: ILifecycleService, @IInstantiationService private instantiationService: IInstantiationService, @IEnvironmentService private environmentService: IEnvironmentService, @@ -110,6 +113,29 @@ export class ExtensionHostProcessWorker { // Initialize extension host process with hand shakes this.initializeExtensionHostProcess = this.doInitializeExtensionHostProcess(opts); + + // Check how well the extension host is doing + if (this.environmentService.isBuilt) { + this.initializeExtensionHostProcess.done(() => { + this.extHostWatchDog.start(); + this.extHostWatchDog.onAlert(() => { + + this.extHostWatchDog.stop(); + + // log the identifiers of those extensions that + // have code and are loaded in the extension host + this.extensionService.getExtensions().then(extensions => { + const ids: string[] = []; + for (const ext of extensions) { + if (ext.main) { + ids.push(ext.id); + } + } + this.telemetryService.publicLog('extHostUnresponsive', ids); + }); + }); + }); + } } public get messagingProtocol(): IMessagePassingProtocol { @@ -191,6 +217,12 @@ export class ExtensionHostProcessWorker { return true; } + // Heartbeat message + if (msg === '__$heartbeat') { + this.extHostWatchDog.reset(); + return false; + } + // Support logging from extension host if (msg && (msg).type === '__$console') { this.logExtensionHostMessage(msg); @@ -209,7 +241,7 @@ export class ExtensionHostProcessWorker { TPromise.join([ this.telemetryService.getTelemetryInfo(), - this.extensionService.readExtensions() + this.extensionService.getExtensions() ]).then(([telemetryInfo, extensionDescriptions]) => { let initData: IInitData = { parentPid: process.pid, @@ -339,4 +371,4 @@ export class ExtensionHostProcessWorker { event.veto(TPromise.timeout(100 /* wait a bit for IPC to get delivered */).then(() => false)); } } -} \ No newline at end of file +} diff --git a/src/vs/workbench/electron-browser/integration.ts b/src/vs/workbench/electron-browser/integration.ts index 6718cac9b89..ed76f6de9de 100644 --- a/src/vs/workbench/electron-browser/integration.ts +++ b/src/vs/workbench/electron-browser/integration.ts @@ -21,7 +21,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { ICommandService } from 'vs/platform/commands/common/commands'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { IWindowService } from 'vs/workbench/services/window/electron-browser/windowService'; +import { IWindowIPCService } from 'vs/workbench/services/window/electron-browser/windowService'; import { AutoSaveConfiguration } from 'vs/platform/files/common/files'; import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; @@ -58,7 +58,7 @@ export class ElectronIntegration { constructor( @IInstantiationService private instantiationService: IInstantiationService, - @IWindowService private windowService: IWindowService, + @IWindowIPCService private windowService: IWindowIPCService, @IPartService private partService: IPartService, @IWorkspaceContextService private contextService: IWorkspaceContextService, @ITelemetryService private telemetryService: ITelemetryService, diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index afc8ea109be..8e07014b83a 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -15,7 +15,7 @@ import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import platform = require('vs/base/common/platform'); import { IKeybindings } from 'vs/platform/keybinding/common/keybinding'; import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { IWindowService } from 'vs/workbench/services/window/electron-browser/windowService'; +import { IWindowIPCService } from 'vs/workbench/services/window/electron-browser/windowService'; import { CloseEditorAction, ReloadWindowAction, ShowStartupPerformance, ReportIssueAction, ZoomResetAction, ZoomOutAction, ZoomInAction, ToggleDevToolsAction, ToggleFullScreenAction, ToggleMenuBarAction, OpenRecentAction, CloseFolderAction, CloseWindowAction, SwitchWindow, NewWindowAction, CloseMessagesAction } from 'vs/workbench/electron-browser/actions'; import { MessagesVisibleContext, NoEditorsVisibleContext } from 'vs/workbench/electron-browser/workbench'; @@ -61,7 +61,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ when: NoEditorsVisibleContext, primary: closeEditorOrWindowKeybindings.primary, handler: accessor => { - const windowService = accessor.get(IWindowService); + const windowService = accessor.get(IWindowIPCService); windowService.getWindow().close(); } }); @@ -79,6 +79,11 @@ configurationRegistry.registerConfiguration({ 'description': nls.localize('showEditorTabs', "Controls if opened editors should show in tabs or not."), 'default': true }, + 'workbench.editor.showTabCloseButton': { + 'type': 'boolean', + 'description': nls.localize('showEditorTabCloseButton', "Controls if editor tabs should have a visible close button or not."), + 'default': true + }, 'workbench.editor.showIcons': { 'type': 'boolean', 'description': nls.localize('showIcons', "Controls if opened editors should show with an icon or not. This requires an icon theme to be enabled as well."), @@ -151,6 +156,11 @@ configurationRegistry.registerConfiguration({ 'type': 'number', 'default': 0, 'description': nls.localize('zoomLevel', "Adjust the zoom level of the window. The original size is 0 and each increment above (e.g. 1) or below (e.g. -1) represents zooming 20% larger or smaller. You can also enter decimals to adjust the zoom level with a finer granularity.") + }, + 'window.showFullPath': { + 'type': 'boolean', + 'default': false, + 'description': nls.localize('showFullPath', "If enabled, will show the full path of opened files in the window title.") } } }); \ No newline at end of file diff --git a/src/vs/workbench/electron-browser/shell.ts b/src/vs/workbench/electron-browser/shell.ts index a8f11a13d9a..2028336d7f5 100644 --- a/src/vs/workbench/electron-browser/shell.ts +++ b/src/vs/workbench/electron-browser/shell.ts @@ -30,7 +30,10 @@ import ErrorTelemetry from 'vs/platform/telemetry/browser/errorTelemetry'; import { resolveWorkbenchCommonProperties } from 'vs/platform/telemetry/node/workbenchCommonProperties'; import { ElectronIntegration } from 'vs/workbench/electron-browser/integration'; import { WorkspaceStats } from 'vs/workbench/services/telemetry/common/workspaceStats'; -import { IWindowService, WindowService } from 'vs/workbench/services/window/electron-browser/windowService'; +import { IWindowIPCService, WindowIPCService } from 'vs/workbench/services/window/electron-browser/windowService'; +import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows'; +import { WindowsChannelClient } from 'vs/platform/windows/common/windowsIpc'; +import { WindowService } from 'vs/platform/windows/electron-browser/windowService'; import { MessageService } from 'vs/workbench/services/message/electron-browser/messageService'; import { IRequestService } from 'vs/platform/request/common/request'; import { RequestService } from 'vs/platform/request/node/requestService'; @@ -50,6 +53,7 @@ import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerServ import { MainProcessExtensionService } from 'vs/workbench/api/node/mainThreadExtensionService'; import { IOptions } from 'vs/workbench/common/options'; import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; @@ -64,7 +68,7 @@ import { IThreadService } from 'vs/workbench/services/thread/common/threadServic import { ICommandService } from 'vs/platform/commands/common/commands'; import { CommandService } from 'vs/platform/commands/common/commandService'; import { IWorkspaceContextService, IWorkspace } from 'vs/platform/workspace/common/workspace'; -import { IExtensionService, IExtensionsRuntimeService } from 'vs/platform/extensions/common/extensions'; +import { IExtensionService } from 'vs/platform/extensions/common/extensions'; import { MainThreadModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; @@ -73,16 +77,16 @@ import { IThemeService } from 'vs/workbench/services/themes/common/themeService' import { ThemeService } from 'vs/workbench/services/themes/electron-browser/themeService'; import { getDelayedChannel } from 'vs/base/parts/ipc/common/ipc'; import { connect as connectNet } from 'vs/base/parts/ipc/node/ipc.net'; -import { Client as ElectronIPCClient } from 'vs/base/parts/ipc/common/ipc.electron'; -import { ipcRenderer } from 'electron'; +import { Client as ElectronIPCClient } from 'vs/base/parts/ipc/electron-browser/ipc.electron-browser'; import { IExtensionManagementChannel, ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/common/extensionManagementIpc'; -import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionManagementService, IExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { ExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService'; import { URLChannelClient } from 'vs/platform/url/common/urlIpc'; import { IURLService } from 'vs/platform/url/common/url'; import { ReloadWindowAction } from 'vs/workbench/electron-browser/actions'; import { WorkspaceConfigurationService } from 'vs/workbench/services/configuration/node/configurationService'; -import { ExtensionHostProcessWorker } from 'vs/workbench/services/extensions/electron-browser/extensionHost'; -import { ExtensionsRuntimeService } from 'vs/workbench/services/extensions/electron-browser/extensions'; +import { ExtensionHostProcessWorker } from 'vs/workbench/electron-browser/extensionHost'; +import { remote } from 'electron'; // self registering services import 'vs/platform/opener/browser/opener.contribution'; @@ -107,7 +111,6 @@ export class WorkbenchShell { private eventService: IEventService; private environmentService: IEnvironmentService; private contextViewService: ContextViewService; - private windowService: IWindowService; private threadService: MainThreadService; private configurationService: IConfigurationService; private themeService: ThemeService; @@ -212,7 +215,7 @@ export class WorkbenchShell { private initServiceCollection(container: HTMLElement): [InstantiationService, ServiceCollection] { const disposables = new Disposables(); - const mainProcessClient = new ElectronIPCClient(ipcRenderer); + const mainProcessClient = new ElectronIPCClient(String(`window${remote.getCurrentWindow().id}`)); disposables.add(mainProcessClient); const serviceCollection = new ServiceCollection(); @@ -221,12 +224,21 @@ export class WorkbenchShell { serviceCollection.set(IConfigurationService, this.configurationService); serviceCollection.set(IEnvironmentService, this.environmentService); - const instantiationService = new InstantiationService(serviceCollection, true); + const instantiationServiceImpl = new InstantiationService(serviceCollection, true); + const instantiationService = instantiationServiceImpl as IInstantiationService; - this.windowService = instantiationService.createInstance(WindowService); - serviceCollection.set(IWindowService, this.windowService); + // TODO@joao remove this + const windowIPCService = instantiationService.createInstance(WindowIPCService); + serviceCollection.set(IWindowIPCService, windowIPCService); - const sharedProcess = connectNet(this.environmentService.sharedIPCHandle, `window:${this.windowService.getWindowId()}`); + const windowsChannel = mainProcessClient.getChannel('windows'); + const windowsChannelClient = new WindowsChannelClient(windowsChannel); + serviceCollection.set(IWindowsService, windowsChannelClient); + + const windowService = new WindowService(windowIPCService.getWindowId(), windowsChannelClient); + serviceCollection.set(IWindowService, windowService); + + const sharedProcess = connectNet(this.environmentService.sharedIPCHandle, `window:${windowIPCService.getWindowId()}`); sharedProcess.done(client => { client.registerChannel('choice', new ChoiceChannel(this.messageService)); @@ -292,9 +304,9 @@ export class WorkbenchShell { const extensionManagementChannelClient = new ExtensionManagementChannelClient(extensionManagementChannel); serviceCollection.set(IExtensionManagementService, extensionManagementChannelClient); - const extensionsRuntimeService = instantiationService.createInstance(ExtensionsRuntimeService); - serviceCollection.set(IExtensionsRuntimeService, extensionsRuntimeService); - disposables.add(extensionsRuntimeService); + const extensionEnablementService = instantiationService.createInstance(ExtensionEnablementService); + serviceCollection.set(IExtensionEnablementService, extensionEnablementService); + disposables.add(extensionEnablementService); const extensionHostProcessWorker = instantiationService.createInstance(ExtensionHostProcessWorker); this.threadService = instantiationService.createInstance(MainThreadService, extensionHostProcessWorker.messagingProtocol); @@ -333,17 +345,17 @@ export class WorkbenchShell { const searchService = instantiationService.createInstance(SearchService); serviceCollection.set(ISearchService, searchService); - const codeEditorService = instantiationService.createInstance(CodeEditorServiceImpl); + const codeEditorService = instantiationServiceImpl.createInstance(CodeEditorServiceImpl); serviceCollection.set(ICodeEditorService, codeEditorService); const integrityService = instantiationService.createInstance(IntegrityServiceImpl); serviceCollection.set(IIntegrityService, integrityService); const urlChannel = mainProcessClient.getChannel('url'); - const urlChannelClient = new URLChannelClient(urlChannel, this.windowService.getWindowId()); + const urlChannelClient = new URLChannelClient(urlChannel, windowIPCService.getWindowId()); serviceCollection.set(IURLService, urlChannelClient); - return [instantiationService, serviceCollection]; + return [instantiationServiceImpl, serviceCollection]; } public open(): void { diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index fc6f72def1f..13d0d826f5d 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -149,6 +149,7 @@ export class ElectronWindow { } public reload(): void { + this.partService.setRestoreSidebar(); // we want the same sidebar after a reload restored ipc.send('vscode:reloadWindow', this.windowId); } diff --git a/src/vs/workbench/node/extensionHostProcess.ts b/src/vs/workbench/node/extensionHostProcess.ts index 726c7231287..19a9b81ef78 100644 --- a/src/vs/workbench/node/extensionHostProcess.ts +++ b/src/vs/workbench/node/extensionHostProcess.ts @@ -106,6 +106,12 @@ function connectToRenderer(): TPromise { stats.length = 0; }, 1000); + + // Send heartbeat + setInterval(function () { + queuedSender.send('__$heartbeat'); + }, 250); + // Tell the outside that we are initialized queuedSender.send('initialized'); @@ -121,4 +127,4 @@ connectToRenderer().then(renderer => { const extensionHostMain = new ExtensionHostMain(renderer.remoteCom, renderer.initData); onTerminate = () => extensionHostMain.terminate(); return extensionHostMain.start(); -}).done(null, err => console.error(err)); \ No newline at end of file +}).done(null, err => console.error(err)); diff --git a/src/vs/workbench/node/extensionPoints.ts b/src/vs/workbench/node/extensionPoints.ts index 94b95832ba5..f318fd69315 100644 --- a/src/vs/workbench/node/extensionPoints.ts +++ b/src/vs/workbench/node/extensionPoints.ts @@ -8,7 +8,7 @@ import * as nls from 'vs/nls'; import * as Platform from 'vs/base/common/platform'; import pfs = require('vs/base/node/pfs'); -import { IExtensionDescription, IMessage } from 'vs/platform/extensions/common/extensions'; +import { IExtensionDescription, IMessage, ExtensionProperties } from 'vs/platform/extensions/common/extensions'; import Severity from 'vs/base/common/severity'; import { TPromise } from 'vs/base/common/winjs.base'; import { groupBy, values } from 'vs/base/common/collections'; @@ -81,16 +81,21 @@ abstract class ExtensionManifestHandler { } class ExtensionManifestParser extends ExtensionManifestHandler { + public parse(): TPromise { return pfs.readFile(this._absoluteManifestPath).then((manifestContents) => { let errors: json.ParseError[] = []; - let extensionDescription: IExtensionDescription = json.parse(manifestContents.toString(), errors); + const parsed = json.parse(manifestContents.toString(), errors); if (errors.length > 0) { errors.forEach((error) => { this._collector.error(this._absoluteFolderPath, nls.localize('jsonParseFail', "Failed to parse {0}: {1}.", this._absoluteManifestPath, json.getParseErrorMessage(error.error))); }); return null; } + const extensionDescription = ExtensionProperties.reduce((previousValue, currentValue) => { + previousValue[currentValue] = parsed[currentValue]; + return previousValue; + }, {}); return extensionDescription; }, (err) => { this._collector.error(this._absoluteFolderPath, nls.localize('fileReadFail', "Cannot read file {0}: {1}.", this._absoluteManifestPath, err.message)); @@ -211,7 +216,7 @@ class ExtensionManifestValidator extends ExtensionManifestHandler { vscode: string; }; main?: string; - enableProposedApi: boolean; + enableProposedApi?: boolean; } let extensionDescription = _extensionDescription; extensionDescription.isBuiltin = this._isBuiltin; diff --git a/src/vs/workbench/parts/debug/browser/debugActions.ts b/src/vs/workbench/parts/debug/browser/debugActions.ts index 835c20d2c4a..986c2aad011 100644 --- a/src/vs/workbench/parts/debug/browser/debugActions.ts +++ b/src/vs/workbench/parts/debug/browser/debugActions.ts @@ -549,12 +549,14 @@ class ToggleBreakpointAction extends EditorAction { const lineNumber = editor.getPosition().lineNumber; const modelUri = editor.getModel().uri; - if (debugService.getConfigurationManager().canSetBreakpointsIn(editor.getModel())) { - const bp = debugService.getModel().getBreakpoints() - .filter(bp => bp.lineNumber === lineNumber && bp.uri.toString() === modelUri.toString()).pop(); + const bp = debugService.getModel().getBreakpoints() + .filter(bp => bp.lineNumber === lineNumber && bp.uri.toString() === modelUri.toString()).pop(); - return bp ? debugService.removeBreakpoints(bp.getId()) - : debugService.addBreakpoints(modelUri, [{ lineNumber }]); + if (bp) { + return debugService.removeBreakpoints(bp.getId()); + } + if (debugService.getConfigurationManager().canSetBreakpointsIn(editor.getModel())) { + return debugService.addBreakpoints(modelUri, [{ lineNumber }]); } } } diff --git a/src/vs/workbench/parts/debug/browser/debugActionsWidget.ts b/src/vs/workbench/parts/debug/browser/debugActionsWidget.ts index 3802fa674a6..745d52d8160 100644 --- a/src/vs/workbench/parts/debug/browser/debugActionsWidget.ts +++ b/src/vs/workbench/parts/debug/browser/debugActionsWidget.ts @@ -72,7 +72,7 @@ export class DebugActionsWidget implements wbext.IWorkbenchContribution { private registerListeners(): void { this.toDispose.push(this.debugService.onDidChangeState(() => { - this.onDebugStateChange(); + this.update(); })); this.toDispose.push(this.actionBar.actionRunner.addListener2(events.EventType.RUN, (e: any) => { // check for error @@ -132,7 +132,7 @@ export class DebugActionsWidget implements wbext.IWorkbenchContribution { return DebugActionsWidget.ID; } - private onDebugStateChange(): void { + private update(): void { const state = this.debugService.state; if (state === debug.State.Disabled || state === debug.State.Inactive) { return this.hide(); diff --git a/src/vs/workbench/parts/debug/common/debug.ts b/src/vs/workbench/parts/debug/common/debug.ts index f20b90112bd..8c5a8a19e47 100644 --- a/src/vs/workbench/parts/debug/common/debug.ts +++ b/src/vs/workbench/parts/debug/common/debug.ts @@ -443,6 +443,11 @@ export interface IDebugService { */ renameWatchExpression(id: string, newName: string): TPromise; + /** + * Moves a watch expression to a new possition. Used for reordering watch expressions. + */ + moveWatchExpression(id: string, position: number): void; + /** * Removes all watch expressions. If id is passed only removes the watch expression with the passed id. */ diff --git a/src/vs/workbench/parts/debug/common/debugModel.ts b/src/vs/workbench/parts/debug/common/debugModel.ts index 1ba518634f1..02615345c64 100644 --- a/src/vs/workbench/parts/debug/common/debugModel.ts +++ b/src/vs/workbench/parts/debug/common/debugModel.ts @@ -106,59 +106,50 @@ export abstract class ExpressionContainer implements debug.IExpressionContainer private static BASE_CHUNK_SIZE = 100; public valueChanged: boolean; - private children: TPromise; private _value: string; constructor( public stackFrame: debug.IStackFrame, public reference: number, private id: string, - private cacheChildren: boolean, public namedVariables: number, public indexedVariables: number, private startOfVariables = 0 - ) { - // noop - } + ) { } public getChildren(): TPromise { - if (!this.cacheChildren || !this.children) { - // only variables with reference > 0 have children. - if (this.reference <= 0) { - this.children = TPromise.as([]); - } else { - if (!this.getChildrenInChunks) { - return this.fetchVariables(undefined, undefined, undefined); - } - - // Check if object has named variables, fetch them independent from indexed variables #9670 - this.children = (!!this.namedVariables ? this.fetchVariables(undefined, undefined, 'named') - : TPromise.as([])).then(childrenArray => { - // Use a dynamic chunk size based on the number of elements #9774 - let chunkSize = ExpressionContainer.BASE_CHUNK_SIZE; - while (this.indexedVariables > chunkSize * ExpressionContainer.BASE_CHUNK_SIZE) { - chunkSize *= ExpressionContainer.BASE_CHUNK_SIZE; - } - - if (this.indexedVariables > chunkSize) { - // There are a lot of children, create fake intermediate values that represent chunks #9537 - const numberOfChunks = Math.ceil(this.indexedVariables / chunkSize); - for (let i = 0; i < numberOfChunks; i++) { - const start = this.startOfVariables + i * chunkSize; - const count = Math.min(chunkSize, this.indexedVariables - i * chunkSize); - childrenArray.push(new Variable(this.stackFrame, this, this.reference, `[${start}..${start + count - 1}]`, '', '', null, count, null, true, start)); - } - - return childrenArray; - } - - return this.fetchVariables(this.startOfVariables, this.indexedVariables, 'indexed') - .then(variables => childrenArray.concat(variables)); - }); - } + // only variables with reference > 0 have children. + if (this.reference <= 0) { + return TPromise.as([]); } - return this.children; + if (!this.getChildrenInChunks) { + return this.fetchVariables(undefined, undefined, undefined); + } + + // Check if object has named variables, fetch them independent from indexed variables #9670 + return (!!this.namedVariables ? this.fetchVariables(undefined, undefined, 'named') : TPromise.as([])).then(childrenArray => { + // Use a dynamic chunk size based on the number of elements #9774 + let chunkSize = ExpressionContainer.BASE_CHUNK_SIZE; + while (this.indexedVariables > chunkSize * ExpressionContainer.BASE_CHUNK_SIZE) { + chunkSize *= ExpressionContainer.BASE_CHUNK_SIZE; + } + + if (this.indexedVariables > chunkSize) { + // There are a lot of children, create fake intermediate values that represent chunks #9537 + const numberOfChunks = Math.ceil(this.indexedVariables / chunkSize); + for (let i = 0; i < numberOfChunks; i++) { + const start = this.startOfVariables + i * chunkSize; + const count = Math.min(chunkSize, this.indexedVariables - i * chunkSize); + childrenArray.push(new Variable(this.stackFrame, this, this.reference, `[${start}..${start + count - 1}]`, '', '', null, count, null, true, start)); + } + + return childrenArray; + } + + return this.fetchVariables(this.startOfVariables, this.indexedVariables, 'indexed') + .then(variables => childrenArray.concat(variables)); + }); } public getId(): string { @@ -205,10 +196,14 @@ export class Expression extends ExpressionContainer implements debug.IExpression public available: boolean; public type: string; - constructor(public name: string, cacheChildren: boolean, id = generateUuid()) { - super(null, 0, id, cacheChildren, 0, 0); - this.value = Expression.DEFAULT_VALUE; + constructor(public name: string, id = generateUuid()) { + super(null, 0, id, 0, 0); this.available = false; + // name is not set if the expression is just being added + // in that case do not set default value to prevent flashing #14499 + if (name) { + this.value = Expression.DEFAULT_VALUE; + } } public evaluate(process: debug.IProcess, stackFrame: debug.IStackFrame, context: string): TPromise { @@ -265,7 +260,7 @@ export class Variable extends ExpressionContainer implements debug.IExpression { public available = true, startOfVariables = 0 ) { - super(stackFrame, reference, `variable:${parent.getId()}:${name}:${reference}`, true, namedVariables, indexedVariables, startOfVariables); + super(stackFrame, reference, `variable:${parent.getId()}:${name}:${reference}`, namedVariables, indexedVariables, startOfVariables); this.value = massageValue(value); } @@ -306,6 +301,9 @@ export class Variable extends ExpressionContainer implements debug.IExpression { if (response && response.body) { this.value = response.body.value; this.type = response.body.type || this.type; + this.reference = response.body.variablesReference; + this.namedVariables = response.body.namedVariables; + this.indexedVariables = response.body.indexedVariables; } // TODO@Isidor notify stackFrame that a change has happened so watch expressions get revelauted }, err => { @@ -324,7 +322,7 @@ export class Scope extends ExpressionContainer implements debug.IScope { namedVariables: number, indexedVariables: number ) { - super(stackFrame, reference, `scope:${stackFrame.getId()}:${name}:${reference}`, true, namedVariables, indexedVariables); + super(stackFrame, reference, `scope:${stackFrame.getId()}:${name}:${reference}`, namedVariables, indexedVariables); } } @@ -661,8 +659,11 @@ export class Model implements debug.IModel { return this.processes; } - public addProcess(name: string, session: debug.ISession & debug.ITreeElement): void { - this.processes.push(new Process(name, session)); + public addProcess(name: string, session: debug.ISession & debug.ITreeElement): Process { + const process = new Process(name, session); + this.processes.push(process); + + return process; } public removeProcess(id: string): void { @@ -811,7 +812,7 @@ export class Model implements debug.IModel { } public addReplExpression(process: debug.IProcess, stackFrame: debug.IStackFrame, name: string): TPromise { - const expression = new Expression(name, true); + const expression = new Expression(name); this.addReplElements([expression]); return expression.evaluate(process, stackFrame, 'repl') .then(() => this._onDidChangeREPLElements.fire()); @@ -886,7 +887,7 @@ export class Model implements debug.IModel { } public addWatchExpression(process: debug.IProcess, stackFrame: debug.IStackFrame, name: string): TPromise { - const we = new Expression(name, false); + const we = new Expression(name); this.watchExpressions.push(we); if (!name) { this._onDidChangeWatchExpressions.fire(we); @@ -930,6 +931,14 @@ export class Model implements debug.IModel { this._onDidChangeWatchExpressions.fire(); } + public moveWatchExpression(id: string, position: number): void { + const we = this.watchExpressions.filter(we => we.getId() === id).pop(); + this.watchExpressions = this.watchExpressions.filter(we => we.getId() !== id); + this.watchExpressions = this.watchExpressions.slice(0, position).concat(we, this.watchExpressions.slice(position)); + + this._onDidChangeWatchExpressions.fire(); + } + public sourceIsUnavailable(source: Source): void { this.processes.forEach(p => p.sourceIsUnavailable(source)); this._onDidChangeCallStack.fire(); diff --git a/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts b/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts index 35fedeb854e..4e8b02bbf83 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts @@ -76,14 +76,16 @@ export class DebugEditorContribution implements debug.IDebugEditorContribution { if (e.target.type !== editorcommon.MouseTargetType.GUTTER_GLYPH_MARGIN || /* after last line */ e.target.detail) { return; } - if (!this.debugService.getConfigurationManager().canSetBreakpointsIn(this.editor.getModel())) { - return; - } + const canSetBreakpoints = this.debugService.getConfigurationManager().canSetBreakpointsIn(this.editor.getModel()); const lineNumber = e.target.position.lineNumber; const uri = this.editor.getModel().uri; if (e.event.rightButton || (env.isMacintosh && e.event.leftButton && e.event.ctrlKey)) { + if (!canSetBreakpoints) { + return; + } + const anchor = { x: e.event.posx + 1, y: e.event.posy }; const breakpoint = this.debugService.getModel().getBreakpoints().filter(bp => bp.lineNumber === lineNumber && bp.uri.toString() === uri.toString()).pop(); @@ -98,7 +100,7 @@ export class DebugEditorContribution implements debug.IDebugEditorContribution { if (breakpoint) { this.debugService.removeBreakpoints(breakpoint.getId()); - } else { + } else if (canSetBreakpoints) { this.debugService.addBreakpoints(uri, [{ lineNumber }]); } } diff --git a/src/vs/workbench/parts/debug/electron-browser/debugHover.ts b/src/vs/workbench/parts/debug/electron-browser/debugHover.ts index a731609d321..79390175568 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugHover.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugHover.ts @@ -160,7 +160,7 @@ export class DebugHoverWidget implements editorbrowser.IContentWidget { const matchingExpression = lineContent.substring(expressionRange.startColumn - 1, expressionRange.endColumn); let promise: TPromise; if (process.session.configuration.capabilities.supportsEvaluateForHovers) { - const result = new Expression(matchingExpression, true); + const result = new Expression(matchingExpression); promise = result.evaluate(process, focusedStackFrame, 'hover').then(() => result); } else { promise = this.findExpressionInStackFrame(matchingExpression.split('.').map(word => word.trim()).filter(word => !!word)); diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts index 6969de6c958..be06e7a9d4d 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts @@ -52,8 +52,8 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IWindowService, IBroadcast } from 'vs/workbench/services/window/electron-browser/windowService'; -import { ILogEntry, EXTENSION_LOG_BROADCAST_CHANNEL, EXTENSION_ATTACH_BROADCAST_CHANNEL, EXTENSION_TERMINATE_BROADCAST_CHANNEL } from 'vs/workbench/services/extensions/electron-browser/extensionHost'; +import { IWindowIPCService, IBroadcast } from 'vs/workbench/services/window/electron-browser/windowService'; +import { ILogEntry, EXTENSION_LOG_BROADCAST_CHANNEL, EXTENSION_ATTACH_BROADCAST_CHANNEL, EXTENSION_TERMINATE_BROADCAST_CHANNEL } from 'vs/workbench/electron-browser/extensionHost'; import { ipcRenderer as ipc } from 'electron'; const DEBUG_BREAKPOINTS_KEY = 'debug.breakpoint'; @@ -87,7 +87,7 @@ export class DebugService implements debug.IDebugService { @IFileService private fileService: IFileService, @IMessageService private messageService: IMessageService, @IPartService private partService: IPartService, - @IWindowService private windowService: IWindowService, + @IWindowIPCService private windowService: IWindowIPCService, @ITelemetryService private telemetryService: ITelemetryService, @IWorkspaceContextService private contextService: IWorkspaceContextService, @IContextKeyService contextKeyService: IContextKeyService, @@ -402,7 +402,7 @@ export class DebugService implements debug.IDebugService { let result: Expression[]; try { result = JSON.parse(this.storageService.get(DEBUG_WATCH_EXPRESSIONS_KEY, StorageScope.WORKSPACE, '[]')).map((watchStoredData: { name: string, id: string }) => { - return new Expression(watchStoredData.name, false, watchStoredData.id); + return new Expression(watchStoredData.name, watchStoredData.id); }); } catch (e) { } @@ -439,9 +439,11 @@ export class DebugService implements debug.IDebugService { return !!this.contextService.getWorkspace(); } - public setFocusedStackFrameAndEvaluate(focusedStackFrame: debug.IStackFrame): TPromise { + public setFocusedStackFrameAndEvaluate(focusedStackFrame: debug.IStackFrame, process?: debug.IProcess): TPromise { const processes = this.model.getProcesses(); - const process = focusedStackFrame ? focusedStackFrame.thread.process : processes.length ? processes[0] : null; + if (!process) { + process = focusedStackFrame ? focusedStackFrame.thread.process : processes.length ? processes[0] : null; + } if (process && !focusedStackFrame) { const thread = process.getAllThreads().pop(); const callStack = thread ? thread.getCachedCallStack() : null; @@ -532,6 +534,10 @@ export class DebugService implements debug.IDebugService { return this.model.renameWatchExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, id, newName); } + public moveWatchExpression(id: string, position: number): void { + this.model.moveWatchExpression(id, position); + } + public removeWatchExpressions(id?: string): void { this.model.removeWatchExpressions(id); } @@ -632,7 +638,12 @@ export class DebugService implements debug.IDebugService { } const session = this.instantiationService.createInstance(RawDebugSession, sessionId, configuration.debugServer, adapter, this.customTelemetryService); - this.model.addProcess(configuration.name, session); + const process = this.model.addProcess(configuration.name, session); + + if (!this.viewModel.focusedProcess) { + this.viewModel.setFocusedStackFrame(null, process); + this._onDidChangeState.fire(); + } this.toDisposeOnSessionEnd[session.getId()] = []; if (client) { this.toDisposeOnSessionEnd[session.getId()].push(client); diff --git a/src/vs/workbench/parts/debug/electron-browser/debugViewer.ts b/src/vs/workbench/parts/debug/electron-browser/debugViewer.ts index 14560bd486c..f179a4825f1 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugViewer.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugViewer.ts @@ -13,13 +13,13 @@ import * as errors from 'vs/base/common/errors'; import { equalsIgnoreCase } from 'vs/base/common/strings'; import { isMacintosh } from 'vs/base/common/platform'; import * as dom from 'vs/base/browser/dom'; -import { IMouseEvent } from 'vs/base/browser/mouseEvent'; +import { IMouseEvent, DragMouseEvent } from 'vs/base/browser/mouseEvent'; import { getPathLabel } from 'vs/base/common/labels'; import { IAction, IActionRunner } from 'vs/base/common/actions'; import { IActionItem, Separator, ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; -import { ITree, IAccessibilityProvider, ContextMenuEvent, IDataSource, IRenderer } from 'vs/base/parts/tree/browser/tree'; +import { ITree, IAccessibilityProvider, ContextMenuEvent, IDataSource, IRenderer, DRAG_OVER_ACCEPT, IDragAndDropData, IDragOverReaction } from 'vs/base/parts/tree/browser/tree'; import { InputBox, IInputValidationOptions } from 'vs/base/browser/ui/inputbox/inputBox'; -import { DefaultController } from 'vs/base/parts/tree/browser/treeDefaults'; +import { DefaultController, DefaultDragAndDrop } from 'vs/base/parts/tree/browser/treeDefaults'; import { IActionProvider } from 'vs/base/parts/tree/browser/actionsRenderer'; import * as debug from 'vs/workbench/parts/debug/common/debug'; import { Expression, Variable, FunctionBreakpoint, StackFrame, Thread, Process, Breakpoint, ExceptionBreakpoint, Model, Scope } from 'vs/workbench/parts/debug/common/debugModel'; @@ -56,7 +56,7 @@ export function renderExpressionValue(expressionOrValue: debug.IExpression | str dom.addClass(container, 'string'); } - if (showChanged && (expressionOrValue).valueChanged) { + if (showChanged && (expressionOrValue).valueChanged && value !== Expression.DEFAULT_VALUE) { // value changed color has priority over other colors. container.className = 'value changed'; } @@ -479,7 +479,7 @@ export class CallStackRenderer implements IRenderer { let data: IStackFrameTemplateData = Object.create(null); data.stackFrame = dom.append(container, $('.stack-frame')); - data.label = dom.append(data.stackFrame, $('span.label')); + data.label = dom.append(data.stackFrame, $('span.label.expression')); data.file = dom.append(data.stackFrame, $('.file')); data.fileName = dom.append(data.file, $('span.file-name')); data.lineNumber = dom.append(data.file, $('span.line-number')); @@ -502,7 +502,7 @@ export class CallStackRenderer implements IRenderer { } private renderProcess(process: debug.IProcess, data: IProcessTemplateData): void { - data.process.title = nls.localize('process', "Process"); + data.process.title = nls.localize({ key: 'process', comment: ['Process is a noun'] }, "Process"); data.name.textContent = process.name; } @@ -991,6 +991,43 @@ export class WatchExpressionsController extends BaseDebugController { } } +export class WatchExpressionsDragAndDrop extends DefaultDragAndDrop { + + constructor( @debug.IDebugService private debugService: debug.IDebugService) { + super(); + } + + public getDragURI(tree: ITree, element: Expression): string { + if (!(element instanceof Expression)) { + return null; + } + + return element.getId(); + } + + public getDragLabel(tree: ITree, elements: Expression[]): string { + if (elements.length > 1) { + return String(elements.length); + } + + return elements[0].name; + } + + public onDragOver(tree: ITree, data: IDragAndDropData, target: Expression | Model, originalEvent: DragMouseEvent): IDragOverReaction { + return DRAG_OVER_ACCEPT; + } + + public drop(tree: ITree, data: IDragAndDropData, target: Expression | Model, originalEvent: DragMouseEvent): void { + const draggedData = data.getData(); + if (Array.isArray(draggedData)) { + const draggedElement = draggedData[0]; + const watches = this.debugService.getModel().getWatchExpressions(); + const position = target instanceof Model ? watches.length - 1 : watches.indexOf(target); + this.debugService.moveWatchExpression(draggedElement.getId(), position); + } + } +} + // breakpoints export class BreakpointsActionProvider implements IActionProvider { diff --git a/src/vs/workbench/parts/debug/electron-browser/debugViews.ts b/src/vs/workbench/parts/debug/electron-browser/debugViews.ts index a273bca799f..5cd54d20588 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugViews.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugViews.ts @@ -3,24 +3,24 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import nls = require('vs/nls'); -import paths = require('vs/base/common/paths'); +import * as nls from 'vs/nls'; +import * as paths from 'vs/base/common/paths'; import { RunOnceScheduler } from 'vs/base/common/async'; -import dom = require('vs/base/browser/dom'); -import builder = require('vs/base/browser/builder'); +import * as dom from 'vs/base/browser/dom'; +import * as builder from 'vs/base/browser/builder'; import { TPromise } from 'vs/base/common/winjs.base'; -import errors = require('vs/base/common/errors'); -import events = require('vs/base/common/events'); -import actions = require('vs/base/common/actions'); -import actionbarregistry = require('vs/workbench/browser/actionBarRegistry'); -import tree = require('vs/base/parts/tree/browser/tree'); -import treeimpl = require('vs/base/parts/tree/browser/treeImpl'); -import splitview = require('vs/base/browser/ui/splitview/splitview'); -import viewlet = require('vs/workbench/browser/viewlet'); -import debug = require('vs/workbench/parts/debug/common/debug'); +import * as errors from 'vs/base/common/errors'; +import { EventType } from 'vs/base/common/events'; +import { IActionRunner, IAction } from 'vs/base/common/actions'; +import { prepareActions } from 'vs/workbench/browser/actionBarRegistry'; +import { ITreeOptions, IFocusEvent, IHighlightEvent, ITree } from 'vs/base/parts/tree/browser/tree'; +import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; +import { CollapsibleState } from 'vs/base/browser/ui/splitview/splitview'; +import { CollapsibleViewletView, AdaptiveCollapsibleViewletView, CollapseAction } from 'vs/workbench/browser/viewlet'; +import * as debug from 'vs/workbench/parts/debug/common/debug'; import { StackFrame, Expression, Variable, ExceptionBreakpoint, FunctionBreakpoint } from 'vs/workbench/parts/debug/common/debugModel'; -import viewer = require('vs/workbench/parts/debug/electron-browser/debugViewer'); -import debugactions = require('vs/workbench/parts/debug/browser/debugActions'); +import * as viewer from 'vs/workbench/parts/debug/electron-browser/debugViewer'; +import { AddWatchExpressionAction, RemoveAllWatchExpressionsAction, AddFunctionBreakpointAction, ToggleBreakpointsActivatedAction, RemoveAllBreakpointsAction } from 'vs/workbench/parts/debug/browser/debugActions'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -30,7 +30,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import IDebugService = debug.IDebugService; const debugTreeOptions = (ariaLabel: string) => { - return { + return { twistiePixels: 20, ariaLabel }; @@ -45,13 +45,13 @@ function renderViewTree(container: HTMLElement): HTMLElement { const $ = builder.$; -export class VariablesView extends viewlet.CollapsibleViewletView { +export class VariablesView extends CollapsibleViewletView { private static MEMENTO = 'variablesview.memento'; private onFocusStackFrameScheduler: RunOnceScheduler; constructor( - actionRunner: actions.IActionRunner, + actionRunner: IActionRunner, private settings: any, @IMessageService messageService: IMessageService, @IContextMenuService contextMenuService: IContextMenuService, @@ -90,7 +90,7 @@ export class VariablesView extends viewlet.CollapsibleViewletView { dom.addClass(container, 'debug-variables'); this.treeContainer = renderViewTree(container); - this.tree = new treeimpl.Tree(this.treeContainer, { + this.tree = new Tree(this.treeContainer, { dataSource: new viewer.VariablesDataSource(), renderer: this.instantiationService.createInstance(viewer.VariablesRenderer), accessibilityProvider: new viewer.VariablesAccessibilityProvider(), @@ -101,8 +101,8 @@ export class VariablesView extends viewlet.CollapsibleViewletView { this.tree.setInput(viewModel); - const collapseAction = this.instantiationService.createInstance(viewlet.CollapseAction, this.tree, false, 'explorer-action collapse-explorer'); - this.toolBar.setActions(actionbarregistry.prepareActions([collapseAction]))(); + const collapseAction = this.instantiationService.createInstance(CollapseAction, this.tree, false, 'explorer-action collapse-explorer'); + this.toolBar.setActions(prepareActions([collapseAction]))(); this.toDispose.push(viewModel.onDidFocusStackFrame(sf => { // Only delay if the stack frames got cleared and there is no active stack frame @@ -118,7 +118,7 @@ export class VariablesView extends viewlet.CollapsibleViewletView { collapseAction.enabled = state === debug.State.Running || state === debug.State.Stopped; })); - this.toDispose.push(this.tree.addListener2(events.EventType.FOCUS, (e: tree.IFocusEvent) => { + this.toDispose.push(this.tree.addListener2(EventType.FOCUS, (e: IFocusEvent) => { const isMouseClick = (e.payload && e.payload.origin === 'mouse'); const isVariableType = (e.focus instanceof Variable); @@ -134,7 +134,7 @@ export class VariablesView extends viewlet.CollapsibleViewletView { this.tree.refresh(expression, false).then(() => { this.tree.setHighlight(expression); - this.tree.addOneTimeDisposableListener(events.EventType.HIGHLIGHT, (e: tree.IHighlightEvent) => { + this.tree.addOneTimeDisposableListener(EventType.HIGHLIGHT, (e: IHighlightEvent) => { if (!e.highlight) { this.debugService.getViewModel().setSelectedExpression(null); } @@ -144,19 +144,19 @@ export class VariablesView extends viewlet.CollapsibleViewletView { } public shutdown(): void { - this.settings[VariablesView.MEMENTO] = (this.state === splitview.CollapsibleState.COLLAPSED); + this.settings[VariablesView.MEMENTO] = (this.state === CollapsibleState.COLLAPSED); super.shutdown(); } } -export class WatchExpressionsView extends viewlet.CollapsibleViewletView { +export class WatchExpressionsView extends CollapsibleViewletView { private static MEMENTO = 'watchexpressionsview.memento'; private onWatchExpressionsUpdatedScheduler: RunOnceScheduler; private toReveal: debug.IExpression; constructor( - actionRunner: actions.IActionRunner, + actionRunner: IActionRunner, private settings: any, @IMessageService messageService: IMessageService, @IContextMenuService contextMenuService: IContextMenuService, @@ -177,7 +177,7 @@ export class WatchExpressionsView extends viewlet.CollapsibleViewletView { this.tree.refresh().done(() => { return this.toReveal instanceof Expression ? this.tree.reveal(this.toReveal) : TPromise.as(true); }, errors.onUnexpectedError); - }, 250); + }, 50); } public renderHeader(container: HTMLElement): void { @@ -192,19 +192,20 @@ export class WatchExpressionsView extends viewlet.CollapsibleViewletView { this.treeContainer = renderViewTree(container); const actionProvider = new viewer.WatchExpressionsActionProvider(this.instantiationService); - this.tree = new treeimpl.Tree(this.treeContainer, { + this.tree = new Tree(this.treeContainer, { dataSource: new viewer.WatchExpressionsDataSource(), renderer: this.instantiationService.createInstance(viewer.WatchExpressionsRenderer, actionProvider, this.actionRunner), accessibilityProvider: new viewer.WatchExpressionsAccessibilityProvider(), - controller: new viewer.WatchExpressionsController(this.debugService, this.contextMenuService, actionProvider) + controller: new viewer.WatchExpressionsController(this.debugService, this.contextMenuService, actionProvider), + dnd: this.instantiationService.createInstance(viewer.WatchExpressionsDragAndDrop) }, debugTreeOptions(nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'watchAriaTreeLabel' }, "Debug Watch Expressions"))); this.tree.setInput(this.debugService.getModel()); - const addWatchExpressionAction = this.instantiationService.createInstance(debugactions.AddWatchExpressionAction, debugactions.AddWatchExpressionAction.ID, debugactions.AddWatchExpressionAction.LABEL); - const collapseAction = this.instantiationService.createInstance(viewlet.CollapseAction, this.tree, true, 'explorer-action collapse-explorer'); - const removeAllWatchExpressionsAction = this.instantiationService.createInstance(debugactions.RemoveAllWatchExpressionsAction, debugactions.RemoveAllWatchExpressionsAction.ID, debugactions.RemoveAllWatchExpressionsAction.LABEL); - this.toolBar.setActions(actionbarregistry.prepareActions([addWatchExpressionAction, collapseAction, removeAllWatchExpressionsAction]))(); + const addWatchExpressionAction = this.instantiationService.createInstance(AddWatchExpressionAction, AddWatchExpressionAction.ID, AddWatchExpressionAction.LABEL); + const collapseAction = this.instantiationService.createInstance(CollapseAction, this.tree, true, 'explorer-action collapse-explorer'); + const removeAllWatchExpressionsAction = this.instantiationService.createInstance(RemoveAllWatchExpressionsAction, RemoveAllWatchExpressionsAction.ID, RemoveAllWatchExpressionsAction.LABEL); + this.toolBar.setActions(prepareActions([addWatchExpressionAction, collapseAction, removeAllWatchExpressionsAction]))(); this.toDispose.push(this.debugService.getModel().onDidChangeWatchExpressions(we => { if (!this.onWatchExpressionsUpdatedScheduler.isScheduled()) { @@ -220,7 +221,7 @@ export class WatchExpressionsView extends viewlet.CollapsibleViewletView { this.tree.refresh(expression, false).then(() => { this.tree.setHighlight(expression); - this.tree.addOneTimeDisposableListener(events.EventType.HIGHLIGHT, (e: tree.IHighlightEvent) => { + this.tree.addOneTimeDisposableListener(EventType.HIGHLIGHT, (e: IHighlightEvent) => { if (!e.highlight) { this.debugService.getViewModel().setSelectedExpression(null); } @@ -230,12 +231,12 @@ export class WatchExpressionsView extends viewlet.CollapsibleViewletView { } public shutdown(): void { - this.settings[WatchExpressionsView.MEMENTO] = (this.state === splitview.CollapsibleState.COLLAPSED); + this.settings[WatchExpressionsView.MEMENTO] = (this.state === CollapsibleState.COLLAPSED); super.shutdown(); } } -export class CallStackView extends viewlet.CollapsibleViewletView { +export class CallStackView extends CollapsibleViewletView { private static MEMENTO = 'callstackview.memento'; private pauseMessage: builder.Builder; @@ -244,7 +245,7 @@ export class CallStackView extends viewlet.CollapsibleViewletView { private onStackFrameFocusScheduler: RunOnceScheduler; constructor( - actionRunner: actions.IActionRunner, + actionRunner: IActionRunner, private settings: any, @IMessageService messageService: IMessageService, @IContextMenuService contextMenuService: IContextMenuService, @@ -314,14 +315,14 @@ export class CallStackView extends viewlet.CollapsibleViewletView { this.treeContainer = renderViewTree(container); const actionProvider = this.instantiationService.createInstance(viewer.CallStackActionProvider); - this.tree = new treeimpl.Tree(this.treeContainer, { + this.tree = new Tree(this.treeContainer, { dataSource: this.instantiationService.createInstance(viewer.CallStackDataSource), renderer: this.instantiationService.createInstance(viewer.CallStackRenderer), accessibilityProvider: this.instantiationService.createInstance(viewer.CallstackAccessibilityProvider), controller: new viewer.CallStackController(this.debugService, this.contextMenuService, actionProvider) }, debugTreeOptions(nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'callStackAriaLabel' }, "Debug Call Stack"))); - this.toDispose.push(this.tree.addListener2(events.EventType.FOCUS, (e: tree.IFocusEvent) => { + this.toDispose.push(this.tree.addListener2(EventType.FOCUS, (e: IFocusEvent) => { const isMouseClick = (e.payload && e.payload.origin === 'mouse'); const isStackFrameType = (e.focus instanceof StackFrame); @@ -349,18 +350,18 @@ export class CallStackView extends viewlet.CollapsibleViewletView { } public shutdown(): void { - this.settings[CallStackView.MEMENTO] = (this.state === splitview.CollapsibleState.COLLAPSED); + this.settings[CallStackView.MEMENTO] = (this.state === CollapsibleState.COLLAPSED); super.shutdown(); } } -export class BreakpointsView extends viewlet.AdaptiveCollapsibleViewletView { +export class BreakpointsView extends AdaptiveCollapsibleViewletView { private static MAX_VISIBLE_FILES = 9; private static MEMENTO = 'breakopintsview.memento'; constructor( - actionRunner: actions.IActionRunner, + actionRunner: IActionRunner, private settings: any, @IContextMenuService contextMenuService: IContextMenuService, @IDebugService private debugService: IDebugService, @@ -386,13 +387,13 @@ export class BreakpointsView extends viewlet.AdaptiveCollapsibleViewletView { this.treeContainer = renderViewTree(container); const actionProvider = new viewer.BreakpointsActionProvider(this.instantiationService); - this.tree = new treeimpl.Tree(this.treeContainer, { + this.tree = new Tree(this.treeContainer, { dataSource: new viewer.BreakpointsDataSource(), renderer: this.instantiationService.createInstance(viewer.BreakpointsRenderer, actionProvider, this.actionRunner), accessibilityProvider: this.instantiationService.createInstance(viewer.BreakpointsAccessibilityProvider), controller: new viewer.BreakpointsController(this.debugService, this.contextMenuService, actionProvider), sorter: { - compare(tree: tree.ITree, element: any, otherElement: any): number { + compare(tree: ITree, element: any, otherElement: any): number { const first = element; const second = otherElement; if (first instanceof ExceptionBreakpoint) { @@ -428,7 +429,7 @@ export class BreakpointsView extends viewlet.AdaptiveCollapsibleViewletView { this.tree.refresh(fbp, false).then(() => { this.tree.setHighlight(fbp); - this.tree.addOneTimeDisposableListener(events.EventType.HIGHLIGHT, (e: tree.IHighlightEvent) => { + this.tree.addOneTimeDisposableListener(EventType.HIGHLIGHT, (e: IHighlightEvent) => { if (!e.highlight) { this.debugService.getViewModel().setSelectedFunctionBreakpoint(null); } @@ -437,11 +438,11 @@ export class BreakpointsView extends viewlet.AdaptiveCollapsibleViewletView { })); } - public getActions(): actions.IAction[] { + public getActions(): IAction[] { return [ - this.instantiationService.createInstance(debugactions.AddFunctionBreakpointAction, debugactions.AddFunctionBreakpointAction.ID, debugactions.AddFunctionBreakpointAction.LABEL), - this.instantiationService.createInstance(debugactions.ToggleBreakpointsActivatedAction, debugactions.ToggleBreakpointsActivatedAction.ID, debugactions.ToggleBreakpointsActivatedAction.ACTIVATE_LABEL), - this.instantiationService.createInstance(debugactions.RemoveAllBreakpointsAction, debugactions.RemoveAllBreakpointsAction.ID, debugactions.RemoveAllBreakpointsAction.LABEL) + this.instantiationService.createInstance(AddFunctionBreakpointAction, AddFunctionBreakpointAction.ID, AddFunctionBreakpointAction.LABEL), + this.instantiationService.createInstance(ToggleBreakpointsActivatedAction, ToggleBreakpointsActivatedAction.ID, ToggleBreakpointsActivatedAction.ACTIVATE_LABEL), + this.instantiationService.createInstance(RemoveAllBreakpointsAction, RemoveAllBreakpointsAction.ID, RemoveAllBreakpointsAction.LABEL) ]; } @@ -460,7 +461,7 @@ export class BreakpointsView extends viewlet.AdaptiveCollapsibleViewletView { } public shutdown(): void { - this.settings[BreakpointsView.MEMENTO] = (this.state === splitview.CollapsibleState.COLLAPSED); + this.settings[BreakpointsView.MEMENTO] = (this.state === CollapsibleState.COLLAPSED); super.shutdown(); } } diff --git a/src/vs/workbench/parts/debug/electron-browser/repl.ts b/src/vs/workbench/parts/debug/electron-browser/repl.ts index 1e477563652..de22f76fdb4 100644 --- a/src/vs/workbench/parts/debug/electron-browser/repl.ts +++ b/src/vs/workbench/parts/debug/electron-browser/repl.ts @@ -18,10 +18,12 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { KeyCode } from 'vs/base/common/keyCodes'; import tree = require('vs/base/parts/tree/browser/tree'); import treeimpl = require('vs/base/parts/tree/browser/treeImpl'); +import { Context as SuggestContext } from 'vs/editor/contrib/suggest/common/suggest'; +import { SuggestController } from 'vs/editor/contrib/suggest/browser/suggestController'; import { IEditorOptions, IReadOnlyModel, EditorContextKeys, ICommonCodeEditor } from 'vs/editor/common/editorCommon'; import { Position } from 'vs/editor/common/core/position'; import * as modes from 'vs/editor/common/modes'; -import { editorAction, ServicesAccessor, EditorAction } from 'vs/editor/common/editorCommonExtensions'; +import { editorAction, ServicesAccessor, EditorAction, EditorCommand, CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; @@ -270,7 +272,8 @@ export class Repl extends Panel implements IPrivateReplService { scrollBeyondLastLine: false, theme: this.themeService.getColorTheme(), renderLineHighlight: false, - fixedOverflowWidgets: true + fixedOverflowWidgets: true, + acceptSuggestionOnEnter: false }; } @@ -349,6 +352,19 @@ class AcceptReplInputAction extends EditorAction { } public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void | TPromise { + SuggestController.get(editor).acceptSelectedSuggestion(); accessor.get(IPrivateReplService).acceptReplInput(); } } + +const SuggestCommand = EditorCommand.bindToContribution(SuggestController.get); +CommonEditorRegistry.registerEditorCommand(new SuggestCommand({ + id: 'repl.action.acceptSuggestion', + precondition: ContextKeyExpr.and(debug.CONTEXT_IN_DEBUG_REPL, SuggestContext.Visible), + handler: x => x.acceptSelectedSuggestion(), + kbOpts: { + weight: 50, + kbExpr: EditorContextKeys.TextFocus, + primary: KeyCode.RightArrow + } +})); diff --git a/src/vs/workbench/parts/debug/test/common/debugViewModel.test.ts b/src/vs/workbench/parts/debug/test/common/debugViewModel.test.ts index 85d9a893eac..c4da203b73f 100644 --- a/src/vs/workbench/parts/debug/test/common/debugViewModel.test.ts +++ b/src/vs/workbench/parts/debug/test/common/debugViewModel.test.ts @@ -34,7 +34,7 @@ suite('Debug - View Model', () => { test('selected expression', () => { assert.equal(model.getSelectedExpression(), null); - const expression = new Expression('my expression', false); + const expression = new Expression('my expression'); model.setSelectedExpression(expression); assert.equal(model.getSelectedExpression(), expression); diff --git a/src/vs/workbench/parts/debug/test/common/mockDebug.ts b/src/vs/workbench/parts/debug/test/common/mockDebug.ts index f3cbcb5b5f3..7b2f9fddc0d 100644 --- a/src/vs/workbench/parts/debug/test/common/mockDebug.ts +++ b/src/vs/workbench/parts/debug/test/common/mockDebug.ts @@ -47,6 +47,8 @@ export class MockDebugService implements debug.IDebugService { public addFunctionBreakpoint(): void { } + public moveWatchExpression(id: string, position: number): void { } + public renameFunctionBreakpoint(id: string, newFunctionName: string): TPromise { return TPromise.as(null); } diff --git a/src/vs/workbench/parts/debug/test/node/debugAdapter.test.ts b/src/vs/workbench/parts/debug/test/node/debugAdapter.test.ts index 07133c7514d..426a1052499 100644 --- a/src/vs/workbench/parts/debug/test/node/debugAdapter.test.ts +++ b/src/vs/workbench/parts/debug/test/node/debugAdapter.test.ts @@ -49,7 +49,7 @@ suite('Debug - Adapter', () => { }; setup(() => { - adapter = new Adapter(rawAdapter, { extensionFolderPath, id: 'adapter', name: 'myAdapter', version: '1.0.0', publisher: 'vscode', isBuiltin: false, engines: null, enableProposedApi: false }, null, null, null); + adapter = new Adapter(rawAdapter, { extensionFolderPath, id: 'adapter', name: 'myAdapter', version: '1.0.0', publisher: 'vscode', isBuiltin: false, engines: null }, null, null, null); }); teardown(() => { diff --git a/src/vs/workbench/parts/extensions/electron-browser/dependenciesViewer.ts b/src/vs/workbench/parts/extensions/browser/dependenciesViewer.ts similarity index 99% rename from src/vs/workbench/parts/extensions/electron-browser/dependenciesViewer.ts rename to src/vs/workbench/parts/extensions/browser/dependenciesViewer.ts index 1ec64f9cb96..e6f7aef8bf8 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/dependenciesViewer.ts +++ b/src/vs/workbench/parts/extensions/browser/dependenciesViewer.ts @@ -11,7 +11,7 @@ import { TPromise, Promise } from 'vs/base/common/winjs.base'; import { IDataSource, ITree, IRenderer } from 'vs/base/parts/tree/browser/tree'; import { DefaultController } from 'vs/base/parts/tree/browser/treeDefaults'; import { Action } from 'vs/base/common/actions'; -import { IExtensionDependencies, IExtensionsWorkbenchService } from '../common/extensions'; +import { IExtensionDependencies, IExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/common/extensions'; import { once } from 'vs/base/common/event'; import { domEvent } from 'vs/base/browser/event'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts b/src/vs/workbench/parts/extensions/browser/extensionEditor.ts similarity index 95% rename from src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts rename to src/vs/workbench/parts/extensions/browser/extensionEditor.ts index d8364fd01f9..18798447bb5 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts +++ b/src/vs/workbench/parts/extensions/browser/extensionEditor.ts @@ -27,15 +27,15 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IExtensionGalleryService, IExtensionManifest, IKeyBinding } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IThemeService } from 'vs/workbench/services/themes/common/themeService'; import { ExtensionsInput } from 'vs/workbench/parts/extensions/common/extensionsInput'; -import { IExtensionsWorkbenchService, IExtensionsViewlet, VIEWLET_ID, IExtension, IExtensionDependencies } from '../common/extensions'; -import { Renderer, DataSource, Controller } from './dependenciesViewer'; +import { IExtensionsWorkbenchService, IExtensionsViewlet, VIEWLET_ID, IExtension, IExtensionDependencies } from 'vs/workbench/parts/extensions/common/extensions'; +import { Renderer, DataSource, Controller } from 'vs/workbench/parts/extensions/browser/dependenciesViewer'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ITemplateData } from './extensionsList'; -import { RatingsWidget, InstallWidget } from './extensionsWidgets'; +import { ITemplateData } from 'vs/workbench/parts/extensions/browser/extensionsList'; +import { RatingsWidget, InstallWidget } from 'vs/workbench/parts/extensions/browser/extensionsWidgets'; import { EditorOptions } from 'vs/workbench/common/editor'; import product from 'vs/platform/product'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; -import { CombinedInstallAction, UpdateAction, EnableAction, DisableAction, ReloadAction, BuiltinStatusLabelAction } from './extensionsActions'; +import { CombinedInstallAction, UpdateAction, EnableAction, DisableAction, BuiltinStatusLabelAction, ReloadAction } from 'vs/workbench/parts/extensions/browser/extensionsActions'; import WebView from 'vs/workbench/parts/html/browser/webview'; import { Keybinding } from 'vs/base/common/keybinding'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -43,6 +43,7 @@ import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableEle import { IMessageService } from 'vs/platform/message/common/message'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; +import { Position } from 'vs/platform/editor/common/editor'; function renderBody(body: string): string { return ` @@ -60,6 +61,7 @@ class NavBar { private _onChange = new Emitter(); get onChange(): Event { return this._onChange.event; } + private currentId: string = null; private actions: Action[]; private actionbar: ActionBar; @@ -70,12 +72,7 @@ class NavBar { } push(id: string, label: string): void { - const run = () => { - this._onChange.fire(id); - this.actions.forEach(a => a.enabled = a.id !== action.id); - return TPromise.as(null); - }; - + const run = () => this._update(id); const action = new Action(id, label, null, true, run); this.actions.push(action); @@ -91,6 +88,17 @@ class NavBar { this.actionbar.clear(); } + update(): void { + this._update(this.currentId); + } + + _update(id: string = this.currentId): TPromise { + this.currentId = id; + this._onChange.fire(id); + this.actions.forEach(a => a.enabled = a.id !== id); + return TPromise.as(null); + } + dispose(): void { this.actionbar = dispose(this.actionbar); } @@ -273,7 +281,7 @@ export class ExtensionEditor extends BaseEditor { reloadAction.extension = extension; this.extensionActionBar.clear(); - this.extensionActionBar.push([enableAction, updateAction, reloadAction, disableAction, installAction, builtinStatusAction], { icon: true, label: true }); + this.extensionActionBar.push([reloadAction, updateAction, enableAction, disableAction, installAction, builtinStatusAction], { icon: true, label: true }); this.transientDisposables.push(enableAction, updateAction, reloadAction, disableAction, installAction, builtinStatusAction); this.navbar.clear(); @@ -288,6 +296,11 @@ export class ExtensionEditor extends BaseEditor { return super.setInput(input, options); } + changePosition(position: Position): void { + this.navbar.update(); + super.changePosition(position); + } + private onNavbarChange(extension: IExtension, id: string): void { this.contentDisposables = dispose(this.contentDisposables); this.content.innerHTML = ''; diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts b/src/vs/workbench/parts/extensions/browser/extensionTipsService.ts similarity index 97% rename from src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts rename to src/vs/workbench/parts/extensions/browser/extensionTipsService.ts index 8c5c2161f9b..ec14e2e4e0e 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts +++ b/src/vs/workbench/parts/extensions/browser/extensionTipsService.ts @@ -8,14 +8,14 @@ import { forEach } from 'vs/base/common/collections'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { match } from 'vs/base/common/glob'; import { IExtensionManagementService, IExtensionGalleryService, IExtensionTipsService, LocalExtensionType, EXTENSION_IDENTIFIER_PATTERN } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IExtensionsConfiguration, ConfigurationKey } from '../common/extensions'; +import { IExtensionsConfiguration, ConfigurationKey } from 'vs/workbench/parts/extensions/common/extensions'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IModel } from 'vs/editor/common/editorCommon'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import product from 'vs/platform/product'; import { IChoiceService } from 'vs/platform/message/common/message'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ShowRecommendedExtensionsAction, ShowWorkspaceRecommendedExtensionsAction } from './extensionsActions'; +import { ShowRecommendedExtensionsAction, ShowWorkspaceRecommendedExtensionsAction } from 'vs/workbench/parts/extensions/browser/extensionsActions'; import Severity from 'vs/base/common/severity'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { Schemas } from 'vs/base/common/network'; diff --git a/src/vs/workbench/parts/extensions/browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/browser/extensionsActions.ts new file mode 100644 index 00000000000..506f02d3007 --- /dev/null +++ b/src/vs/workbench/parts/extensions/browser/extensionsActions.ts @@ -0,0 +1,1258 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/css!./media/extensionActions'; +import { localize } from 'vs/nls'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IAction, Action } from 'vs/base/common/actions'; +import { Throttler } from 'vs/base/common/async'; +import * as DOM from 'vs/base/browser/dom'; +import severity from 'vs/base/common/severity'; +import paths = require('vs/base/common/paths'); +import Event from 'vs/base/common/event'; +import { ActionItem, IActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewlet, ConfigurationKey } from 'vs/workbench/parts/extensions/common/extensions'; +import { ExtensionsConfigurationInitialContent } from 'vs/workbench/parts/extensions/common/extensionsFileTemplate'; +import { LocalExtensionType, IExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { ToggleViewletAction } from 'vs/workbench/browser/viewlet'; +import { IViewletService } from 'vs/workbench/services/viewlet/common/viewletService'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { Query } from 'vs/workbench/parts/extensions/common/extensionQuery'; +import { IFileService } from 'vs/platform/files/common/files'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IExtensionService, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import URI from 'vs/base/common/uri'; + + +export class InstallAction extends Action { + + private static InstallLabel = localize('installAction', "Install"); + private static InstallingLabel = localize('installing', "Installing"); + + private static Class = 'extension-action install'; + private static InstallingClass = 'extension-action install installing'; + + private disposables: IDisposable[] = []; + private _extension: IExtension; + get extension(): IExtension { return this._extension; } + set extension(extension: IExtension) { this._extension = extension; this.update(); } + + constructor( + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService + ) { + super('extensions.install', InstallAction.InstallLabel, InstallAction.Class, false); + + this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); + this.update(); + } + + private update(): void { + if (!this.extension || this.extension.type === LocalExtensionType.System) { + this.enabled = false; + this.class = InstallAction.Class; + this.label = InstallAction.InstallLabel; + return; + } + + this.enabled = this.extensionsWorkbenchService.canInstall(this.extension) && this.extension.state === ExtensionState.Uninstalled; + + if (this.extension.state === ExtensionState.Installing) { + this.label = InstallAction.InstallingLabel; + this.class = InstallAction.InstallingClass; + } else { + this.label = InstallAction.InstallLabel; + this.class = InstallAction.Class; + } + } + + run(): TPromise { + return this.extensionsWorkbenchService.install(this.extension); + } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } +} + +export class UninstallAction extends Action { + + private static UninstallLabel = localize('uninstallAction', "Uninstall"); + private static UninstallingLabel = localize('Uninstalling', "Uninstalling"); + + private static UninstallClass = 'extension-action uninstall'; + private static UnInstallingClass = 'extension-action uninstall uninstalling'; + + private disposables: IDisposable[] = []; + private _extension: IExtension; + get extension(): IExtension { return this._extension; } + set extension(extension: IExtension) { this._extension = extension; this.update(); } + + constructor( + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, + @IMessageService private messageService: IMessageService, + @IInstantiationService private instantiationService: IInstantiationService + ) { + super('extensions.uninstall', UninstallAction.UninstallLabel, UninstallAction.UninstallClass, false); + + this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); + this.update(); + } + + private update(): void { + if (!this.extension) { + this.enabled = false; + return; + } + + const state = this.extension.state; + + if (state === ExtensionState.Uninstalling) { + this.label = UninstallAction.UninstallingLabel; + this.class = UninstallAction.UnInstallingClass; + this.enabled = false; + return; + } + + this.label = UninstallAction.UninstallLabel; + this.class = UninstallAction.UninstallClass; + + const installedExtensions = this.extensionsWorkbenchService.local.filter(e => e.identifier === this.extension.identifier); + + if (!installedExtensions.length) { + this.enabled = false; + return; + } + + if (installedExtensions[0].type !== LocalExtensionType.User) { + this.enabled = false; + return; + } + + this.enabled = true; + } + + run(): TPromise { + return this.extensionsWorkbenchService.uninstall(this.extension); + } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } +} + +export class CombinedInstallAction extends Action { + + private static NoExtensionClass = 'extension-action install no-extension'; + private installAction: InstallAction; + private uninstallAction: UninstallAction; + private disposables: IDisposable[] = []; + private _extension: IExtension; + get extension(): IExtension { return this._extension; } + set extension(extension: IExtension) { + this._extension = extension; + this.installAction.extension = extension; + this.uninstallAction.extension = extension; + } + + constructor( + @IInstantiationService instantiationService: IInstantiationService + ) { + super('extensions.combinedInstall', '', '', false); + + this.installAction = instantiationService.createInstance(InstallAction); + this.uninstallAction = instantiationService.createInstance(UninstallAction); + this.disposables.push(this.installAction, this.uninstallAction); + + this.installAction.onDidChange(this.update, this, this.disposables); + this.uninstallAction.onDidChange(this.update, this, this.disposables); + this.update(); + } + + private update(): void { + if (!this.extension || this.extension.type === LocalExtensionType.System) { + this.enabled = false; + this.class = CombinedInstallAction.NoExtensionClass; + } else if (this.installAction.enabled) { + this.enabled = true; + this.label = this.installAction.label; + this.class = this.installAction.class; + } else if (this.uninstallAction.enabled) { + this.enabled = true; + this.label = this.uninstallAction.label; + this.class = this.uninstallAction.class; + } else if (this.extension.state === ExtensionState.Installing) { + this.enabled = false; + this.label = this.installAction.label; + this.class = this.installAction.class; + } else if (this.extension.state === ExtensionState.Uninstalling) { + this.enabled = false; + this.label = this.uninstallAction.label; + this.class = this.uninstallAction.class; + } else { + this.enabled = false; + this.label = this.installAction.label; + this.class = this.installAction.class; + } + } + + run(): TPromise { + if (this.installAction.enabled) { + return this.installAction.run(); + } else if (this.uninstallAction.enabled) { + return this.uninstallAction.run(); + } + + return TPromise.as(null); + } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } +} + +export class UpdateAction extends Action { + + private static EnabledClass = 'extension-action update'; + private static DisabledClass = `${UpdateAction.EnabledClass} disabled`; + + private disposables: IDisposable[] = []; + private _extension: IExtension; + get extension(): IExtension { return this._extension; } + set extension(extension: IExtension) { this._extension = extension; this.update(); } + + constructor( + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService + ) { + super('extensions.update', localize('updateAction', "Update"), UpdateAction.DisabledClass, false); + + this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); + this.update(); + } + + private update(): void { + if (!this.extension) { + this.enabled = false; + this.class = UpdateAction.DisabledClass; + return; + } + + if (this.extension.type !== LocalExtensionType.User) { + this.enabled = false; + this.class = UpdateAction.DisabledClass; + return; + } + + const canInstall = this.extensionsWorkbenchService.canInstall(this.extension); + const isInstalled = this.extension.state === ExtensionState.Installed; + + this.enabled = canInstall && isInstalled && this.extension.outdated; + this.class = this.enabled ? UpdateAction.EnabledClass : UpdateAction.DisabledClass; + } + + run(): TPromise { + return this.extensionsWorkbenchService.install(this.extension); + } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } +} + +export interface IExtensionAction extends IAction { + extension: IExtension; +} + +export class DropDownMenuActionItem extends ActionItem { + + private disposables: IDisposable[] = []; + private _extension: IExtension; + + constructor(action: IAction, private menuActionGroups: IExtensionAction[][], private contextMenuService: IContextMenuService) { + super(null, action, { icon: true, label: true }); + for (const menuActions of menuActionGroups) { + this.disposables = [...this.disposables, ...menuActions]; + } + } + + get extension(): IExtension { return this._extension; } + + set extension(extension: IExtension) { + this._extension = extension; + for (const menuActions of this.menuActionGroups) { + for (const menuAction of menuActions) { + menuAction.extension = extension; + } + } + } + + public showMenu(): void { + const actions = this.getActions(); + let elementPosition = DOM.getDomNodePagePosition(this.builder.getHTMLElement()); + const anchor = { x: elementPosition.left, y: elementPosition.top + elementPosition.height + 10 }; + this.contextMenuService.showContextMenu({ + getAnchor: () => anchor, + getActions: () => TPromise.wrap(actions) + }); + } + + private getActions(): IAction[] { + let actions = []; + for (const menuActions of this.menuActionGroups) { + const filtered = menuActions.filter(a => a.enabled); + if (filtered.length > 0) { + actions = [...actions, ...filtered, new Separator()]; + } + } + return actions.length ? actions.slice(0, actions.length - 1) : actions; + } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } +} + +export class ManageExtensionActionItem extends DropDownMenuActionItem { + constructor( + action: IAction, + @IInstantiationService instantiationService: IInstantiationService, + @IContextMenuService contextMenuService: IContextMenuService) { + super(action, [ + [instantiationService.createInstance(EnableForWorkspaceAction, localize('enableForWorkspaceAction.label', "Enable (Workspace)")), + instantiationService.createInstance(EnableGloballyAction, localize('enableAlwaysAction.label', "Enable")), + instantiationService.createInstance(DisableForWorkspaceAction, localize('disableForWorkspaceAction.label', "Disable (Workspace)")), + instantiationService.createInstance(DisableGloballyAction, localize('disableAlwaysAction.label', "Disable"))], + [instantiationService.createInstance(UninstallAction)] + ], contextMenuService); + } +} + +export class ManageExtensionAction extends Action { + + static ID = 'extensions.manage'; + + private static Class = 'extension-action manage'; + private static NoExtensionClass = `${ManageExtensionAction.Class} no-extension`; + + private _actionItem: EnableActionItem; + get actionItem(): IActionItem { return this._actionItem; } + + private disposables: IDisposable[] = []; + private _extension: IExtension; + get extension(): IExtension { return this._extension; } + set extension(extension: IExtension) { this._extension = extension; this._actionItem.extension = extension; this.update(); } + + constructor( + @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, + @IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService, + @IInstantiationService private instantiationService: IInstantiationService + ) { + super(ManageExtensionAction.ID); + + this._actionItem = this.instantiationService.createInstance(ManageExtensionActionItem, this); + this.disposables.push(this._actionItem); + + this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); + this.update(); + } + + private update(): void { + this.class = ManageExtensionAction.NoExtensionClass; + this.tooltip = ''; + this.enabled = false; + if (this.extension) { + const state = this.extension.state; + this.enabled = state === ExtensionState.Installed; + this.class = this.enabled || state === ExtensionState.Uninstalling ? ManageExtensionAction.Class : ManageExtensionAction.NoExtensionClass; + this.tooltip = state === ExtensionState.Uninstalling ? localize('ManageExtensionAction.uninstallingTooltip', "Uninstalling") : ''; + } + } + + public run(): TPromise { + this._actionItem.showMenu(); + return TPromise.wrap(null); + } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } +} + +export class EnableForWorkspaceAction extends Action implements IExtensionAction { + + static ID = 'extensions.enableForWorkspace'; + static LABEL = localize('enableForWorkspaceAction', "Workspace"); + + private disposables: IDisposable[] = []; + + private _extension: IExtension; + get extension(): IExtension { return this._extension; } + set extension(extension: IExtension) { this._extension = extension; this.update(); } + + constructor(label: string, + @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, + @IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService, + @IInstantiationService private instantiationService: IInstantiationService + ) { + super(EnableForWorkspaceAction.ID, label); + + this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); + this.update(); + } + + private update(): void { + this.enabled = false; + if (this.extension) { + this.enabled = !this.extension.disabledGlobally && this.extension.disabledForWorkspace && this.extensionEnablementService.canEnable(this.extension.identifier); + } + } + + run(): TPromise { + return this.extensionsWorkbenchService.setEnablement(this.extension, true, true); + } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } +} + +export class EnableGloballyAction extends Action implements IExtensionAction { + + static ID = 'extensions.enableGlobally'; + static LABEL = localize('enableGloballyAction', "Always"); + + private disposables: IDisposable[] = []; + + private _extension: IExtension; + get extension(): IExtension { return this._extension; } + set extension(extension: IExtension) { this._extension = extension; this.update(); } + + constructor(label: string, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, + @IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService, + @IInstantiationService private instantiationService: IInstantiationService + ) { + super(EnableGloballyAction.ID, label); + + this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); + this.update(); + } + + private update(): void { + this.enabled = false; + if (this.extension) { + this.enabled = this.extension.disabledGlobally && this.extensionEnablementService.canEnable(this.extension.identifier); + } + } + + run(): TPromise { + return this.extensionsWorkbenchService.setEnablement(this.extension, true, false); + } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } +} + +export class EnableAction extends Action { + + static ID = 'extensions.enable'; + private static EnabledClass = 'extension-action enable'; + private static DisabledClass = `${EnableAction.EnabledClass} disabled`; + + private disposables: IDisposable[] = []; + + private _actionItem: EnableActionItem; + get actionItem(): IActionItem { return this._actionItem; } + + private _extension: IExtension; + get extension(): IExtension { return this._extension; } + set extension(extension: IExtension) { this._extension = extension; this._actionItem.extension = extension; this.update(); } + + + constructor( + @IInstantiationService private instantiationService: IInstantiationService, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, + @IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService + ) { + super(EnableAction.ID, localize('enableAction', "Enable"), EnableAction.DisabledClass, false); + + this._actionItem = this.instantiationService.createInstance(EnableActionItem, this); + this.disposables.push(this._actionItem); + + this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); + this.update(); + } + + private update(): void { + if (!this.extension) { + this.enabled = false; + this.class = EnableAction.DisabledClass; + return; + } + + this.enabled = this.extension.state === ExtensionState.Installed && (this.extension.disabledGlobally || this.extension.disabledForWorkspace) && this.extensionEnablementService.canEnable(this.extension.identifier); + this.class = this.enabled ? EnableAction.EnabledClass : EnableAction.DisabledClass; + } + + public run(): TPromise { + this._actionItem.showMenu(); + return TPromise.wrap(null); + } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } + +} + +export class DisableForWorkspaceAction extends Action implements IExtensionAction { + + static ID = 'extensions.disableForWorkspace'; + static LABEL = localize('disableForWorkspaceAction', "Workspace"); + + private disposables: IDisposable[] = []; + + private _extension: IExtension; + get extension(): IExtension { return this._extension; } + set extension(extension: IExtension) { this._extension = extension; this.update(); } + + constructor(label: string, + @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, + @IInstantiationService private instantiationService: IInstantiationService + ) { + super(DisableForWorkspaceAction.ID, label); + + this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); + this.update(); + } + + private update(): void { + this.enabled = false; + if (this.extension && this.workspaceContextService.getWorkspace()) { + this.enabled = this.extension.type !== LocalExtensionType.System && !this.extension.disabledGlobally && !this.extension.disabledForWorkspace; + } + } + + run(): TPromise { + return this.extensionsWorkbenchService.setEnablement(this.extension, false, true); + } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } +} + +export class DisableGloballyAction extends Action implements IExtensionAction { + + static ID = 'extensions.disableGlobally'; + static LABEL = localize('disableGloballyAction', "Always"); + + private disposables: IDisposable[] = []; + + private _extension: IExtension; + get extension(): IExtension { return this._extension; } + set extension(extension: IExtension) { this._extension = extension; this.update(); } + + constructor(label: string, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, + @IInstantiationService private instantiationService: IInstantiationService + ) { + super(DisableGloballyAction.ID, label); + + this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); + this.update(); + } + + private update(): void { + this.enabled = false; + if (this.extension) { + this.enabled = this.extension.type !== LocalExtensionType.System && !this.extension.disabledGlobally && !this.extension.disabledForWorkspace; + } + } + + run(): TPromise { + return this.extensionsWorkbenchService.setEnablement(this.extension, false, false); + } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } +} + +export class DisableAction extends Action { + + static ID = 'extensions.disable'; + + private static EnabledClass = 'extension-action disable'; + private static DisabledClass = `${DisableAction.EnabledClass} disabled`; + + private disposables: IDisposable[] = []; + private _actionItem: DisableActionItem; + get actionItem(): IActionItem { return this._actionItem; } + + private _extension: IExtension; + get extension(): IExtension { return this._extension; } + set extension(extension: IExtension) { this._extension = extension; this._actionItem.extension = extension; this.update(); } + + + constructor( + @IInstantiationService private instantiationService: IInstantiationService, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, + ) { + super(DisableAction.ID, localize('disableAction', "Disable"), DisableAction.DisabledClass, false); + this._actionItem = this.instantiationService.createInstance(DisableActionItem, this); + this.disposables.push(this._actionItem); + + this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); + this.update(); + } + + private update(): void { + if (!this.extension) { + this.enabled = false; + this.class = DisableAction.DisabledClass; + return; + } + + this.enabled = this.extension.state === ExtensionState.Installed && this.extension.type !== LocalExtensionType.System && !this.extension.disabledGlobally && !this.extension.disabledForWorkspace; + this.class = this.enabled ? DisableAction.EnabledClass : DisableAction.DisabledClass; + } + + public run(): TPromise { + this._actionItem.showMenu(); + return TPromise.wrap(null); + } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } +} + +export class EnableActionItem extends DropDownMenuActionItem { + constructor( + action: IAction, + @IInstantiationService instantiationService: IInstantiationService, + @IContextMenuService contextMenuService: IContextMenuService) { + super(action, [[instantiationService.createInstance(EnableForWorkspaceAction, EnableForWorkspaceAction.LABEL), instantiationService.createInstance(EnableGloballyAction, EnableGloballyAction.LABEL)]], contextMenuService); + } +} + +export class DisableActionItem extends DropDownMenuActionItem { + constructor( + action: IAction, + @IInstantiationService instantiationService: IInstantiationService, + @IContextMenuService contextMenuService: IContextMenuService) { + super(action, [[instantiationService.createInstance(DisableForWorkspaceAction, DisableForWorkspaceAction.LABEL), instantiationService.createInstance(DisableGloballyAction, DisableGloballyAction.LABEL)]], contextMenuService); + } +} + + + +export class UpdateAllAction extends Action { + + static ID = 'workbench.extensions.action.updateAllExtensions'; + static LABEL = localize('updateAll', "Update All Extensions"); + + private disposables: IDisposable[] = []; + + constructor( + id = UpdateAllAction.ID, + label = UpdateAllAction.LABEL, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService + ) { + super(id, label, '', false); + + this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); + this.update(); + } + + private get outdated(): IExtension[] { + return this.extensionsWorkbenchService.local.filter(e => e.outdated && e.state !== ExtensionState.Installing); + } + + private update(): void { + this.enabled = this.outdated.length > 0; + } + + run(): TPromise { + return TPromise.join(this.outdated.map(e => this.extensionsWorkbenchService.install(e))); + } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } +} + +export class ReloadAction extends Action { + + private static EnabledClass = 'extension-action reload'; + private static DisabledClass = `${ReloadAction.EnabledClass} disabled`; + + private disposables: IDisposable[] = []; + private _extension: IExtension; + get extension(): IExtension { return this._extension; } + set extension(extension: IExtension) { this._extension = extension; this.update(); } + + private reloadMessaage: string = ''; + private throttler: Throttler; + + constructor( + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, + @IMessageService private messageService: IMessageService, + @IInstantiationService private instantiationService: IInstantiationService, + @IExtensionService private extensionService: IExtensionService, + @ICommandService private commandService: ICommandService + ) { + super('extensions.reload', localize('reloadAction', "Reload"), ReloadAction.DisabledClass, false); + this.throttler = new Throttler(); + + this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); + this.update(); + } + + private update(): void { + this.throttler.queue(() => { + this.enabled = false; + this.tooltip = ''; + this.reloadMessaage = ''; + if (!this.extension) { + return TPromise.wrap(null); + } + const state = this.extension.state; + if (state === ExtensionState.Installing || state === ExtensionState.Uninstalling) { + return TPromise.wrap(null); + } + return this.extensionService.getExtensions() + .then(runningExtensions => this.computeReloadState(runningExtensions)); + }).done(() => { + this.class = this.enabled ? ReloadAction.EnabledClass : ReloadAction.DisabledClass; + }); + } + + private computeReloadState(runningExtensions: IExtensionDescription[]): void { + const isInstalled = this.extensionsWorkbenchService.local.some(e => e.identifier === this.extension.identifier); + const isUninstalled = this.extension.state === ExtensionState.Uninstalled; + const isDisabled = this.extension.disabledForWorkspace || this.extension.disabledGlobally; + + const filteredExtensions = runningExtensions.filter(e => e.id === this.extension.identifier); + const isExtensionRunning = filteredExtensions.length > 0; + const isDifferentVersionRunning = filteredExtensions.length > 0 && this.extension.version !== filteredExtensions[0].version; + + if (isInstalled) { + if (isDifferentVersionRunning && !isDisabled) { + // Requires reload to run the updated extension + this.enabled = true; + this.tooltip = localize('postUpdateTooltip', "Reload to update"); + this.reloadMessaage = localize('postUpdateMessage', "Reload this window to activate the updated extension '{0}'?", this.extension.displayName); + return; + } + + if (!isExtensionRunning && !isDisabled) { + // Requires reload to enable the extension + this.enabled = true; + this.tooltip = localize('postEnableTooltip', "Reload to activate"); + this.reloadMessaage = localize('postEnableMessage', "Reload this window to activate the extension '{0}'?", this.extension.displayName); + return; + } + + if (isExtensionRunning && isDisabled) { + // Requires reload to disable the extension + this.enabled = true; + this.tooltip = localize('postDisableTooltip', "Reload to deactivate"); + this.reloadMessaage = localize('postDisableMessage', "Reload this window to deactivate the extension '{0}'?", this.extension.displayName); + return; + } + return; + } + + if (isUninstalled && isExtensionRunning) { + // Requires reload to deactivate the extension + this.enabled = true; + this.tooltip = localize('postUninstallTooltip', "Reload to deactivate"); + this.reloadMessaage = localize('postUninstallMessage', "Reload this window to deactivate the uninstalled extension '{0}'?", this.extension.displayName); + return; + } + } + + run(): TPromise { + if (this.messageService.confirm({ message: this.reloadMessaage })) { + // TODO: @sandy: Temporary hack. Adopt to new IWindowService from @bpasero and @jaoa + this.commandService.executeCommand('workbench.action.reloadWindow'); + } + return TPromise.wrap(null); + } +} + +export class OpenExtensionsViewletAction extends ToggleViewletAction { + + static ID = VIEWLET_ID; + static LABEL = localize('toggleExtensionsViewlet', "Show Extensions"); + + constructor( + id: string, + label: string, + @IViewletService viewletService: IViewletService, + @IWorkbenchEditorService editorService: IWorkbenchEditorService + ) { + super(id, label, VIEWLET_ID, viewletService, editorService); + } +} + +export class InstallExtensionsAction extends OpenExtensionsViewletAction { + static ID = 'workbench.extensions.action.installExtensions'; + static LABEL = localize('installExtensions', "Install Extensions"); +} + +export class ShowInstalledExtensionsAction extends Action { + + static ID = 'workbench.extensions.action.showInstalledExtensions'; + static LABEL = localize('showInstalledExtensions', "Show Installed Extensions"); + + constructor( + id: string, + label: string, + @IViewletService private viewletService: IViewletService, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService + ) { + super(id, label, 'clear-extensions', true); + } + + run(): TPromise { + return this.viewletService.openViewlet(VIEWLET_ID, true) + .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => { + viewlet.search(''); + viewlet.focus(); + }); + } +} + +export class ShowDisabledExtensionsAction extends Action { + + static ID = 'workbench.extensions.action.showDisabledExtensions'; + static LABEL = localize('showDisabledExtensions', "Show Disabled Extensions"); + + constructor( + id: string, + label: string, + @IViewletService private viewletService: IViewletService, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService + ) { + super(id, label, 'null', true); + } + + run(): TPromise { + return this.viewletService.openViewlet(VIEWLET_ID, true) + .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => { + viewlet.search('@disabled '); + viewlet.focus(); + }); + } +} + +export class ClearExtensionsInputAction extends ShowInstalledExtensionsAction { + + static ID = 'workbench.extensions.action.clearExtensionsInput'; + static LABEL = localize('clearExtensionsInput', "Clear Extensions Input"); + + private disposables: IDisposable[] = []; + + constructor( + id: string, + label: string, + onSearchChange: Event, + @IViewletService viewletService: IViewletService, + @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService + ) { + super(id, label, viewletService, extensionsWorkbenchService); + this.enabled = false; + onSearchChange(this.onSearchChange, this, this.disposables); + } + + private onSearchChange(value: string): void { + this.enabled = !!value; + } + + dispose(): void { + this.disposables = dispose(this.disposables); + } +} + +export class ShowOutdatedExtensionsAction extends Action { + + static ID = 'workbench.extensions.action.listOutdatedExtensions'; + static LABEL = localize('showOutdatedExtensions', "Show Outdated Extensions"); + + constructor( + id: string, + label: string, + @IViewletService private viewletService: IViewletService + ) { + super(id, label, null, true); + } + + run(): TPromise { + return this.viewletService.openViewlet(VIEWLET_ID, true) + .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => { + viewlet.search('@outdated '); + viewlet.focus(); + }); + } + + protected isEnabled(): boolean { + return true; + } +} + +export class ShowPopularExtensionsAction extends Action { + + static ID = 'workbench.extensions.action.showPopularExtensions'; + static LABEL = localize('showPopularExtensions', "Show Popular Extensions"); + + constructor( + id: string, + label: string, + @IViewletService private viewletService: IViewletService + ) { + super(id, label, null, true); + } + + run(): TPromise { + return this.viewletService.openViewlet(VIEWLET_ID, true) + .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => { + viewlet.search('@sort:installs '); + viewlet.focus(); + }); + } + + protected isEnabled(): boolean { + return true; + } +} + +export class ShowRecommendedExtensionsAction extends Action { + + static ID = 'workbench.extensions.action.showRecommendedExtensions'; + static LABEL = localize('showRecommendedExtensions', "Show Recommended Extensions"); + + constructor( + id: string, + label: string, + @IViewletService private viewletService: IViewletService + ) { + super(id, label, null, true); + } + + run(): TPromise { + return this.viewletService.openViewlet(VIEWLET_ID, true) + .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => { + viewlet.search('@recommended '); + viewlet.focus(); + }); + } + + protected isEnabled(): boolean { + return true; + } +} + +export class ShowWorkspaceRecommendedExtensionsAction extends Action { + + static ID = 'workbench.extensions.action.showWorkspaceRecommendedExtensions'; + static LABEL = localize('showWorkspaceRecommendedExtensions', "Show Workspace Recommended Extensions"); + + constructor( + id: string, + label: string, + @IWorkspaceContextService contextService: IWorkspaceContextService, + @IViewletService private viewletService: IViewletService + ) { + super(id, label, null, !!contextService.getWorkspace()); + } + + run(): TPromise { + return this.viewletService.openViewlet(VIEWLET_ID, true) + .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => { + viewlet.search('@recommended:workspace '); + viewlet.focus(); + }); + } + + protected isEnabled(): boolean { + return true; + } +} + +export class ChangeSortAction extends Action { + + private query: Query; + private disposables: IDisposable[] = []; + + constructor( + id: string, + label: string, + onSearchChange: Event, + private sortBy: string, + private sortOrder: string, + @IViewletService private viewletService: IViewletService + ) { + super(id, label, null, true); + + if (sortBy === undefined && sortOrder === undefined) { + throw new Error('bad arguments'); + } + + this.query = Query.parse(''); + this.enabled = false; + onSearchChange(this.onSearchChange, this, this.disposables); + } + + private onSearchChange(value: string): void { + const query = Query.parse(value); + this.query = new Query(query.value, this.sortBy || query.sortBy, this.sortOrder || query.sortOrder); + this.enabled = value && this.query.isValid() && !this.query.equals(query); + } + + run(): TPromise { + return this.viewletService.openViewlet(VIEWLET_ID, true) + .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => { + viewlet.search(this.query.toString()); + viewlet.focus(); + }); + } + + protected isEnabled(): boolean { + return true; + } +} + +export class ConfigureWorkspaceRecommendedExtensionsAction extends Action { + + static ID = 'workbench.extensions.action.configureWorkspaceRecommendedExtensions'; + static LABEL = localize('configureWorkspaceRecommendedExtensions', "Configure Recommended Extensions (Workspace)"); + + constructor( + id: string, + label: string, + @IFileService private fileService: IFileService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IExtensionsWorkbenchService private extensionsService: IExtensionsWorkbenchService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IMessageService private messageService: IMessageService + ) { + super(id, label, null, !!contextService.getWorkspace()); + } + + public run(event: any): TPromise { + return this.openExtensionsFile(); + } + + private openExtensionsFile(): TPromise { + if (!this.contextService.getWorkspace()) { + this.messageService.show(severity.Info, localize('ConfigureWorkspaceRecommendations.noWorkspace', 'Recommendations are only available on a workspace folder.')); + return TPromise.as(undefined); + } + + return this.getOrCreateExtensionsFile().then(value => { + return this.editorService.openEditor({ + resource: value.extensionsFileResource, + options: { + forceOpen: true, + pinned: value.created + }, + }); + }, (error) => TPromise.wrapError(new Error(localize('OpenExtensionsFile.failed', "Unable to create 'extensions.json' file inside the '.vscode' folder ({0}).", error)))); + } + + private getOrCreateExtensionsFile(): TPromise<{ created: boolean, extensionsFileResource: URI }> { + const extensionsFileResource = URI.file(paths.join(this.contextService.getWorkspace().resource.fsPath, '.vscode', `${ConfigurationKey}.json`)); + + return this.fileService.resolveContent(extensionsFileResource).then(content => { + return { created: false, extensionsFileResource }; + }, err => { + return this.fileService.updateContent(extensionsFileResource, ExtensionsConfigurationInitialContent).then(() => { + return { created: true, extensionsFileResource }; + }); + }); + } +} + +export class BuiltinStatusLabelAction extends Action { + + private static Class = 'extension-action built-in-status'; + + private _extension: IExtension; + get extension(): IExtension { return this._extension; } + set extension(extension: IExtension) { this._extension = extension; this.update(); } + + constructor() { + super('extensions.install', localize('builtin', "Built-in"), '', false); + } + + private update(): void { + if (this.extension && this.extension.type === LocalExtensionType.System) { + this.class = `${BuiltinStatusLabelAction.Class} system`; + } else { + this.class = `${BuiltinStatusLabelAction.Class} user`; + } + } + + run(): TPromise { + return TPromise.as(null); + } +} + +export class DisableAllAction extends Action { + + static ID = 'workbench.extensions.action.disableAll'; + static LABEL = localize('disableAll', "Disable All Installed Extensions"); + + private disposables: IDisposable[] = []; + + constructor( + id: string = DisableAllAction.ID, label: string = DisableAllAction.LABEL, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, + @IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService + ) { + super(id, label); + this.update(); + this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); + } + + private update(): void { + this.enabled = this.extensionsWorkbenchService.local.some(e => e.type === LocalExtensionType.User && !e.disabledForWorkspace && !e.disabledGlobally); + } + + run(): TPromise { + return TPromise.join(this.extensionsWorkbenchService.local.map(e => this.extensionsWorkbenchService.setEnablement(e, false))); + } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } +} + +export class DisableAllWorkpsaceAction extends Action { + + static ID = 'workbench.extensions.action.disableAllWorkspace'; + static LABEL = localize('disableAllWorkspace', "Disable All Installed Extensions for this Workspace"); + + private disposables: IDisposable[] = []; + + constructor( + id: string = DisableAllWorkpsaceAction.ID, label: string = DisableAllWorkpsaceAction.LABEL, + @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, + @IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService + ) { + super(id, label); + this.update(); + this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); + } + + private update(): void { + this.enabled = !!this.workspaceContextService.getWorkspace() && this.extensionsWorkbenchService.local.some(e => e.type === LocalExtensionType.User && !e.disabledForWorkspace && !e.disabledGlobally); + } + + run(): TPromise { + return TPromise.join(this.extensionsWorkbenchService.local.map(e => this.extensionsWorkbenchService.setEnablement(e, false, true))); + } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } +} + +export class EnableAllAction extends Action { + + static ID = 'workbench.extensions.action.enableAll'; + static LABEL = localize('enableAll', "Enable All Installed Extensions"); + + private disposables: IDisposable[] = []; + + constructor( + id: string = EnableAllAction.ID, label: string = EnableAllAction.LABEL, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, + @IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService + ) { + super(id, label); + this.update(); + this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); + } + + private update(): void { + this.enabled = this.extensionsWorkbenchService.local.some(e => this.extensionEnablementService.canEnable(e.identifier) && e.disabledGlobally); + } + + run(): TPromise { + return TPromise.join(this.extensionsWorkbenchService.local.map(e => this.extensionsWorkbenchService.setEnablement(e, true))); + } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } +} + +export class EnableAllWorkpsaceAction extends Action { + + static ID = 'workbench.extensions.action.enableAllWorkspace'; + static LABEL = localize('enableAllWorkspace', "Enable All Installed Extensions for this Workspace"); + + private disposables: IDisposable[] = []; + + constructor( + id: string = EnableAllWorkpsaceAction.ID, label: string = EnableAllWorkpsaceAction.LABEL, + @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, + @IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService + ) { + super(id, label); + this.update(); + this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); + } + + private update(): void { + this.enabled = !!this.workspaceContextService.getWorkspace() && this.extensionsWorkbenchService.local.some(e => this.extensionEnablementService.canEnable(e.identifier) && !e.disabledGlobally && e.disabledForWorkspace); + } + + run(): TPromise { + return TPromise.join(this.extensionsWorkbenchService.local.map(e => this.extensionsWorkbenchService.setEnablement(e, true, true))); + } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts b/src/vs/workbench/parts/extensions/browser/extensionsList.ts similarity index 85% rename from src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts rename to src/vs/workbench/parts/extensions/browser/extensionsList.ts index b1b8d37e986..c5dd01e143a 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts +++ b/src/vs/workbench/parts/extensions/browser/extensionsList.ts @@ -15,11 +15,12 @@ import { IDelegate } from 'vs/base/browser/ui/list/list'; import { IPagedRenderer } from 'vs/base/browser/ui/list/listPaging'; import { once } from 'vs/base/common/event'; import { domEvent } from 'vs/base/browser/event'; -import { IExtension, ExtensionState } from '../common/extensions'; -import { InstallAction, UpdateAction, BuiltinStatusLabelAction, ReloadAction, ManageExtensionAction } from './extensionsActions'; -import { Label, RatingsWidget, InstallWidget } from './extensionsWidgets'; +import { IExtension, IExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/common/extensions'; +import { InstallAction, UpdateAction, BuiltinStatusLabelAction, ManageExtensionAction, ReloadAction } from 'vs/workbench/parts/extensions/browser/extensionsActions'; +import { Label, RatingsWidget, InstallWidget } from 'vs/workbench/parts/extensions/browser/extensionsWidgets'; import { EventType } from 'vs/base/common/events'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IExtensionService } from 'vs/platform/extensions/common/extensions'; export interface ITemplateData { element: HTMLElement; @@ -46,7 +47,9 @@ export class Renderer implements IPagedRenderer { constructor( @IInstantiationService private instantiationService: IInstantiationService, @IContextMenuService private contextMenuService: IContextMenuService, - @IMessageService private messageService: IMessageService + @IMessageService private messageService: IMessageService, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, + @IExtensionService private extensionService: IExtensionService ) { } get templateId() { return 'extension'; } @@ -122,7 +125,11 @@ export class Renderer implements IPagedRenderer { data.extensionDisposables = dispose(data.extensionDisposables); - toggleClass(data.element, 'disabled', ExtensionState.Disabled === extension.state); + this.extensionService.getExtensions().then(enabledExtensions => { + const isExtensionRunning = enabledExtensions.some(e => e.id === extension.identifier); + const isInstalled = this.extensionsWorkbenchService.local.some(e => e.identifier === extension.identifier); + toggleClass(data.element, 'disabled', isInstalled && !isExtensionRunning); + }); const onError = once(domEvent(data.icon, 'error')); onError(() => data.icon.src = extension.iconUrlFallback, null, data.extensionDisposables); diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsQuickOpen.ts b/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.ts similarity index 95% rename from src/vs/workbench/parts/extensions/electron-browser/extensionsQuickOpen.ts rename to src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.ts index d900f5f9ae2..152bff0beac 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsQuickOpen.ts +++ b/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.ts @@ -3,12 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import nls = require('vs/nls'); +import * as nls from 'vs/nls'; import { TPromise } from 'vs/base/common/winjs.base'; import { IAutoFocus, Mode, IModel } from 'vs/base/parts/quickopen/common/quickOpen'; import { QuickOpenEntry, QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel'; import { QuickOpenHandler } from 'vs/workbench/browser/quickopen'; -import { IExtensionsViewlet, VIEWLET_ID } from '../common/extensions'; +import { IExtensionsViewlet, VIEWLET_ID } from 'vs/workbench/parts/extensions/common/extensions'; import { IViewletService } from 'vs/workbench/services/viewlet/common/viewletService'; class SimpleEntry extends QuickOpenEntry { diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsWidgets.ts b/src/vs/workbench/parts/extensions/browser/extensionsWidgets.ts similarity index 68% rename from src/vs/workbench/parts/extensions/electron-browser/extensionsWidgets.ts rename to src/vs/workbench/parts/extensions/browser/extensionsWidgets.ts index c38459112a2..a77817b64f9 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsWidgets.ts +++ b/src/vs/workbench/parts/extensions/browser/extensionsWidgets.ts @@ -5,12 +5,10 @@ 'use strict'; -import { localize } from 'vs/nls'; import 'vs/css!./media/extensionsWidgets'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { IExtension, IExtensionsWorkbenchService, ExtensionState } from '../common/extensions'; -import { append, $, addClass, toggleClass } from 'vs/base/browser/dom'; -import { IExtensionsRuntimeService } from 'vs/platform/extensions/common/extensions'; +import { IExtension, IExtensionsWorkbenchService } from '../common/extensions'; +import { append, $, addClass } from 'vs/base/browser/dom'; export interface IOptions { extension?: IExtension; @@ -42,48 +40,6 @@ export class Label implements IDisposable { } } -export class StatusWidget implements IDisposable { - - private listener: IDisposable; - get extension(): IExtension { return this._extension; } - set extension(extension: IExtension) { this._extension = extension; this.render(); } - - constructor( - private container: HTMLElement, - private _extension: IExtension, - @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService, - @IExtensionsRuntimeService private extensionsRuntimeService: IExtensionsRuntimeService - ) { - this.render(); - this.listener = extensionsWorkbenchService.onChange(this.render, this); - } - - private render(): void { - this.container.innerHTML = ''; - if (!this.extension) { - return; - } - - this.extensionsRuntimeService.getExtensions().done(extensions => { - const status = append(this.container, $('span.extension-status')); - const state = this.extension.state; - const enabled = state === ExtensionState.Enabled || extensions.some(e => e.id === this.extension.identifier); - const disabled = state === ExtensionState.Disabled; - const installed = state === ExtensionState.Installed; - toggleClass(status, 'disabled', disabled || installed); - toggleClass(status, 'active', enabled); - - status.title = disabled ? this.extensionsRuntimeService.isDisabledAlways(this.extension.identifier) ? localize('disabled', "Disabled") : localize('disabledWorkspace', "Disabled (Workspace)") - : installed ? localize('installed', "Installed") - : enabled ? localize('enabled', "Enabled") : ''; - }); - } - - dispose(): void { - this.listener = dispose(this.listener); - } -} - export class InstallWidget implements IDisposable { private disposables: IDisposable[] = []; diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/EmptyStar.svg b/src/vs/workbench/parts/extensions/browser/media/EmptyStar.svg similarity index 100% rename from src/vs/workbench/parts/extensions/electron-browser/media/EmptyStar.svg rename to src/vs/workbench/parts/extensions/browser/media/EmptyStar.svg diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/FullStarLight.svg b/src/vs/workbench/parts/extensions/browser/media/FullStarLight.svg similarity index 100% rename from src/vs/workbench/parts/extensions/electron-browser/media/FullStarLight.svg rename to src/vs/workbench/parts/extensions/browser/media/FullStarLight.svg diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/HalfStarLight.svg b/src/vs/workbench/parts/extensions/browser/media/HalfStarLight.svg similarity index 100% rename from src/vs/workbench/parts/extensions/electron-browser/media/HalfStarLight.svg rename to src/vs/workbench/parts/extensions/browser/media/HalfStarLight.svg diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/clear-inverse.svg b/src/vs/workbench/parts/extensions/browser/media/clear-inverse.svg similarity index 100% rename from src/vs/workbench/parts/extensions/electron-browser/media/clear-inverse.svg rename to src/vs/workbench/parts/extensions/browser/media/clear-inverse.svg diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/clear.svg b/src/vs/workbench/parts/extensions/browser/media/clear.svg similarity index 100% rename from src/vs/workbench/parts/extensions/electron-browser/media/clear.svg rename to src/vs/workbench/parts/extensions/browser/media/clear.svg diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/defaultIcon.png b/src/vs/workbench/parts/extensions/browser/media/defaultIcon.png similarity index 100% rename from src/vs/workbench/parts/extensions/electron-browser/media/defaultIcon.png rename to src/vs/workbench/parts/extensions/browser/media/defaultIcon.png diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/extensionActions.css b/src/vs/workbench/parts/extensions/browser/media/extensionActions.css similarity index 100% rename from src/vs/workbench/parts/extensions/electron-browser/media/extensionActions.css rename to src/vs/workbench/parts/extensions/browser/media/extensionActions.css diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/extensionEditor.css b/src/vs/workbench/parts/extensions/browser/media/extensionEditor.css similarity index 100% rename from src/vs/workbench/parts/extensions/electron-browser/media/extensionEditor.css rename to src/vs/workbench/parts/extensions/browser/media/extensionEditor.css diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/extensionsWidgets.css b/src/vs/workbench/parts/extensions/browser/media/extensionsWidgets.css similarity index 100% rename from src/vs/workbench/parts/extensions/electron-browser/media/extensionsWidgets.css rename to src/vs/workbench/parts/extensions/browser/media/extensionsWidgets.css diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/manage-inverse.svg b/src/vs/workbench/parts/extensions/browser/media/manage-inverse.svg similarity index 100% rename from src/vs/workbench/parts/extensions/electron-browser/media/manage-inverse.svg rename to src/vs/workbench/parts/extensions/browser/media/manage-inverse.svg diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/manage.svg b/src/vs/workbench/parts/extensions/browser/media/manage.svg similarity index 100% rename from src/vs/workbench/parts/extensions/electron-browser/media/manage.svg rename to src/vs/workbench/parts/extensions/browser/media/manage.svg diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/markdown.css b/src/vs/workbench/parts/extensions/browser/media/markdown.css similarity index 100% rename from src/vs/workbench/parts/extensions/electron-browser/media/markdown.css rename to src/vs/workbench/parts/extensions/browser/media/markdown.css diff --git a/src/vs/workbench/parts/extensions/common/extensions.ts b/src/vs/workbench/parts/extensions/common/extensions.ts index cb012aa1ea1..d756390fcfc 100644 --- a/src/vs/workbench/parts/extensions/common/extensions.ts +++ b/src/vs/workbench/parts/extensions/common/extensions.ts @@ -19,8 +19,6 @@ export interface IExtensionsViewlet extends IViewlet { export enum ExtensionState { Installing, Installed, - Enabled, - Disabled, Uninstalling, Uninstalled } @@ -43,7 +41,8 @@ export interface IExtension { rating: number; ratingCount: number; outdated: boolean; - reload: boolean; + disabledGlobally: boolean; + disabledForWorkspace: boolean; dependencies: string[]; telemetryData: any; getManifest(): TPromise; diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsFileTemplate.ts b/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.ts similarity index 100% rename from src/vs/workbench/parts/extensions/electron-browser/extensionsFileTemplate.ts rename to src/vs/workbench/parts/extensions/common/extensionsFileTemplate.ts diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts b/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts index 4d1ba542fe8..89cde057706 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts @@ -12,7 +12,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IExtensionGalleryService, IExtensionTipsService, ExtensionsLabel, ExtensionsChannelId } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService'; import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actionRegistry'; -import { ExtensionTipsService } from 'vs/workbench/parts/extensions/electron-browser/extensionTipsService'; +import { ExtensionTipsService } from 'vs/workbench/parts/extensions/browser/extensionTipsService'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { IOutputChannelRegistry, Extensions as OutputExtensions } from 'vs/workbench/parts/output/common/output'; import { EditorDescriptor } from 'vs/workbench/browser/parts/editor/baseEditor'; @@ -22,17 +22,18 @@ import { VIEWLET_ID, IExtensionsWorkbenchService } from '../common/extensions'; import { ExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/node/extensionsWorkbenchService'; import { OpenExtensionsViewletAction, InstallExtensionsAction, ShowOutdatedExtensionsAction, ShowRecommendedExtensionsAction, ShowWorkspaceRecommendedExtensionsAction, ShowPopularExtensionsAction, - ShowInstalledExtensionsAction, ShowDisabledExtensionsAction, UpdateAllAction, OpenExtensionsFolderAction, ConfigureWorkspaceRecommendedExtensionsAction, InstallVSIXAction, + ShowInstalledExtensionsAction, ShowDisabledExtensionsAction, UpdateAllAction, ConfigureWorkspaceRecommendedExtensionsAction, EnableAllAction, EnableAllWorkpsaceAction, DisableAllAction, DisableAllWorkpsaceAction -} from './extensionsActions'; +} from 'vs/workbench/parts/extensions/browser/extensionsActions'; +import { OpenExtensionsFolderAction, InstallVSIXAction } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions'; import { ExtensionsInput } from 'vs/workbench/parts/extensions/common/extensionsInput'; import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor } from 'vs/workbench/browser/viewlet'; -import { ExtensionEditor } from './extensionEditor'; -import { StatusUpdater } from './extensionsViewlet'; +import { ExtensionEditor } from 'vs/workbench/parts/extensions/browser/extensionEditor'; +import { StatusUpdater } from 'vs/workbench/parts/extensions/electron-browser/extensionsViewlet'; import { IQuickOpenRegistry, Extensions, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; import jsonContributionRegistry = require('vs/platform/jsonschemas/common/jsonContributionRegistry'); -import { ExtensionsConfigurationSchema, ExtensionsConfigurationSchemaId } from 'vs/workbench/parts/extensions/electron-browser/extensionsFileTemplate'; +import { ExtensionsConfigurationSchema, ExtensionsConfigurationSchemaId } from 'vs/workbench/parts/extensions/common/extensionsFileTemplate'; // Singletons registerSingleton(IExtensionGalleryService, ExtensionGalleryService); @@ -48,7 +49,7 @@ Registry.as(OutputExtensions.OutputChannels) // Quickopen Registry.as(Extensions.Quickopen).registerQuickOpenHandler( new QuickOpenHandlerDescriptor( - 'vs/workbench/parts/extensions/electron-browser/extensionsQuickOpen', + 'vs/workbench/parts/extensions/browser/extensionsQuickOpen', 'ExtensionsHandler', 'ext ', localize('extensionsCommands', "Manage Extensions"), @@ -58,7 +59,7 @@ Registry.as(Extensions.Quickopen).registerQuickOpenHandler( Registry.as(Extensions.Quickopen).registerQuickOpenHandler( new QuickOpenHandlerDescriptor( - 'vs/workbench/parts/extensions/electron-browser/extensionsQuickOpen', + 'vs/workbench/parts/extensions/browser/extensionsQuickOpen', 'GalleryExtensionsHandler', 'ext install ', localize('galleryExtensionsCommands', "Install Gallery Extensions"), @@ -70,7 +71,7 @@ Registry.as(Extensions.Quickopen).registerQuickOpenHandler( const editorDescriptor = new EditorDescriptor( ExtensionEditor.ID, localize('extension', "Extension"), - 'vs/workbench/parts/extensions/electron-browser/extensionEditor', + 'vs/workbench/parts/extensions/browser/extensionEditor', 'ExtensionEditor' ); diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts index 097079f1f76..d763f2c2a37 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts @@ -3,1013 +3,21 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./media/extensionActions'; import { localize } from 'vs/nls'; import { TPromise } from 'vs/base/common/winjs.base'; -import { IAction, Action } from 'vs/base/common/actions'; -import * as DOM from 'vs/base/browser/dom'; +import { Action } from 'vs/base/common/actions'; import severity from 'vs/base/common/severity'; import paths = require('vs/base/common/paths'); -import Event from 'vs/base/common/event'; -import { ActionItem, IActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { ReloadWindowAction } from 'vs/workbench/electron-browser/actions'; -import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewlet, ConfigurationKey } from '../common/extensions'; -import { LocalExtensionType } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/common/extensions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IMessageService } from 'vs/platform/message/common/message'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { ToggleViewletAction } from 'vs/workbench/browser/viewlet'; -import { IViewletService } from 'vs/workbench/services/viewlet/common/viewletService'; -import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { Query } from '../common/extensionQuery'; import { remote } from 'electron'; -import { ExtensionsConfigurationInitialContent } from 'vs/workbench/parts/extensions/electron-browser/extensionsFileTemplate'; -import { IFileService } from 'vs/platform/files/common/files'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import URI from 'vs/base/common/uri'; -import { IExtensionsRuntimeService } from 'vs/platform/extensions/common/extensions'; -import { IWindowService } from 'vs/workbench/services/window/electron-browser/windowService'; +import { IWindowIPCService } from 'vs/workbench/services/window/electron-browser/windowService'; const dialog = remote.dialog; -export class InstallAction extends Action { - - private static InstallLabel = localize('installAction', "Install"); - private static InstallingLabel = localize('installing', "Installing"); - - private static Class = 'extension-action install'; - private static InstallingClass = 'extension-action install installing'; - - private disposables: IDisposable[] = []; - private _extension: IExtension; - get extension(): IExtension { return this._extension; } - set extension(extension: IExtension) { this._extension = extension; this.update(); } - - constructor( - @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService - ) { - super('extensions.install', InstallAction.InstallLabel, InstallAction.Class, false); - - this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); - this.update(); - } - - private update(): void { - if (!this.extension || this.extension.type === LocalExtensionType.System) { - this.enabled = false; - this.class = InstallAction.Class; - this.label = InstallAction.InstallLabel; - return; - } - - this.enabled = this.extensionsWorkbenchService.canInstall(this.extension) && this.extension.state === ExtensionState.Uninstalled; - - if (this.extension.state === ExtensionState.Installing) { - this.label = InstallAction.InstallingLabel; - this.class = InstallAction.InstallingClass; - } else { - this.label = InstallAction.InstallLabel; - this.class = InstallAction.Class; - } - } - - run(): TPromise { - return this.extensionsWorkbenchService.install(this.extension); - } - - dispose(): void { - super.dispose(); - this.disposables = dispose(this.disposables); - } -} - -export class UninstallAction extends Action { - - private static UninstallLabel = localize('uninstallAction', "Uninstall"); - private static UninstallingLabel = localize('Uninstalling', "Uninstalling"); - - private static UninstallClass = 'extension-action uninstall'; - private static UnInstallingClass = 'extension-action uninstall uninstalling'; - - private disposables: IDisposable[] = []; - private _extension: IExtension; - get extension(): IExtension { return this._extension; } - set extension(extension: IExtension) { this._extension = extension; this.update(); } - - constructor( - @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, - @IMessageService private messageService: IMessageService, - @IInstantiationService private instantiationService: IInstantiationService - ) { - super('extensions.uninstall', UninstallAction.UninstallLabel, UninstallAction.UninstallClass, false); - - this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); - this.update(); - } - - private update(): void { - if (!this.extension) { - this.enabled = false; - return; - } - - const state = this.extension.state; - - if (state === ExtensionState.Uninstalling) { - this.label = UninstallAction.UninstallingLabel; - this.class = UninstallAction.UnInstallingClass; - } else { - this.label = UninstallAction.UninstallLabel; - this.class = UninstallAction.UninstallClass; - } - - if (ExtensionState.Installed === state) { - this.enabled = true; - return; - } - - if (this.extension.type !== LocalExtensionType.User) { - this.enabled = false; - return; - } - - this.enabled = state === ExtensionState.Enabled || state === ExtensionState.Disabled; - } - - run(): TPromise { - return this.extensionsWorkbenchService.uninstall(this.extension); - } - - dispose(): void { - super.dispose(); - this.disposables = dispose(this.disposables); - } -} - -export class CombinedInstallAction extends Action { - - private static NoExtensionClass = 'extension-action install no-extension'; - private installAction: InstallAction; - private uninstallAction: UninstallAction; - private disposables: IDisposable[] = []; - private _extension: IExtension; - get extension(): IExtension { return this._extension; } - set extension(extension: IExtension) { - this._extension = extension; - this.installAction.extension = extension; - this.uninstallAction.extension = extension; - } - - constructor( - @IInstantiationService instantiationService: IInstantiationService - ) { - super('extensions.combinedInstall', '', '', false); - - this.installAction = instantiationService.createInstance(InstallAction); - this.uninstallAction = instantiationService.createInstance(UninstallAction); - this.disposables.push(this.installAction, this.uninstallAction); - - this.installAction.onDidChange(this.update, this, this.disposables); - this.uninstallAction.onDidChange(this.update, this, this.disposables); - this.update(); - } - - private update(): void { - if (!this.extension || this.extension.type === LocalExtensionType.System) { - this.enabled = false; - this.class = CombinedInstallAction.NoExtensionClass; - } else if (this.installAction.enabled) { - this.enabled = true; - this.label = this.installAction.label; - this.class = this.installAction.class; - } else if (this.uninstallAction.enabled) { - this.enabled = true; - this.label = this.uninstallAction.label; - this.class = this.uninstallAction.class; - } else if (this.extension.state === ExtensionState.Installing) { - this.enabled = false; - this.label = this.installAction.label; - this.class = this.installAction.class; - } else if (this.extension.state === ExtensionState.Uninstalling) { - this.enabled = false; - this.label = this.uninstallAction.label; - this.class = this.uninstallAction.class; - } else { - this.enabled = false; - this.label = this.installAction.label; - this.class = this.installAction.class; - } - } - - run(): TPromise { - if (this.installAction.enabled) { - return this.installAction.run(); - } else if (this.uninstallAction.enabled) { - return this.uninstallAction.run(); - } - - return TPromise.as(null); - } - - dispose(): void { - super.dispose(); - this.disposables = dispose(this.disposables); - } -} - -export class UpdateAction extends Action { - - private static EnabledClass = 'extension-action update'; - private static DisabledClass = `${UpdateAction.EnabledClass} disabled`; - - private disposables: IDisposable[] = []; - private _extension: IExtension; - get extension(): IExtension { return this._extension; } - set extension(extension: IExtension) { this._extension = extension; this.update(); } - - constructor( - @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService - ) { - super('extensions.update', localize('updateAction', "Update"), UpdateAction.DisabledClass, false); - - this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); - this.update(); - } - - private update(): void { - if (!this.extension) { - this.enabled = false; - this.class = UpdateAction.DisabledClass; - return; - } - - if (this.extension.type !== LocalExtensionType.User) { - this.enabled = false; - this.class = UpdateAction.DisabledClass; - return; - } - - const canInstall = this.extensionsWorkbenchService.canInstall(this.extension); - const isInstalled = this.extension.state === ExtensionState.Enabled - || this.extension.state === ExtensionState.Disabled - || this.extension.state === ExtensionState.Installed; - - this.enabled = canInstall && isInstalled && this.extension.outdated; - this.class = this.enabled ? UpdateAction.EnabledClass : UpdateAction.DisabledClass; - } - - run(): TPromise { - return this.extensionsWorkbenchService.install(this.extension); - } - - dispose(): void { - super.dispose(); - this.disposables = dispose(this.disposables); - } -} - -export interface IExtensionAction extends IAction { - extension: IExtension; -} - -export class DropDownMenuActionItem extends ActionItem { - - private disposables: IDisposable[] = []; - private _extension: IExtension; - - constructor(action: IAction, private menuActionGroups: IExtensionAction[][], private contextMenuService: IContextMenuService) { - super(null, action, { icon: true, label: true }); - for (const menuActions of menuActionGroups) { - this.disposables = [...this.disposables, ...menuActions]; - } - } - - get extension(): IExtension { return this._extension; } - - set extension(extension: IExtension) { - this._extension = extension; - for (const menuActions of this.menuActionGroups) { - for (const menuAction of menuActions) { - menuAction.extension = extension; - } - } - } - - public showMenu(): void { - const actions = this.getActions(); - let elementPosition = DOM.getDomNodePagePosition(this.builder.getHTMLElement()); - const anchor = { x: elementPosition.left, y: elementPosition.top + elementPosition.height + 10 }; - this.contextMenuService.showContextMenu({ - getAnchor: () => anchor, - getActions: () => TPromise.wrap(actions) - }); - } - - private getActions(): IAction[] { - let actions = []; - for (const menuActions of this.menuActionGroups) { - const filtered = menuActions.filter(a => a.enabled); - if (filtered.length > 0) { - actions = [...actions, ...filtered, new Separator()]; - } - } - return actions.length ? actions.slice(0, actions.length - 1) : actions; - } - - dispose(): void { - super.dispose(); - this.disposables = dispose(this.disposables); - } -} - -export class ManageExtensionActionItem extends DropDownMenuActionItem { - constructor( - action: IAction, - @IInstantiationService instantiationService: IInstantiationService, - @IContextMenuService contextMenuService: IContextMenuService) { - super(action, [ - [instantiationService.createInstance(EnableForWorkspaceAction, localize('enableForWorkspaceAction.label', "Enable (Workspace)")), - instantiationService.createInstance(EnableGloballyAction, localize('enableAlwaysAction.label', "Enable")), - instantiationService.createInstance(DisableForWorkspaceAction, localize('disableForWorkspaceAction.label', "Disable (Workspace)")), - instantiationService.createInstance(DisableGloballyAction, localize('disableAlwaysAction.label', "Disable"))], - [instantiationService.createInstance(UninstallAction)] - ], contextMenuService); - } -} - -export class ManageExtensionAction extends Action { - - static ID = 'extensions.manage'; - - private static Class = 'extension-action manage'; - private static NoExtensionClass = `${ManageExtensionAction.Class} no-extension`; - - private _actionItem: EnableActionItem; - get actionItem(): IActionItem { return this._actionItem; } - - private disposables: IDisposable[] = []; - private _extension: IExtension; - get extension(): IExtension { return this._extension; } - set extension(extension: IExtension) { this._extension = extension; this._actionItem.extension = extension; this.update(); } - - constructor( - @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService, - @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, - @IExtensionsRuntimeService private extensionRuntimeService: IExtensionsRuntimeService, - @IInstantiationService private instantiationService: IInstantiationService - ) { - super(ManageExtensionAction.ID); - - this._actionItem = this.instantiationService.createInstance(ManageExtensionActionItem, this); - this.disposables.push(this._actionItem); - - this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); - this.update(); - } - - private update(): void { - this.class = ManageExtensionAction.NoExtensionClass; - this.enabled = false; - if (this.extension) { - const state = this.extension.state; - this.enabled = ExtensionState.Uninstalled !== state && ExtensionState.Installing !== state && ExtensionState.Uninstalling !== state; - this.class = ExtensionState.Uninstalled === state || ExtensionState.Installing === state ? ManageExtensionAction.NoExtensionClass : ManageExtensionAction.Class; - } - } - - public run(): TPromise { - this._actionItem.showMenu(); - return TPromise.wrap(null); - } - - dispose(): void { - super.dispose(); - this.disposables = dispose(this.disposables); - } -} - -export class EnableForWorkspaceAction extends Action implements IExtensionAction { - - static ID = 'extensions.enableForWorkspace'; - static LABEL = localize('enableForWorkspaceAction', "Workspace"); - - private disposables: IDisposable[] = []; - - private _extension: IExtension; - get extension(): IExtension { return this._extension; } - set extension(extension: IExtension) { this._extension = extension; this.update(); } - - constructor(label: string, - @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService, - @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, - @IExtensionsRuntimeService private extensionsRuntimeService: IExtensionsRuntimeService, - @IInstantiationService private instantiationService: IInstantiationService - ) { - super(EnableForWorkspaceAction.ID, label); - - this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); - this.update(); - } - - private update(): void { - this.enabled = false; - if (this.extension && this.workspaceContextService.getWorkspace()) { - this.enabled = this.extension.type !== LocalExtensionType.System && ExtensionState.Disabled === this.extension.state - && !this.extensionsRuntimeService.isDisabledAlways(this.extension.identifier) && this.extensionsRuntimeService.canEnable(this.extension.identifier); - } - } - - run(): TPromise { - return this.extensionsWorkbenchService.setEnablement(this.extension, true, true); - } - - dispose(): void { - super.dispose(); - this.disposables = dispose(this.disposables); - } -} - -export class EnableGloballyAction extends Action implements IExtensionAction { - - static ID = 'extensions.enableGlobally'; - static LABEL = localize('enableGloballyAction', "Always"); - - private disposables: IDisposable[] = []; - - private _extension: IExtension; - get extension(): IExtension { return this._extension; } - set extension(extension: IExtension) { this._extension = extension; this.update(); } - - constructor(label: string, - @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, - @IExtensionsRuntimeService private extensionsRuntimeService: IExtensionsRuntimeService, - @IInstantiationService private instantiationService: IInstantiationService - ) { - super(EnableGloballyAction.ID, label); - - this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); - this.update(); - } - - private update(): void { - this.enabled = false; - if (this.extension) { - this.enabled = this.extension.type !== LocalExtensionType.System && this.extensionsRuntimeService.isDisabledAlways(this.extension.identifier) && this.extensionsRuntimeService.canEnable(this.extension.identifier); - } - } - - run(): TPromise { - return this.extensionsWorkbenchService.setEnablement(this.extension, true, false); - } - - dispose(): void { - super.dispose(); - this.disposables = dispose(this.disposables); - } -} - -export class EnableAction extends Action { - - static ID = 'extensions.enable'; - private static EnabledClass = 'extension-action enable'; - private static DisabledClass = `${EnableAction.EnabledClass} disabled`; - - private disposables: IDisposable[] = []; - - private _actionItem: EnableActionItem; - get actionItem(): IActionItem { return this._actionItem; } - - private _extension: IExtension; - get extension(): IExtension { return this._extension; } - set extension(extension: IExtension) { this._extension = extension; this._actionItem.extension = extension; this.update(); } - - - constructor( - @IInstantiationService private instantiationService: IInstantiationService, - @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, - @IExtensionsRuntimeService private extensionsRuntimeService: IExtensionsRuntimeService - ) { - super(EnableAction.ID, localize('enableAction', "Enable"), EnableAction.DisabledClass, false); - - this._actionItem = this.instantiationService.createInstance(EnableActionItem, this); - this.disposables.push(this._actionItem); - - this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); - this.update(); - } - - private update(): void { - if (!this.extension) { - this.enabled = false; - this.class = EnableAction.DisabledClass; - return; - } - - this.enabled = this.extension.type !== LocalExtensionType.System && !this.extension.reload && ExtensionState.Disabled === this.extension.state && this.extensionsRuntimeService.canEnable(this.extension.identifier); - this.class = this.enabled ? EnableAction.EnabledClass : EnableAction.DisabledClass; - } - - public run(): TPromise { - this._actionItem.showMenu(); - return TPromise.wrap(null); - } - - dispose(): void { - super.dispose(); - this.disposables = dispose(this.disposables); - } - -} - -export class DisableForWorkspaceAction extends Action implements IExtensionAction { - - static ID = 'extensions.disableForWorkspace'; - static LABEL = localize('disableForWorkspaceAction', "Workspace"); - - private disposables: IDisposable[] = []; - - private _extension: IExtension; - get extension(): IExtension { return this._extension; } - set extension(extension: IExtension) { this._extension = extension; this.update(); } - - constructor(label: string, - @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService, - @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, - @IInstantiationService private instantiationService: IInstantiationService - ) { - super(DisableForWorkspaceAction.ID, label); - - this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); - this.update(); - } - - private update(): void { - this.enabled = false; - if (this.extension && this.workspaceContextService.getWorkspace()) { - this.enabled = this.extension.type !== LocalExtensionType.System && ExtensionState.Enabled === this.extension.state; - } - } - - run(): TPromise { - return this.extensionsWorkbenchService.setEnablement(this.extension, false, true); - } - - dispose(): void { - super.dispose(); - this.disposables = dispose(this.disposables); - } -} - -export class DisableGloballyAction extends Action implements IExtensionAction { - - static ID = 'extensions.disableGlobally'; - static LABEL = localize('disableGloballyAction', "Always"); - - private disposables: IDisposable[] = []; - - private _extension: IExtension; - get extension(): IExtension { return this._extension; } - set extension(extension: IExtension) { this._extension = extension; this.update(); } - - constructor(label: string, - @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, - @IInstantiationService private instantiationService: IInstantiationService - ) { - super(DisableGloballyAction.ID, label); - - this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); - this.update(); - } - - private update(): void { - this.enabled = false; - if (this.extension) { - this.enabled = this.extension.type !== LocalExtensionType.System && ExtensionState.Enabled === this.extension.state; - } - } - - run(): TPromise { - return this.extensionsWorkbenchService.setEnablement(this.extension, false, false); - } - - dispose(): void { - super.dispose(); - this.disposables = dispose(this.disposables); - } -} - -export class DisableAction extends Action { - - static ID = 'extensions.disable'; - - private static EnabledClass = 'extension-action disable'; - private static DisabledClass = `${DisableAction.EnabledClass} disabled`; - - private disposables: IDisposable[] = []; - private _actionItem: DisableActionItem; - get actionItem(): IActionItem { return this._actionItem; } - - private _extension: IExtension; - get extension(): IExtension { return this._extension; } - set extension(extension: IExtension) { this._extension = extension; this._actionItem.extension = extension; this.update(); } - - - constructor( - @IInstantiationService private instantiationService: IInstantiationService, - @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, - ) { - super(DisableAction.ID, localize('disableAction', "Disable"), DisableAction.DisabledClass, false); - this._actionItem = this.instantiationService.createInstance(DisableActionItem, this); - this.disposables.push(this._actionItem); - - this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); - this.update(); - } - - private update(): void { - if (!this.extension) { - this.enabled = false; - this.class = DisableAction.DisabledClass; - return; - } - - this.enabled = this.extension.type !== LocalExtensionType.System && !this.extension.reload && this.extension.state === ExtensionState.Enabled; - this.class = this.enabled ? DisableAction.EnabledClass : DisableAction.DisabledClass; - } - - public run(): TPromise { - this._actionItem.showMenu(); - return TPromise.wrap(null); - } - - dispose(): void { - super.dispose(); - this.disposables = dispose(this.disposables); - } -} - -export class EnableActionItem extends DropDownMenuActionItem { - constructor( - action: IAction, - @IInstantiationService instantiationService: IInstantiationService, - @IContextMenuService contextMenuService: IContextMenuService) { - super(action, [[instantiationService.createInstance(EnableForWorkspaceAction, EnableForWorkspaceAction.LABEL), instantiationService.createInstance(EnableGloballyAction, EnableGloballyAction.LABEL)]], contextMenuService); - } -} - -export class DisableActionItem extends DropDownMenuActionItem { - constructor( - action: IAction, - @IInstantiationService instantiationService: IInstantiationService, - @IContextMenuService contextMenuService: IContextMenuService) { - super(action, [[instantiationService.createInstance(DisableForWorkspaceAction, DisableForWorkspaceAction.LABEL), instantiationService.createInstance(DisableGloballyAction, DisableGloballyAction.LABEL)]], contextMenuService); - } -} - -export class ReloadAction extends Action { - - private static EnabledClass = 'extension-action reload'; - private static DisabledClass = `${ReloadAction.EnabledClass} disabled`; - - private disposables: IDisposable[] = []; - private _extension: IExtension; - get extension(): IExtension { return this._extension; } - set extension(extension: IExtension) { this._extension = extension; this.update(); } - - constructor( - @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, - @IMessageService private messageService: IMessageService, - @IInstantiationService private instantiationService: IInstantiationService, - @IExtensionsRuntimeService private extensionsRuntimeService: IExtensionsRuntimeService - ) { - super('extensions.reload', localize('reloadAction', "Reload"), ReloadAction.DisabledClass, false); - - this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); - this.update(); - } - - private update(): void { - if (!this.extension) { - this.enabled = false; - this.class = ReloadAction.DisabledClass; - return; - } - - const state = this.extension.state; - this.enabled = state !== ExtensionState.Installing && state !== ExtensionState.Uninstalling && (this.extension.reload || /* Following is needed due to extension is stale */this.extension.state === ExtensionState.Installed); - this.class = this.enabled ? ReloadAction.EnabledClass : ReloadAction.DisabledClass; - this.tooltip = this.getMessage(true); - } - - run(): TPromise { - if (window.confirm(this.getMessage(false))) { - this.instantiationService.createInstance(ReloadWindowAction, ReloadWindowAction.ID, localize('reloadNow', "Reload Now")).run(); - } - return TPromise.wrap(null); - } - - private getMessage(tooltip: boolean): string { - const state = this.extension.state; - if (state === ExtensionState.Installed) { - return tooltip ? localize('postInstallTooltip', "Reload to activate") : localize('postInstallMessage', "Reload this window to activate '{0}' extension?", this.extension.displayName); - } - if (state === ExtensionState.Disabled) { - return tooltip ? localize('postEnableTooltip', "Reload to enable") : localize('postEnableMessage', "Reload this window to enable '{0}' extension?", this.extension.displayName); - } - if (state === ExtensionState.Enabled) { - return tooltip ? localize('postDisableTooltip', "Reload to disable") : localize('postDisableMessage', "Reload this window to disable '{0}' extension?", this.extension.displayName); - } - return tooltip ? localize('postUninstallTooltip', "Reload to deactivate") : localize('postUninstallMessage', "Reload this window to deactivate '{0}' extension?", this.extension.displayName); - } -} - -export class UpdateAllAction extends Action { - - static ID = 'workbench.extensions.action.updateAllExtensions'; - static LABEL = localize('updateAll', "Update All Extensions"); - - private disposables: IDisposable[] = []; - - constructor( - id = UpdateAllAction.ID, - label = UpdateAllAction.LABEL, - @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService - ) { - super(id, label, '', false); - - this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); - this.update(); - } - - private get outdated(): IExtension[] { - return this.extensionsWorkbenchService.local.filter(e => e.outdated && e.state !== ExtensionState.Installing); - } - - private update(): void { - this.enabled = this.outdated.length > 0; - } - - run(): TPromise { - return TPromise.join(this.outdated.map(e => this.extensionsWorkbenchService.install(e))); - } - - dispose(): void { - super.dispose(); - this.disposables = dispose(this.disposables); - } -} - -export class OpenExtensionsViewletAction extends ToggleViewletAction { - - static ID = VIEWLET_ID; - static LABEL = localize('toggleExtensionsViewlet', "Show Extensions"); - - constructor( - id: string, - label: string, - @IViewletService viewletService: IViewletService, - @IWorkbenchEditorService editorService: IWorkbenchEditorService - ) { - super(id, label, VIEWLET_ID, viewletService, editorService); - } -} - -export class InstallExtensionsAction extends OpenExtensionsViewletAction { - static ID = 'workbench.extensions.action.installExtensions'; - static LABEL = localize('installExtensions', "Install Extensions"); -} - -export class ShowInstalledExtensionsAction extends Action { - - static ID = 'workbench.extensions.action.showInstalledExtensions'; - static LABEL = localize('showInstalledExtensions', "Show Installed Extensions"); - - constructor( - id: string, - label: string, - @IViewletService private viewletService: IViewletService, - @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService - ) { - super(id, label, 'clear-extensions', true); - } - - run(): TPromise { - return this.viewletService.openViewlet(VIEWLET_ID, true) - .then(viewlet => viewlet as IExtensionsViewlet) - .then(viewlet => { - viewlet.search(''); - viewlet.focus(); - }); - } -} - -export class ShowDisabledExtensionsAction extends Action { - - static ID = 'workbench.extensions.action.showDisabledExtensions'; - static LABEL = localize('showDisabledExtensions', "Show Disabled Extensions"); - - constructor( - id: string, - label: string, - @IViewletService private viewletService: IViewletService, - @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService - ) { - super(id, label, 'null', true); - } - - run(): TPromise { - return this.viewletService.openViewlet(VIEWLET_ID, true) - .then(viewlet => viewlet as IExtensionsViewlet) - .then(viewlet => { - viewlet.search('@disabled '); - viewlet.focus(); - }); - } -} - -export class ClearExtensionsInputAction extends ShowInstalledExtensionsAction { - - static ID = 'workbench.extensions.action.clearExtensionsInput'; - static LABEL = localize('clearExtensionsInput', "Clear Extensions Input"); - - private disposables: IDisposable[] = []; - - constructor( - id: string, - label: string, - onSearchChange: Event, - @IViewletService viewletService: IViewletService, - @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService - ) { - super(id, label, viewletService, extensionsWorkbenchService); - this.enabled = false; - onSearchChange(this.onSearchChange, this, this.disposables); - } - - private onSearchChange(value: string): void { - this.enabled = !!value; - } - - dispose(): void { - this.disposables = dispose(this.disposables); - } -} - -export class ShowOutdatedExtensionsAction extends Action { - - static ID = 'workbench.extensions.action.listOutdatedExtensions'; - static LABEL = localize('showOutdatedExtensions', "Show Outdated Extensions"); - - constructor( - id: string, - label: string, - @IViewletService private viewletService: IViewletService - ) { - super(id, label, null, true); - } - - run(): TPromise { - return this.viewletService.openViewlet(VIEWLET_ID, true) - .then(viewlet => viewlet as IExtensionsViewlet) - .then(viewlet => { - viewlet.search('@outdated '); - viewlet.focus(); - }); - } - - protected isEnabled(): boolean { - return true; - } -} - -export class ShowPopularExtensionsAction extends Action { - - static ID = 'workbench.extensions.action.showPopularExtensions'; - static LABEL = localize('showPopularExtensions', "Show Popular Extensions"); - - constructor( - id: string, - label: string, - @IViewletService private viewletService: IViewletService - ) { - super(id, label, null, true); - } - - run(): TPromise { - return this.viewletService.openViewlet(VIEWLET_ID, true) - .then(viewlet => viewlet as IExtensionsViewlet) - .then(viewlet => { - viewlet.search('@sort:installs '); - viewlet.focus(); - }); - } - - protected isEnabled(): boolean { - return true; - } -} - -export class ShowRecommendedExtensionsAction extends Action { - - static ID = 'workbench.extensions.action.showRecommendedExtensions'; - static LABEL = localize('showRecommendedExtensions', "Show Recommended Extensions"); - - constructor( - id: string, - label: string, - @IViewletService private viewletService: IViewletService - ) { - super(id, label, null, true); - } - - run(): TPromise { - return this.viewletService.openViewlet(VIEWLET_ID, true) - .then(viewlet => viewlet as IExtensionsViewlet) - .then(viewlet => { - viewlet.search('@recommended '); - viewlet.focus(); - }); - } - - protected isEnabled(): boolean { - return true; - } -} - -export class ShowWorkspaceRecommendedExtensionsAction extends Action { - - static ID = 'workbench.extensions.action.showWorkspaceRecommendedExtensions'; - static LABEL = localize('showWorkspaceRecommendedExtensions', "Show Workspace Recommended Extensions"); - - constructor( - id: string, - label: string, - @IWorkspaceContextService contextService: IWorkspaceContextService, - @IViewletService private viewletService: IViewletService - ) { - super(id, label, null, !!contextService.getWorkspace()); - } - - run(): TPromise { - return this.viewletService.openViewlet(VIEWLET_ID, true) - .then(viewlet => viewlet as IExtensionsViewlet) - .then(viewlet => { - viewlet.search('@recommended:workspace '); - viewlet.focus(); - }); - } - - protected isEnabled(): boolean { - return true; - } -} - -export class ChangeSortAction extends Action { - - private query: Query; - private disposables: IDisposable[] = []; - - constructor( - id: string, - label: string, - onSearchChange: Event, - private sortBy: string, - private sortOrder: string, - @IViewletService private viewletService: IViewletService - ) { - super(id, label, null, true); - - if (sortBy === undefined && sortOrder === undefined) { - throw new Error('bad arguments'); - } - - this.query = Query.parse(''); - this.enabled = false; - onSearchChange(this.onSearchChange, this, this.disposables); - } - - private onSearchChange(value: string): void { - const query = Query.parse(value); - this.query = new Query(query.value, this.sortBy || query.sortBy, this.sortOrder || query.sortOrder); - this.enabled = value && this.query.isValid() && !this.query.equals(query); - } - - run(): TPromise { - return this.viewletService.openViewlet(VIEWLET_ID, true) - .then(viewlet => viewlet as IExtensionsViewlet) - .then(viewlet => { - viewlet.search(this.query.toString()); - viewlet.focus(); - }); - } - - protected isEnabled(): boolean { - return true; - } -} - export class OpenExtensionsFolderAction extends Action { static ID = 'workbench.extensions.action.openExtensionsFolder'; @@ -1018,7 +26,7 @@ export class OpenExtensionsFolderAction extends Action { constructor( id: string, label: string, - @IWindowService private windowService: IWindowService, + @IWindowIPCService private windowService: IWindowIPCService, @IEnvironmentService private environmentService: IEnvironmentService ) { super(id, label, null, true); @@ -1036,57 +44,6 @@ export class OpenExtensionsFolderAction extends Action { } } -export class ConfigureWorkspaceRecommendedExtensionsAction extends Action { - - static ID = 'workbench.extensions.action.configureWorkspaceRecommendedExtensions'; - static LABEL = localize('configureWorkspaceRecommendedExtensions', "Configure Recommended Extensions (Workspace)"); - - constructor( - id: string, - label: string, - @IFileService private fileService: IFileService, - @IWorkspaceContextService private contextService: IWorkspaceContextService, - @IExtensionsWorkbenchService private extensionsService: IExtensionsWorkbenchService, - @IWorkbenchEditorService private editorService: IWorkbenchEditorService, - @IMessageService private messageService: IMessageService - ) { - super(id, label, null, !!contextService.getWorkspace()); - } - - public run(event: any): TPromise { - return this.openExtensionsFile(); - } - - private openExtensionsFile(): TPromise { - if (!this.contextService.getWorkspace()) { - this.messageService.show(severity.Info, localize('ConfigureWorkspaceRecommendations.noWorkspace', 'Recommendations are only available on a workspace folder.')); - return TPromise.as(undefined); - } - - return this.getOrCreateExtensionsFile().then(value => { - return this.editorService.openEditor({ - resource: value.extensionsFileResource, - options: { - forceOpen: true, - pinned: value.created - }, - }); - }, (error) => TPromise.wrapError(new Error(localize('OpenExtensionsFile.failed', "Unable to create 'extensions.json' file inside the '.vscode' folder ({0}).", error)))); - } - - private getOrCreateExtensionsFile(): TPromise<{ created: boolean, extensionsFileResource: URI }> { - const extensionsFileResource = URI.file(paths.join(this.contextService.getWorkspace().resource.fsPath, '.vscode', `${ConfigurationKey}.json`)); - - return this.fileService.resolveContent(extensionsFileResource).then(content => { - return { created: false, extensionsFileResource }; - }, err => { - return this.fileService.updateContent(extensionsFileResource, ExtensionsConfigurationInitialContent).then(() => { - return { created: true, extensionsFileResource }; - }); - }); - } -} - export class InstallVSIXAction extends Action { static ID = 'workbench.extensions.action.installVSIX'; @@ -1122,155 +79,4 @@ export class InstallVSIXAction extends Action { ); }); } -} - -export class BuiltinStatusLabelAction extends Action { - - private static Class = 'extension-action built-in-status'; - - private _extension: IExtension; - get extension(): IExtension { return this._extension; } - set extension(extension: IExtension) { this._extension = extension; this.update(); } - - constructor() { - super('extensions.install', localize('builtin', "Built-in"), '', false); - } - - private update(): void { - if (this.extension && this.extension.type === LocalExtensionType.System) { - this.class = `${BuiltinStatusLabelAction.Class} system`; - } else { - this.class = `${BuiltinStatusLabelAction.Class} user`; - } - } - - run(): TPromise { - return TPromise.as(null); - } -} - -export class DisableAllAction extends Action { - - static ID = 'workbench.extensions.action.disableAll'; - static LABEL = localize('disableAll', "Disable All Installed Extensions"); - - private disposables: IDisposable[] = []; - - constructor( - id: string = DisableAllAction.ID, label: string = DisableAllAction.LABEL, - @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, - @IExtensionsRuntimeService private extensionRuntimeService: IExtensionsRuntimeService - ) { - super(id, label); - this.update(); - this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); - } - - private update(): void { - this.enabled = this.extensionsWorkbenchService.local.some(e => e.type === LocalExtensionType.User && !this.extensionRuntimeService.isDisabled(e.identifier)); - } - - run(): TPromise { - return TPromise.join(this.extensionsWorkbenchService.local.map(e => this.extensionsWorkbenchService.setEnablement(e, false))); - } - - dispose(): void { - super.dispose(); - this.disposables = dispose(this.disposables); - } -} - -export class DisableAllWorkpsaceAction extends Action { - - static ID = 'workbench.extensions.action.disableAllWorkspace'; - static LABEL = localize('disableAllWorkspace', "Disable All Installed Extensions for this Workspace"); - - private disposables: IDisposable[] = []; - - constructor( - id: string = DisableAllWorkpsaceAction.ID, label: string = DisableAllWorkpsaceAction.LABEL, - @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService, - @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, - @IExtensionsRuntimeService private extensionRuntimeService: IExtensionsRuntimeService - ) { - super(id, label); - this.update(); - this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); - } - - private update(): void { - this.enabled = !!this.workspaceContextService.getWorkspace() && this.extensionsWorkbenchService.local.some(e => e.type === LocalExtensionType.User && !this.extensionRuntimeService.isDisabled(e.identifier)); - } - - run(): TPromise { - return TPromise.join(this.extensionsWorkbenchService.local.map(e => this.extensionsWorkbenchService.setEnablement(e, false, true))); - } - - dispose(): void { - super.dispose(); - this.disposables = dispose(this.disposables); - } -} - -export class EnableAllAction extends Action { - - static ID = 'workbench.extensions.action.enableAll'; - static LABEL = localize('enableAll', "Enable All Installed Extensions"); - - private disposables: IDisposable[] = []; - - constructor( - id: string = EnableAllAction.ID, label: string = EnableAllAction.LABEL, - @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, - @IExtensionsRuntimeService private extensionRuntimeService: IExtensionsRuntimeService - ) { - super(id, label); - this.update(); - this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); - } - - private update(): void { - this.enabled = this.extensionsWorkbenchService.local.some(e => this.extensionRuntimeService.canEnable(e.identifier)); - } - - run(): TPromise { - return TPromise.join(this.extensionsWorkbenchService.local.map(e => this.extensionsWorkbenchService.setEnablement(e, true))); - } - - dispose(): void { - super.dispose(); - this.disposables = dispose(this.disposables); - } -} - -export class EnableAllWorkpsaceAction extends Action { - - static ID = 'workbench.extensions.action.enableAllWorkspace'; - static LABEL = localize('enableAllWorkspace', "Enable All Installed Extensions for this Workspace"); - - private disposables: IDisposable[] = []; - - constructor( - id: string = EnableAllWorkpsaceAction.ID, label: string = EnableAllWorkpsaceAction.LABEL, - @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService, - @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, - @IExtensionsRuntimeService private extensionRuntimeService: IExtensionsRuntimeService - ) { - super(id, label); - this.update(); - this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); - } - - private update(): void { - this.enabled = !!this.workspaceContextService.getWorkspace() && this.extensionsWorkbenchService.local.some(e => this.extensionRuntimeService.canEnable(e.identifier) && !this.extensionRuntimeService.isDisabledAlways(e.identifier)); - } - - run(): TPromise { - return TPromise.join(this.extensionsWorkbenchService.local.map(e => this.extensionsWorkbenchService.setEnablement(e, true, true))); - } - - dispose(): void { - super.dispose(); - this.disposables = dispose(this.disposables); - } } \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts index eb4bdc1f49a..fb94987213e 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts @@ -28,12 +28,13 @@ import { PagedModel, IPagedModel } from 'vs/base/common/paging'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { PagedList } from 'vs/base/browser/ui/list/listPaging'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { Delegate, Renderer } from './extensionsList'; +import { Delegate, Renderer } from 'vs/workbench/parts/extensions/browser/extensionsList'; import { IExtensionsWorkbenchService, IExtension, IExtensionsViewlet, VIEWLET_ID, ExtensionState } from '../common/extensions'; import { ShowRecommendedExtensionsAction, ShowWorkspaceRecommendedExtensionsAction, ShowPopularExtensionsAction, ShowInstalledExtensionsAction, ShowDisabledExtensionsAction, - ShowOutdatedExtensionsAction, ClearExtensionsInputAction, ChangeSortAction, UpdateAllAction, InstallVSIXAction -} from './extensionsActions'; + ShowOutdatedExtensionsAction, ClearExtensionsInputAction, ChangeSortAction, UpdateAllAction +} from 'vs/workbench/parts/extensions/browser/extensionsActions'; +import { InstallVSIXAction } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions'; import { IExtensionManagementService, IExtensionGalleryService, IExtensionTipsService, SortBy, SortOrder, IQueryOptions, LocalExtensionType } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionsInput } from 'vs/workbench/parts/extensions/common/extensionsInput'; import { Query } from '../common/extensionQuery'; @@ -44,6 +45,7 @@ import { IEditorGroupService } from 'vs/workbench/services/group/common/groupSer import { IMessageService, CloseAction } from 'vs/platform/message/common/message'; import Severity from 'vs/base/common/severity'; import { IActivityService, ProgressBadge, NumberBadge } from 'vs/workbench/services/activity/common/activityService'; +import { IExtensionService } from 'vs/platform/extensions/common/extensions'; interface SearchInputEvent extends Event { target: HTMLInputElement; @@ -66,7 +68,7 @@ export class ExtensionsViewlet extends Viewlet implements IExtensionsViewlet { constructor( @ITelemetryService telemetryService: ITelemetryService, @IExtensionGalleryService private galleryService: IExtensionGalleryService, - @IExtensionManagementService private extensionService: IExtensionManagementService, + @IExtensionManagementService private extensionManagementService: IExtensionManagementService, @IProgressService private progressService: IProgressService, @IInstantiationService private instantiationService: IInstantiationService, @IWorkbenchEditorService private editorService: IWorkbenchEditorService, @@ -74,7 +76,8 @@ export class ExtensionsViewlet extends Viewlet implements IExtensionsViewlet { @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, @IExtensionTipsService private tipsService: IExtensionTipsService, @IMessageService private messageService: IMessageService, - @IViewletService private viewletService: IViewletService + @IViewletService private viewletService: IViewletService, + @IExtensionService private extensionService: IExtensionService ) { super(VIEWLET_ID, telemetryService); this.searchDelayer = new ThrottledDelayer(500); @@ -240,7 +243,8 @@ export class ExtensionsViewlet extends Viewlet implements IExtensionsViewlet { if (/@disabled/i.test(value)) { return this.extensionsWorkbenchService.queryLocal() .then(result => result.sort((e1, e2) => e1.displayName.localeCompare(e2.displayName))) - .then(result => result.filter(e => e.state === ExtensionState.Disabled)) + .then(result => this.extensionService.getExtensions() + .then(runningExtensions => result.filter(e => runningExtensions.every(r => r.id !== e.identifier)))) .then(result => new PagedModel(result)); } diff --git a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts index 6e2a6a40502..29739425a0f 100644 --- a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts +++ b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts @@ -22,7 +22,7 @@ import { IPager, mapPager, singlePagePager } from 'vs/base/common/paging'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, IQueryOptions, IExtensionManifest, - InstallExtensionEvent, DidInstallExtensionEvent, LocalExtensionType, DidUninstallExtensionEvent + InstallExtensionEvent, DidInstallExtensionEvent, LocalExtensionType, DidUninstallExtensionEvent, IExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData } from 'vs/platform/extensionManagement/common/extensionTelemetry'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -34,7 +34,6 @@ import { IExtension, IExtensionDependencies, ExtensionState, IExtensionsWorkbenc import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IURLService } from 'vs/platform/url/common/url'; import { ExtensionsInput } from 'vs/workbench/parts/extensions/common/extensionsInput'; -import { IExtensionsRuntimeService } from 'vs/platform/extensions/common/extensions'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; interface IExtensionStateProvider { @@ -43,7 +42,8 @@ interface IExtensionStateProvider { class Extension implements IExtension { - public needsReload = false; + public disabledGlobally = false; + public disabledForWorkspace = false; constructor( private galleryService: IExtensionGalleryService, @@ -138,7 +138,7 @@ class Extension implements IExtension { } private get defaultIconUrl(): string { - return require.toUrl('./media/defaultIcon.png'); + return require.toUrl('../browser/media/defaultIcon.png'); } get licenseUrl(): string { @@ -165,10 +165,6 @@ class Extension implements IExtension { return this.gallery && this.type === LocalExtensionType.User && semver.gt(this.latestVersion, this.version); } - get reload(): boolean { - return this.needsReload; - } - get telemetryData(): any { const { local, gallery } = this; @@ -302,10 +298,6 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService { private autoUpdateDelayer: ThrottledDelayer; private disposables: IDisposable[] = []; - // TODO: @sandy - Remove these when IExtensionsRuntimeService exposes sync API to get extensions. - private newlyInstalled: Extension[] = []; - private unInstalled: Extension[] = []; - private _onChange: Emitter = new Emitter(); get onChange(): Event { return this._onChange.event; } @@ -318,7 +310,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService { @ITelemetryService private telemetryService: ITelemetryService, @IMessageService private messageService: IMessageService, @IURLService urlService: IURLService, - @IExtensionsRuntimeService private extensionsRuntimeService: IExtensionsRuntimeService, + @IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService, @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService, ) { this.stateProvider = ext => this.getExtensionState(ext); @@ -327,6 +319,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService { extensionService.onDidInstallExtension(this.onDidInstallExtension, this, this.disposables); extensionService.onUninstallExtension(this.onUninstallExtension, this, this.disposables); extensionService.onDidUninstallExtension(this.onDidUninstallExtension, this, this.disposables); + extensionEnablementService.onEnablementChanged(this.onEnablementChanged, this, this.disposables); this.syncDelayer = new ThrottledDelayer(ExtensionsWorkbenchService.SyncPeriod); this.autoUpdateDelayer = new ThrottledDelayer(1000); @@ -349,10 +342,13 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService { queryLocal(): TPromise { return this.extensionService.getInstalled().then(result => { const installedById = index(this.installed, e => e.local.id); - + const globallyDisabledExtensions = this.extensionEnablementService.getGloballyDisabledExtensions(); + const workspaceDisabledExtensions = this.extensionEnablementService.getWorkspaceDisabledExtensions(); this.installed = result.map(local => { const extension = installedById[local.id] || new Extension(this.galleryService, this.stateProvider, local); extension.local = local; + extension.disabledGlobally = globallyDisabledExtensions.indexOf(extension.identifier) !== -1; + extension.disabledForWorkspace = workspaceDisabledExtensions.indexOf(extension.identifier) !== -1; return extension; }); @@ -489,7 +485,6 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService { } return this.doSetEnablement(extension, enable, workspace).then(reload => { - (extension).needsReload = reload; this.telemetryService.publicLog(enable ? 'extension:enable' : 'extension:disable', extension.telemetryData); this._onChange.fire(); }); @@ -512,14 +507,14 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService { private doSetEnablement(extension: IExtension, enable: boolean, workspace: boolean): TPromise { if (workspace) { - return this.extensionsRuntimeService.setEnablement(extension.identifier, enable, workspace); + return this.extensionEnablementService.setEnablement(extension.identifier, enable, workspace); } - const globalElablement = this.extensionsRuntimeService.setEnablement(extension.identifier, enable, false); + const globalElablement = this.extensionEnablementService.setEnablement(extension.identifier, enable, false); if (!this.workspaceContextService.getWorkspace()) { return globalElablement; } - return TPromise.join([globalElablement, this.extensionsRuntimeService.setEnablement(extension.identifier, enable, true)]) + return TPromise.join([globalElablement, this.extensionEnablementService.setEnablement(extension.identifier, enable, true)]) .then(values => values[0] || values[1]); } @@ -555,9 +550,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService { this.installing = this.installing.filter(e => e.id !== id); if (!error) { - this.newlyInstalled.push(extension); extension.local = local; - extension.needsReload = true; const galleryId = local.metadata && local.metadata.id; const installed = this.installed.filter(e => (e.local && e.local.metadata && e.local.metadata.id) === galleryId)[0]; @@ -595,10 +588,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService { } private onDidUninstallExtension({id, error}: DidUninstallExtensionEvent): void { - let newlyInstalled = false; if (!error) { - newlyInstalled = this.newlyInstalled.filter(e => e.local.id === id).length > 0; - this.newlyInstalled = this.newlyInstalled.filter(e => e.local.id !== id); this.installed = this.installed.filter(e => e.local.id !== id); } @@ -609,14 +599,22 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService { } if (!error) { - this.unInstalled.push(uninstalling.extension); - uninstalling.extension.needsReload = !newlyInstalled; this.reportTelemetry(uninstalling, true); } this._onChange.fire(); } + private onEnablementChanged(extensionIdentifier: string) { + const [extension] = this.local.filter(e => e.identifier === extensionIdentifier); + if (extension) { + const globallyDisabledExtensions = this.extensionEnablementService.getGloballyDisabledExtensions(); + const workspaceDisabledExtensions = this.extensionEnablementService.getWorkspaceDisabledExtensions(); + extension.disabledGlobally = globallyDisabledExtensions.indexOf(extension.identifier) !== -1; + extension.disabledForWorkspace = workspaceDisabledExtensions.indexOf(extension.identifier) !== -1; + } + } + private getExtensionState(extension: Extension): ExtensionState { if (extension.gallery && this.installing.some(e => e.extension.gallery.id === extension.gallery.id)) { return ExtensionState.Installing; @@ -627,15 +625,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService { } const local = this.installed.filter(e => e === extension || (e.gallery && extension.gallery && e.gallery.id === extension.gallery.id))[0]; - - if (local) { - if (this.newlyInstalled.some(e => e.gallery && extension.gallery && e.gallery.id === extension.gallery.id)) { - return ExtensionState.Installed; - } - return this.extensionsRuntimeService.isDisabled(extension.identifier) ? ExtensionState.Disabled : ExtensionState.Enabled; - } - - return ExtensionState.Uninstalled; + return local ? ExtensionState.Installed : ExtensionState.Uninstalled; } private reportTelemetry(active: IActiveExtension, success: boolean): void { diff --git a/src/vs/workbench/parts/files/browser/media/explorerviewlet.css b/src/vs/workbench/parts/files/browser/media/explorerviewlet.css index 8faf4014dd8..5cf0f855b9e 100644 --- a/src/vs/workbench/parts/files/browser/media/explorerviewlet.css +++ b/src/vs/workbench/parts/files/browser/media/explorerviewlet.css @@ -122,9 +122,8 @@ .explorer-viewlet:lang(ko) .explorer-open-editors .monaco-tree .monaco-tree-row .editor-group { font-weight: normal; } /* Disable tree twistie */ -.explorer-viewlet .explorer-open-editors > .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content:after, .explorer-viewlet .explorer-open-editors > .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content:before { - border: none; + display: none; } /* High Contrast Theming */ diff --git a/src/vs/workbench/parts/files/common/editors/fileEditorInput.ts b/src/vs/workbench/parts/files/common/editors/fileEditorInput.ts index 4a3c9782aec..726b02bc31c 100644 --- a/src/vs/workbench/parts/files/common/editors/fileEditorInput.ts +++ b/src/vs/workbench/parts/files/common/editors/fileEditorInput.ts @@ -10,7 +10,7 @@ import labels = require('vs/base/common/labels'); import URI from 'vs/base/common/uri'; import { EditorModel, EncodingMode, ConfirmResult } from 'vs/workbench/common/editor'; import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel'; -import { IFileOperationResult, FileOperationResult, FileChangesEvent, EventType } from 'vs/platform/files/common/files'; +import { IFileOperationResult, FileOperationResult, FileChangesEvent, EventType, FileChangeType } from 'vs/platform/files/common/files'; import { BINARY_FILE_EDITOR_ID, TEXT_FILE_EDITOR_ID, FILE_EDITOR_INPUT_ID, FileEditorInput as CommonFileEditorInput } from 'vs/workbench/parts/files/common/files'; import { ITextFileService, AutoSaveMode, ModelState, TextFileModelChangeEvent, LocalFileChangeEvent } from 'vs/workbench/services/textfile/common/textfiles'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; @@ -78,9 +78,9 @@ export class FileEditorInput extends CommonFileEditorInput { } private onFileChanges(e: FileChangesEvent): void { - e.getDeleted().forEach(deleted => { - this.disposeIfRelated(deleted.resource); - }); + if (e.gotDeleted()) { + this.disposeIfRelated(e); + } } private onDirtyStateChange(e: TextFileModelChangeEvent): void { @@ -207,7 +207,7 @@ export class FileEditorInput extends CommonFileEditorInput { }); } - private disposeIfRelated(resource: URI, movedTo?: URI): void { + private disposeIfRelated(arg1: URI | FileChangesEvent, movedTo?: URI): void { if (this.isDirty()) { return; // we never dispose dirty files } @@ -219,9 +219,14 @@ export class FileEditorInput extends CommonFileEditorInput { return; } - // Check if path is identical or path is a folder that the content is inside - if (paths.isEqualOrParent(this.resource.toString(), resource.toString())) { - this.historyService.remove(this); + let matches = false; + if (arg1 instanceof FileChangesEvent) { + matches = arg1.contains(this.resource, FileChangeType.DELETED); + } else { + matches = paths.isEqualOrParent(this.resource.toString(), arg1.toString()); + } + + if (matches) { this.dispose(); } } diff --git a/src/vs/workbench/parts/files/common/editors/fileEditorTracker.ts b/src/vs/workbench/parts/files/common/editors/fileEditorTracker.ts index bfaa70922c4..51402d85092 100644 --- a/src/vs/workbench/parts/files/common/editors/fileEditorTracker.ts +++ b/src/vs/workbench/parts/files/common/editors/fileEditorTracker.ts @@ -175,55 +175,27 @@ export class FileEditorTracker implements IWorkbenchContribution { return input instanceof FileEditorInput && input.getResource().toString() === resource.toString(); } - private getMatchingFileEditorInputFromDiff(input: DiffEditorInput, deletedResource: URI): FileEditorInput; - private getMatchingFileEditorInputFromDiff(input: DiffEditorInput, updatedFiles: FileChangesEvent): FileEditorInput; - private getMatchingFileEditorInputFromDiff(input: DiffEditorInput, arg: any): FileEditorInput { + private getMatchingFileEditorInputFromDiff(input: DiffEditorInput, e: FileChangesEvent): FileEditorInput { // First try modifiedInput const modifiedInput = input.modifiedInput; - const res = this.getMatchingFileEditorInputFromInput(modifiedInput, arg); + const res = this.getMatchingFileEditorInputFromInput(modifiedInput, e); if (res) { return res; } // Second try originalInput - return this.getMatchingFileEditorInputFromInput(input.originalInput, arg); + return this.getMatchingFileEditorInputFromInput(input.originalInput, e); } - private getMatchingFileEditorInputFromInput(input: EditorInput, deletedResource: URI): FileEditorInput; - private getMatchingFileEditorInputFromInput(input: EditorInput, updatedFiles: FileChangesEvent): FileEditorInput; - private getMatchingFileEditorInputFromInput(input: EditorInput, arg: any): FileEditorInput { - if (input instanceof FileEditorInput) { - if (arg instanceof URI) { - const deletedResource = arg; - if (this.containsResource(input, deletedResource)) { - return input; - } - } else { - const updatedFiles = arg; - if (updatedFiles.contains(input.getResource(), FileChangeType.UPDATED)) { - return input; - } - } + private getMatchingFileEditorInputFromInput(input: EditorInput, e: FileChangesEvent): FileEditorInput { + if (input instanceof FileEditorInput && e.contains(input.getResource(), FileChangeType.UPDATED)) { + return input; } return null; } - private containsResource(input: FileEditorInput, resource: URI): boolean; - private containsResource(input: EditorInput, resource: URI): boolean { - let fileResource: URI; - if (input instanceof FileEditorInput) { - fileResource = input.getResource(); - } - - if (paths.isEqualOrParent(fileResource.fsPath, resource.fsPath)) { - return true; - } - - return false; - } - public dispose(): void { this.toUnbind = dispose(this.toUnbind); } diff --git a/src/vs/workbench/parts/files/common/explorerViewModel.ts b/src/vs/workbench/parts/files/common/explorerViewModel.ts index 7f6c195a9e4..35974c06d7b 100644 --- a/src/vs/workbench/parts/files/common/explorerViewModel.ts +++ b/src/vs/workbench/parts/files/common/explorerViewModel.ts @@ -7,9 +7,8 @@ import assert = require('vs/base/common/assert'); import URI from 'vs/base/common/uri'; -import { isLinux } from 'vs/base/common/platform'; import paths = require('vs/base/common/paths'); -import { IFileStat } from 'vs/platform/files/common/files'; +import { IFileStat, isEqual, isParent } from 'vs/platform/files/common/files'; export enum StatType { FILE, @@ -257,7 +256,7 @@ export class FileStat implements IFileStat { public find(resource: URI): FileStat { // Return if path found - if (this.fileResourceEquals(resource, this.resource)) { + if (isEqual(resource.toString(), this.resource.toString())) { return this; } @@ -269,26 +268,17 @@ export class FileStat implements IFileStat { for (let i = 0; i < this.children.length; i++) { const child = this.children[i]; - if (this.fileResourceEquals(resource, child.resource)) { + if (isEqual(resource.toString(), child.resource.toString())) { return child; } - if (child.isDirectory && paths.isEqualOrParent(resource.fsPath, child.resource.fsPath)) { + if (child.isDirectory && isParent(resource.fsPath, child.resource.fsPath)) { return child.find(resource); } } return null; //Unable to find } - - private fileResourceEquals(r1: URI, r2: URI) { - const identityEquals = (r1.toString() === r2.toString()); - if (isLinux || identityEquals) { - return identityEquals; - } - - return r1.toString().toLowerCase() === r2.toString().toLowerCase(); - } } /* A helper that can be used to show a placeholder when creating a new stat */ diff --git a/src/vs/workbench/parts/files/electron-browser/dirtyFilesTracker.ts b/src/vs/workbench/parts/files/electron-browser/dirtyFilesTracker.ts index b0bd17d7111..88443df9bde 100644 --- a/src/vs/workbench/parts/files/electron-browser/dirtyFilesTracker.ts +++ b/src/vs/workbench/parts/files/electron-browser/dirtyFilesTracker.ts @@ -11,7 +11,7 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { VIEWLET_ID } from 'vs/workbench/parts/files/common/files'; import { TextFileModelChangeEvent, ITextFileService, AutoSaveMode } from 'vs/workbench/services/textfile/common/textfiles'; import { platform, Platform } from 'vs/base/common/platform'; -import { IWindowService } from 'vs/workbench/services/window/electron-browser/windowService'; +import { IWindowIPCService } from 'vs/workbench/services/window/electron-browser/windowService'; import { Position } from 'vs/platform/editor/common/editor'; import { IEditorStacksModel } from 'vs/workbench/common/editor'; import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; @@ -41,7 +41,7 @@ export class DirtyFilesTracker implements IWorkbenchContribution { @IEditorGroupService editorGroupService: IEditorGroupService, @IWorkbenchEditorService private editorService: IWorkbenchEditorService, @IActivityService private activityService: IActivityService, - @IWindowService private windowService: IWindowService, + @IWindowIPCService private windowService: IWindowIPCService, @IUntitledEditorService private untitledEditorService: IUntitledEditorService ) { this.toUnbind = []; diff --git a/src/vs/workbench/parts/files/electron-browser/electronFileActions.ts b/src/vs/workbench/parts/files/electron-browser/electronFileActions.ts index ccd67bbb34f..d973c906835 100644 --- a/src/vs/workbench/parts/files/electron-browser/electronFileActions.ts +++ b/src/vs/workbench/parts/files/electron-browser/electronFileActions.ts @@ -17,7 +17,8 @@ import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/edi import { asFileEditorInput } from 'vs/workbench/common/editor'; import { IMessageService } from 'vs/platform/message/common/message'; import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; -import { IWindowService } from 'vs/workbench/services/window/electron-browser/windowService'; +import { IWindowIPCService } from 'vs/workbench/services/window/electron-browser/windowService'; +import { IWindowService } from 'vs/platform/windows/common/windows'; import { ipcRenderer as ipc, clipboard } from 'electron'; @@ -26,7 +27,7 @@ export class RevealInOSAction extends Action { constructor( resource: uri, - @IWindowService private windowService: IWindowService + @IWindowIPCService private windowService: IWindowIPCService ) { super('workbench.action.files.revealInWindows', platform.isWindows ? nls.localize('revealInWindows', "Reveal in Explorer") : (platform.isMacintosh ? nls.localize('revealInMac', "Reveal in Finder") : nls.localize('openContainer', "Open Containing Folder"))); @@ -51,7 +52,7 @@ export class GlobalRevealInOSAction extends Action { id: string, label: string, @IWorkbenchEditorService private editorService: IWorkbenchEditorService, - @IWindowService private windowService: IWindowService, + @IWindowIPCService private windowService: IWindowIPCService, @IMessageService private messageService: IMessageService ) { super(id, label); @@ -116,63 +117,65 @@ export class GlobalCopyPathAction extends Action { } } -export class BaseOpenAction extends Action { - - private ipcMsg: string; - - constructor(id: string, label: string, ipcMsg: string) { - super(id, label); - - this.ipcMsg = ipcMsg; - } - - public run(): TPromise { - ipc.send(this.ipcMsg); // Handle in browser process - - return TPromise.as(true); - } -} - export class OpenFileAction extends Action { - public static ID = 'workbench.action.files.openFile'; - public static LABEL = nls.localize('openFile', "Open File..."); + static ID = 'workbench.action.files.openFile'; + static LABEL = nls.localize('openFile', "Open File..."); - constructor(id: string, label: string, @IWorkbenchEditorService private editorService: IWorkbenchEditorService) { + constructor( + id: string, + label: string, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IWindowService private windowService: IWindowService + ) { super(id, label); } - public run(): TPromise { + run(): TPromise { const fileInput = asFileEditorInput(this.editorService.getActiveEditorInput(), true); // Handle in browser process if (fileInput) { - ipc.send('vscode:openFilePicker', false, paths.dirname(fileInput.getResource().fsPath)); - } else { - ipc.send('vscode:openFilePicker'); + return this.windowService.openFilePicker(false, paths.dirname(fileInput.getResource().fsPath)); } - return TPromise.as(true); + return this.windowService.openFilePicker(); } } -export class OpenFolderAction extends BaseOpenAction { +export class OpenFolderAction extends Action { - public static ID = 'workbench.action.files.openFolder'; - public static LABEL = nls.localize('openFolder', "Open Folder..."); + static ID = 'workbench.action.files.openFolder'; + static LABEL = nls.localize('openFolder', "Open Folder..."); - constructor(id: string, label: string) { - super(id, label, 'vscode:openFolderPicker'); + constructor( + id: string, + label: string, + @IWindowService private windowService: IWindowService + ) { + super(id, label); + } + + run(): TPromise { + return this.windowService.openFolderPicker(); } } -export class OpenFileFolderAction extends BaseOpenAction { +export class OpenFileFolderAction extends Action { - public static ID = 'workbench.action.files.openFileFolder'; - public static LABEL = nls.localize('openFileFolder', "Open..."); + static ID = 'workbench.action.files.openFileFolder'; + static LABEL = nls.localize('openFileFolder', "Open..."); - constructor(id: string, label: string) { - super(id, label, 'vscode:openFileFolderPicker'); + constructor( + id: string, + label: string, + @IWindowService private windowService: IWindowService + ) { + super(id, label); + } + + run(): TPromise { + return this.windowService.openFileFolderPicker(); } } diff --git a/src/vs/workbench/parts/files/electron-browser/files.electron.contribution.ts b/src/vs/workbench/parts/files/electron-browser/files.electron.contribution.ts index 09b6dbf8482..334f527af2b 100644 --- a/src/vs/workbench/parts/files/electron-browser/files.electron.contribution.ts +++ b/src/vs/workbench/parts/files/electron-browser/files.electron.contribution.ts @@ -19,6 +19,9 @@ import { DirtyFilesTracker } from 'vs/workbench/parts/files/electron-browser/dir import { OpenFolderAction, OpenFileAction, OpenFileFolderAction, ShowOpenedFileInNewWindow, GlobalRevealInOSAction, GlobalCopyPathAction, CopyPathAction, RevealInOSAction } from 'vs/workbench/parts/files/electron-browser/electronFileActions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { IWindowService } from 'vs/platform/windows/common/windows'; class FileViewerActionContributor extends ActionBarContributor { @@ -76,4 +79,10 @@ actionsRegistry.registerActionBarContributor(Scope.VIEWER, FileViewerActionContr // Register Dirty Files Tracker (Registry.as(WorkbenchExtensions.Workbench)).registerWorkbenchContribution( DirtyFilesTracker -); \ No newline at end of file +); + +// Register Commands +CommandsRegistry.registerCommand('_files.openFolderPicker', (accessor: ServicesAccessor, forceNewWindow: boolean) => { + const windowService = accessor.get(IWindowService); + windowService.openFolderPicker(forceNewWindow); +}); \ No newline at end of file diff --git a/src/vs/workbench/parts/git/browser/gitActions.contribution.ts b/src/vs/workbench/parts/git/browser/gitActions.contribution.ts index d2749be5361..1fefc2634d1 100644 --- a/src/vs/workbench/parts/git/browser/gitActions.contribution.ts +++ b/src/vs/workbench/parts/git/browser/gitActions.contribution.ts @@ -39,7 +39,8 @@ function getStatus(gitService: IGitService, contextService: IWorkspaceContextSer const repositoryRelativePath = paths.normalize(paths.relative(repositoryRoot, input.getResource().fsPath)); return statusModel.getWorkingTreeStatus().find(repositoryRelativePath) || - statusModel.getIndexStatus().find(repositoryRelativePath); + statusModel.getIndexStatus().find(repositoryRelativePath) || + statusModel.getMergeStatus().find(repositoryRelativePath); } class OpenInDiffAction extends baseeditor.EditorInputAction { diff --git a/src/vs/workbench/parts/git/browser/gitActions.ts b/src/vs/workbench/parts/git/browser/gitActions.ts index 5288018c6ac..a9d7dd31615 100644 --- a/src/vs/workbench/parts/git/browser/gitActions.ts +++ b/src/vs/workbench/parts/git/browser/gitActions.ts @@ -29,7 +29,7 @@ import { IGitService, IFileStatus, Status, StatusType, ServiceState, IModel, IBr import { IQuickOpenService } from 'vs/workbench/services/quickopen/common/quickOpenService'; import paths = require('vs/base/common/paths'); import URI from 'vs/base/common/uri'; -import { IWindowService } from 'vs/workbench/services/window/electron-browser/windowService'; +import { IWindowIPCService } from 'vs/workbench/services/window/electron-browser/windowService'; import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; function flatten(context?: any, preferFocus = false): IFileStatus[] { @@ -1152,7 +1152,7 @@ export class SyncAction extends GitAction { @IGitService gitService: IGitService, @IConfigurationService private configurationService: IConfigurationService, @IConfigurationEditingService private configurationEditingService: IConfigurationEditingService, - @IWindowService private windowService: IWindowService + @IWindowIPCService private windowService: IWindowIPCService ) { super(id, label, 'git-action sync', gitService); } diff --git a/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.ts b/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.ts index d0030df0562..816f39c9bfa 100644 --- a/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.ts +++ b/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.ts @@ -12,11 +12,10 @@ import { IEntryRunContext, Mode, IAutoFocus } from 'vs/base/parts/quickopen/comm import { QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel'; import { KeyMod } from 'vs/base/common/keyCodes'; import { QuickOpenHandler, EditorQuickOpenEntry, QuickOpenAction } from 'vs/workbench/browser/quickopen'; -import { TextEditorOptions, EditorOptions } from 'vs/workbench/common/editor'; import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; import { IEditor, IModelDecorationsChangeAccessor, OverviewRulerLane, IModelDeltaDecoration, IRange, IEditorViewState, ITextModel, IDiffEditorModel } from 'vs/editor/common/editorCommon'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { Position, IEditorInput } from 'vs/platform/editor/common/editor'; +import { Position, IEditorInput, ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { IQuickOpenService } from 'vs/workbench/services/quickopen/common/quickOpenService'; export const GOTO_LINE_PREFIX = ':'; @@ -92,12 +91,10 @@ class GotoLineEntry extends EditorQuickOpenEntry { return this.editorService.getActiveEditorInput(); } - public getOptions(): EditorOptions { - let range = this.toSelection(); - let options = new TextEditorOptions(); - options.selection(range.startLineNumber, range.startColumn, range.startLineNumber, range.startColumn); - - return options; + public getOptions(): ITextEditorOptions { + return { + selection: this.toSelection() + }; } public runOpen(context: IEntryRunContext): boolean { diff --git a/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts b/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts index 8eb8b945176..03723ac122c 100644 --- a/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts +++ b/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts @@ -15,13 +15,12 @@ import { IEntryRunContext, Mode, IAutoFocus } from 'vs/base/parts/quickopen/comm import { QuickOpenModel, IHighlight } from 'vs/base/parts/quickopen/browser/quickOpenModel'; import { QuickOpenHandler, EditorQuickOpenEntryGroup, QuickOpenAction } from 'vs/workbench/browser/quickopen'; import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; -import { TextEditorOptions, EditorOptions } from 'vs/workbench/common/editor'; import filters = require('vs/base/common/filters'); import { KeyMod } from 'vs/base/common/keyCodes'; import { IEditor, IModelDecorationsChangeAccessor, OverviewRulerLane, IModelDeltaDecoration, IRange, IModel, ITokenizedModel, IDiffEditorModel, IEditorViewState } from 'vs/editor/common/editorCommon'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IQuickOpenService } from 'vs/workbench/services/quickopen/common/quickOpenService'; -import { Position, IEditorInput } from 'vs/platform/editor/common/editor'; +import { Position, IEditorInput, ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { getDocumentSymbols } from 'vs/editor/contrib/quickOpen/common/quickOpen'; import { DocumentSymbolProviderRegistry, SymbolInformation, SymbolKind } from 'vs/editor/common/modes'; @@ -292,11 +291,10 @@ class SymbolEntry extends EditorQuickOpenEntryGroup { return this.editorService.getActiveEditorInput(); } - public getOptions(): EditorOptions { - let options = new TextEditorOptions(); - options.selection(this.range.startLineNumber, this.range.startColumn, this.range.startLineNumber, this.range.startColumn); - - return options; + public getOptions(): ITextEditorOptions { + return { + selection: this.toSelection() + }; } public run(mode: Mode, context: IEntryRunContext): boolean { diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts index 678723d77bc..232218b4794 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts @@ -26,12 +26,15 @@ import { TabFocus } from 'vs/editor/common/config/commonEditorConfig'; import { TerminalConfigHelper, IShell } from 'vs/workbench/parts/terminal/electron-browser/terminalConfigHelper'; export class TerminalInstance implements ITerminalInstance { - private static EOL_REGEX = /\r?\n/g; + /** The amount of time to consider terminal errors to be related to the launch */ + private static readonly LAUNCHING_DURATION = 500; + private static readonly EOL_REGEX = /\r?\n/g; private static _idCounter = 1; private _id: number; private _isExiting: boolean; + private _isLaunching: boolean; private _isVisible: boolean; private _onDisposed: Emitter; private _onProcessIdReady: Emitter; @@ -65,6 +68,7 @@ export class TerminalInstance implements ITerminalInstance { this._toDispose = []; this._skipTerminalKeybindings = []; this._isExiting = false; + this._isLaunching = true; this._isVisible = false; this._id = TerminalInstance._idCounter++; @@ -286,10 +290,18 @@ export class TerminalInstance implements ITerminalInstance { if (!this._isExiting) { this.dispose(); if (exitCode) { - this._messageService.show(Severity.Error, nls.localize('terminal.integrated.exitedWithCode', 'The terminal process terminated with exit code: {0}', exitCode)); + if (this._isLaunching) { + const args = shell.args && shell.args.length ? ' ' + shell.args.map(a => a.indexOf(' ') !== -1 ? `'${a}'` : a).join(' ') : ''; + this._messageService.show(Severity.Error, nls.localize('terminal.integrated.launchFailed', 'The terminal process command `{0}{1}` failed to launch (exit code: {2})', shell.executable, args, exitCode)); + } else { + this._messageService.show(Severity.Error, nls.localize('terminal.integrated.exitedWithCode', 'The terminal process terminated with exit code: {0}', exitCode)); + } } } }); + setTimeout(() => { + this._isLaunching = false; + }, TerminalInstance.LAUNCHING_DURATION); } public static createTerminalEnv(parentEnv: IStringDictionary, shell: IShell, workspace: IWorkspace, locale?: string): IStringDictionary { diff --git a/src/vs/workbench/parts/themes/test/electron-browser/themes.test.contribution.ts b/src/vs/workbench/parts/themes/test/electron-browser/themes.test.contribution.ts index 0a936ef6a86..de8706f2bbe 100644 --- a/src/vs/workbench/parts/themes/test/electron-browser/themes.test.contribution.ts +++ b/src/vs/workbench/parts/themes/test/electron-browser/themes.test.contribution.ts @@ -118,18 +118,22 @@ class Snapper { return this.modeService.getOrCreateModeByFilenameOrFirstLine(fileName).then(mode => { let result: Data[] = []; let model = new TextModelWithTokens([], TextModel.toRawText(content, TextModel.DEFAULT_CREATION_OPTIONS), mode.getId()); - model.tokenIterator({ lineNumber: 1, column: 1 }, iterator => { - while (iterator.hasNext()) { - let tokenInfo = iterator.next(); - let lineNumber = tokenInfo.lineNumber; - let content = model.getValueInRange({ startLineNumber: lineNumber, endLineNumber: lineNumber, startColumn: tokenInfo.startColumn, endColumn: tokenInfo.endColumn }); + for (let lineNumber = 1, lineCount = model.getLineCount(); lineNumber <= lineCount; lineNumber++) { + let lineTokens = model.getLineTokens(lineNumber, false); + let lineContent = model.getLineContent(lineNumber); + + for (let i = 0, len = lineTokens.getTokenCount(); i < len; i++) { + let tokenType = lineTokens.getTokenType(i); + let tokenStartOffset = lineTokens.getTokenStartOffset(i); + let tokenEndOffset = lineTokens.getTokenEndOffset(i); + result.push({ - c: content, - t: this.normalizeType(tokenInfo.type), + c: lineContent.substring(tokenStartOffset, tokenEndOffset), + t: this.normalizeType(tokenType), r: {} }); } - }); + } return this.appendThemeInformation(result); }); } diff --git a/src/vs/workbench/services/configurationResolver/node/configurationResolverService.ts b/src/vs/workbench/services/configurationResolver/node/configurationResolverService.ts index bf4f1e7a983..f8dacad1445 100644 --- a/src/vs/workbench/services/configurationResolver/node/configurationResolverService.ts +++ b/src/vs/workbench/services/configurationResolver/node/configurationResolverService.ts @@ -65,6 +65,11 @@ export class ConfigurationResolverService implements IConfigurationResolverServi return paths.basename(this.getFilePath()); } + private get fileBasenameNoExtension(): string { + const basename = this.fileBasename; + return basename.slice(0, basename.length - paths.extname(basename).length); + } + private get fileDirname(): string { return paths.dirname(this.getFilePath()); } diff --git a/src/vs/workbench/services/extensions/electron-browser/extensions.ts b/src/vs/workbench/services/extensions/electron-browser/extensions.ts deleted file mode 100644 index 3d0c67bba29..00000000000 --- a/src/vs/workbench/services/extensions/electron-browser/extensions.ts +++ /dev/null @@ -1,247 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import pkg from 'vs/platform/package'; -import { localize } from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; -import { distinct } from 'vs/base/common/arrays'; -import * as paths from 'vs/base/common/paths'; -import URI from 'vs/base/common/uri'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { ExtensionScanner, MessagesCollector } from 'vs/workbench/node/extensionPoints'; -import { IExtensionManagementService, DidUninstallExtensionEvent } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IWorkspaceContextService, IWorkspace } from 'vs/platform/workspace/common/workspace'; -import { IExtensionsRuntimeService, IExtensionDescription, IMessage } from 'vs/platform/extensions/common/extensions'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { Severity, IMessageService } from 'vs/platform/message/common/message'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; - - -const DIRNAME = URI.parse(require.toUrl('./')).fsPath; -const BASE_PATH = paths.normalize(paths.join(DIRNAME, '../../../../../..')); -const BUILTIN_EXTENSIONS_PATH = paths.join(BASE_PATH, 'extensions'); -const DISABLED_EXTENSIONS_STORAGE_PATH = 'extensions/disabled'; - -export class ExtensionsRuntimeService implements IExtensionsRuntimeService { - - _serviceBrand: any; - - private workspace: IWorkspace; - - private installedExtensions: TPromise; - private _globalDisabledExtensions: string[]; - private _workspaceDisabledExtensions: string[]; - - private disposables: IDisposable[] = []; - - constructor( - @IStorageService private storageService: IStorageService, - @IWorkspaceContextService contextService: IWorkspaceContextService, - @IMessageService private messageService: IMessageService, - @IEnvironmentService private environmentService: IEnvironmentService, - @IExtensionManagementService private extensionManagementService: IExtensionManagementService - ) { - this.workspace = contextService.getWorkspace(); - extensionManagementService.onDidUninstallExtension(this.onDidUninstallExtension, this, this.disposables); - } - - public getExtensions(): TPromise { - if (!this.installedExtensions) { - this.installedExtensions = this.scanExtensions(); - } - return this.installedExtensions.then(extensionDescriptions => { - const disabledExtensions = this.getDisabledExtensions(); - return disabledExtensions.length ? extensionDescriptions.filter(e => disabledExtensions.indexOf(`${e.publisher}.${e.name}`) === -1) : extensionDescriptions; - }); - } - - public canEnable(identifier: string): boolean { - if (this.environmentService.disableExtensions) { - return false; - } - - return this.getDisabledExtensions().indexOf(identifier) !== -1; - } - - public isDisabled(identifier: string): boolean { - if (this.environmentService.disableExtensions) { - return true; - } - - return this.getDisabledExtensions().indexOf(identifier) !== -1; - } - - public isDisabledAlways(identifier: string): boolean { - return this.globalDisabledExtensions.indexOf(identifier) !== -1; - } - - public setEnablement(identifier: string, enable: boolean, workspace: boolean = false): TPromise { - if (workspace && !this.workspace) { - return TPromise.wrapError(localize('noWorkspace', "No workspace.")); - } - - if (this.environmentService.disableExtensions) { - return TPromise.wrap(false); - } - - if (this.isDisabled(identifier) === !enable) { - return TPromise.wrap(false); - } - - if (enable) { - if (workspace) { - this.enableExtension(identifier, StorageScope.WORKSPACE); - } else { - this.enableExtension(identifier, StorageScope.GLOBAL); - } - } else { - if (workspace) { - this.disableExtension(identifier, StorageScope.WORKSPACE); - } else { - this.disableExtension(identifier, StorageScope.GLOBAL); - } - } - - return TPromise.wrap(true); - } - - private getDisabledExtensions(): string[] { - return [...this.globalDisabledExtensions, ...this.workspaceDisabledExtensions]; - } - - private get workspaceDisabledExtensions(): string[] { - if (!this._workspaceDisabledExtensions) { - this._workspaceDisabledExtensions = this.getDisabledExtensionsFromStorage(StorageScope.WORKSPACE); - } - return this._workspaceDisabledExtensions; - } - - private get globalDisabledExtensions(): string[] { - if (!this._globalDisabledExtensions) { - this._globalDisabledExtensions = this.getDisabledExtensionsFromStorage(StorageScope.GLOBAL); - } - return this._globalDisabledExtensions; - } - - private getDisabledExtensionsFromStorage(scope?: StorageScope): string[] { - if (scope !== void 0) { - return this._getDisabledExtensions(scope); - } - - const globallyDisabled = this._getDisabledExtensions(StorageScope.GLOBAL); - const workspaceDisabled = this._getDisabledExtensions(StorageScope.WORKSPACE); - return [...globallyDisabled, ...workspaceDisabled]; - } - - private disableExtension(identifier: string, scope: StorageScope): TPromise { - let disabledExtensions = this._getDisabledExtensions(scope); - disabledExtensions.push(identifier); - this._setDisabledExtensions(disabledExtensions, scope); - return TPromise.wrap(true); - } - - private enableExtension(identifier: string, scope: StorageScope): TPromise { - let disabledExtensions = this._getDisabledExtensions(scope); - const index = disabledExtensions.indexOf(identifier); - if (index !== -1) { - disabledExtensions.splice(index, 1); - this._setDisabledExtensions(disabledExtensions, scope); - } - return TPromise.wrap(true); - } - - private _getDisabledExtensions(scope: StorageScope): string[] { - const value = this.storageService.get(DISABLED_EXTENSIONS_STORAGE_PATH, scope, ''); - return value ? distinct(value.split(',')) : []; - } - - private _setDisabledExtensions(disabledExtensions: string[], scope: StorageScope): void { - if (disabledExtensions.length) { - this.storageService.store(DISABLED_EXTENSIONS_STORAGE_PATH, disabledExtensions.join(','), scope); - } else { - this.storageService.remove(DISABLED_EXTENSIONS_STORAGE_PATH, scope); - } - } - - private onDidUninstallExtension({id, error}: DidUninstallExtensionEvent): void { - if (!error) { - id = stripVersion(id); - this.enableExtension(id, StorageScope.WORKSPACE); - this.enableExtension(id, StorageScope.GLOBAL); - } - } - - private scanExtensions(): TPromise { - const collector = new MessagesCollector(); - const version = pkg.version; - const builtinExtensions = ExtensionScanner.scanExtensions(version, collector, BUILTIN_EXTENSIONS_PATH, true); - const userExtensions = this.environmentService.disableExtensions || !this.environmentService.extensionsPath ? TPromise.as([]) : ExtensionScanner.scanExtensions(version, collector, this.environmentService.extensionsPath, false); - const developedExtensions = this.environmentService.disableExtensions || !this.environmentService.extensionDevelopmentPath ? TPromise.as([]) : ExtensionScanner.scanOneOrMultipleExtensions(version, collector, this.environmentService.extensionDevelopmentPath, false); - const isDev = !this.environmentService.isBuilt || !!this.environmentService.extensionDevelopmentPath; - - return TPromise.join([builtinExtensions, userExtensions, developedExtensions]).then((extensionDescriptions: IExtensionDescription[][]) => { - let builtinExtensions = extensionDescriptions[0]; - let userExtensions = extensionDescriptions[1]; - let developedExtensions = extensionDescriptions[2]; - - let result: { [extensionId: string]: IExtensionDescription; } = {}; - builtinExtensions.forEach((builtinExtension) => { - result[builtinExtension.id] = builtinExtension; - }); - userExtensions.forEach((userExtension) => { - if (result.hasOwnProperty(userExtension.id)) { - collector.warn(userExtension.extensionFolderPath, localize('overwritingExtension', "Overwriting extension {0} with {1}.", result[userExtension.id].extensionFolderPath, userExtension.extensionFolderPath)); - } - result[userExtension.id] = userExtension; - }); - developedExtensions.forEach(developedExtension => { - collector.info('', localize('extensionUnderDevelopment', "Loading development extension at {0}", developedExtension.extensionFolderPath)); - if (result.hasOwnProperty(developedExtension.id)) { - collector.warn(developedExtension.extensionFolderPath, localize('overwritingExtension', "Overwriting extension {0} with {1}.", result[developedExtension.id].extensionFolderPath, developedExtension.extensionFolderPath)); - } - result[developedExtension.id] = developedExtension; - }); - - return Object.keys(result).map(name => result[name]); - }).then(null, err => { - collector.error('', err); - return []; - }).then(extensions => { - collector.getMessages().forEach(entry => this._handleMessage(entry, isDev)); - return extensions; - }); - } - - private _handleMessage(message: IMessage, isDev: boolean): void { - let messageShown = false; - if (message.type === Severity.Error || message.type === Severity.Warning) { - if (isDev) { - // Only show nasty intrusive messages if doing extension development. - this.messageService.show(message.type, (message.source ? '[' + message.source + ']: ' : '') + message.message); - messageShown = true; - } - } - if (!messageShown) { - switch (message.type) { - case Severity.Error: - console.error(message); - break; - case Severity.Warning: - console.warn(message); - break; - default: - console.log(message); - } - } - } - - dispose(): void { - this.disposables = dispose(this.disposables); - } -} - -function stripVersion(id: string): string { - return id.replace(/-\d+\.\d+\.\d+$/, ''); -} \ No newline at end of file diff --git a/src/vs/workbench/services/files/node/watcher/common.ts b/src/vs/workbench/services/files/node/watcher/common.ts index 41bbd7f0a97..5b821565616 100644 --- a/src/vs/workbench/services/files/node/watcher/common.ts +++ b/src/vs/workbench/services/files/node/watcher/common.ts @@ -6,8 +6,7 @@ 'use strict'; import uri from 'vs/base/common/uri'; -import paths = require('vs/base/common/paths'); -import { FileChangeType, FileChangesEvent } from 'vs/platform/files/common/files'; +import { FileChangeType, FileChangesEvent, isParent } from 'vs/platform/files/common/files'; export interface IRawFileChange { type: FileChangeType; @@ -106,7 +105,7 @@ class EventNormalizer { }).sort((e1, e2) => { return e1.path.length - e2.path.length; // shortest path first }).filter(e => { - if (deletedPaths.some(d => this.isParent(e.path, d))) { + if (deletedPaths.some(d => isParent(e.path, d))) { return false; // DELETE is ignored if parent is deleted already } @@ -116,8 +115,4 @@ class EventNormalizer { return true; }).concat(addedChangeEvents); } - - private isParent(p: string, candidate: string): boolean { - return p.indexOf(candidate + paths.nativeSep) === 0; - } } \ No newline at end of file diff --git a/src/vs/workbench/services/files/test/node/fileService.test.ts b/src/vs/workbench/services/files/test/node/fileService.test.ts index fa545b24e26..9010079c455 100644 --- a/src/vs/workbench/services/files/test/node/fileService.test.ts +++ b/src/vs/workbench/services/files/test/node/fileService.test.ts @@ -32,7 +32,11 @@ suite('FileService', () => { testDir = path.join(parentDir, id); let sourceDir = require.toUrl('./fixtures/service'); - extfs.copy(sourceDir, testDir, () => { + extfs.copy(sourceDir, testDir, (error) => { + if (error) { + return onError(error, done); + } + events = new utils.TestEventService(); service = new FileService(testDir, { disableWatcher: true }, events); done(); diff --git a/src/vs/workbench/services/history/browser/history.ts b/src/vs/workbench/services/history/browser/history.ts index 84ac57a6ad1..10cd09a9078 100644 --- a/src/vs/workbench/services/history/browser/history.ts +++ b/src/vs/workbench/services/history/browser/history.ts @@ -9,13 +9,16 @@ import { TPromise } from 'vs/base/common/winjs.base'; import errors = require('vs/base/common/errors'); import platform = require('vs/base/common/platform'); import nls = require('vs/nls'); +import labels = require('vs/base/common/labels'); import URI from 'vs/base/common/uri'; import product from 'vs/platform/product'; import { IEditor as IBaseEditor } from 'vs/platform/editor/common/editor'; import { EditorInput, IGroupEvent, IEditorRegistry, Extensions, asFileEditorInput, IEditorGroup } from 'vs/workbench/common/editor'; import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IEventService } from 'vs/platform/event/common/event'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; +import { FileChangesEvent, EventType, FileChangeType } from 'vs/platform/files/common/files'; import { Selection } from 'vs/editor/common/core/selection'; import { IEditorInput, ITextEditorOptions, IResourceInput } from 'vs/platform/editor/common/editor'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; @@ -24,6 +27,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/platform'; import { once } from 'vs/base/common/event'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -49,32 +53,24 @@ export class EditorState { public justifiesNewPushState(other: EditorState): boolean { if (!this._editorInput.matches(other._editorInput)) { - // push different editor inputs - return true; + return true; // push different editor inputs } if (!Selection.isISelection(this._selection) || !Selection.isISelection(other._selection)) { - // unknown selections - return true; + return true; // unknown selections } const liftedSelection = Selection.liftSelection(this._selection); const liftedOtherSelection = Selection.liftSelection(other._selection); if (Math.abs(liftedSelection.getStartPosition().lineNumber - liftedOtherSelection.getStartPosition().lineNumber) < EditorState.EDITOR_SELECTION_THRESHOLD) { - // ignore selection changes in the range of EditorState.EDITOR_SELECTION_THRESHOLD lines - return false; + return false; // ignore selection changes in the range of EditorState.EDITOR_SELECTION_THRESHOLD lines } return true; } } -interface ILegacySerializedEditorInput { - id: string; - value: string; -} - interface ISerializedFileEditorInput { resource: string; } @@ -83,31 +79,49 @@ export abstract class BaseHistoryService { protected toUnbind: IDisposable[]; private activeEditorListeners: IDisposable[]; - private _isPure: boolean; + private isPure: boolean; + private showFullPath: boolean; + + private static NLS_UNSUPPORTED = nls.localize('patchedWindowTitle', "[Unsupported]"); constructor( protected editorGroupService: IEditorGroupService, protected editorService: IWorkbenchEditorService, protected contextService: IWorkspaceContextService, + private configurationService: IConfigurationService, private environmentService: IEnvironmentService, integrityService: IIntegrityService ) { this.toUnbind = []; this.activeEditorListeners = []; - this._isPure = true; + this.isPure = true; // Window Title window.document.title = this.getWindowTitle(null); + // Integrity + integrityService.isPure().then(r => { + if (!r.isPure) { + this.isPure = false; + window.document.title = this.getWindowTitle(this.editorService.getActiveEditorInput()); + } + }); + // Editor Input Changes this.toUnbind.push(this.editorGroupService.onEditorsChanged(() => this.onEditorsChanged())); - integrityService.isPure().then((r) => { - if (!r.isPure) { - this._isPure = false; - window.document.title = this.getWindowTitle(null); - } - }); + // Configuration Changes + this.toUnbind.push(this.configurationService.onDidUpdateConfiguration(() => this.onConfigurationChanged(true))); + this.onConfigurationChanged(); + } + + private onConfigurationChanged(update?: boolean): void { + const currentShowPath = this.showFullPath; + this.showFullPath = this.configurationService.lookup('window.showFullPath').value; + + if (update && currentShowPath !== this.showFullPath) { + this.updateWindowTitle(this.editorService.getActiveEditorInput()); + } } private onEditorsChanged(): void { @@ -169,8 +183,8 @@ export abstract class BaseHistoryService { protected getWindowTitle(input?: IEditorInput): string { let title = this.doGetWindowTitle(input); - if (!this._isPure) { - title += nls.localize('patchedWindowTitle', " [Unsupported]"); + if (!this.isPure) { + title = `${title} ${BaseHistoryService.NLS_UNSUPPORTED}`; } // Extension Development Host gets a special title to identify itself @@ -184,9 +198,19 @@ export abstract class BaseHistoryService { private doGetWindowTitle(input?: IEditorInput): string { const appName = product.nameLong; - let prefix = input && input.getName(); + let prefix: string; + const fileInput = asFileEditorInput(input); + if (fileInput && this.showFullPath) { + prefix = labels.getPathLabel(fileInput.getResource()); + if ((platform.isMacintosh || platform.isLinux) && prefix.indexOf(this.environmentService.userHome) === 0) { + prefix = `~${prefix.substr(this.environmentService.userHome.length)}`; + } + } else { + prefix = input && input.getName(); + } + if (prefix && input) { - if ((input).isDirty() && !platform.isMacintosh /* Mac has its own decoration in window */) { + if (input.isDirty() && !platform.isMacintosh /* Mac has its own decoration in window */) { prefix = nls.localize('prefixDecoration', "\u25cf {0}", prefix); } } @@ -261,11 +285,13 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic @IEnvironmentService environmentService: IEnvironmentService, @IWorkspaceContextService contextService: IWorkspaceContextService, @IStorageService private storageService: IStorageService, + @IConfigurationService configurationService: IConfigurationService, @ILifecycleService private lifecycleService: ILifecycleService, + @IEventService private eventService: IEventService, @IInstantiationService private instantiationService: IInstantiationService, @IIntegrityService integrityService: IIntegrityService ) { - super(editorGroupService, editorService, contextService, environmentService, integrityService); + super(editorGroupService, editorService, contextService, configurationService, environmentService, integrityService); this.index = -1; this.stack = []; @@ -280,6 +306,15 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic this.toUnbind.push(this.lifecycleService.onShutdown(() => this.save())); this.toUnbind.push(this.editorGroupService.onEditorOpenFail(editor => this.remove(editor))); this.toUnbind.push(this.editorGroupService.getStacksModel().onEditorClosed(event => this.onEditorClosed(event))); + + // File changes + this.toUnbind.push(this.eventService.addListener2(EventType.FILE_CHANGES, (e: FileChangesEvent) => this.onFileChanges(e))); + } + + private onFileChanges(e: FileChangesEvent): void { + if (e.gotDeleted()) { + this.remove(e); // remove from history files that got deleted or moved + } } private onEditorClosed(event: IGroupEvent): void { @@ -302,7 +337,7 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic } public reopenLastClosedEditor(): void { - this.ensureLoaded(); + this.ensureHistoryLoaded(); const stacks = this.editorGroupService.getStacksModel(); @@ -331,7 +366,7 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic } public clear(): void { - this.ensureLoaded(); + this.ensureHistoryLoaded(); this.index = -1; this.stack.splice(0); @@ -360,7 +395,7 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic openEditorPromise.done(() => { this.blockStackChanges = false; - }, (error) => { + }, error => { this.blockStackChanges = false; errors.onUnexpectedError(error); }); @@ -383,7 +418,7 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic return; } - this.ensureLoaded(); + this.ensureHistoryLoaded(); const historyInput = this.preferResourceInput(input); @@ -406,33 +441,18 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic } } - public remove(input: IEditorInput | IResourceInput): void { - this.removeFromHistory(input); - this.removeFromStack(input); - this.removeFromRecentlyClosedFiles(input); + public remove(input: IEditorInput | IResourceInput): void; + public remove(input: FileChangesEvent): void; + public remove(arg1: IEditorInput | IResourceInput | FileChangesEvent): void { + this.removeFromHistory(arg1); + this.removeFromStack(arg1); + this.removeFromRecentlyClosedFiles(arg1); } - private removeFromHistory(input: IEditorInput | IResourceInput, index?: number): void { - this.ensureLoaded(); + private removeFromHistory(arg1: IEditorInput | IResourceInput | FileChangesEvent): void { + this.ensureHistoryLoaded(); - if (typeof index !== 'number') { - index = this.indexOf(input); - } - - if (index >= 0) { - this.history.splice(index, 1); - } - } - - private indexOf(input: IEditorInput | IResourceInput): number { - for (let i = 0; i < this.history.length; i++) { - const entry = this.history[i]; - if (this.matches(input, entry)) { - return i; - } - } - - return -1; + this.history = this.history.filter(e => !this.matches(arg1, e)); } private handleEditorEventInStack(editor: IBaseEditor, storeSelection: boolean): void { @@ -567,23 +587,13 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic return s1.startLineNumber === s2.startLineNumber; // we consider the history entry same if we are on the same line } - private removeFromStack(input: IEditorInput | IResourceInput): void { - this.stack.forEach((e, i) => { - if (this.matches(input, e.input)) { - this.stack.splice(i, 1); - if (this.index >= i) { - this.index--; // reduce index if the element is before index - } - } - }); + private removeFromStack(arg1: IEditorInput | IResourceInput | FileChangesEvent): void { + this.stack = this.stack.filter(e => !this.matches(arg1, e.input)); + this.index = this.stack.length - 1; // reset index } - private removeFromRecentlyClosedFiles(input: IEditorInput | IResourceInput): void { - this.recentlyClosedFiles.forEach((e, i) => { - if (this.matchesFile(e.resource, input)) { - this.recentlyClosedFiles.splice(i, 1); - } - }); + private removeFromRecentlyClosedFiles(arg1: IEditorInput | IResourceInput | FileChangesEvent): void { + this.recentlyClosedFiles = this.recentlyClosedFiles.filter(e => !this.matchesFile(e.resource, arg1)); } private isFileOpened(resource: URI, group: IEditorGroup): boolean { @@ -598,46 +608,60 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic return group.getEditors().some(e => this.matchesFile(resource, e)); } - private matches(inputA: IEditorInput | IResourceInput, inputB: IEditorInput | IResourceInput): boolean { - if (inputA instanceof EditorInput && inputB instanceof EditorInput) { - return inputA.matches(inputB); + private matches(arg1: IEditorInput | IResourceInput | FileChangesEvent, inputB: IEditorInput | IResourceInput): boolean { + if (arg1 instanceof FileChangesEvent) { + if (inputB instanceof EditorInput) { + return false; // we only support this for IResourceInput + } + + const resourceInputB = inputB as IResourceInput; + + return arg1.contains(resourceInputB.resource, FileChangeType.DELETED); } - if (inputA instanceof EditorInput) { - return this.matchesFile((inputB as IResourceInput).resource, inputA); + if (arg1 instanceof EditorInput && inputB instanceof EditorInput) { + return arg1.matches(inputB); + } + + if (arg1 instanceof EditorInput) { + return this.matchesFile((inputB as IResourceInput).resource, arg1); } if (inputB instanceof EditorInput) { - return this.matchesFile((inputA as IResourceInput).resource, inputB); + return this.matchesFile((arg1 as IResourceInput).resource, inputB); } - const resourceInputA = inputA as IResourceInput; + const resourceInputA = arg1 as IResourceInput; const resourceInputB = inputB as IResourceInput; return resourceInputA && resourceInputB && resourceInputA.resource.toString() === resourceInputB.resource.toString(); } - private matchesFile(resource: URI, input: IEditorInput | IResourceInput): boolean { - if (input instanceof EditorInput) { - const fileInput = asFileEditorInput(input); + private matchesFile(resource: URI, arg2: IEditorInput | IResourceInput | FileChangesEvent): boolean { + if (arg2 instanceof FileChangesEvent) { + return arg2.contains(resource, FileChangeType.DELETED); + } + + if (arg2 instanceof EditorInput) { + const fileInput = asFileEditorInput(arg2); return fileInput && fileInput.getResource().toString() === resource.toString(); } - const resourceInput = input as IResourceInput; + const resourceInput = arg2 as IResourceInput; return resourceInput && resourceInput.resource.toString() === resource.toString(); } public getHistory(): (IEditorInput | IResourceInput)[] { - this.ensureLoaded(); + this.ensureHistoryLoaded(); return this.history.slice(0); } - private ensureLoaded(): void { + private ensureHistoryLoaded(): void { if (!this.loaded) { - this.load(); + this.loadHistory(); } this.loaded = true; @@ -659,8 +683,8 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic this.storageService.store(HistoryService.STORAGE_KEY, JSON.stringify(entries), StorageScope.WORKSPACE); } - private load(): void { - let entries: (ILegacySerializedEditorInput | ISerializedFileEditorInput)[] = []; + private loadHistory(): void { + let entries: ISerializedFileEditorInput[] = []; const entriesRaw = this.storageService.get(HistoryService.STORAGE_KEY, StorageScope.WORKSPACE); if (entriesRaw) { @@ -668,24 +692,8 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic } this.history = entries.map(entry => { - const serializedLegacyInput = entry as ILegacySerializedEditorInput; const serializedFileInput = entry as ISerializedFileEditorInput; - - // Legacy support (TODO@Ben remove me - migration) - if (serializedLegacyInput.id) { - const factory = this.registry.getEditorInputFactory(serializedLegacyInput.id); - if (factory && typeof serializedLegacyInput.value === 'string') { - const fileInput = asFileEditorInput(factory.deserialize(this.instantiationService, serializedLegacyInput.value)); - if (fileInput) { - return { resource: fileInput.getResource() } as IResourceInput; - } - - return void 0; - } - } - - // New resource input support - else if (serializedFileInput.resource) { + if (serializedFileInput.resource) { return { resource: URI.parse(serializedFileInput.resource) } as IResourceInput; } diff --git a/src/vs/workbench/services/lifecycle/electron-browser/lifecycleService.ts b/src/vs/workbench/services/lifecycle/electron-browser/lifecycleService.ts index 21bceef3d25..fe3b0d3fcaf 100644 --- a/src/vs/workbench/services/lifecycle/electron-browser/lifecycleService.ts +++ b/src/vs/workbench/services/lifecycle/electron-browser/lifecycleService.ts @@ -9,7 +9,7 @@ import Severity from 'vs/base/common/severity'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { ILifecycleService, ShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; import { IMessageService } from 'vs/platform/message/common/message'; -import { IWindowService } from 'vs/workbench/services/window/electron-browser/windowService'; +import { IWindowIPCService } from 'vs/workbench/services/window/electron-browser/windowService'; import { ipcRenderer as ipc } from 'electron'; import Event, { Emitter } from 'vs/base/common/event'; @@ -24,7 +24,7 @@ export class LifecycleService implements ILifecycleService { constructor( @IMessageService private messageService: IMessageService, - @IWindowService private windowService: IWindowService + @IWindowIPCService private windowService: IWindowIPCService ) { this.registerListeners(); } diff --git a/src/vs/workbench/services/message/browser/messageService.ts b/src/vs/workbench/services/message/browser/messageService.ts index 341bfb1e814..cc321faff2d 100644 --- a/src/vs/workbench/services/message/browser/messageService.ts +++ b/src/vs/workbench/services/message/browser/messageService.ts @@ -6,14 +6,12 @@ import errors = require('vs/base/common/errors'); import { toErrorMessage } from 'vs/base/common/errorMessage'; -import { TPromise } from 'vs/base/common/winjs.base'; import types = require('vs/base/common/types'); import { MessageList, Severity as BaseSeverity } from 'vs/workbench/services/message/browser/messagelist/messageList'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { IMessageService, IChoiceService, IMessageWithAction, IConfirmation, Severity } from 'vs/platform/message/common/message'; +import { IMessageService, IMessageWithAction, IConfirmation, Severity } from 'vs/platform/message/common/message'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import Event from 'vs/base/common/event'; -import { Action } from 'vs/base/common/actions'; interface IBufferedMessage { severity: Severity; @@ -22,7 +20,7 @@ interface IBufferedMessage { disposeFn: () => void; } -export class WorkbenchMessageService implements IMessageService, IChoiceService { +export class WorkbenchMessageService implements IMessageService { public _serviceBrand: any; @@ -147,23 +145,6 @@ export class WorkbenchMessageService implements IMessageService, IChoiceService return window.confirm(messageText); } - choose(severity: Severity, message: string, options: string[]): TPromise { - let onCancel = null; - - const promise = new TPromise((c, e) => { - const callback = index => () => { - c(index); - return TPromise.as(true); - }; - - const actions = options.map((option, index) => new Action('?', option, '', true, callback(index))); - - onCancel = this.show(severity, { message, actions }, () => promise.cancel()); - }, () => onCancel()); - - return promise; - } - public dispose(): void { while (this.disposeables.length) { this.disposeables.pop().dispose(); diff --git a/src/vs/workbench/services/message/electron-browser/messageService.ts b/src/vs/workbench/services/message/electron-browser/messageService.ts index 4074e981512..676204fe1d4 100644 --- a/src/vs/workbench/services/message/electron-browser/messageService.ts +++ b/src/vs/workbench/services/message/electron-browser/messageService.ts @@ -5,19 +5,21 @@ 'use strict'; -import { IWindowService } from 'vs/workbench/services/window/electron-browser/windowService'; +import { IWindowIPCService } from 'vs/workbench/services/window/electron-browser/windowService'; import nls = require('vs/nls'); import product from 'vs/platform/product'; +import { TPromise } from 'vs/base/common/winjs.base'; import { WorkbenchMessageService } from 'vs/workbench/services/message/browser/messageService'; -import { IConfirmation } from 'vs/platform/message/common/message'; +import { IConfirmation, Severity, IChoiceService } from 'vs/platform/message/common/message'; import { isWindows, isLinux } from 'vs/base/common/platform'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { Action } from 'vs/base/common/actions'; -export class MessageService extends WorkbenchMessageService { +export class MessageService extends WorkbenchMessageService implements IChoiceService { constructor( container: HTMLElement, - @IWindowService private windowService: IWindowService, + @IWindowIPCService private windowService: IWindowIPCService, @ITelemetryService telemetryService: ITelemetryService ) { super(container, telemetryService); @@ -27,28 +29,18 @@ export class MessageService extends WorkbenchMessageService { if (!confirmation.primaryButton) { confirmation.primaryButton = nls.localize({ key: 'yesButton', comment: ['&& denotes a mnemonic'] }, "&&Yes"); } - if (!confirmation.secondaryButton) { confirmation.secondaryButton = nls.localize('cancelButton', "Cancel"); } let opts: Electron.ShowMessageBoxOptions = { - title: confirmation.title || product.nameLong, + title: confirmation.title, message: confirmation.message, - buttons: [ - isLinux ? this.mnemonicLabel(confirmation.secondaryButton) : this.mnemonicLabel(confirmation.primaryButton), - isLinux ? this.mnemonicLabel(confirmation.primaryButton) : this.mnemonicLabel(confirmation.secondaryButton) - ], - noLink: true, + buttons: [confirmation.primaryButton, confirmation.secondaryButton], + defaultId: 0, cancelId: 1 }; - // Linux: buttons are swapped - if (isLinux) { - opts.defaultId = 1; - opts.cancelId = 0; - } - if (confirmation.detail) { opts.detail = confirmation.detail; } @@ -57,15 +49,52 @@ export class MessageService extends WorkbenchMessageService { opts.type = confirmation.type; } - let result = this.windowService.getWindow().showMessageBox(opts); - - if (isLinux) { - return result === 1 ? true : false; // Linux: buttons are swapped - } + let result = this.showMessageBox(opts); return result === 0 ? true : false; } + choose(severity: Severity, message: string, options: string[], modal: boolean = false): TPromise { + if (modal) { + const type: 'none' | 'info' | 'error' | 'question' | 'warning' = severity === Severity.Info ? 'question' : severity === Severity.Error ? 'error' : severity === Severity.Warning ? 'warning' : 'none'; + return TPromise.wrap(this.showMessageBox({ message, buttons: options, type })); + } + + let onCancel = null; + + const promise = new TPromise((c, e) => { + const callback = index => () => { + c(index); + return TPromise.as(true); + }; + + const actions = options.map((option, index) => new Action('?', option, '', true, callback(index))); + + onCancel = this.show(severity, { message, actions }, () => promise.cancel()); + }, () => onCancel()); + + return promise; + } + + private showMessageBox(opts: Electron.ShowMessageBoxOptions): number { + opts.buttons = opts.buttons.map(button => this.mnemonicLabel(button)); + opts.buttons = isLinux ? opts.buttons.reverse() : opts.buttons; + + if (opts.defaultId !== void 0) { + opts.defaultId = isLinux ? opts.buttons.length - opts.defaultId - 1 : opts.defaultId; + } + + if (opts.cancelId !== void 0) { + opts.cancelId = isLinux ? opts.buttons.length - opts.cancelId - 1 : opts.cancelId; + } + + opts.noLink = true; + opts.title = opts.title || product.nameLong; + + const result = this.windowService.getWindow().showMessageBox(opts); + return isLinux ? opts.buttons.length - result - 1 : result; + } + private mnemonicLabel(label: string): string { if (!isWindows) { return label.replace(/\(&&\w\)|&&/g, ''); // no mnemonic support on mac/linux diff --git a/src/vs/workbench/services/textfile/electron-browser/textFileService.ts b/src/vs/workbench/services/textfile/electron-browser/textFileService.ts index f71a2929e06..980c1bbd88a 100644 --- a/src/vs/workbench/services/textfile/electron-browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/electron-browser/textFileService.ts @@ -22,7 +22,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IWindowService } from 'vs/workbench/services/window/electron-browser/windowService'; +import { IWindowIPCService } from 'vs/workbench/services/window/electron-browser/windowService'; import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ModelBuilder } from 'vs/editor/node/model/modelBuilder'; @@ -45,7 +45,7 @@ export class TextFileService extends AbstractTextFileService { @IModeService private modeService: IModeService, @IWorkbenchEditorService editorService: IWorkbenchEditorService, @IEditorGroupService editorGroupService: IEditorGroupService, - @IWindowService private windowService: IWindowService, + @IWindowIPCService private windowService: IWindowIPCService, @IModelService private modelService: IModelService, @IEnvironmentService private environmentService: IEnvironmentService ) { diff --git a/src/vs/workbench/services/themes/electron-browser/themeService.ts b/src/vs/workbench/services/themes/electron-browser/themeService.ts index 604ed663470..fce74dd687e 100644 --- a/src/vs/workbench/services/themes/electron-browser/themeService.ts +++ b/src/vs/workbench/services/themes/electron-browser/themeService.ts @@ -14,7 +14,7 @@ import { ExtensionsRegistry, ExtensionMessageCollector } from 'vs/platform/exten import { IThemeService, IThemeData, IThemeSetting, IThemeDocument } from 'vs/workbench/services/themes/common/themeService'; import { TokenStylesContribution, EditorStylesContribution, SearchViewStylesContribution, TerminalStylesContribution } from 'vs/workbench/services/themes/electron-browser/stylesContributions'; import { getBaseThemeId } from 'vs/platform/theme/common/themes'; -import { IWindowService } from 'vs/workbench/services/window/electron-browser/windowService'; +import { IWindowIPCService } from 'vs/workbench/services/window/electron-browser/windowService'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { Registry } from 'vs/platform/platform'; @@ -163,7 +163,7 @@ export class ThemeService implements IThemeService { constructor( @IExtensionService private extensionService: IExtensionService, - @IWindowService private windowService: IWindowService, + @IWindowIPCService private windowService: IWindowIPCService, @IStorageService private storageService: IStorageService, @ITelemetryService private telemetryService: ITelemetryService) { diff --git a/src/vs/workbench/services/untitled/common/untitledEditorService.ts b/src/vs/workbench/services/untitled/common/untitledEditorService.ts index e064aa9da5f..dfa4462df83 100644 --- a/src/vs/workbench/services/untitled/common/untitledEditorService.ts +++ b/src/vs/workbench/services/untitled/common/untitledEditorService.ts @@ -162,6 +162,11 @@ export class UntitledEditorService implements IUntitledEditorService { } const input = this.instantiationService.createInstance(UntitledEditorInput, resource, hasAssociatedFilePath, modeId); + if (input.isDirty()) { + setTimeout(() => { + this._onDidChangeDirty.fire(resource); + }, 0 /* prevent race condition between creating input and emitting dirty event */); + } const dirtyListener = input.onDidChangeDirty(() => { this._onDidChangeDirty.fire(resource); diff --git a/src/vs/workbench/services/window/electron-browser/windowService.ts b/src/vs/workbench/services/window/electron-browser/windowService.ts index 3eece926686..40c876c545b 100644 --- a/src/vs/workbench/services/window/electron-browser/windowService.ts +++ b/src/vs/workbench/services/window/electron-browser/windowService.ts @@ -13,10 +13,10 @@ import { ipcRenderer as ipc, remote } from 'electron'; const windowId = remote.getCurrentWindow().id; -export const IWindowService = createDecorator('windowService'); +export const IWindowIPCService = createDecorator('windowIPCService'); export interface IWindowServices { - windowService?: IWindowService; + windowService?: IWindowIPCService; } export interface IBroadcast { @@ -24,7 +24,7 @@ export interface IBroadcast { payload: any; } -export interface IWindowService { +export interface IWindowIPCService { _serviceBrand: any; getWindowId(): number; @@ -38,7 +38,11 @@ export interface IWindowService { onBroadcast: Event; } -export class WindowService implements IWindowService { +/** + * TODO@Joao: remove this service + * @deprecated + */ +export class WindowIPCService implements IWindowIPCService { public _serviceBrand: any; private win: ElectronWindow; diff --git a/src/vs/workbench/workbench.main.ts b/src/vs/workbench/workbench.main.ts index db6f5bf951c..5b880768ba8 100644 --- a/src/vs/workbench/workbench.main.ts +++ b/src/vs/workbench/workbench.main.ts @@ -57,7 +57,7 @@ import 'vs/workbench/parts/markers/browser/markersPanel'; // can be packaged sep import 'vs/workbench/parts/html/browser/html.contribution'; import 'vs/workbench/parts/extensions/electron-browser/extensions.contribution'; -import 'vs/workbench/parts/extensions/electron-browser/extensionsQuickOpen'; +import 'vs/workbench/parts/extensions/browser/extensionsQuickOpen'; import 'vs/workbench/parts/extensions/electron-browser/extensionsViewlet'; // can be packaged separately import 'vs/workbench/parts/explorers/browser/treeExplorerViewlet.contribution';