Skip to content
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
110 changes: 108 additions & 2 deletions MacDown/Code/Document/MPDocument.m
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@
@"editorBaseFontInfo", @"extensionFootnotes",
@"editorHorizontalInset", @"editorVerticalInset",
@"editorWidthLimited", @"editorMaximumWidth", @"editorLineSpacing",
@"editorColumnGuideEnabled", @"editorColumnGuideWidth",
@"editorWrapsAtColumnGuide",
@"editorOnRight", @"editorStyleName", @"editorShowWordCount",
@"editorScrollsPastEnd",
@"htmlMathJax", @"htmlMathJaxInlineDollar", nil
Expand Down Expand Up @@ -267,6 +269,7 @@ - (void)refreshHeaderCacheAfterResize;
- (void)windowDidEndLiveResize:(NSNotification *)notification;
- (void)windowDidChangeFullScreen:(NSNotification *)notification;
- (void)applyEditorStartInPreviewModePreference;
- (void)applyColumnGuidePreferences;
// Commit 8 (gap 9): MathJax generation counter accessor (used by tests via category)
- (NSUInteger)mathJaxRenderGeneration;

Expand Down Expand Up @@ -899,6 +902,39 @@ - (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item
return NO;
}
}
else if (action == @selector(toggleColumnGuide:))
{
((NSMenuItem *)item).state = self.preferences.editorColumnGuideEnabled
? NSControlStateValueOn : NSControlStateValueOff;
return self.editor != nil;
}
else if (action == @selector(toggleWrapAtColumnGuide:))
{
((NSMenuItem *)item).state = self.preferences.editorWrapsAtColumnGuide
? NSControlStateValueOn : NSControlStateValueOff;
return self.editor != nil;
}
else if (action == @selector(setColumnGuideWidth80:))
{
((NSMenuItem *)item).state =
self.preferences.editorColumnGuideWidth == 80
? NSControlStateValueOn : NSControlStateValueOff;
return self.editor != nil;
}
else if (action == @selector(setColumnGuideWidth100:))
{
((NSMenuItem *)item).state =
self.preferences.editorColumnGuideWidth == 100
? NSControlStateValueOn : NSControlStateValueOff;
return self.editor != nil;
}
else if (action == @selector(setColumnGuideWidth120:))
{
((NSMenuItem *)item).state =
self.preferences.editorColumnGuideWidth == 120
? NSControlStateValueOn : NSControlStateValueOff;
return self.editor != nil;
}
return result;
}

Expand Down Expand Up @@ -1995,6 +2031,33 @@ - (IBAction)toggleEditorPane:(id)sender
[self toggleSplitterCollapsingEditorPane:YES];
}

- (IBAction)toggleColumnGuide:(id)sender
{
self.preferences.editorColumnGuideEnabled =
!self.preferences.editorColumnGuideEnabled;
}

- (IBAction)toggleWrapAtColumnGuide:(id)sender
{
self.preferences.editorWrapsAtColumnGuide =
!self.preferences.editorWrapsAtColumnGuide;
}

- (IBAction)setColumnGuideWidth80:(id)sender
{
self.preferences.editorColumnGuideWidth = 80;
}

- (IBAction)setColumnGuideWidth100:(id)sender
{
self.preferences.editorColumnGuideWidth = 100;
}

- (IBAction)setColumnGuideWidth120:(id)sender
{
self.preferences.editorColumnGuideWidth = 120;
}

- (IBAction)render:(id)sender
{
[self.renderer parseAndRenderLater];
Expand Down Expand Up @@ -2152,14 +2215,19 @@ - (void)setupEditor:(NSString *)changedKey
if (!changedKey || [changedKey isEqualToString:@"editorHorizontalInset"]
|| [changedKey isEqualToString:@"editorVerticalInset"]
|| [changedKey isEqualToString:@"editorWidthLimited"]
|| [changedKey isEqualToString:@"editorMaximumWidth"])
|| [changedKey isEqualToString:@"editorMaximumWidth"]
|| [changedKey isEqualToString:@"editorColumnGuideEnabled"]
|| [changedKey isEqualToString:@"editorColumnGuideWidth"]
|| [changedKey isEqualToString:@"editorWrapsAtColumnGuide"])
{
[self adjustEditorInsets];
[self applyColumnGuidePreferences];
}

if (!changedKey || [changedKey isEqualToString:@"editorBaseFontInfo"]
|| [changedKey isEqualToString:@"editorStyleName"]
|| [changedKey isEqualToString:@"editorLineSpacing"])
|| [changedKey isEqualToString:@"editorLineSpacing"]
|| [changedKey isEqualToString:@"editorColumnGuideWidth"])
{
NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
style.lineSpacing = self.preferences.editorLineSpacing;
Expand Down Expand Up @@ -2209,6 +2277,7 @@ - (void)setupEditor:(NSString *)changedKey
if (backgroundCGColor)
layer.backgroundColor = backgroundCGColor;
self.editorContainer.layer = layer;
[self applyColumnGuidePreferences];
}

