mirror of
https://github.com/keathmilligan/obsidian-paste-reformatter.git
synced 2026-07-22 05:43:44 +00:00
feat: add single-spacing transformer option
- Add convertToSingleSpaced setting to collapse multiple consecutive blank lines into single blank lines - Add "Convert to single-spaced" toggle in settings UI positioned above "Remove empty lines" - Implement conditional disabling when "Remove empty lines" is enabled with visual indication - Add transformer logic that processes before empty line removal with optimization to skip when not needed - Add OpenSpec documentation for markdown-transformations capability - Update version to 1.3.0
This commit is contained in:
parent
7c4cc6dec9
commit
ec8685eddc
10 changed files with 262 additions and 3 deletions
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "paste-reformatter",
|
||||
"name": "Paste Reformatter",
|
||||
"version": "1.2.2",
|
||||
"version": "1.3.0",
|
||||
"minAppVersion": "0.15.0",
|
||||
"description": "Reformat pasted text for precise control.",
|
||||
"author": "Keath Milligan",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
# Change: Add single-spacing transformer for markdown
|
||||
|
||||
## Why
|
||||
|
||||
Users may want to collapse multiple consecutive blank lines in pasted content into single blank lines without removing all blank lines entirely. This provides a middle-ground option between preserving all blank lines as-is and removing them completely.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Add a new markdown transformer that collapses multiple consecutive blank lines (2+) into a single blank line
|
||||
- Add a new setting "Convert to single-spaced" that appears above "Remove empty lines" in the Markdown transformations section
|
||||
- The setting defaults to Off (false) to preserve existing behavior
|
||||
- When "Remove empty lines" is enabled, the "Convert to single-spaced" option is disabled (not applicable) since all blank lines are removed anyway
|
||||
- The transformer is applied in the markdown transformation pipeline before empty line removal
|
||||
|
||||
## Impact
|
||||
|
||||
- Affected specs: `markdown-transformations`
|
||||
- Affected code:
|
||||
- `src/markdownTransformer.ts` - Add single-spacing logic
|
||||
- `src/main.ts` - Add `convertToSingleSpaced` setting and UI toggle with conditional disabling
|
||||
- No breaking changes
|
||||
- Backward compatible: defaults to Off, existing behavior unchanged
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
# Markdown Transformations
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Single-spacing transformation
|
||||
The system SHALL provide an option to collapse multiple consecutive blank lines into a single blank line in the markdown output.
|
||||
|
||||
#### Scenario: Multiple blank lines collapsed to single
|
||||
- **WHEN** `convertToSingleSpaced` is enabled (true)
|
||||
- **AND** the markdown content contains 2 or more consecutive blank lines
|
||||
- **THEN** the consecutive blank lines SHALL be replaced with exactly 1 blank line
|
||||
- **AND** the `appliedTransformations` flag SHALL be set to true
|
||||
|
||||
#### Scenario: Single blank lines preserved
|
||||
- **WHEN** `convertToSingleSpaced` is enabled (true)
|
||||
- **AND** the markdown content contains single blank lines (not consecutive)
|
||||
- **THEN** those single blank lines SHALL be preserved as-is
|
||||
- **AND** no transformation is applied to those lines
|
||||
|
||||
#### Scenario: Non-blank lines unaffected
|
||||
- **WHEN** `convertToSingleSpaced` is enabled (true)
|
||||
- **THEN** all non-blank lines SHALL remain unchanged
|
||||
|
||||
#### Scenario: Feature disabled preserves original behavior
|
||||
- **WHEN** `convertToSingleSpaced` is disabled (false)
|
||||
- **THEN** all blank lines SHALL remain unchanged regardless of how many are consecutive
|
||||
- **AND** the `appliedTransformations` flag SHALL NOT be set based on spacing
|
||||
|
||||
#### Scenario: Interaction with removeEmptyLines
|
||||
- **WHEN** `removeEmptyLines` is enabled (true)
|
||||
- **THEN** the single-spacing transformer SHALL be skipped (not applied)
|
||||
- **AND** the empty line removal logic SHALL be applied instead
|
||||
|
||||
### Requirement: Single-spacing configuration
|
||||
The system SHALL provide a setting to enable or disable the single-spacing transformation.
|
||||
|
||||
#### Scenario: Setting defaults to disabled
|
||||
- **WHEN** the plugin is initialized with no prior settings
|
||||
- **THEN** the `convertToSingleSpaced` setting SHALL default to false
|
||||
|
||||
#### Scenario: Setting persists across sessions
|
||||
- **WHEN** a user enables or disables the `convertToSingleSpaced` setting
|
||||
- **AND** the settings are saved
|
||||
- **THEN** the setting value SHALL be persisted and restored on plugin reload
|
||||
|
||||
### Requirement: Single-spacing UI control
|
||||
The system SHALL provide a user interface control for the single-spacing transformation setting.
|
||||
|
||||
#### Scenario: Toggle positioned above "Remove empty lines"
|
||||
- **WHEN** the settings UI is displayed
|
||||
- **THEN** the "Convert to single-spaced" toggle SHALL appear in the Markdown transformations section
|
||||
- **AND** it SHALL be positioned above the "Remove empty lines" toggle
|
||||
|
||||
#### Scenario: Toggle description
|
||||
- **WHEN** the "Convert to single-spaced" toggle is displayed
|
||||
- **THEN** the description SHALL read "Collapse multiple consecutive blank lines into a single blank line"
|
||||
|
||||
#### Scenario: Toggle disabled when removeEmptyLines is enabled
|
||||
- **WHEN** the "Remove empty lines" setting is enabled (true)
|
||||
- **THEN** the "Convert to single-spaced" toggle SHALL be disabled (non-interactive)
|
||||
- **AND** it SHALL be visually indicated as disabled (grayed out or similar)
|
||||
|
||||
#### Scenario: Toggle enabled when removeEmptyLines is disabled
|
||||
- **WHEN** the "Remove empty lines" setting is disabled (false)
|
||||
- **THEN** the "Convert to single-spaced" toggle SHALL be enabled (interactive)
|
||||
- **AND** users SHALL be able to toggle it on or off
|
||||
|
||||
#### Scenario: UI updates when removeEmptyLines changes
|
||||
- **WHEN** the "Remove empty lines" setting is toggled
|
||||
- **THEN** the disabled/enabled state of the "Convert to single-spaced" toggle SHALL update accordingly
|
||||
|
||||
### Requirement: Transform pipeline order
|
||||
The system SHALL apply transformations in a defined order to ensure consistent behavior.
|
||||
|
||||
#### Scenario: Single-spacing before empty line removal
|
||||
- **WHEN** both `convertToSingleSpaced` and `removeEmptyLines` are configured (regardless of values)
|
||||
- **THEN** the single-spacing transformation logic SHALL be evaluated before the empty line removal logic in the code
|
||||
- **AND** if `removeEmptyLines` is true, single-spacing SHALL be skipped as an optimization
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
# Implementation Tasks
|
||||
|
||||
## 1. Update settings interface and defaults
|
||||
- [x] 1.1 Add `convertToSingleSpaced: boolean` property to `PasteReformmatterSettings` interface in `src/main.ts`
|
||||
- [x] 1.2 Add `convertToSingleSpaced: false` to `DEFAULT_SETTINGS` in `src/main.ts`
|
||||
|
||||
## 2. Implement single-spacing transformer
|
||||
- [x] 2.1 Add `convertToSingleSpaced` parameter to `transformMarkdown` function signature in `src/markdownTransformer.ts`
|
||||
- [x] 2.2 Implement single-spacing logic that collapses 2+ consecutive blank lines into 1 blank line
|
||||
- [x] 2.3 Apply transformer before the `removeEmptyLines` logic in the transformation pipeline
|
||||
- [x] 2.4 Skip single-spacing transformer if `removeEmptyLines` is enabled (optimization - no point processing if lines will be removed)
|
||||
- [x] 2.5 Set `appliedTransformations` flag when single-spacing makes changes
|
||||
|
||||
## 3. Add settings UI
|
||||
- [x] 3.1 Add "Convert to single-spaced" toggle in settings UI, positioned above "Remove empty lines" setting
|
||||
- [x] 3.2 Set description text: "Collapse multiple consecutive blank lines into a single blank line"
|
||||
- [x] 3.3 Implement conditional disabling: when `removeEmptyLines` is true, disable the toggle and show it as disabled/grayed
|
||||
- [x] 3.4 Update onChange handler to save settings
|
||||
- [x] 3.5 When `removeEmptyLines` changes, refresh display to update disabled state of "Convert to single-spaced" toggle
|
||||
|
||||
## 4. Integration
|
||||
- [x] 4.1 Pass `convertToSingleSpaced` setting to `transformMarkdown` call in `src/main.ts:doPaste()`
|
||||
- [x] 4.2 Verify backward compatibility with existing settings (defaults to false)
|
||||
|
||||
## 5. Testing
|
||||
- [x] 5.1 Build project: `npm run build`
|
||||
- [x] 5.2 Test single-spacing with text containing 2+ consecutive blank lines
|
||||
- [x] 5.3 Test that single-spacing is skipped when "Remove empty lines" is enabled
|
||||
- [x] 5.4 Test UI toggle behavior and disabled state interaction
|
||||
- [x] 5.5 Test that existing behavior is preserved when feature is disabled (default)
|
||||
80
openspec/specs/markdown-transformations/spec.md
Normal file
80
openspec/specs/markdown-transformations/spec.md
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
# markdown-transformations Specification
|
||||
|
||||
## Purpose
|
||||
TBD - created by archiving change add-single-spacing-transformer. Update Purpose after archive.
|
||||
## Requirements
|
||||
### Requirement: Single-spacing transformation
|
||||
The system SHALL provide an option to collapse multiple consecutive blank lines into a single blank line in the markdown output.
|
||||
|
||||
#### Scenario: Multiple blank lines collapsed to single
|
||||
- **WHEN** `convertToSingleSpaced` is enabled (true)
|
||||
- **AND** the markdown content contains 2 or more consecutive blank lines
|
||||
- **THEN** the consecutive blank lines SHALL be replaced with exactly 1 blank line
|
||||
- **AND** the `appliedTransformations` flag SHALL be set to true
|
||||
|
||||
#### Scenario: Single blank lines preserved
|
||||
- **WHEN** `convertToSingleSpaced` is enabled (true)
|
||||
- **AND** the markdown content contains single blank lines (not consecutive)
|
||||
- **THEN** those single blank lines SHALL be preserved as-is
|
||||
- **AND** no transformation is applied to those lines
|
||||
|
||||
#### Scenario: Non-blank lines unaffected
|
||||
- **WHEN** `convertToSingleSpaced` is enabled (true)
|
||||
- **THEN** all non-blank lines SHALL remain unchanged
|
||||
|
||||
#### Scenario: Feature disabled preserves original behavior
|
||||
- **WHEN** `convertToSingleSpaced` is disabled (false)
|
||||
- **THEN** all blank lines SHALL remain unchanged regardless of how many are consecutive
|
||||
- **AND** the `appliedTransformations` flag SHALL NOT be set based on spacing
|
||||
|
||||
#### Scenario: Interaction with removeEmptyLines
|
||||
- **WHEN** `removeEmptyLines` is enabled (true)
|
||||
- **THEN** the single-spacing transformer SHALL be skipped (not applied)
|
||||
- **AND** the empty line removal logic SHALL be applied instead
|
||||
|
||||
### Requirement: Single-spacing configuration
|
||||
The system SHALL provide a setting to enable or disable the single-spacing transformation.
|
||||
|
||||
#### Scenario: Setting defaults to disabled
|
||||
- **WHEN** the plugin is initialized with no prior settings
|
||||
- **THEN** the `convertToSingleSpaced` setting SHALL default to false
|
||||
|
||||
#### Scenario: Setting persists across sessions
|
||||
- **WHEN** a user enables or disables the `convertToSingleSpaced` setting
|
||||
- **AND** the settings are saved
|
||||
- **THEN** the setting value SHALL be persisted and restored on plugin reload
|
||||
|
||||
### Requirement: Single-spacing UI control
|
||||
The system SHALL provide a user interface control for the single-spacing transformation setting.
|
||||
|
||||
#### Scenario: Toggle positioned above "Remove empty lines"
|
||||
- **WHEN** the settings UI is displayed
|
||||
- **THEN** the "Convert to single-spaced" toggle SHALL appear in the Markdown transformations section
|
||||
- **AND** it SHALL be positioned above the "Remove empty lines" toggle
|
||||
|
||||
#### Scenario: Toggle description
|
||||
- **WHEN** the "Convert to single-spaced" toggle is displayed
|
||||
- **THEN** the description SHALL read "Collapse multiple consecutive blank lines into a single blank line"
|
||||
|
||||
#### Scenario: Toggle disabled when removeEmptyLines is enabled
|
||||
- **WHEN** the "Remove empty lines" setting is enabled (true)
|
||||
- **THEN** the "Convert to single-spaced" toggle SHALL be disabled (non-interactive)
|
||||
- **AND** it SHALL be visually indicated as disabled (grayed out or similar)
|
||||
|
||||
#### Scenario: Toggle enabled when removeEmptyLines is disabled
|
||||
- **WHEN** the "Remove empty lines" setting is disabled (false)
|
||||
- **THEN** the "Convert to single-spaced" toggle SHALL be enabled (interactive)
|
||||
- **AND** users SHALL be able to toggle it on or off
|
||||
|
||||
#### Scenario: UI updates when removeEmptyLines changes
|
||||
- **WHEN** the "Remove empty lines" setting is toggled
|
||||
- **THEN** the disabled/enabled state of the "Convert to single-spaced" toggle SHALL update accordingly
|
||||
|
||||
### Requirement: Transform pipeline order
|
||||
The system SHALL apply transformations in a defined order to ensure consistent behavior.
|
||||
|
||||
#### Scenario: Single-spacing before empty line removal
|
||||
- **WHEN** both `convertToSingleSpaced` and `removeEmptyLines` are configured (regardless of values)
|
||||
- **THEN** the single-spacing transformation logic SHALL be evaluated before the empty line removal logic in the code
|
||||
- **AND** if `removeEmptyLines` is true, single-spacing SHALL be skipped as an optimization
|
||||
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "obsidian-sample-plugin",
|
||||
"version": "1.2.2",
|
||||
"version": "1.3.0",
|
||||
"description": "This is a sample plugin for Obsidian (https://obsidian.md)",
|
||||
"main": "dist/main.js",
|
||||
"scripts": {
|
||||
|
|
|
|||
21
src/main.ts
21
src/main.ts
|
|
@ -17,6 +17,7 @@ interface PasteReformmatterSettings {
|
|||
cascadeHeadingLevels: boolean; // Whether to cascade heading levels (e.g., H1→H2→H3 becomes H2→H3→H4 when max level is H2)
|
||||
contextualCascade: boolean; // Whether to cascade headings based on the current context (e.g., if cursor is in an H2 section, headings will start from H3)
|
||||
stripLineBreaks: boolean; // Whether to strip hard line breaks (br tags) when reformatting pasted content
|
||||
convertToSingleSpaced: boolean; // Whether to collapse multiple consecutive blank lines into a single blank line
|
||||
removeEmptyLines: boolean; // Whether to remove blank lines in the Markdown output
|
||||
htmlRegexReplacements: RegexReplacement[]; // Regular expression replacements to apply to the HTML content before converting to Markdown
|
||||
markdownRegexReplacements: RegexReplacement[]; // Regular expression replacements to apply to the Markdown content after HTML conversion
|
||||
|
|
@ -29,6 +30,7 @@ const DEFAULT_SETTINGS: PasteReformmatterSettings = {
|
|||
cascadeHeadingLevels: true,
|
||||
contextualCascade: true,
|
||||
stripLineBreaks: false,
|
||||
convertToSingleSpaced: false,
|
||||
removeEmptyLines: false,
|
||||
htmlRegexReplacements: [],
|
||||
markdownRegexReplacements: []
|
||||
|
|
@ -525,6 +527,22 @@ class PasteReformmatterSettingsTab extends PluginSettingTab {
|
|||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
const singleSpacedSetting = new Setting(containerEl)
|
||||
.setName('Convert to single-spaced')
|
||||
.setDesc('Collapse multiple consecutive blank lines into a single blank line')
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.convertToSingleSpaced)
|
||||
.setDisabled(this.plugin.settings.removeEmptyLines)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.convertToSingleSpaced = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
// Add visual indication when disabled
|
||||
if (this.plugin.settings.removeEmptyLines) {
|
||||
singleSpacedSetting.settingEl.addClass('paste-reformatter-disabled-setting');
|
||||
}
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Remove empty lines')
|
||||
.setDesc('Remove blank lines in the Markdown output')
|
||||
|
|
@ -533,6 +551,9 @@ class PasteReformmatterSettingsTab extends PluginSettingTab {
|
|||
.onChange(async (value) => {
|
||||
this.plugin.settings.removeEmptyLines = value;
|
||||
await this.plugin.saveSettings();
|
||||
|
||||
// Refresh the display to update the disabled state of "Convert to single-spaced"
|
||||
this.display();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ export function transformMarkdown(
|
|||
maxHeadingLevel: number,
|
||||
cascadeHeadingLevels: boolean,
|
||||
stripLineBreaks: boolean,
|
||||
convertToSingleSpaced: boolean,
|
||||
removeEmptyLines: boolean
|
||||
},
|
||||
contextLevel: number = 0,
|
||||
|
|
@ -146,6 +147,21 @@ export function transformMarkdown(
|
|||
// First handle the special line break markers
|
||||
let preserveLineBreaks = !settings.stripLineBreaks;
|
||||
|
||||
// Convert multiple consecutive blank lines to single blank line if enabled
|
||||
// Skip this if removeEmptyLines is enabled (optimization - lines will be removed anyway)
|
||||
if (settings.convertToSingleSpaced && !settings.removeEmptyLines) {
|
||||
// Normalize line endings to ensure consistent processing
|
||||
markdown = markdown.replace(/\r\n/g, '\n');
|
||||
|
||||
// Replace 2 or more consecutive newlines with exactly 2 newlines (1 blank line)
|
||||
const originalMarkdown = markdown;
|
||||
markdown = markdown.replace(/\n{3,}/g, '\n\n');
|
||||
|
||||
if (originalMarkdown !== markdown) {
|
||||
appliedTransformations = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Process empty lines with a sliding window approach
|
||||
if (settings.removeEmptyLines) {
|
||||
// First, normalize line endings to ensure consistent processing
|
||||
|
|
|
|||
11
styles.css
11
styles.css
|
|
@ -115,3 +115,14 @@ If your plugin does not need CSS, delete this file.
|
|||
outline: 2px solid var(--interactive-accent);
|
||||
outline-offset: 2px;
|
||||
} */
|
||||
|
||||
/* Disabled Setting Styles */
|
||||
.paste-reformatter-disabled-setting {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.paste-reformatter-disabled-setting .setting-item-name,
|
||||
.paste-reformatter-disabled-setting .setting-item-description {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,5 +4,6 @@
|
|||
"1.1.1": "0.15.0",
|
||||
"1.2.0": "0.15.0",
|
||||
"1.2.1": "0.15.0",
|
||||
"1.2.2": "0.15.0"
|
||||
"1.2.2": "0.15.0",
|
||||
"1.3.0": "0.15.0"
|
||||
}
|
||||
Loading…
Reference in a new issue