or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

file-io.mdfile-system.mdindex.mdpath-operations.mdrecursive-operations.mdtree-traversal.md

tree-traversal.mddocs/

0

# Tree Traversal Operations

1

2

Advanced directory tree traversal operations with walking sequences, customizable file visitor patterns, and comprehensive options for complex file system navigation and processing.

3

4

## Capabilities

5

6

### Path Walking

7

8

Traverse directory trees using lazy sequences with multiple traversal options.

9

10

```kotlin { .api }

11

/**

12

* Returns a sequence of paths for visiting this directory and all its contents.

13

* By default, the returned sequence includes only files in the file tree, in depth-first order.

14

* @param options options to control walk behavior

15

*/

16

fun Path.walk(vararg options: PathWalkOption): Sequence<Path>

17

18

/**

19

* An enumeration to provide walk options for the Path.walk function.

20

*/

21

@SinceKotlin("2.1")

22

enum class PathWalkOption {

23

/** Includes directory paths in the resulting sequence of the walk */

24

INCLUDE_DIRECTORIES,

25

/** Walks in breadth-first order instead of depth-first */

26

BREADTH_FIRST,

27

/** Follows symbolic links to the directories they point to */

28

FOLLOW_LINKS

29

}

30

```

31

32

**Usage Examples:**

33

34

```kotlin

35

import kotlin.io.path.*

36

import java.nio.file.Paths

37

38

val projectDir = Paths.get("/home/user/my-project")

39

40

// Basic walk - files only, depth-first

41

projectDir.walk().forEach { file ->

42

println("File: ${file.relativeTo(projectDir)}")

43

}

44

45

// Include directories in the walk

46

projectDir.walk(PathWalkOption.INCLUDE_DIRECTORIES).forEach { path ->

47

val type = if (path.isDirectory()) "DIR" else "FILE"

48

println("$type: ${path.relativeTo(projectDir)}")

49

}

50

51

// Breadth-first traversal with directories

52

projectDir.walk(

53

PathWalkOption.INCLUDE_DIRECTORIES,

54

PathWalkOption.BREADTH_FIRST

55

).forEach { path ->

56

val depth = path.relativeTo(projectDir).nameCount

57

val indent = " ".repeat(depth)

58

println("$indent${path.name}")

59

}

60

61

// Follow symbolic links (be careful of cycles)

62

try {

63

projectDir.walk(PathWalkOption.FOLLOW_LINKS).forEach { file ->

64

println("File (following links): ${file.relativeTo(projectDir)}")

65

}

66

} catch (e: java.nio.file.FileSystemLoopException) {

67

println("Detected symbolic link loop: ${e.message}")

68

}

69

70

// Filter and process specific files

71

val kotlinFiles = projectDir.walk()

72

.filter { it.extension == "kt" }

73

.toList()

74

75

println("Found ${kotlinFiles.size} Kotlin files")

76

77

// Count files by extension

78

val filesByExtension = projectDir.walk()

79

.groupBy { it.extension }

80

.mapValues { (_, files) -> files.size }

81

82

filesByExtension.forEach { (ext, count) ->

83

val extension = if (ext.isEmpty()) "(no extension)" else ext

84

println(".$extension: $count files")

85

}

86

```

87

88

### File Visitor Pattern

89

90

Use the visitor pattern for complex file tree operations with precise control over traversal behavior.

91

92

```kotlin { .api }

93

/**

94

* Visits this directory and all its contents with the specified visitor.

95

* The traversal is in depth-first order and starts at this directory.

96

*/

97

fun Path.visitFileTree(

98

visitor: FileVisitor<Path>,

99

maxDepth: Int = Int.MAX_VALUE,

100

followLinks: Boolean = false

101

)

102

103

/**

104

* Visits this directory and all its contents with the FileVisitor defined in builderAction.

105

*/

106

fun Path.visitFileTree(

107

maxDepth: Int = Int.MAX_VALUE,

108

followLinks: Boolean = false,

109

builderAction: FileVisitorBuilder.() -> Unit

110

)

111

112

/**

113

* Builds a FileVisitor whose implementation is defined in builderAction.

114

*/

115

fun fileVisitor(builderAction: FileVisitorBuilder.() -> Unit): FileVisitor<Path>

116

```

117

118

**Usage Examples:**

119

120

