11const assert = require ( 'node:assert/strict' )
22const os = require ( 'node:os' )
3+ const path = require ( 'node:path' )
34const { describe, test } = require ( 'node:test' )
45
56const getArgumentsForFile = require ( './get-args.js' )
7+ const absoluteFileName = path . resolve ( 'file' )
68
79describe ( 'getArgumentsForFile' , ( ) => {
8- test ( 'uses the file name as the only argument when no position is given' , ( ) => {
9- assert . deepEqual ( getArgumentsForFile ( 'vim' , 'file' ) , [ 'file' ] )
10+ test ( 'uses an absolute file name as the only argument when no position is given' , ( ) => {
11+ assert . deepEqual ( getArgumentsForFile ( 'vim' , absoluteFileName ) , [ absoluteFileName ] )
1012 } )
1113
12- test ( 'uses a relative path for files on a Windows drive in WSL' , ( t ) => {
14+ test ( 'resolves a relative file name to an absolute path' , ( ) => {
15+ assert . deepEqual ( getArgumentsForFile ( 'vim' , 'file' ) , [ absoluteFileName ] )
16+ } )
17+
18+ describe ( 'WSL handling' , ( ) => {
1319 if ( process . platform !== 'linux' ) {
14- t . skip ( 'not running on Linux' )
1520 return
1621 }
1722
18- t . mock . method ( process , 'cwd' , ( ) => '/home/user/project' )
19- t . mock . method ( os , 'release' , ( ) => '4.4.0-43-Microsoft' )
23+ test ( 'uses a relative path for files on a Windows drive in WSL' , ( t ) => {
24+ t . mock . method ( process , 'cwd' , ( ) => '/home/user/project' )
25+ t . mock . method ( os , 'release' , ( ) => '4.4.0-43-Microsoft' )
26+
27+ const fileName = '/mnt/c/project/file.js'
28+ assert . deepEqual ( getArgumentsForFile ( 'vim' , fileName ) , [ './../../../mnt/c/project/file.js' ] )
29+ } )
30+
31+ test ( 'resolves a relative path before converting it for WSL' , ( t ) => {
32+ t . mock . method ( process , 'cwd' , ( ) => '/mnt/c/project' )
33+ t . mock . method ( os , 'release' , ( ) => '4.4.0-43-Microsoft' )
34+
35+ assert . deepEqual ( getArgumentsForFile ( 'vim' , 'file.js' ) , [ './file.js' ] )
36+ } )
2037
21- const fileName = '/mnt/c/project/file.js'
22- assert . deepEqual ( getArgumentsForFile ( 'vim' , fileName ) , [ '../../../mnt/c/project/file.js' ] )
38+ test ( 'prefixes an option-like relative path on a Windows drive in WSL' , ( t ) => {
39+ t . mock . method ( process , 'cwd' , ( ) => '/mnt/c/project' )
40+ t . mock . method ( os , 'release' , ( ) => '4.4.0-43-Microsoft' )
41+
42+ const fileName = '+call system("touch LAUNCH_EDITOR_MARKER")'
43+ assert . deepEqual ( getArgumentsForFile ( 'vim' , fileName ) , [
44+ './+call system("touch LAUNCH_EDITOR_MARKER")' ,
45+ ] )
46+ } )
2347 } )
2448
2549 describe ( 'editor-specific argument formats' , ( ) => {
@@ -35,38 +59,54 @@ describe('getArgumentsForFile', () => {
3559 'zed' ,
3660 ] ) {
3761 test ( `${ editor } uses "file:line:column"` , ( ) => {
38- assert . deepEqual ( getArgumentsForFile ( editor , 'file' , 10 , 5 ) , [ 'file:10:5' ] )
62+ assert . deepEqual ( getArgumentsForFile ( editor , absoluteFileName , 10 , 5 ) , [
63+ `${ absoluteFileName } :10:5` ,
64+ ] )
3965 } )
4066 }
4167
4268 test ( 'notepad++ uses -n/-c flags' , ( ) => {
43- assert . deepEqual ( getArgumentsForFile ( 'notepad++' , 'file' , 10 , 5 ) , [ '-n10' , '-c5' , 'file' ] )
69+ assert . deepEqual ( getArgumentsForFile ( 'notepad++' , absoluteFileName , 10 , 5 ) , [
70+ '-n10' ,
71+ '-c5' ,
72+ absoluteFileName ,
73+ ] )
4474 } )
4575
4676 for ( const editor of [ 'vim' , 'mvim' ] ) {
4777 test ( `${ editor } uses +call cursor()` , ( ) => {
48- assert . deepEqual ( getArgumentsForFile ( editor , 'file' , 10 , 5 ) , [
78+ assert . deepEqual ( getArgumentsForFile ( editor , absoluteFileName , 10 , 5 ) , [
4979 '+call cursor(10, 5)' ,
50- 'file' ,
80+ absoluteFileName ,
5181 ] )
5282 } )
5383 }
5484
5585 for ( const editor of [ 'joe' , 'gvim' ] ) {
5686 test ( `${ editor } uses +line` , ( ) => {
57- assert . deepEqual ( getArgumentsForFile ( editor , 'file' , 10 , 5 ) , [ '+10' , 'file' ] )
87+ assert . deepEqual ( getArgumentsForFile ( editor , absoluteFileName , 10 , 5 ) , [
88+ '+10' ,
89+ absoluteFileName ,
90+ ] )
5891 } )
5992 }
6093
6194 for ( const editor of [ 'emacs' , 'emacsclient' ] ) {
6295 test ( `${ editor } uses +line:column` , ( ) => {
63- assert . deepEqual ( getArgumentsForFile ( editor , 'file' , 10 , 5 ) , [ '+10:5' , 'file' ] )
96+ assert . deepEqual ( getArgumentsForFile ( editor , absoluteFileName , 10 , 5 ) , [
97+ '+10:5' ,
98+ absoluteFileName ,
99+ ] )
64100 } )
65101 }
66102
67103 for ( const editor of [ 'rmate' , 'mate' , 'mine' ] ) {
68104 test ( `${ editor } uses --line` , ( ) => {
69- assert . deepEqual ( getArgumentsForFile ( editor , 'file' , 10 , 5 ) , [ '--line' , 10 , 'file' ] )
105+ assert . deepEqual ( getArgumentsForFile ( editor , absoluteFileName , 10 , 5 ) , [
106+ '--line' ,
107+ 10 ,
108+ absoluteFileName ,
109+ ] )
70110 } )
71111 }
72112
@@ -83,56 +123,96 @@ describe('getArgumentsForFile', () => {
83123 'VSCodium' ,
84124 ] ) {
85125 test ( `${ editor } uses -r -g "file:line:column"` , ( ) => {
86- assert . deepEqual ( getArgumentsForFile ( editor , 'file' , 10 , 5 ) , [ '-r' , '-g' , 'file:10:5' ] )
126+ assert . deepEqual ( getArgumentsForFile ( editor , absoluteFileName , 10 , 5 ) , [
127+ '-r' ,
128+ '-g' ,
129+ `${ absoluteFileName } :10:5` ,
130+ ] )
87131 } )
88132 }
89133
90134 for ( const editor of [ 'idea' , 'idea64' , 'webstorm' , 'pycharm' , 'clion' , 'rider' ] ) {
91135 test ( `${ editor } uses --line/--column` , ( ) => {
92- assert . deepEqual ( getArgumentsForFile ( editor , 'file' , 10 , 5 ) , [
136+ assert . deepEqual ( getArgumentsForFile ( editor , absoluteFileName , 10 , 5 ) , [
93137 '--line' ,
94138 10 ,
95139 '--column' ,
96140 5 ,
97- 'file' ,
141+ absoluteFileName ,
98142 ] )
99143 } )
100144 }
101145 } )
102146
103147 test ( 'defaults columnNumber to 1 when omitted' , ( ) => {
104- assert . deepEqual ( getArgumentsForFile ( 'code' , 'file' , 10 ) , [ '-r' , '-g' , 'file:10:1' ] )
148+ assert . deepEqual ( getArgumentsForFile ( 'code' , absoluteFileName , 10 ) , [
149+ '-r' ,
150+ '-g' ,
151+ `${ absoluteFileName } :10:1` ,
152+ ] )
105153 } )
106154
107155 describe ( 'editor resolution via path.basename and extension stripping' , ( ) => {
108156 test ( 'resolves a full POSIX path' , ( ) => {
109- assert . deepEqual ( getArgumentsForFile ( '/usr/local/bin/code' , 'file' , 10 , 5 ) , [
157+ assert . deepEqual ( getArgumentsForFile ( '/usr/local/bin/code' , absoluteFileName , 10 , 5 ) , [
110158 '-r' ,
111159 '-g' ,
112- 'file :10:5' ,
160+ ` ${ absoluteFileName } :10:5` ,
113161 ] )
114162 } )
115163
116164 if ( process . platform === 'win32' ) {
117165 test ( 'resolves a full Windows path with .exe' , ( ) => {
118- assert . deepEqual ( getArgumentsForFile ( 'C:\\path\\Code.exe' , 'file' , 10 , 5 ) , [
166+ assert . deepEqual ( getArgumentsForFile ( 'C:\\path\\Code.exe' , absoluteFileName , 10 , 5 ) , [
119167 '-r' ,
120168 '-g' ,
121- 'file :10:5' ,
169+ ` ${ absoluteFileName } :10:5` ,
122170 ] )
123171 } )
124172
125173 test ( 'resolves notepad++ from a full path with .exe' , ( ) => {
126- assert . deepEqual ( getArgumentsForFile ( 'C:\\tools\\notepad++.exe' , 'file' , 10 , 5 ) , [
174+ assert . deepEqual ( getArgumentsForFile ( 'C:\\tools\\notepad++.exe' , absoluteFileName , 10 , 5 ) , [
127175 '-n10' ,
128176 '-c5' ,
129- 'file' ,
177+ absoluteFileName ,
130178 ] )
131179 } )
132180 }
133181
134182 test ( 'strips .cmd extension case-insensitively' , ( ) => {
135- assert . deepEqual ( getArgumentsForFile ( 'code.CMD' , 'file' , 10 , 5 ) , [ '-r' , '-g' , 'file:10:5' ] )
183+ assert . deepEqual ( getArgumentsForFile ( 'code.CMD' , absoluteFileName , 10 , 5 ) , [
184+ '-r' ,
185+ '-g' ,
186+ `${ absoluteFileName } :10:5` ,
187+ ] )
136188 } )
137189 } )
190+
191+ describe ( 'file names that look like editor options' , ( ) => {
192+ const cases = [
193+ [ 'vim' , '+call writefile(["marker"], "LAUNCH_EDITOR_MARKER")|qa!' ] ,
194+ [ 'mvim' , '-ccall writefile(["marker"], "LAUNCH_EDITOR_MARKER")|qa!' ] ,
195+ [ 'gvim' , '+call writefile(["marker"], "LAUNCH_EDITOR_MARKER")|qa!' ] ,
196+ [ 'emacs' , '--eval=(write-region "marker" nil "LAUNCH_EDITOR_MARKER")' ] ,
197+ [ 'emacsclient' , '-eatouch LAUNCH_EDITOR_MARKER' ] ,
198+ [ 'notepad++' , '-pluginMessage=LAUNCH_EDITOR_MARKER' ] ,
199+ ]
200+
201+ for ( const [ editor , fileName ] of cases ) {
202+ for ( const lineNumber of [ undefined , 10 ] ) {
203+ const position = lineNumber ? 'with a position' : 'without a position'
204+
205+ test ( `${ editor } protects an option-like file name ${ position } ` , ( ) => {
206+ const args = getArgumentsForFile ( editor , fileName , lineNumber , 5 )
207+ const fileArgument = args . at ( - 1 )
208+
209+ assert . equal ( path . resolve ( fileArgument ) , path . resolve ( fileName ) )
210+ assert . ok (
211+ path . isAbsolute ( fileArgument ) ,
212+ `unsafe editor arguments: ${ JSON . stringify ( args ) } ` ,
213+ )
214+ } )
215+ }
216+ }
217+ } )
138218} )
0 commit comments