Script to add / remove underscores?

Hi, I’m using a set of TaskPaper files to organize sets of hashtags, and I’m wondering if anyone could help me with a script (or two scripts) for (1) removing all underscores (2) adding underscores (preferable just for second level items) wherever there’s a space separating words/text.

Thanks so much for your help with this!

Wouldn’t the Find and Replace command do that?

Under the Edit menu > Find > Find and Replace

Or type ⌘⌥F

2 Likes

Thanks! Well that could very well work for removing all underscores.

But what about adding underscores (preferable just for second level items) wherever there’s a space separating words/text?

Thanks!

Although not as easy, you can use the Find and Replace command for that too.

Type a space in the Find field.
Type a underscore in the Replace with field.

Then use the Next button to skip through spaces that you don’t want to replace. When you get to a space that you want to replace, use the Replace or Replace & Find button.

I hope that helps!

1 Like

Thanks very much. Appreciate your help!

1 Like

So…I’ve been using the Find & Replace approach, and it has generally worked fine, but…I’m not having to switch back and forth between finding & replacing multiple formatting.

Is there a simple script or approach in AppleScript or Keyboard Maestro that would enable me to do this? Thanks again!

Hi @printer printer. I have seen similar things to what you have requested done with RegEx. You will have to come up with the RegEx (expression) that accomplishes what you want. Then you can run that many different ways.

I am not sure if there is a forum for RegEx experts that can help you come up with that first. Maybe it is a question for StackExchange. You will have to define specifically what you want so they understand your request. I think that there are a couple of people in this forum that are very fluent in RegEx, but I haven’t seen them in a while. @mattgemmell is the one that comes to my mind.

My understanding is that you will need two RegEx

  1. One that selects all the hashtags that have underscores in them and then selects the underscores in that hash. You can then remove them easily
  2. One that selects all the hashtags that have spaces in them and then selects those spaces. This is be more challenging since you have to somehow determine how many spaces your hashes can have or you need a particular way to tell the computer when you are done with your hash. You then can substitute those particular spaces with something else.

Having those RegEx’s you can have a simple script that runs through the RegEx’s and checks for the level location.

Good luck with your

1 Like

This regular expression should select all instances of underscores: /_/g

See it in action here.

1 Like

Hey @printer, I tried to figure out your problem using https://regex101.com

It seems as if what you want is REALLY REALLY difficult because you need a way to know when your tag is done. I would not like to say that you want is impossible because RegEx is its own programming language with experts there. But I would recommend you to try something other than spaces. I have seen people using comas to distinguish when the tag is done, like the following

#This is, #an, #example

That way you can have the program know when the tag is done. If you think that you like spaces, then comas are the way to go. Of course, that complicates the RegEx expression even more, but I think that someone can come up with something to help you.

Also, you will have to make sure that you are ONLY placing the tags at the end of the line. If you add any other text at the end of the line, the RegEx will fail. Hope this helps.

1 Like

Thanks so much for everyone’s input! I greatly appreciate it, and I’m sorry for my late reply… Basically, I’m imagining two scripts that would toggle between two functions:

  • Find & Replace: # ‘s - with nothing ; :’ s - with nothing ; and _ 's with a space;

  • Undo the Find & Replace functions above;

That’s it!

So, how’d you suggest I set up scripts to accomplish this?

Thanks!

Sorry, guys, but I’m still seeking a solution for how to do the following:

SCRIPT 1

  • Find & Replace: # ‘s [hashtags] - with nothing
  • Find & Replace: : ’s [colons] - with nothing
  • Find & Replace: _ 's [underscores] with a space;

SCRIPT 2

  • Undo the Find & Replace functions above

Any ideas / suggestions?

Thanks!

I think it might be helpful, particularly in the light of wanting an Undo, to understand a bit more of the context.

What is the problem that you are trying to solve with a reversible find and replace ?

Can you show us a simple before and after example ?

(and perhaps explain roughly how this fits into your workflows ?)

1 Like

Fair enough. In this instance, I use TaskPaper as a way to organize a set of tags that I use when annotating PDF files – and then I use the same TaskPaper file in tandem with OmniOutliner to render (further organize) those annotations further. So, I used the normal syntax in TaskPaper (with hashtags and underscores) when copying and pasting tags – which is also why I keep asking if it’s possible to Copy multiple selected items? (or select & copy discontiguous text) !