```kotlin

121

import kotlin.io.path.*

122

import java.nio.file.*

123

import java.nio.file.attribute.BasicFileAttributes

124

125

val projectDir = Paths.get("/home/user/project")

126

127

// Clean build artifacts using file visitor

128

val cleanVisitor = fileVisitor {

129

onPreVisitDirectory { directory, _ ->

130

when (directory.name) {

131

"build", "target", ".gradle", "node_modules" -> {

132

println("Cleaning: ${directory.relativeTo(projectDir)}")

133

directory.deleteRecursively()

134

FileVisitResult.SKIP_SUBTREE

135

}

136

else -> FileVisitResult.CONTINUE

137

}

138

}

139

140

onVisitFile { file, _ ->

141

when (file.extension) {

142

"class", "o", "obj", "pyc" -> {

143

println("Removing: ${file.relativeTo(projectDir)}")

144

file.deleteIfExists()

145

}

146

}

147

FileVisitResult.CONTINUE

148

}

149

150

onVisitFileFailed { file, exception ->

151

println("Failed to visit ${file.relativeTo(projectDir)}: ${exception.message}")

152

FileVisitResult.CONTINUE

153

}

154

}

155

156

projectDir.visitFileTree(cleanVisitor)

157

158

// Count files and directories with size calculation

159

var totalFiles = 0

160

var totalDirs = 0

161

var totalSize = 0L

162

163

projectDir.visitFileTree {

164

onPreVisitDirectory { directory, _ ->

165

totalDirs++

166

FileVisitResult.CONTINUE

167

}

168

169

onVisitFile { file, attributes ->

170

totalFiles++

171

totalSize += attributes.size()

172

FileVisitResult.CONTINUE

173

}

174

}

175

176

println("Project statistics:")

177

println("- Directories: $totalDirs")

178

println("- Files: $totalFiles")

179

println("- Total size: ${totalSize / 1024 / 1024} MB")

180

181

// Find large files with depth limit

182

val largeFiles = mutableListOf<Pair<Path, Long>>()

183

184

projectDir.visitFileTree(maxDepth = 3) {

185

onVisitFile { file, attributes ->

186

val size = attributes.size()

187

if (size > 10_000_000) { // Files larger than 10MB

188

largeFiles.add(file to size)

189

}

190

FileVisitResult.CONTINUE

191

}

192

}

193

194

println("Large files (>10MB):")

195

largeFiles.sortedByDescending { it.second }.forEach { (file, size) ->

196

println("${size / 1024 / 1024} MB: ${file.relativeTo(projectDir)}")

197

}

198

```

199

200

### File Visitor Builder Interface

201

202

Comprehensive interface for building custom file visitors with event handlers.

203

204

```kotlin { .api }

205

/**

206

* The builder to provide implementation of the FileVisitor that fileVisitor function builds.

207

*/

208

@SinceKotlin("2.1")

209

interface FileVisitorBuilder {

210

/**

211

* Overrides the corresponding function of the built FileVisitor with the provided function.

212

* The provided callback is invoked for a directory before its contents are visited.

213

*/

214

fun onPreVisitDirectory(function: (directory: Path, attributes: BasicFileAttributes) -> FileVisitResult)

215

216

/**

217

* Overrides the corresponding function of the built FileVisitor with the provided function.

218

* The provided callback is invoked when a file is visited.

219

*/

220

fun onVisitFile(function: (file: Path, attributes: BasicFileAttributes) -> FileVisitResult)

221

222

/**

223

* Overrides the corresponding function of the built FileVisitor with the provided function.

224

* The provided callback is invoked for an entry that could not be visited for some reason.

225

*/

226

fun onVisitFileFailed(function: (file: Path, exception: IOException) -> FileVisitResult)

227

228

/**

229

* Overrides the corresponding function of the built FileVisitor with the provided function.

230

* The provided callback is invoked for a directory after its contents have been visited.

231

*/

232

fun onPostVisitDirectory(function: (directory: Path, exception: IOException?) -> FileVisitResult)

233

}

234

```

235

236

**Usage Examples:**

237

238

```kotlin

239

import kotlin.io.path.*

240

import java.nio.file.*

241

import java.nio.file.attribute.BasicFileAttributes

242

import java.io.IOException

243

244

val sourceDir = Paths.get("/home/user/source")

245

val reportFile = Paths.get("/home/user/analysis-report.txt")

246

247

// Comprehensive file analysis visitor

248

val analysisReport = mutableListOf<String>()

249

250

val analysisVisitor = fileVisitor {

251

onPreVisitDirectory { directory, attributes ->

252

analysisReport.add("ENTER DIR: ${directory.name} (created: ${attributes.creationTime()})")

253

FileVisitResult.CONTINUE

254

}

255

256

onVisitFile { file, attributes ->

257

val size = attributes.size()

258

val modified = attributes.lastModifiedTime()

259

val type = when (file.extension.lowercase()) {

260

"kt", "java", "scala" -> "Source Code"

261

"xml", "json", "yaml", "yml" -> "Configuration"

262

"md", "txt", "rst" -> "Documentation"

263

"jpg", "png", "gif", "svg" -> "Image"

264

else -> "Other"

265

}

266

267

analysisReport.add("FILE: ${file.name} | Type: $type | Size: $size bytes | Modified: $modified")

268

FileVisitResult.CONTINUE

269

}

270

271

onVisitFileFailed { file, exception ->

272

analysisReport.add("FAILED: ${file.name} | Error: ${exception.message}")

273

FileVisitResult.CONTINUE // Continue despite errors

274

}

275

276

onPostVisitDirectory { directory, exception ->

277

if (exception != null) {

278

analysisReport.add("DIR ERROR: ${directory.name} | ${exception.message}")

279

} else {

280

analysisReport.add("EXIT DIR: ${directory.name}")

281

}

282

FileVisitResult.CONTINUE

283

}

284

}

285

286

// Run analysis

287

sourceDir.visitFileTree(analysisVisitor)

288

289

// Write report

290

reportFile.writeLines(analysisReport)

291

println("Analysis complete. Report written to: $reportFile")

292

293

// Search visitor with early termination

294

var targetFound = false

295

val searchTerm = "important-file.txt"

296

297

val searchVisitor = fileVisitor {

298

onVisitFile { file, _ ->

299

if (file.name == searchTerm) {

300

println("Found target file: $file")

301

targetFound = true

302

FileVisitResult.TERMINATE // Stop the entire traversal

303

} else {

304

FileVisitResult.CONTINUE

305

}

306

}

307

308

onPreVisitDirectory { directory, _ ->

309

// Skip hidden directories

310

if (directory.name.startsWith(".")) {

311

FileVisitResult.SKIP_SUBTREE

312

} else {

313

FileVisitResult.CONTINUE

314

}

315

}

316

}

317

318

sourceDir.visitFileTree(searchVisitor)

319

320

if (targetFound) {

321

println("Search completed - target found!")

322

} else {

323

println("Search completed - target not found.")

324

}

325

```