if ([changedKey isEqualToString:@"editorBaseFontInfo"])
Expand Down Expand Up @@ -2298,6 +2367,43 @@ - (void)adjustEditorInsets
self.editor.textContainerInset = NSMakeSize(x, y);
}

- (void)applyColumnGuidePreferences
{
NSInteger column = self.preferences.editorColumnGuideWidth;
if (column <= 0)
column = 80;

self.editor.columnGuideVisible =
self.preferences.editorColumnGuideEnabled;
self.editor.columnGuideColumn = column;
self.editor.wrapsAtColumnGuide =
self.preferences.editorWrapsAtColumnGuide;

NSTextContainer *container = self.editor.textContainer;
if (!container)
return;

if (self.editor.wrapsAtColumnGuide)
{
NSFont *font = self.editor.font ?:
self.preferences.editorBaseFont ?:
[NSFont userFixedPitchFontOfSize:0.0];
NSDictionary *attributes = @{NSFontAttributeName: font};
CGFloat characterWidth = [@"0" sizeWithAttributes:attributes].width;
CGFloat width = characterWidth * column +
2.0 * container.lineFragmentPadding;
container.widthTracksTextView = NO;
container.containerSize = NSMakeSize(width, CGFLOAT_MAX);
}
else
{
container.widthTracksTextView = YES;
container.containerSize = NSMakeSize(CGFLOAT_MAX, CGFLOAT_MAX);
}

self.editor.needsDisplay = YES;
}

- (void)redrawDivider
{
if (!self.editorVisible)
Expand Down
3 changes: 3 additions & 0 deletions MacDown/Code/Preferences/MPPreferences.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ extern NSString * const MPDidDetectFreshInstallationNotification;
@property (assign) CGFloat editorLineSpacing;
@property (assign) BOOL editorWidthLimited;
@property (assign) CGFloat editorMaximumWidth;
@property (assign) BOOL editorColumnGuideEnabled;
@property (assign) NSInteger editorColumnGuideWidth;
@property (assign) BOOL editorWrapsAtColumnGuide;
@property (assign) BOOL editorOnRight;
@property (assign) BOOL editorStartInPreviewMode;
@property (assign) BOOL editorShowWordCount;
Expand Down
5 changes: 5 additions & 0 deletions MacDown/Code/Preferences/MPPreferences.m
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,9 @@ - (void)migratePreferencesFromLegacyBundleIdentifierIfNeeded
@dynamic editorLineSpacing;
@dynamic editorWidthLimited;
@dynamic editorMaximumWidth;
@dynamic editorColumnGuideEnabled;
@dynamic editorColumnGuideWidth;
@dynamic editorWrapsAtColumnGuide;
@dynamic editorOnRight;
@dynamic editorStartInPreviewMode;
@dynamic editorShowWordCount;
Expand Down Expand Up @@ -427,6 +430,8 @@ - (void)loadDefaultUserDefaults
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if (![defaults objectForKey:@"editorMaximumWidth"])
self.editorMaximumWidth = 1000.0;
if (![defaults objectForKey:@"editorColumnGuideWidth"])
self.editorColumnGuideWidth = 80;
if (![defaults objectForKey:@"editorAutoIncrementNumberedLists"])
self.editorAutoIncrementNumberedLists = YES;
if (![defaults objectForKey:@"editorInsertPrefixInBlock"])
Expand Down
4 changes: 4 additions & 0 deletions MacDown/Code/View/MPEditorView.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@
@interface MPEditorView : NSTextView

@property BOOL scrollsPastEnd;
@property BOOL columnGuideVisible;
@property NSInteger columnGuideColumn;
@property BOOL wrapsAtColumnGuide;
- (NSRect)contentRect;
- (CGFloat)columnGuideXPosition;
- (void)paste:(id)sender;

@end
41 changes: 41 additions & 0 deletions MacDown/Code/View/MPEditorView.m
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,35 @@ @implementation MPEditorView
@synthesize contentRect = _contentRect;
@synthesize scrollsPastEnd = _scrollsPastEnd;

- (void)setColumnGuideVisible:(BOOL)columnGuideVisible
{
_columnGuideVisible = columnGuideVisible;
self.needsDisplay = YES;
}

- (void)setColumnGuideColumn:(NSInteger)columnGuideColumn
{
_columnGuideColumn = MAX(1, columnGuideColumn);
self.needsDisplay = YES;
}

- (BOOL)scrollsPastEnd
{
@synchronized(self) {
return _scrollsPastEnd;
}
}

- (CGFloat)columnGuideXPosition
{
NSFont *font = self.font ?: [NSFont userFixedPitchFontOfSize:0.0];
NSDictionary *attributes = @{NSFontAttributeName: font};
CGFloat characterWidth = [@"0" sizeWithAttributes:attributes].width;
CGFloat padding = self.textContainer.lineFragmentPadding;
return self.textContainerOrigin.x + padding +
characterWidth * MAX(1, self.columnGuideColumn);
}

- (void)awakeFromNib {
[self registerForDraggedTypes:@[NSPasteboardTypeFileURL]];
[super awakeFromNib];
Expand Down Expand Up @@ -168,6 +190,25 @@ - (void)didChangeText
[self updateContentGeometry];
}

- (void)drawViewBackgroundInRect:(NSRect)rect
{
[super drawViewBackgroundInRect:rect];

if (!self.columnGuideVisible || self.columnGuideColumn <= 0)
return;

CGFloat x = floor([self columnGuideXPosition]) + 0.5;
if (x < NSMinX(rect) || x > NSMaxX(rect))
return;

NSBezierPath *path = [NSBezierPath bezierPath];
[path moveToPoint:NSMakePoint(x, NSMinY(rect))];
[path lineToPoint:NSMakePoint(x, NSMaxY(rect))];
[[NSColor separatorColor] setStroke];
path.lineWidth = 1.0;
[path stroke];
}

/** Overridden to advertise markdown UTType support for pasteboard operations.
*/
- (NSArray<NSPasteboardType> *)writablePasteboardTypes
Expand Down
38 changes: 38 additions & 0 deletions MacDown/Localization/Base.lproj/MainMenu.xib
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,44 @@
<binding destination="LEY-zf-22t" name="enabled" keyPath="self.currentDocument.previewVisible" id="5e7-rJ-oX2"/>
</connections>
</menuItem>
<menuItem title="Column Guide" id="K8f-gQ-VwD">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Column Guide" id="v6J-FR-3yq">
<items>
<menuItem title="Show Column Guide" id="f4Z-W1-vnP">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleColumnGuide:" target="-1" id="5P6-eg-Xqa"/>
</connections>
</menuItem>
<menuItem title="Wrap at Column Guide" id="Ujc-WO-zaz">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleWrapAtColumnGuide:" target="-1" id="mzd-N9-YqB"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="v7y-Ag-a51"/>
<menuItem title="80 Columns" id="yvz-tM-mAM">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="setColumnGuideWidth80:" target="-1" id="YAc-DN-hJD"/>
</connections>
</menuItem>
<menuItem title="100 Columns" id="NDe-1A-hS0">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="setColumnGuideWidth100:" target="-1" id="ypl-hW-kv9"/>
</connections>
</menuItem>
<menuItem title="120 Columns" id="WXW-t8-dCa">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="setColumnGuideWidth120:" target="-1" id="OaW-Jw-spo"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="onf-Mq-Pah"/>
<menuItem title="Left 1:3 Right" id="VW7-VH-9yl">
<modifierMask key="keyEquivalentModifierMask"/>
Expand Down
27 changes: 27 additions & 0 deletions MacDownTests/MPEditorViewSubstitutionTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,33 @@ - (void)removePreferenceForKey:(NSString *)key
[defaults synchronize];
}

