Skip to content

Line Reader extension for TurboWarp #2193

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion CNAME
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should not delete CNAME, it's what allows the custom domain name (extensions.turbowarp.org) to work

This file was deleted.

3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,6 @@ Sample projects (in the `samples` folder) are licensed under [CC-BY 4.0](./licen
Everything else, such as the extension images, development server, and website are licensed under the [GNU General Public License version 3](licenses/GPL-3.0.txt).

See [images/README.md](images/README.md) for attribution information for each image.

This specific mirror contains most TurboWarp extensions plus Pear ones. Here are the Pear ones:
-Microboy-/line-reader.js
140 changes: 140 additions & 0 deletions extensions/-Microboy-/line-reader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// Name: Line Reader
// ID: linereader
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ID should contain your username

Suggested change
// ID: linereader
// ID: microBoyLineReader

// Description: A simple extension for reading newlines and supports special tilde delimiters. Use ~n for UNIX, ~r for Classic Mac OS, and ~z for Windows.
// By: Pear Computer LLC. <https://scratch.mit.edu/users/-Microboy-/>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this a LLC?

// License: BSD-2-Clause
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just out of curiosity wondering why you have special licensing, generally speaking this isn't necessary. Turbowarp recommends Mozilla Public License v2. See CONTRIBUTING.md.

/*
Copyright 2025 Pear Computer LLC.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
(function(Scratch) {
"use strict";

class LineReaderExtension {
getInfo() {
return {
id: 'linereader', // Unique ID for the extension
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This ID should also contain your username and be the same as the one in the header comment

Suggested change
id: 'linereader', // Unique ID for the extension
id: 'microBoyLineReader', // Unique ID for the extension

name: 'Line Reader', // Name displayed in Scratch

Check failure on line 24 in extensions/-Microboy-/line-reader.js

View workflow job for this annotation

GitHub Actions / lint

Extension name should usually be translated
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The lint is falling because you have a lot of translation errors. You should use Scratch.translate() on the extension name and block texts.

Suggested change
name: 'Line Reader', // Name displayed in Scratch
name: Scratch.translate('Line Reader'), // Name displayed in Scratch

blocks: [
{
opcode: 'getLine', // Opcode for getting a specific line
blockType: Scratch.BlockType.REPORTER, // It's a reporter block (returns a value)
text: 'line [LINE_NUM] in [TEXT_INPUT]', // The text displayed on the block

Check failure on line 29 in extensions/-Microboy-/line-reader.js

View workflow job for this annotation

GitHub Actions / lint

Block text should usually be translated
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here and for the rest of the blocks. Also, I have to ask why you're using so many comments for things that should be obvious? It just distracts from the code

arguments: {
LINE_NUM: {
type: Scratch.ArgumentType.NUMBER, // Argument for the line number
defaultValue: 1 // Default value for the line number
},
TEXT_INPUT: {
type: Scratch.ArgumentType.STRING, // Argument for the multi-line text
// Default value demonstrating different newline types using custom escape sequences
defaultValue: 'Hello from UNIX!~nHello from Classic Mac!~rHello from Windows!~zFinal line.'
}
}
}
,
{
opcode: 'countLines', // Opcode for counting lines
blockType: Scratch.BlockType.REPORTER, // It's a reporter block (returns a number)
text: 'number of lines in [TEXT_INPUT]', // The text displayed on the block

Check failure on line 46 in extensions/-Microboy-/line-reader.js

View workflow job for this annotation

GitHub Actions / lint

Block text should usually be translated
arguments: {
TEXT_INPUT: {
type: Scratch.ArgumentType.STRING, // Argument for the multi-line text
// Default value demonstrating different newline types using custom escape sequences
defaultValue: 'First line!~nSecond line!~rThird line!~zFourth line.'
}
}
}
]
};
}

/**
* Helper function to process the input text by replacing custom escape sequences
* with actual newline characters.
* @param {string} text - The input string from a Scratch block.
* @returns {string} The processed string with actual newlines.
*/
_processNewlines(text) {
// Ensure the input is treated as a string
let processedText = String(text);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You shouldn't use JavaScript's native casting functions for block arguments as scratch has numerous odd quirks that could lead to issues. Use Scratch.Cast.toString() or Scratch.Cast.toNumber() for all block inputs.

Suggested change
let processedText = String(text);
let processedText = Scratch.Cast.toString(text);


// --- Pre-process custom newline escape sequences ---
// It's crucial to replace the most specific (or potentially conflicting)
// sequences first to avoid partial replacements.
// Order: ~z (for \r\n), then ~r (for \r), then ~n (for \n)

// ~z is for \r\n (Windows newline)
processedText = processedText.replace(/~z/g, '\r\n');
// ~r is for \r (Classic Mac OS newline)
processedText = processedText.replace(/~r/g, '\r');
// ~n is for \n (UNIX newline)
processedText = processedText.replace(/~n/g, '\n');

return processedText;
}

/**
* Retrieves a specific line from a given string, automatically handling
* common newline conventions and custom escape sequences.
* @param {object} args - The arguments passed to the block.
* @param {number} args.LINE_NUM - The 1-indexed line number to retrieve.
* @param {string} args.TEXT_INPUT - The multi-line string to process.
* @returns {string} The requested line, or an empty string if out of bounds.
*/
getLine(args) {
// Process custom escape sequences into actual newlines
const processedText = this._processNewlines(args.TEXT_INPUT);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should also use casting, assuming it's a string it should probably look like this:

Suggested change
const processedText = this._processNewlines(args.TEXT_INPUT);
const processedText = this._processNewlines(Scratch.Cast.toString(args.TEXT_INPUT));

// Ensure the line number is an integer
const lineNum = parseInt(args.LINE_NUM, 10);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This argument too and same goes for the rest of the args in every block


// Regular expression to split by any common newline sequence:
// \r\n (Windows) OR \r (Classic Mac) OR \n (UNIX)
const linesArray = processedText.split(/\r\n|\r|\n/);

// Scratch line numbers are 1-indexed, so convert to 0-indexed for JavaScript array
const index = lineNum - 1;

// Check if the requested line number is within the valid range
if (index >= 0 && index < linesArray.length) {
return linesArray[index]; // Return the requested line
} else {
return ''; // Return an empty string if the line number is out of bounds
}
}

/**
* Counts the number of lines in a given string, automatically handling
* common newline conventions and custom escape sequences.
* @param {object} args - The arguments passed to the block.
* @param {string} args.TEXT_INPUT - The multi-line string to process.
* @returns {number} The total number of lines in the string.
*/
countLines(args) {
// Process custom escape sequences into actual newlines
const processedText = this._processNewlines(args.TEXT_INPUT);

// Regular expression to split by any common newline sequence:
// \r\n (Windows) OR \r (Classic Mac) OR \n (UNIX)
const linesArray = processedText.split(/\r\n|\r|\n/);

// If the input string is empty, split() will return [''], so length will be 1.
// We want 0 lines for an empty string.
if (processedText === '') {
return 0;
}
return linesArray.length;
}
}

// Register the extension with Scratch
Scratch.extensions.register(new LineReaderExtension());

})(Scratch); // Pass the global Scratch object into the IIFE
1 change: 1 addition & 0 deletions images/-Microboy-/line-reader.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions images/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -321,3 +321,6 @@ All images in this folder are licensed under the [GNU General Public License ver

## CubesterYT/KeySimulation.svg
- Created by [@SharkPool-SP](https://github.com/SharkPool-SP/)

## -Microboy-/line-reader.svg
- Created by [@-Microboy-](https://scratch.mit.edu/users/-Microboy-/)
Loading