London | 25-SDC-July | Andrei Filippov | Sprint 3 | Implement shell tools#1
London | 25-SDC-July | Andrei Filippov | Sprint 3 | Implement shell tools#1Droid-An wants to merge 3 commits intoCodeYourFuture:mainfrom
Conversation
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
Thanks for opening a new PR! AI started to review it |
| async function example(path) { | ||
| try { | ||
| const data = await fs.readFile(path, { encoding: "utf8" }); | ||
| if (opts["b"]) { | ||
| countLines(data); | ||
| } else if (opts["n"]) { | ||
| countLines(data); | ||
| } else { | ||
| console.log(data.trimEnd()); | ||
| } | ||
| } catch (err) { | ||
| console.error(err); | ||
| } | ||
| } |
There was a problem hiding this comment.
How do you usually choose names for your functions and variables? What could you rename 'example' and 'argv' to make their purposes clearer, both to you in the future and to someone else reading your code?
| for (const line of lines) { | ||
| if (opts.b) { | ||
| if (line.trim() === "") { | ||
| console.log(); | ||
| } else { | ||
| console.log(`${lineNum} ${line}`); | ||
| lineNum++; | ||
| } | ||
| } else if (opts.n) { | ||
| console.log(`${lineNum} ${line}`); | ||
| lineNum++; | ||
| } | ||
| } |
There was a problem hiding this comment.
When you have two code sections that are very similar except for a small difference, is there a way you could combine them? What would that look like here?
| if (programArgv.length === 1) { | ||
| pathToFile = programArgv[0]; | ||
| try { | ||
| const stats = await fs.stat(pathToFile); | ||
| if (stats.isFile()) { | ||
| await listFiles("file"); | ||
| } else if (stats.isDirectory()) { | ||
| listFiles("directory"); | ||
| } else { | ||
| console.error("Not a file or directory."); | ||
| } | ||
| } catch (err) { | ||
| console.error("Invalid path:", err.message); | ||
| } | ||
| } else if (programArgv.length === 0) { | ||
| pathToFile = process.cwd(); | ||
| await listFiles("directory"); | ||
| } else { | ||
| console.error( | ||
| `Expected no more than 1 argument (sample-files) but got ${programArgv.length}.` | ||
| ); | ||
| } | ||
| })(); |
There was a problem hiding this comment.
Is there a way you could structure your code so the decision of whether to list files, directories, or error messages, and how to display them, is handled in just one spot? Why might that make maintenance simpler over time?
| if (opts["l"]) { | ||
| outputPerFile.push(flag_l(content)); | ||
| } | ||
| if (opts["w"]) { | ||
| outputPerFile.push(flag_w(content)); | ||
| } | ||
| if (opts["c"]) { | ||
| outputPerFile.push(flag_c(content)); | ||
| } |
There was a problem hiding this comment.
If you look for patterns in the way you're handling each of the '-l', '-w', and '-c' options, can you imagine a way to combine the operations to make your code shorter and easier to change in the future?
| const total = []; | ||
| const output = []; | ||
| const countsPerFile = []; | ||
| let columnWidth = 0; |
There was a problem hiding this comment.
What would happen if you moved the declaration of 'columnWidth' inside 'handleInput' instead of declaring it outside? How might that affect readability and maintainability for you or someone else looking at your code?
| .argument("<sample-files...>", "The file path to process") | ||
| .parse(); | ||
|
|
||
| const argv = program.args; |
There was a problem hiding this comment.
How do you usually choose names for your functions and variables? What could you rename 'example' and 'argv' to make their purposes clearer, both to you in the future and to someone else reading your code?
| async function listFiles(type) { | ||
| let output = []; | ||
| let formattedPath = ""; | ||
| if (type == "directory") { | ||
| formattedPath = pathToFile; | ||
| } else if (type == "file") { | ||
| formattedPath = path.dirname(pathToFile); | ||
| } | ||
| const char = program.opts(); | ||
| const files = await fs.readdir(formattedPath); | ||
| const sortedOutput = files.sort((a, b) => a.localeCompare(b)); | ||
|
|
||
| if (char["a"]) { | ||
| output = flag_a(sortedOutput); | ||
| } else { | ||
| sortedOutput.forEach(function (file) { | ||
| if (file[0] != ".") { | ||
| output.push(file); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| if (char["1"]) { | ||
| flag_1(output); | ||
| } else { | ||
| console.log(output.join(" ")); | ||
| } | ||
| } |
There was a problem hiding this comment.
Is there a way you could structure your code so the decision of whether to list files, directories, or error messages, and how to display them, is handled in just one spot? Why might that make maintenance simpler over time?
| for (const row of output) { | ||
| for (let i = 0; i < numOfColumns; i++) { | ||
| columnWidth = Math.max(columnWidth, String(row[i]).length); | ||
| } | ||
| } | ||
|
|
||
| for (let row of output) { | ||
| const line_parts = []; | ||
| for (let i = 0; i < numOfColumns; i++) { | ||
| line_parts.push(String(row[i]).padStart(columnWidth + 4)); | ||
| } |
There was a problem hiding this comment.
What would happen if you moved the declaration of 'columnWidth' inside 'handleInput' instead of declaring it outside? How might that affect readability and maintainability for you or someone else looking at your code?
| } else if (type == "file") { | ||
| formattedPath = path.dirname(pathToFile); | ||
| } | ||
| const char = program.opts(); |
There was a problem hiding this comment.
If you explained what each function and variable does to a friend who had never seen the code, what might you name them instead so they're easy to understand?
| console.error("Invalid path:", err.message); | ||
| } | ||
| } else if (programArgv.length === 0) { | ||
| pathToFile = process.cwd(); |
There was a problem hiding this comment.
Can you spot a way to limit the scope of 'pathToFile' so it's only available where it's actually used? What changes would that require in your structure?
| formattedPath = pathToFile; | ||
| } else if (type == "file") { | ||
| formattedPath = path.dirname(pathToFile); |
There was a problem hiding this comment.
Can you spot a way to limit the scope of 'pathToFile' so it's only available where it's actually used? What changes would that require in your structure?
|
Thanks for opening a new PR! AI started to review it |
| async function example(path) { | ||
| try { | ||
| const data = await fs.readFile(path, { encoding: "utf8" }); | ||
| if (opts["b"]) { | ||
| countLines(data); | ||
| } else if (opts["n"]) { | ||
| countLines(data); | ||
| } else { | ||
| console.log(data.trimEnd()); | ||
| } | ||
| } catch (err) { | ||
| console.error(err); | ||
| } | ||
| } |
There was a problem hiding this comment.
How do you decide on meaningful names for your functions? Can you think of a name that might better communicate this function's purpose?
| const data = await fs.readFile(path, { encoding: "utf8" }); | ||
| if (opts["b"]) { | ||
| countLines(data); | ||
| } else if (opts["n"]) { | ||
| countLines(data); | ||
| } else { |
There was a problem hiding this comment.
When two different options lead to almost the same behavior, what pattern could you use to reduce duplicated code and keep it easier to update later?
| for (const line of lines) { | ||
| if (opts.b) { | ||
| if (line.trim() === "") { | ||
| console.log(); | ||
| } else { | ||
| console.log(`${lineNum} ${line}`); | ||
| lineNum++; | ||
| } | ||
| } else if (opts.n) { | ||
| console.log(`${lineNum} ${line}`); | ||
| lineNum++; | ||
| } | ||
| } |
There was a problem hiding this comment.
When two different options lead to almost the same behavior, what pattern could you use to reduce duplicated code and keep it easier to update later?
|
|
||
| program.parse(); | ||
|
|
||
| let pathToFile = ""; |
There was a problem hiding this comment.
Why do you think keeping variables inside the smallest possible scope is a helpful practice? Can you imagine how this variable could be kept within the async block or passed as a parameter instead of being an outer variable?
|
|
||
| (async () => { | ||
| if (programArgv.length === 1) { | ||
| pathToFile = programArgv[0]; |
There was a problem hiding this comment.
Why do you think keeping variables inside the smallest possible scope is a helpful practice? Can you imagine how this variable could be kept within the async block or passed as a parameter instead of being an outer variable?
| if (type == "directory") { | ||
| formattedPath = pathToFile; | ||
| } else if (type == "file") { | ||
| formattedPath = path.dirname(pathToFile); |
There was a problem hiding this comment.
Why do you think keeping variables inside the smallest possible scope is a helpful practice? Can you imagine how this variable could be kept within the async block or passed as a parameter instead of being an outer variable?
| const flag_1 = (files) => { | ||
| files.forEach(function (file) { | ||
| console.log(file); | ||
| }); | ||
| }; |
There was a problem hiding this comment.
When you name your functions, how do you decide on a name? What might be some more descriptive names for flag_1?
| sortedOutput.forEach(function (file) { | ||
| if (file[0] != ".") { | ||
| output.push(file); | ||
| } | ||
| }); |
There was a problem hiding this comment.
Have you encountered array filter methods before? How could you use them to make this block of code shorter or clearer?
| const countAndDisplay = async (path) => { | ||
| const outputPerFile = []; | ||
| const content = await fs.readFile(path, "utf-8"); | ||
| if (opts["l"]) { | ||
| outputPerFile.push(flag_l(content)); | ||
| } | ||
| if (opts["w"]) { | ||
| outputPerFile.push(flag_w(content)); | ||
| } | ||
| if (opts["c"]) { | ||
| outputPerFile.push(flag_c(content)); | ||
| } | ||
| if (argv.length > 1) { | ||
| if (total.length == 0) { | ||
| total.push(...outputPerFile); | ||
| } else { | ||
| for (let index = 0; index < outputPerFile.length; index++) { | ||
| total[index] += outputPerFile[index]; | ||
| } | ||
| } | ||
| countsPerFile.push(...outputPerFile); | ||
| } | ||
| outputPerFile.push(path); | ||
| output.push([...outputPerFile]); | ||
| }; |
There was a problem hiding this comment.
If you wanted to adapt this code to work differently (for example, outputting results in a different format), how might breaking this apart into smaller functions make it easier to change?
| if (opts["l"]) { | ||
| outputPerFile.push(flag_l(content)); | ||
| } | ||
| if (opts["w"]) { | ||
| outputPerFile.push(flag_w(content)); | ||
| } | ||
| if (opts["c"]) { | ||
| outputPerFile.push(flag_c(content)); | ||
| } |
There was a problem hiding this comment.
What would happen if you had to add more options like -m for characters? Can you imagine a way to pair up the options with their processing functions so the code to handle them doesn't need to be written out every time?
|
Thanks for opening a new PR! AI started to review it |
| async function listFiles(type) { | ||
| let output = []; | ||
| let formattedPath = ""; | ||
| if (type == "directory") { | ||
| formattedPath = pathToFile; | ||
| } else if (type == "file") { | ||
| formattedPath = path.dirname(pathToFile); | ||
| } | ||
| const char = program.opts(); | ||
| const files = await fs.readdir(formattedPath); | ||
| const sortedOutput = files.sort((a, b) => a.localeCompare(b)); | ||
|
|
||
| if (char["a"]) { | ||
| output = flag_a(sortedOutput); | ||
| } else { | ||
| sortedOutput.forEach(function (file) { | ||
| if (file[0] != ".") { | ||
| output.push(file); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| if (char["1"]) { | ||
| flag_1(output); | ||
| } else { | ||
| console.log(output.join(" ")); | ||
| } | ||
| } |
There was a problem hiding this comment.
What do you think a better name for the function that handles both files and directories could be? Does calling it 'listFiles' match what you expect it to do when reading code?
| const flag_a = (files) => { | ||
| files.unshift(".."); | ||
| files.unshift("."); | ||
| return files; | ||
| }; |
There was a problem hiding this comment.
How could you ensure that you're not duplicating '.' and '..' entries if flag_a gets called several times on the same array? What do you think would happen currently if someone tried that?
|
|
||
| program.parse(); | ||
|
|
||
| let pathToFile = ""; |
There was a problem hiding this comment.
What would be the effect of declaring pathToFile inside your main async function, instead of at the top? Would this make it clearer where it's used and how it's set?
| const total = []; | ||
| const output = []; | ||
| const countsPerFile = []; |
There was a problem hiding this comment.
Do you see a way to combine or clarify these arrays so it's obvious what each one does, or maybe structure your results a little differently for clarity and future flexibility?
| const flag_l = (content) => { | ||
| return content.split("\n").length - 1; | ||
| }; |
There was a problem hiding this comment.
How could you ensure that the count matches typical behavior, especially for files that may or may not end with a newline? Could you test with different kinds of files and see what outputs you get?
| if (opts["b"]) { | ||
| countLines(data); | ||
| } else if (opts["n"]) { | ||
| countLines(data); | ||
| } else { |
There was a problem hiding this comment.
Is there a case where both -b and -n could be set? What does the standard tool do if this happens? Could you adjust your logic to handle this in a way that would make sense to users?
| async function example(path) { | ||
| try { | ||
| const data = await fs.readFile(path, { encoding: "utf8" }); | ||
| if (opts["b"]) { | ||
| countLines(data); | ||
| } else if (opts["n"]) { | ||
| countLines(data); | ||
| } else { | ||
| console.log(data.trimEnd()); | ||
| } | ||
| } catch (err) { | ||
| console.error(err); | ||
| } | ||
| } |
There was a problem hiding this comment.
Is there another function name that would help a future reader quickly understand what this function does? What about something like 'processFile'?
Learners, PR Template
Self checklist
Changelist
Done shell tools
Questions
none