#pragma mark - Column Guide Tests

- (void)testColumnGuideXPositionUsesConfiguredColumn
{
NSFont *font = [NSFont fontWithName:@"Menlo-Regular" size:12.0];
self.editorView.font = font;
self.editorView.textContainer.lineFragmentPadding = 0.0;
self.editorView.textContainerInset = NSZeroSize;
self.editorView.columnGuideColumn = 10;

CGFloat characterWidth =
[@"0" sizeWithAttributes:@{NSFontAttributeName: font}].width;

XCTAssertEqualWithAccuracy([self.editorView columnGuideXPosition],
characterWidth * 10.0,
0.5,
@"Guide should be placed after the configured column");
}

- (void)testColumnGuideColumnClampsToPositiveValue
{
self.editorView.columnGuideColumn = 0;

XCTAssertEqual(self.editorView.columnGuideColumn, 1,
@"Column guide should clamp non-positive widths");
}

#pragma mark - Automatic Dash Substitution Tests

/**
Expand Down
24 changes: 24 additions & 0 deletions MacDownTests/MPPreferencesTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,30 @@ - (void)testStartInPreviewModeToggle
[self.preferences synchronize];
}

- (void)testColumnGuidePreferences
{
BOOL originalEnabled = self.preferences.editorColumnGuideEnabled;
BOOL originalWrap = self.preferences.editorWrapsAtColumnGuide;
NSInteger originalWidth = self.preferences.editorColumnGuideWidth;

self.preferences.editorColumnGuideEnabled = YES;
self.preferences.editorWrapsAtColumnGuide = YES;
self.preferences.editorColumnGuideWidth = 100;
[self.preferences synchronize];

XCTAssertTrue(self.preferences.editorColumnGuideEnabled,
@"Column guide should be ON");
XCTAssertTrue(self.preferences.editorWrapsAtColumnGuide,
@"Column wrapping should be ON");
XCTAssertEqual(self.preferences.editorColumnGuideWidth, 100,
@"Column guide width should persist");

self.preferences.editorColumnGuideEnabled = originalEnabled;
self.preferences.editorWrapsAtColumnGuide = originalWrap;
self.preferences.editorColumnGuideWidth = originalWidth;
[self.preferences synchronize];
}

- (void)testExtensionFlags
{
// Save originals
Expand Down
Loading