But in order to use TaskPaper files with OmniOutliner, I need to first Find & Replace: # ‘s - with nothing ; : ’ s - with nothing ; and _ 's with a space. Because I sometimes go back to the original file, and use it to do more annotations, I also want the option to undo those Find & Replace functions. Does this make sense?

Of course. Here’s a section where the hashtags, colons, and underscores are intact…

…and here’s what they look like after the find and replace of that syntax…

Please let me know if I can answer any more questions. Thanks for your help!

So you’re happy, in the (space -> underscore) phase, for all nodes to be affected ?

(Or might the less indented project/parent rows contain spaces that you don’t want to leave intact, rather than converting them to _ ?)

Here, in the meanwhile, is a first draft of a toggling script, which aims to flip ⇄ between the two states on each run.

Expand disclosure triangle to view JS Source
(() => {
    "use strict";

    // TaskPaper front document toggled between two states
    // 1. Projects with hash-prefixed underscored children
    // 2. Plain lines with plain children.

    // Rob Trew @2020
    // Ver 0.01

    // eslint-disable-next-line max-lines-per-function
    const TaskPaperContext = editor => {
        const
            outline = editor.outline,
            topRows = outline.root.children,
            hashRows = outline.evaluateItemPath(
                // eslint-disable-next-line quotes
                '/*/(not @text="")'
            ),
            inHashState = hashRows.some(
                item => item.bodyContentString.startsWith(
                    "#"
                )
            );

        const fromHashState = () => (
            topRows.forEach(
                item => item.setAttribute(
                    "data-type", "note"
                )
            ),
            hashRows.forEach(item => {
                item.bodyContentString = (
                    item.bodyContentString
                    .replace(/^#/u, "")
                    .replace(/_/gu, " ")
                );
            })
        );

        const toHashState = () => (
            topRows.forEach(
                item => item.setAttribute(
                    "data-type", "project"
                )
            ),
            hashRows.forEach(item => {
                const s = item.bodyContentString;

                item.bodyContentString = (
                        s.startsWith("#") ? (
                            s
                        ) : `#${s}`
                    )
                    .replace(/ /gu, "_");
            })
        );

        outline.groupUndoAndChanges(() => {
            if (inHashState) {
                fromHashState();
            } else {
                toHashState();
            }
        });

        return inHashState ? (
            "CLEARED hashes, underscores, colons."
        ) : "ADDED hashes, underscores, colons.";

    };

    const main = () => {
        const
            taskpaper = Application("TaskPaper"),
            frontDoc = taskpaper.documents.at(0);

        return frontDoc.exists() ? (
            frontDoc.evaluate({
                script: `${TaskPaperContext}`
            })
        ) : "No documents open in TaskPaper";
    };

    // MAIN ---
    return main();
})();

I personally tend to attach this kind of thing to a keystroke with Keyboard Maestro, but more generally, see:


PS make sure, of course, that you copy the whole of the script, scrolling all the way down to the bottom, which looks like:

    // MAIN ---
    return main();
})();
2 Likes

Yes.

I don’t believe so. That’s why the parent row “titles” don’t have spaces, e.g., AskQuestions , ResearchQuestions m, CriticalPoint, etc.

Oh wow. Thank you so much for this! I’m excited to give it a try…

I’ve used Keyboard Maestro periodically, and would certainly be happy to use that with your script. How would one do so?

Also, do you think there’s a way to use Keyboard Maestro to select & copy discontiguous text, as I outlined in this post: Copy multiple selected items?

Thanks again for your help!

Just tried your script. It’s brilliant; it does exactly what I was seeking. Thank you so much!!!

1 Like

You can paste the JS code into a Keyboard Maestro Execute JavaScript for Automation action, and then choose a hotkey to trigger the macro:

Toggled hash, underscore, colon.kmmacros.zip (2.1 KB)

1 Like

Out of time now, but might take a look at the solution suggested by Jesse over the weekend.

I think it is a generic solution into which you can plug some problem-specific script, so you would probably need to show me a couple of use-cases with before and after examples.

```
Preferably as text pasted here between triple backticks, 
rather than graphic images, so that I can copy them for testing.
```

Something like a pair of macros:

  • toggle a @selected tag on and off in selected lines.
  • apply some other change to all lines that are tagged as @selected, perhaps clearing that tag once the changes have been made.

i.e. you would need to show me before and after examples of a couple of instances of what other change might be.


Unless you just want to copy the text of all items marked with that tag ?

1 Like