1
1
// Copyright (c) Microsoft Corporation.
2
2
// Licensed under the MIT License.
3
3
4
- import * as os from "os" ;
5
4
import * as path from "path" ;
6
5
import * as vscode from "vscode" ;
7
6
import {
@@ -141,6 +140,7 @@ export interface IStatusBarMessageDetails {
141
140
message : string ;
142
141
timeout ?: number ;
143
142
}
143
+
144
144
interface IInvokeRegisteredEditorCommandParameter {
145
145
commandName : string ;
146
146
}
@@ -161,11 +161,8 @@ export class ExtensionCommandsFeature extends LanguageClientConsumer {
161
161
162
162
vscode . commands . registerCommand ( "PowerShell.InvokeRegisteredEditorCommand" ,
163
163
async ( param : IInvokeRegisteredEditorCommandParameter ) => {
164
- if ( this . extensionCommands . length === 0 ) {
165
- return ;
166
- }
167
-
168
- const commandToExecute = this . extensionCommands . find ( ( x ) => x . name === param . commandName ) ;
164
+ const commandToExecute = this . extensionCommands . find (
165
+ ( x ) => x . name === param . commandName ) ;
169
166
170
167
if ( commandToExecute ) {
171
168
await this . languageClient ?. sendRequest (
@@ -219,7 +216,8 @@ export class ExtensionCommandsFeature extends LanguageClientConsumer {
219
216
220
217
this . languageClient . onRequest (
221
218
NewFileRequestType ,
222
- // TODO: Shouldn't this use the file path?
219
+ // NOTE: The VS Code API does not support naming a file as it's
220
+ // opened, only when it's saved. Hence the argument is not used.
223
221
( _filePath ) => this . newFile ( ) ) ,
224
222
225
223
this . languageClient . onRequest (
@@ -300,7 +298,7 @@ export class ExtensionCommandsFeature extends LanguageClientConsumer {
300
298
301
299
const selectedCommand = await vscode . window . showQuickPick (
302
300
quickPickItems ,
303
- { placeHolder : "Select a command" } ) ;
301
+ { placeHolder : "Select a command... " } ) ;
304
302
return this . onCommandSelected ( selectedCommand , client ) ;
305
303
}
306
304
@@ -364,15 +362,16 @@ export class ExtensionCommandsFeature extends LanguageClientConsumer {
364
362
}
365
363
366
364
private async openFile ( openFileDetails : IOpenFileDetails ) : Promise < EditorOperationResponse > {
367
- const filePath = await this . normalizeFilePath ( openFileDetails . filePath ) ;
365
+ const filePath = await this . resolveFilePathWithCwd ( openFileDetails . filePath ) ;
368
366
const doc = await vscode . workspace . openTextDocument ( filePath ) ;
369
367
await vscode . window . showTextDocument ( doc , { preview : openFileDetails . preview } ) ;
370
368
return EditorOperationResponse . Completed ;
371
369
}
372
370
373
371
private async closeFile ( filePath : string ) : Promise < EditorOperationResponse > {
374
- if ( this . findTextDocument ( await this . normalizeFilePath ( filePath ) ) ) {
375
- const doc = await vscode . workspace . openTextDocument ( filePath ) ;
372
+ filePath = await this . resolveFilePathWithCwd ( filePath ) ;
373
+ const doc = vscode . workspace . textDocuments . find ( ( x ) => x . fileName === filePath ) ;
374
+ if ( doc != undefined && ! doc . isClosed ) {
376
375
await vscode . window . showTextDocument ( doc ) ;
377
376
await vscode . commands . executeCommand ( "workbench.action.closeActiveEditor" ) ;
378
377
}
@@ -384,142 +383,88 @@ export class ExtensionCommandsFeature extends LanguageClientConsumer {
384
383
* @param saveFileDetails the object detailing the path of the file to save and optionally its new path to save to
385
384
*/
386
385
private async saveFile ( saveFileDetails : ISaveFileDetails ) : Promise < EditorOperationResponse > {
387
- // Try to interpret the filepath as a URI, defaulting to "file://" if we don't succeed
386
+ // Try to interpret the filePath as a URI, defaulting to "file://" if we don't succeed
388
387
let currentFileUri : vscode . Uri ;
389
388
if ( saveFileDetails . filePath . startsWith ( "untitled" ) || saveFileDetails . filePath . startsWith ( "file" ) ) {
390
389
currentFileUri = vscode . Uri . parse ( saveFileDetails . filePath ) ;
391
390
} else {
392
391
currentFileUri = vscode . Uri . file ( saveFileDetails . filePath ) ;
393
392
}
394
393
395
- let newFileAbsolutePath : string ;
396
- switch ( currentFileUri . scheme ) {
397
- case "file" : {
398
- // If the file to save can't be found, just complete the request
399
- if ( ! this . findTextDocument ( await this . normalizeFilePath ( currentFileUri . fsPath ) ) ) {
400
- void this . logger . writeAndShowError ( `File to save not found: ${ currentFileUri . fsPath } .` ) ;
401
- return EditorOperationResponse . Completed ;
402
- }
394
+ // If the file to save can't be found, just complete the request
395
+ const doc = vscode . workspace . textDocuments . find ( ( x ) => x . uri === currentFileUri ) ;
396
+ if ( doc === undefined ) {
397
+ void this . logger . writeAndShowError ( `File to save not found: ${ currentFileUri . fsPath } ` ) ;
398
+ return EditorOperationResponse . Completed ;
399
+ }
403
400
401
+ let newFilePath = saveFileDetails . newPath ;
402
+ if ( currentFileUri . scheme === "file" ) {
404
403
// If no newFile is given, just save the current file
405
- if ( ! saveFileDetails . newPath ) {
406
- const doc = await vscode . workspace . openTextDocument ( currentFileUri . fsPath ) ;
404
+ if ( newFilePath === undefined ) {
407
405
if ( doc . isDirty ) {
408
406
await doc . save ( ) ;
409
407
}
410
408
return EditorOperationResponse . Completed ;
411
409
}
412
410
413
- // Make sure we have an absolute path
414
- if ( path . isAbsolute ( saveFileDetails . newPath ) ) {
415
- newFileAbsolutePath = saveFileDetails . newPath ;
416
- } else {
417
- // If not, interpret the path as relative to the current file
418
- newFileAbsolutePath = path . join ( path . dirname ( currentFileUri . fsPath ) , saveFileDetails . newPath ) ;
411
+ // Special case where we interpret a path as relative to the current
412
+ // file, not the CWD!
413
+ if ( ! path . isAbsolute ( newFilePath ) ) {
414
+ newFilePath = path . join ( path . dirname ( currentFileUri . fsPath ) , newFilePath ) ;
419
415
}
420
- break ; }
421
-
422
- case "untitled" : {
416
+ } else if ( currentFileUri . scheme === "untitled" ) {
423
417
// We need a new name to save an untitled file
424
- if ( ! saveFileDetails . newPath ) {
425
- // TODO: Create a class handle vscode warnings and errors so we can warn easily
426
- // without logging
427
- void this . logger . writeAndShowWarning ( "Cannot save untitled file. Try SaveAs(\"path/to/file.ps1\") instead." ) ;
418
+ if ( newFilePath === undefined ) {
419
+ void this . logger . writeAndShowError ( "Cannot save untitled file! Try SaveAs(\"path/to/file.ps1\") instead." ) ;
428
420
return EditorOperationResponse . Completed ;
429
421
}
430
422
431
- // Make sure we have an absolute path
432
- if ( path . isAbsolute ( saveFileDetails . newPath ) ) {
433
- newFileAbsolutePath = saveFileDetails . newPath ;
434
- } else {
435
- const cwd = await validateCwdSetting ( this . logger ) ;
436
- newFileAbsolutePath = path . join ( cwd , saveFileDetails . newPath ) ;
437
- }
438
- break ; }
439
-
440
- default : {
423
+ newFilePath = await this . resolveFilePathWithCwd ( newFilePath ) ;
424
+ } else {
441
425
// Other URI schemes are not supported
442
426
const msg = JSON . stringify ( saveFileDetails , undefined , 2 ) ;
443
- this . logger . writeVerbose (
427
+ void this . logger . writeAndShowError (
444
428
`<${ ExtensionCommandsFeature . name } >: Saving a document with scheme '${ currentFileUri . scheme } ' ` +
445
- `is currently unsupported. Message: '${ msg } '` ) ;
446
- return EditorOperationResponse . Completed ; }
429
+ `is currently unsupported. Message: '${ msg } '` ) ;
430
+ return EditorOperationResponse . Completed ;
447
431
}
448
432
449
- await this . saveDocumentContentToAbsolutePath ( currentFileUri , newFileAbsolutePath ) ;
433
+ await this . saveFileAs ( doc , newFilePath ) ;
450
434
return EditorOperationResponse . Completed ;
451
435
452
436
}
453
437
454
438
/**
455
439
* Take a document available to vscode at the given URI and save it to the given absolute path
456
440
* @param documentUri the URI of the vscode document to save
457
- * @param destinationAbsolutePath the absolute path to save the document contents to
441
+ * @param filePath the absolute path to save the document contents to
458
442
*/
459
- private async saveDocumentContentToAbsolutePath (
460
- documentUri : vscode . Uri ,
461
- destinationAbsolutePath : string ) : Promise < void > {
462
- // Retrieve the text out of the current document
463
- const oldDocument = await vscode . workspace . openTextDocument ( documentUri ) ;
464
-
465
- // Write it to the new document path
443
+ private async saveFileAs ( doc : vscode . TextDocument , filePath : string ) : Promise < void > {
444
+ // Write the old document's contents to the new document path
445
+ const newFileUri = vscode . Uri . file ( filePath ) ;
466
446
try {
467
447
await vscode . workspace . fs . writeFile (
468
- vscode . Uri . file ( destinationAbsolutePath ) ,
469
- Buffer . from ( oldDocument . getText ( ) ) ) ;
448
+ newFileUri ,
449
+ Buffer . from ( doc . getText ( ) ) ) ;
470
450
} catch ( err ) {
471
451
void this . logger . writeAndShowWarning ( `<${ ExtensionCommandsFeature . name } >: ` +
472
- `Unable to save file to path '${ destinationAbsolutePath } ': ${ err } ` ) ;
452
+ `Unable to save file to path '${ filePath } ': ${ err } ` ) ;
473
453
return ;
474
454
}
475
455
476
456
// Finally open the new document
477
- const newFileUri = vscode . Uri . file ( destinationAbsolutePath ) ;
478
457
const newFile = await vscode . workspace . openTextDocument ( newFileUri ) ;
479
458
await vscode . window . showTextDocument ( newFile , { preview : true } ) ;
480
459
}
481
460
482
- private async normalizeFilePath ( filePath : string ) : Promise < string > {
483
- const cwd = await validateCwdSetting ( this . logger ) ;
484
- const platform = os . platform ( ) ;
485
- if ( platform === "win32" ) {
486
- // Make sure the file path is absolute
487
- if ( ! path . win32 . isAbsolute ( filePath ) ) {
488
- filePath = path . win32 . resolve ( cwd , filePath ) ;
489
- }
490
-
491
- // Normalize file path case for comparison for Windows
492
- return filePath . toLowerCase ( ) ;
493
- } else {
494
- // Make sure the file path is absolute
495
- if ( ! path . isAbsolute ( filePath ) ) {
496
- filePath = path . resolve ( cwd , filePath ) ;
497
- }
498
-
499
- // macOS is case-insensitive
500
- if ( platform === "darwin" ) {
501
- filePath = filePath . toLowerCase ( ) ;
502
- }
503
-
504
- return filePath ;
461
+ // Resolve file path against user's CWD setting
462
+ private async resolveFilePathWithCwd ( filePath : string ) : Promise < string > {
463
+ if ( ! path . isAbsolute ( filePath ) ) {
464
+ const cwd = await validateCwdSetting ( this . logger ) ;
465
+ return path . resolve ( cwd , filePath ) ;
505
466
}
506
- }
507
-
508
- private findTextDocument ( filePath : string ) : boolean {
509
- // since Windows and macOS are case-insensitive, we need to normalize them differently
510
- const canFind = vscode . workspace . textDocuments . find ( ( doc ) => {
511
- let docPath : string ;
512
- const platform = os . platform ( ) ;
513
- if ( platform === "win32" || platform === "darwin" ) {
514
- // for Windows and macOS paths, they are normalized to be lowercase
515
- docPath = doc . fileName . toLowerCase ( ) ;
516
- } else {
517
- docPath = doc . fileName ;
518
- }
519
- return docPath === filePath ;
520
- } ) ;
521
-
522
- return canFind != null ;
467
+ return filePath ;
523
468
}
524
469
525
470
private setSelection ( details : ISetSelectionRequestArguments ) : EditorOperationResponse {
0 commit comments