Arrange Markdown Blocks

Posted by RoyRogers, Last update 16 days ago

Arrange blocks grouped by Markdown heading levels

Uses the Arrange Mode dialog with selected text or whole draft if no selection.
Make sure to use Arrange in ‘Block’ mode!

Overwrites the selected text with the results from this dialog, if any changes.

NOTE!
Loops for easy change of level if Arrange is canceled or unchanged.
Rearrange blocks or cancel level prompt, will exit loop.

Steps

  • script

    // Arrange blocks grouped by MD heading levels
    // RV 2020-03-01 at 16:48 EST
    
    // Uses the Arrange dialog with selected text or whole draft, if no selection.
    // Overwrites the selected text with the results from this dialog, if changes.
    // NOTE!
    // Loops for easy change of level if Arrange is canceled or unchanged.
    // Rearrange blocks or cancel level prompt, will exit loop.
    
    
    main: {
    	let range = editor.getSelectedRange();
    	do {
    		let level = SelectHeadingLevel();
    		if (!level) { 
    			// cancel prompt to break loop
    			context.cancel('Canceled');
    			break main;
    		}
    		let text = GetText(range);
    		if (Arrange(text, level, range)) {
    			// text was rearanged and draft replaced 
    			break main;
    		}
    	} while(true);
    }
    
    
    function SelectHeadingLevel() {
    	var p = Prompt.create();
    	p.title = "Markdown Heading Blocks";
    	p.message = "Use 'block mode' in 'Arrange'!...";
    
    	var options = ["H1: #", "H2: ##", "H3: ###", "H4: ####",
      		             "H5: #####", "H6: ######"];
    	var selected = ["H3: ###"];
    
    	// single selection
    	p.addSelect("s1", "Select heading group level...", options, selected, false);
    	p.addButton("OK");
    
    	if (!p.show()) {
    		// Prompt 'Cancel'
    		return false;
    	}
    	// Prompt 'OK'
    	return String(p.fieldValues["s1"])[1]; // 2nd char: the number only.
    }
    
    
    function GetText(range) {
    	// Get all text or selected text from editor
    	let text = '';
    	
    	if (range[1] == 0) {
    		text = draft.content;
    	}
    	else {
    		text = editor.getSelectedText();
    		
    		// preserving whitespace at end of selection
    		m = text.match(/\s+$/g);
    		if (m != null) {
    			endSpace = m;
    		}
    		text = text.trimEnd();
    	}
    	return text;
    }
    
    
    function Arrange(text, level, range) {
    	let endSpace = '';
    	let len = text.length;
    
    	// insert '¶'-character at all blank lines and clean possible space/tabs,
    	// to make/fake a contigous block of all text:
    	text = text.replace(/\n[ \t]*\n/g, '\n¶\n');
    	// Run twice to deal with multiples blank lines:
    	text = text.replace(/\n[ \t]*\n/g, '\n¶\n'); 
    
    	// remove '¶'-character above all md headings at selected levels,
    	// to divide text into chapter/section blocks:
    	var repl = '¶(\\n#{1,'+level+'} )';
    	var re = new RegExp(repl,"g", '$1');
    	text = text.replace(re, '$1');
    	// text = text.replace(¶(\n#{1,3} )/g, '$1') // hardcoded at lev 3
    
    	let processed = editor.arrange(text);
    	if (processed == text) {
    		// Content not changed or "Arrange" was canceled,
    		return false;
    	}
    	
    	// 'Arrange' changed and was not canceled:
    	if (range[1] == 0) {
    		// select all text if no inital selection:
    		editor.setSelectedRange(0, len);
    	}
    	
    	// remove all '¶'-characters from blank lines,
    	// and replace selected text (or all text if no initial selection):
    	editor.setSelectedText(processed.replace(/\n¶/g, '\n').trimEnd() + endSpace);
    		
    	if (range[1] == 0) {
    		// If no initial selection,
    		// reset to original position, with 0 length:
    		editor.setSelectedRange(range[0], 0);
    	}
    	return true;
    }
    

Options

  • After Success Default
    Notification Info
    Log Level Info
Actions available in the Action Directory are uploaded by community members. Use appropriate caution reviewing downloaded actions before use.