326

327

### Advanced Tree Traversal Patterns

328

329

Complex patterns combining walking and visitor approaches for sophisticated file operations.

330

331

**Usage Examples:**

332

333

```kotlin

334

import kotlin.io.path.*

335

import java.nio.file.*

336

import java.time.Instant

337

import java.time.temporal.ChronoUnit

338

339

val projectRoot = Paths.get("/home/user/projects")

340

val backupDir = Paths.get("/backup/projects")

341

val archiveDir = Paths.get("/archive")

342

343

// Multi-pass processing: analyze then act

344

// Pass 1: Collect statistics

345

data class DirectoryStats(var fileCount: Int = 0, var totalSize: Long = 0, var lastModified: Instant = Instant.MIN)

346

347

val dirStats = mutableMapOf<Path, DirectoryStats>()

348

349

projectRoot.walk(PathWalkOption.INCLUDE_DIRECTORIES).forEach { path ->

350

if (path.isDirectory()) {

351

dirStats[path] = DirectoryStats()

352

}

353

}

354

355

projectRoot.walk().forEach { file ->

356

val parent = file.parent

357

if (parent != null && parent in dirStats) {

358

val stats = dirStats[parent]!!

359

stats.fileCount++

360

stats.totalSize += file.fileSize()

361

val fileModified = file.getLastModifiedTime().toInstant()

362

if (fileModified.isAfter(stats.lastModified)) {

363

stats.lastModified = fileModified

364

}

365

}

366

}

367

368

// Pass 2: Archive old directories

369

val oneYearAgo = Instant.now().minus(365, ChronoUnit.DAYS)

370

371

dirStats.filter { (_, stats) ->

372

stats.lastModified.isBefore(oneYearAgo) && stats.fileCount > 0

373

}.forEach { (dir, stats) ->

374

println("Archiving old directory: $dir (${stats.fileCount} files, ${stats.totalSize / 1024 / 1024} MB)")

375

376

val archivePath = archiveDir / dir.relativeTo(projectRoot)

377

archivePath.createParentDirectories()

378

dir.copyToRecursively(archivePath, followLinks = false, overwrite = true)

379

dir.deleteRecursively()

380

}

381

382

// Selective backup with filtering

383

val importantExtensions = setOf("kt", "java", "xml", "md", "gradle")

384

val lastBackup = Instant.now().minus(7, ChronoUnit.DAYS)

385

386

projectRoot.walk()

387

.filter { file ->

388

file.extension in importantExtensions &&

389

file.getLastModifiedTime().toInstant().isAfter(lastBackup)

390

}

391

.forEach { file ->

392

val backupPath = backupDir / file.relativeTo(projectRoot)

393

backupPath.createParentDirectories()

394

file.copyTo(backupPath, overwrite = true)

395

println("Backed up: ${file.relativeTo(projectRoot)}")

396

}

397

398

// Complex filtering with visitor pattern

399

val duplicateFiles = mutableMapOf<Long, MutableList<Path>>()

400

401

projectRoot.visitFileTree {

402

onVisitFile { file, attributes ->

403

val size = attributes.size()

404

duplicateFiles.computeIfAbsent(size) { mutableListOf() }.add(file)

405

FileVisitResult.CONTINUE

406

}

407

}

408

409

// Find potential duplicates (same size)

410

val potentialDuplicates = duplicateFiles.filter { (_, files) -> files.size > 1 }

411

412

println("Potential duplicate files (same size):")

413

potentialDuplicates.forEach { (size, files) ->

414

println("Size: $size bytes")

415

files.forEach { file ->

416

println(" - ${file.relativeTo(projectRoot)}")

417

}

418

println()

419

}

420

```