18
18
--- to this. Allows us to compute `filename` from "metadata" parsed once on
19
19
--- instantiation.
20
20
21
- --- TODO: rework `split_root` logic according to python 3.12
22
21
--- TODO: rework `_filename` according to `_format_parsed_parts`
23
22
24
23
local uv = vim .loop
@@ -49,76 +48,46 @@ function _WindowsPath:convert_altsep(p)
49
48
return (p :gsub (self .altsep , self .sep ))
50
49
end
51
50
52
- --- @param part string path
51
+ --- @param part string path with only ` \ ` separators
53
52
--- @return string drv
54
53
--- @return string root
55
54
--- @return string relpath
56
55
function _WindowsPath :split_root (part )
57
56
-- https://learn.microsoft.com/en-us/dotnet/standard/io/file-path-formats
58
- local prefix = " "
57
+ local unc_prefix = " \\\\ ? \\ UNC \\ "
59
58
local first , second = part :sub (1 , 1 ), part :sub (2 , 2 )
60
59
61
- if first == self .sep and second == self .sep then
62
- prefix , part = self :_split_extended_path (part )
63
- first , second = part :sub (1 , 1 ), part :sub (2 , 2 )
64
- end
65
-
66
- local third = part :sub (3 , 3 )
67
-
68
- if first == self .sep and second == self .sep and third ~= self .sep then
69
- -- is a UNC path:
70
- -- vvvvvvvvvvvvvvvvvvvvv root
71
- -- \\machine\mountpoint\directory\etc\...
72
- -- directory ^^^^^^^^^^^^^^
60
+ if first == self .sep then
61
+ if second == self .sep then
62
+ -- UNC drives, e.g. \\server\share or \\?\UNC\server\share
63
+ -- Device drives, e.g. \\.\device or \\?\device
64
+ local start = part :sub (1 , 8 ):upper () == unc_prefix and 8 or 2
65
+ local index = part :find (self .sep , start )
66
+ if index == nil then
67
+ return part , " " , " " -- paths only has drive info
68
+ end
73
69
74
- local index = part :find (self .sep , 3 )
75
- if index ~= nil then
76
70
local index2 = part :find (self .sep , index + 1 )
77
- if index2 ~= index + 1 then
78
- if index2 == nil then
79
- index2 = # part
80
- end
81
-
82
- if prefix ~= " " then
83
- return prefix + part :sub (2 , index2 - 1 ), self .sep , part :sub (index2 + 1 )
84
- else
85
- return part :sub (1 , index2 - 1 ), self .sep , part :sub (index2 + 1 )
86
- end
71
+ if index2 == nil then
72
+ return part , " " , " " -- still paths only has drive info
87
73
end
74
+ return part :sub (1 , index2 - 1 ), self .sep , part :sub (index2 + 1 )
75
+ else
76
+ -- Relative path with root, eg. \Windows
77
+ return " " , part :sub (1 , 1 ), part :sub (2 )
88
78
end
89
- end
90
-
91
- local drv , root = " " , " "
92
- if second == " :" and first :match " %a" then
93
- drv , part = part :sub (1 , 2 ), part :sub (3 )
94
- first = third
95
- end
96
-
97
- if first == self .sep then
98
- root = first
99
- part = part :gsub (" ^" .. self .sep .. " +" , " " )
100
- end
101
-
102
- return prefix .. drv , root , part
103
- end
104
-
105
- --- @param p string path
106
- --- @return string
107
- --- @return string
108
- function _WindowsPath :_split_extended_path (p )
109
- local ext_prefix = [[ \\?\]]
110
- local prefix = " "
111
-
112
- if p :sub (1 , # ext_prefix ) == ext_prefix then
113
- prefix = p :sub (1 , 4 )
114
- p = p :sub (5 )
115
- if p :sub (1 , 3 ) == " UNC" .. self .sep then
116
- prefix = prefix .. p :sub (1 , 3 )
117
- p = self .sep .. p :sub (4 )
79
+ elseif part :sub (2 , 2 ) == " :" then
80
+ if part :sub (3 , 3 ) == self .sep then
81
+ -- absolute path with drive, eg. C:\Windows
82
+ return part :sub (1 , 2 ), self .sep , part :sub (3 )
83
+ else
84
+ -- relative path with drive, eg. C:Windows
85
+ return part :sub (1 , 2 ), " " , part :sub (3 )
118
86
end
87
+ else
88
+ -- relative path, eg. Windows
89
+ return " " , " " , part
119
90
end
120
-
121
- return prefix , p
122
91
end
123
92
124
93
--- @class plenary._PosixPath : plenary._Path
@@ -206,21 +175,21 @@ path.root = (function()
206
175
end )()
207
176
208
177
--- @param parts string[]
209
- --- @param _path plenary._Path
178
+ --- @param _flavor plenary._Path
210
179
--- @return string drv
211
180
--- @return string root
212
181
--- @return string[]
213
- local function parse_parts (parts , _path )
182
+ local function parse_parts (parts , _flavor )
214
183
local drv , root , rel , parsed = " " , " " , " " , {}
215
184
216
185
for i = # parts , 1 , - 1 do
217
186
local part = parts [i ]
218
- part = _path :convert_altsep (part )
187
+ part = _flavor :convert_altsep (part )
219
188
220
- drv , root , rel = _path :split_root (part )
189
+ drv , root , rel = _flavor :split_root (part )
221
190
222
- if rel :match (_path .sep ) then
223
- local relparts = vim .split (rel , _path .sep )
191
+ if rel :match (_flavor .sep ) then
192
+ local relparts = vim .split (rel , _flavor .sep )
224
193
for j = # relparts , 1 , - 1 do
225
194
local p = relparts [j ]
226
195
if p ~= " " and p ~= " ." then
@@ -233,19 +202,18 @@ local function parse_parts(parts, _path)
233
202
end
234
203
end
235
204
236
- if drv or root then
205
+ if drv ~= " " or root ~= " " then
237
206
if not drv then
238
- for k = # parts , 1 , - 1 do
239
- local p = parts [k ]
240
- p = _path :convert_altsep (p )
241
- drv = _path :split_root (p )
242
- if drv then
207
+ for j = # parts , 1 , - 1 do
208
+ local p = parts [j ]
209
+ p = _flavor :convert_altsep (p )
210
+ drv = _flavor :split_root (p )
211
+ if drv ~= " " then
243
212
break
244
213
end
245
214
end
246
-
247
- break
248
215
end
216
+ break
249
217
end
250
218
end
251
219
@@ -259,22 +227,30 @@ end
259
227
260
228
--- @class plenary.Path2
261
229
--- @field path plenary.path2
262
- --- @field private _path plenary._Path
230
+ --- @field private _flavor plenary._Path
231
+ --- @field private _raw_parts string[]
263
232
--- @field drv string drive name , eg. ' C:' (only for Windows )
264
233
--- @field root string root path (excludes drive name )
265
- --- @field relparts string[] relative path parts excluding separators
234
+ --- @field relparts string[] path separator separated relative path parts
266
235
---
267
236
--- @field filename string
268
237
--- @field cwd string
269
238
--- @field private _absolute string ? lazy eval ' ed fully resolved absolute path
270
239
local Path = { path = path }
271
240
241
+ --- @param t plenary.Path2
242
+ --- @param k string
272
243
Path .__index = function (t , k )
273
244
local raw = rawget (Path , k )
274
245
if raw then
275
246
return raw
276
247
end
277
248
249
+ if k == " drv" or k == " root" or k == " relparts" then
250
+ t .drv , t .root , t .relparts = parse_parts (t ._raw_parts , t ._flavor )
251
+ return rawget (t , k )
252
+ end
253
+
278
254
if k == " filename" then
279
255
t .filename = t :_filename ()
280
256
return t .filename
@@ -337,22 +313,20 @@ function Path:new(...)
337
313
end
338
314
end
339
315
340
- local relparts = {}
316
+ local raw_parts = {}
341
317
for _ , a in ipairs (args ) do
342
318
if self .is_path (a ) then
343
- vim .list_extend (relparts , a .relparts )
319
+ vim .list_extend (raw_parts , a ._raw_parts )
344
320
else
345
321
if a ~= " " then
346
- table.insert (relparts , a )
322
+ table.insert (raw_parts , a )
347
323
end
348
324
end
349
325
end
350
326
351
- local _path = iswin and _WindowsPath or _PosixPath
352
- local drv , root
353
- drv , root , relparts = parse_parts (relparts , _path )
327
+ local _flavor = iswin and _WindowsPath or _PosixPath
354
328
355
- local proxy = { _path = _path , drv = drv , root = root , relparts = relparts }
329
+ local proxy = { _flavor = _flavor , _raw_parts = raw_parts }
356
330
setmetatable (proxy , Path )
357
331
358
332
local obj = { __inner = proxy }
@@ -385,17 +359,17 @@ end
385
359
--- @return string
386
360
function Path :_filename (drv , root , relparts )
387
361
drv = vim .F .if_nil (drv , self .drv )
388
- drv = self .drv ~= " " and self .drv :gsub (self ._path .sep , path .sep ) or " "
362
+ drv = self .drv ~= " " and self .drv :gsub (self ._flavor .sep , path .sep ) or " "
389
363
390
- if self ._path .has_drv and drv == " " then
364
+ if self ._flavor .has_drv and drv == " " then
391
365
root = " "
392
366
else
393
367
root = vim .F .if_nil (root , self .root )
394
368
root = self .root ~= " " and path .sep :rep (# self .root ) or " "
395
369
end
396
370
397
371
relparts = vim .F .if_nil (relparts , self .relparts )
398
- local relpath = table.concat (relparts , path .sep )
372
+ local relpath = table.concat (relparts , path .sep )
399
373
400
374
return drv .. root .. relpath
401
375
end
@@ -412,7 +386,7 @@ function Path:is_absolute()
412
386
return false
413
387
end
414
388
415
- return self ._path .has_drv and self .drv ~= " "
389
+ return self ._flavor .has_drv and self .drv ~= " "
416
390
end
417
391
418
392
--- @return boolean
@@ -535,8 +509,8 @@ function Path:make_relative(to)
535
509
end
536
510
537
511
-- vim.o.shellslash = false
538
- -- vim.print(p)
539
- -- print(p.filename, p:is_absolute(), p:absolute() )
512
+ local p = Path : new { " C: " , " lua " , " .. " , " README.md " }
513
+ print (p .filename )
540
514
-- vim.o.shellslash = true
541
515
542
516
return Path
0 commit comments