Merge remote-tracking branch 'origin/master' into tyriar/xterm-update

This commit is contained in:
Daniel Imms 2019-05-10 19:54:14 -07:00
commit 86f3cb92d8
11 changed files with 124 additions and 85 deletions

View file

@ -60,6 +60,18 @@ interface PreviewStyleLoadErrorMessage extends WebviewMessage {
};
}
export class PreviewDocumentVersion {
public constructor(
public readonly resource: vscode.Uri,
public readonly version: number,
) { }
public equals(other: PreviewDocumentVersion): boolean {
return this.resource.fsPath === other.resource.fsPath
&& this.version === other.version;
}
}
export class MarkdownPreview extends Disposable {
public static viewType = 'markdown.preview';
@ -71,7 +83,7 @@ export class MarkdownPreview extends Disposable {
private throttleTimer: any;
private line: number | undefined = undefined;
private firstUpdate = true;
private currentVersion?: { resource: vscode.Uri, version: number };
private currentVersion?: PreviewDocumentVersion;
private forceUpdate = false;
private isScrolling = false;
private _disposed: boolean = false;
@ -389,7 +401,8 @@ export class MarkdownPreview extends Disposable {
return;
}
if (!this.forceUpdate && this.currentVersion && this.currentVersion.resource.fsPath === resource.fsPath && this.currentVersion.version === document.version) {
const pendingVersion = new PreviewDocumentVersion(resource, document.version);
if (!this.forceUpdate && this.currentVersion && this.currentVersion.equals(pendingVersion)) {
if (this.line) {
this.updateForView(resource, this.line);
}
@ -397,12 +410,16 @@ export class MarkdownPreview extends Disposable {
}
this.forceUpdate = false;
this.currentVersion = { resource, version: document.version };
this.currentVersion = pendingVersion;
if (this._resource === resource) {
const content = await this._contentProvider.provideTextDocumentContent(document, this._previewConfigurations, this.line, this.state);
// Another call to `doUpdate` may have happened.
// Make sure we are still updating for the correct document
if (this.currentVersion && this.currentVersion.equals(pendingVersion)) {
this.setContent(content);
}
}
}
private static getWebviewOptions(
resource: vscode.Uri,

View file

@ -6,7 +6,7 @@
import * as assert from 'assert';
import 'mocha';
import * as vscode from 'vscode';
import { snippetForFunctionCall } from "../utils/snippetForFunctionCall";
import { snippetForFunctionCall } from '../utils/snippetForFunctionCall';
suite('typescript function call snippets', () => {
test('Should use label as function name', async () => {

View file

@ -370,7 +370,7 @@ class InlineImageView {
dispose: () => combinedDisposable(disposables).dispose()
};
const cacheKey = descriptor.resource.toString();
const cacheKey = `${descriptor.resource.toString()}:${descriptor.etag}`;
let ctrlPressed = false;
let altPressed = false;

View file

@ -203,7 +203,7 @@ export class OpenFileHandler extends QuickOpenHandler {
const queryOptions: IFileQueryBuilderOptions = {
_reason: 'openFileHandler',
extraFileResources: getOutOfWorkspaceEditorResources(this.editorService, this.contextService),
filePattern: query.value,
filePattern: query.original,
cacheKey
};

View file

@ -2013,7 +2013,7 @@ class TaskService extends Disposable implements ITaskService {
Severity.Info,
nls.localize('TaskService.ignoredFolder', 'The following workspace folders are ignored since they use task version 0.1.0: {0}', this.ignoredWorkspaceFolders.map(f => f.name).join(', ')),
[{
label: nls.localize('TaskService.notAgain', 'Don\'t Show Again'),
label: nls.localize('TaskService.notAgain', "Don't Show Again"),
isSecondary: true,
run: () => {
this.storageService.store(TaskService.IgnoreTask010DonotShowAgain_key, true, StorageScope.WORKSPACE);

View file

@ -25,7 +25,7 @@ import { activeContrastBorder, scrollbarSliderActiveBackground, scrollbarSliderB
import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { PANEL_BACKGROUND } from 'vs/workbench/common/theme';
import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/terminalWidgetManager';
import { IShellLaunchConfig, ITerminalDimensions, ITerminalInstance, ITerminalProcessManager, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, ProcessState, TERMINAL_PANEL_ID, IWindowsShellHelper } from 'vs/workbench/contrib/terminal/common/terminal';
import { IShellLaunchConfig, ITerminalDimensions, ITerminalInstance, ITerminalProcessManager, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, ProcessState, TERMINAL_PANEL_ID, IWindowsShellHelper, SHELL_PATH_INVALID_EXIT_CODE } from 'vs/workbench/contrib/terminal/common/terminal';
import { ansiColorIdentifiers, TERMINAL_BACKGROUND_COLOR, TERMINAL_CURSOR_BACKGROUND_COLOR, TERMINAL_CURSOR_FOREGROUND_COLOR, TERMINAL_FOREGROUND_COLOR, TERMINAL_SELECTION_BACKGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry';
import { TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminalCommands';
import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper';
@ -969,19 +969,41 @@ export class TerminalInstance implements ITerminalInstance {
}
this._isExiting = true;
let exitCodeMessage: string;
let exitCodeMessage: string | undefined;
// Create exit code message
if (exitCode) {
if (exitCode === SHELL_PATH_INVALID_EXIT_CODE) {
exitCodeMessage = nls.localize('terminal.integrated.exitedWithInvalidPath', 'The terminal shell path does not exist: {0}', this._shellLaunchConfig.executable);
} else if (this._processManager && this._processManager.processState === ProcessState.KILLED_DURING_LAUNCH) {
let args = '';
if (typeof this._shellLaunchConfig.args === 'string') {
args = ` ${this._shellLaunchConfig.args}`;
} else if (this._shellLaunchConfig.args && this._shellLaunchConfig.args.length) {
args = ' ' + this._shellLaunchConfig.args.map(a => {
if (typeof a === 'string' && a.indexOf(' ') !== -1) {
return `'${a}'`;
}
return a;
}).join(' ');
}
if (this._shellLaunchConfig.executable) {
exitCodeMessage = nls.localize('terminal.integrated.launchFailed', 'The terminal process command \'{0}{1}\' failed to launch (exit code: {2})', this._shellLaunchConfig.executable, args, exitCode);
} else {
exitCodeMessage = nls.localize('terminal.integrated.launchFailedExtHost', 'The terminal process failed to launch (exit code: {0})', exitCode);
}
} else {
exitCodeMessage = nls.localize('terminal.integrated.exitedWithCode', 'The terminal process terminated with exit code: {0}', exitCode);
}
}
this._logService.debug(`Terminal process exit (id: ${this.id})${this._processManager ? ' state ' + this._processManager.processState : ''}`);
// Only trigger wait on exit when the exit was *not* triggered by the
// user (via the `workbench.action.terminal.kill` command).
if (this._shellLaunchConfig.waitOnExit && (!this._processManager || this._processManager.processState !== ProcessState.KILLED_BY_USER)) {
if (exitCode) {
this._xterm.writeln(exitCodeMessage!);
if (exitCodeMessage) {
this._xterm.writeln(exitCodeMessage);
}
if (typeof this._shellLaunchConfig.waitOnExit === 'string') {
let message = this._shellLaunchConfig.waitOnExit;
@ -996,29 +1018,14 @@ export class TerminalInstance implements ITerminalInstance {
}
} else {
this.dispose();
if (exitCode) {
if (exitCodeMessage) {
if (this._processManager && this._processManager.processState === ProcessState.KILLED_DURING_LAUNCH) {
let args = '';
if (typeof this._shellLaunchConfig.args === 'string') {
args = this._shellLaunchConfig.args;
} else if (this._shellLaunchConfig.args && this._shellLaunchConfig.args.length) {
args = ' ' + this._shellLaunchConfig.args.map(a => {
if (typeof a === 'string' && a.indexOf(' ') !== -1) {
return `'${a}'`;
}
return a;
}).join(' ');
}
if (this._shellLaunchConfig.executable) {
this._notificationService.error(nls.localize('terminal.integrated.launchFailed', 'The terminal process command \'{0}{1}\' failed to launch (exit code: {2})', this._shellLaunchConfig.executable, args, exitCode));
} else {
this._notificationService.error(nls.localize('terminal.integrated.launchFailedExtHost', 'The terminal process failed to launch (exit code: {0})', exitCode));
}
this._notificationService.error(exitCodeMessage);
} else {
if (this._configHelper.config.showExitAlert) {
this._notificationService.error(exitCodeMessage!);
this._notificationService.error(exitCodeMessage);
} else {
console.warn(exitCodeMessage!);
console.warn(exitCodeMessage);
}
}
}

View file

@ -58,6 +58,7 @@ export const TERMINAL_CONFIG_SECTION = 'terminal.integrated';
export const DEFAULT_LETTER_SPACING = 0;
export const MINIMUM_LETTER_SPACING = -5;
export const DEFAULT_LINE_HEIGHT = 1;
export const SHELL_PATH_INVALID_EXIT_CODE = -1;
export type FontWeight = 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900';
@ -695,7 +696,6 @@ export const enum ProcessState {
KILLED_BY_PROCESS
}
export interface ITerminalProcessExtHostProxy extends IDisposable {
readonly terminalId: number;

View file

@ -11,13 +11,13 @@ import * as fs from 'fs';
import { Event, Emitter } from 'vs/base/common/event';
import { getWindowsBuildNumber } from 'vs/workbench/contrib/terminal/node/terminal';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IShellLaunchConfig, ITerminalChildProcess } from 'vs/workbench/contrib/terminal/common/terminal';
import { IShellLaunchConfig, ITerminalChildProcess, SHELL_PATH_INVALID_EXIT_CODE } from 'vs/workbench/contrib/terminal/common/terminal';
import { exec } from 'child_process';
export class TerminalProcess implements ITerminalChildProcess, IDisposable {
private _exitCode: number;
private _closeTimeout: any;
private _ptyProcess: pty.IPty;
private _ptyProcess: pty.IPty | undefined;
private _currentTitle: string = '';
private _processStartupComplete: Promise<void>;
private _isDisposed: boolean = false;
@ -69,37 +69,39 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable {
experimentalUseConpty: useConpty
};
try {
this._ptyProcess = pty.spawn(shellLaunchConfig.executable!, shellLaunchConfig.args || [], options);
this._processStartupComplete = new Promise<void>(c => {
this.onProcessIdReady((pid) => {
c();
});
});
} catch (error) {
// The only time this is expected to happen is when the file specified to launch with does not exist.
this._exitCode = 2;
fs.stat(shellLaunchConfig.executable!, (err) => {
if (err && err.code === 'ENOENT') {
this._exitCode = SHELL_PATH_INVALID_EXIT_CODE;
this._queueProcessExit();
this._processStartupComplete = Promise.resolve(undefined);
return;
}
this._ptyProcess.on('data', (data) => {
this.setupPtyProcess(shellLaunchConfig, options);
});
}
private setupPtyProcess(shellLaunchConfig: IShellLaunchConfig, options: pty.IPtyForkOptions): void {
const ptyProcess = pty.spawn(shellLaunchConfig.executable!, shellLaunchConfig.args || [], options);
this._ptyProcess = ptyProcess;
this._processStartupComplete = new Promise<void>(c => {
this.onProcessIdReady(() => c());
});
ptyProcess.on('data', (data) => {
this._onProcessData.fire(data);
if (this._closeTimeout) {
clearTimeout(this._closeTimeout);
this._queueProcessExit();
}
});
this._ptyProcess.on('exit', (code) => {
ptyProcess.on('exit', (code) => {
this._exitCode = code;
this._queueProcessExit();
});
this._setupTitlePolling(ptyProcess);
// TODO: We should no longer need to delay this since pty.spawn is sync
setTimeout(() => {
this._sendProcessId();
this._sendProcessId(ptyProcess);
}, 500);
this._setupTitlePolling();
}
public dispose(): void {
@ -114,15 +116,15 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable {
this._onProcessTitleChanged.dispose();
}
private _setupTitlePolling() {
private _setupTitlePolling(ptyProcess: pty.IPty) {
// Send initial timeout async to give event listeners a chance to init
setTimeout(() => {
this._sendProcessTitle();
this._sendProcessTitle(ptyProcess);
}, 0);
// Setup polling
this._titleInterval = setInterval(() => {
if (this._currentTitle !== this._ptyProcess.process) {
this._sendProcessTitle();
if (this._currentTitle !== ptyProcess.process) {
this._sendProcessTitle(ptyProcess);
}
}, 200);
}
@ -146,7 +148,9 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable {
// Attempt to kill the pty, it may have already been killed at this
// point but we want to make sure
try {
if (this._ptyProcess) {
this._ptyProcess.kill();
}
} catch (ex) {
// Swallow, the pty has already been killed
}
@ -155,15 +159,15 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable {
});
}
private _sendProcessId() {
this._onProcessIdReady.fire(this._ptyProcess.pid);
private _sendProcessId(ptyProcess: pty.IPty) {
this._onProcessIdReady.fire(ptyProcess.pid);
}
private _sendProcessTitle(): void {
private _sendProcessTitle(ptyProcess: pty.IPty): void {
if (this._isDisposed) {
return;
}
this._currentTitle = this._ptyProcess.process;
this._currentTitle = ptyProcess.process;
this._onProcessTitleChanged.fire(this._currentTitle);
}
@ -176,7 +180,7 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable {
}
public input(data: string): void {
if (this._isDisposed) {
if (this._isDisposed || !this._ptyProcess) {
return;
}
this._ptyProcess.write(data);
@ -188,8 +192,10 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable {
}
// Ensure that cols and rows are always >= 1, this prevents a native
// exception in winpty.
if (this._ptyProcess) {
this._ptyProcess.resize(Math.max(cols, 1), Math.max(rows, 1));
}
}
public getInitialCwd(): Promise<string> {
return Promise.resolve(this._initialCwd);
@ -198,6 +204,10 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable {
public getCwd(): Promise<string> {
if (platform.isMacintosh) {
return new Promise<string>(resolve => {
if (!this._ptyProcess) {
resolve(this._initialCwd);
return;
}
exec('lsof -p ' + this._ptyProcess.pid + ' | grep cwd', (error, stdout, stderr) => {
if (stdout !== '') {
resolve(stdout.substring(stdout.indexOf('/'), stdout.length - 1));
@ -208,6 +218,10 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable {
if (platform.isLinux) {
return new Promise<string>(resolve => {
if (!this._ptyProcess) {
resolve(this._initialCwd);
return;
}
fs.readlink('/proc/' + this._ptyProcess.pid + '/cwd', (err, linkedstr) => {
if (err) {
resolve(this._initialCwd);

View file

@ -22,6 +22,7 @@ import { URI } from 'vs/base/common/uri';
import { readdir } from 'vs/base/node/pfs';
import { IFileQuery, IFolderQuery, IProgressMessage, ISearchEngineStats, IRawFileMatch, ISearchEngine, ISearchEngineSuccess } from 'vs/workbench/services/search/common/search';
import { spawnRipgrepCmd } from './ripgrepFileSearch';
import { prepareQuery } from 'vs/base/parts/quickopen/common/quickOpenScorer';
interface IDirectoryEntry {
base: string;
@ -76,7 +77,7 @@ export class FileWalker {
this.errors = [];
if (this.filePattern) {
this.normalizedFilePatternLowercase = strings.stripWildcards(this.filePattern).toLowerCase();
this.normalizedFilePatternLowercase = prepareQuery(this.filePattern).value;
}
this.globalExcludePattern = config.excludePattern && glob.parse(config.excludePattern);

View file

@ -312,7 +312,7 @@ export class SearchService implements IRawSearchService {
// Pattern match on results
const results: IRawFileMatch[] = [];
const normalizedSearchValueLowercase = strings.stripWildcards(searchValue).toLowerCase();
const normalizedSearchValueLowercase = prepareQuery(searchValue).value;
for (const entry of cachedEntries) {
// Check if this entry is a match for the search value

View file

@ -290,25 +290,25 @@ suite('FileSearchEngine', () => {
});
});
test('Files: NPE (CamelCase)', function (done: () => void) {
this.timeout(testTimeout);
const engine = new FileSearchEngine({
type: QueryType.File,
folderQueries: ROOT_FOLDER_QUERY,
filePattern: 'NullPE'
});
// test('Files: NPE (CamelCase)', function (done: () => void) {
// this.timeout(testTimeout);
// const engine = new FileSearchEngine({
// type: QueryType.File,
// folderQueries: ROOT_FOLDER_QUERY,
// filePattern: 'NullPE'
// });
let count = 0;
engine.search((result) => {
if (result) {
count++;
}
}, () => { }, (error) => {
assert.ok(!error);
assert.equal(count, 1);
done();
});
});
// let count = 0;
// engine.search((result) => {
// if (result) {
// count++;
// }
// }, () => { }, (error) => {
// assert.ok(!error);
// assert.equal(count, 1);
// done();
// });
// });
test('Files: *.*', function (done: () => void) {
this.timeout(testTimeout);