Skip to content

Commit

Permalink
Merge pull request #2274 from ricksladkey/format-text
Browse files Browse the repository at this point in the history
Format text lines
  • Loading branch information
jaredpar committed Jul 31, 2018
2 parents 65fc260 + 00ffa30 commit 1a5bee0
Show file tree
Hide file tree
Showing 14 changed files with 446 additions and 47 deletions.
49 changes: 33 additions & 16 deletions Src/VimCore/CommandUtil.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1058,26 +1058,40 @@ type internal CommandUtil

CommandResult.Completed ModeSwitch.SwitchPreviousMode

/// Format the 'count' lines in the buffer
member x.FormatLines count =
/// Format the 'count' code lines in the buffer
member x.FormatCodeLines count =
let range = SnapshotLineRangeUtil.CreateForLineAndMaxCount x.CaretLine count
_commonOperations.FormatLines range
_commonOperations.FormatCodeLines range
CommandResult.Completed ModeSwitch.NoSwitch

/// Format the selected lines
member x.FormatLinesVisual (visualSpan: VisualSpan) =
/// Format the 'count' text lines in the buffer
member x.FormatTextLines count preserveCaretPosition =
let range = SnapshotLineRangeUtil.CreateForLineAndMaxCount x.CaretLine count
_commonOperations.FormatTextLines range preserveCaretPosition
CommandResult.Completed ModeSwitch.NoSwitch

// Use a transaction so the formats occur as a single operation
x.EditWithUndoTransaction "Format" (fun () ->
visualSpan.Spans
|> Seq.map SnapshotLineRangeUtil.CreateForSpan
|> Seq.iter _commonOperations.FormatLines)
/// Format the selected code lines
member x.FormatCodeLinesVisual (visualSpan: VisualSpan) =
visualSpan.EditSpan.OverarchingSpan
|> SnapshotLineRangeUtil.CreateForSpan
|> _commonOperations.FormatCodeLines
CommandResult.Completed ModeSwitch.SwitchPreviousMode

/// Format the selected text lines
member x.FormatTextLinesVisual (visualSpan: VisualSpan) (preserveCaretPosition: bool) =
visualSpan.EditSpan.OverarchingSpan
|> SnapshotLineRangeUtil.CreateForSpan
|> (fun lineRange -> _commonOperations.FormatTextLines lineRange preserveCaretPosition)
CommandResult.Completed ModeSwitch.SwitchPreviousMode

/// Format the lines in the Motion
member x.FormatMotion (result: MotionResult) =
_commonOperations.FormatLines result.LineRange
/// Format the code lines in the Motion
member x.FormatCodeMotion (result: MotionResult) =
_commonOperations.FormatCodeLines result.LineRange
CommandResult.Completed ModeSwitch.NoSwitch

/// Format the text lines in the Motion
member x.FormatTextMotion (result: MotionResult) preserveCaretPosition =
_commonOperations.FormatTextLines result.LineRange preserveCaretPosition
CommandResult.Completed ModeSwitch.NoSwitch

/// Get the appropriate register for the CommandData
Expand Down Expand Up @@ -2611,8 +2625,10 @@ type internal CommandUtil
| NormalCommand.FilterMotion motion -> x.RunWithMotion motion x.FilterMotion
| NormalCommand.FoldLines -> x.FoldLines data.CountOrDefault
| NormalCommand.FoldMotion motion -> x.RunWithMotion motion x.FoldMotion
| NormalCommand.FormatLines -> x.FormatLines count
| NormalCommand.FormatMotion motion -> x.RunWithMotion motion x.FormatMotion
| NormalCommand.FormatCodeLines -> x.FormatCodeLines count
| NormalCommand.FormatCodeMotion motion -> x.RunWithMotion motion x.FormatCodeMotion
| NormalCommand.FormatTextLines preserveCaretPosition -> x.FormatTextLines count preserveCaretPosition
| NormalCommand.FormatTextMotion (preserveCaretPosition, motion) -> x.RunWithMotion motion (fun motion -> x.FormatTextMotion motion preserveCaretPosition)
| NormalCommand.GoToDefinition -> x.GoToDefinition()
| NormalCommand.GoToFileUnderCaret useNewWindow -> x.GoToFileUnderCaret useNewWindow
| NormalCommand.GoToGlobalDeclaration -> x.GoToGlobalDeclaration()
Expand Down Expand Up @@ -2703,7 +2719,8 @@ type internal CommandUtil
| VisualCommand.DeleteLineSelection -> x.DeleteLineSelection registerName visualSpan
| VisualCommand.ExtendSelectionToNextMatch searchPath -> x.ExtendSelectionToNextMatch searchPath data.Count
| VisualCommand.FilterLines -> x.FilterLinesVisual visualSpan
| VisualCommand.FormatLines -> x.FormatLinesVisual visualSpan
| VisualCommand.FormatCodeLines -> x.FormatCodeLinesVisual visualSpan
| VisualCommand.FormatTextLines preserveCaretPosition -> x.FormatTextLinesVisual visualSpan preserveCaretPosition
| VisualCommand.FoldSelection -> x.FoldSelection visualSpan
| VisualCommand.GoToFileInSelectionInNewWindow -> x.GoToFileInSelectionInNewWindow visualSpan
| VisualCommand.GoToFileInSelection -> x.GoToFileInSelection visualSpan
Expand Down
153 changes: 150 additions & 3 deletions Src/VimCore/CommonOperations.fs
Original file line number Diff line number Diff line change
Expand Up @@ -228,10 +228,156 @@ type internal CommonOperations
TextViewUtil.MoveCaretToPoint _textView firstLine.Start
_editorOperations.MoveToStartOfLineAfterWhiteSpace(false)

/// Format the specified line range
member x.FormatLines range =
/// Format the code lines in the specified line range
member x.FormatCodeLines range =
_vimHost.FormatLines _textView range

// Place the cursor on the first non-blank character of the first line formatted.
let firstLine = SnapshotUtil.GetLine _textView.TextSnapshot range.StartLineNumber
TextViewUtil.MoveCaretToPoint _textView firstLine.Start
_editorOperations.MoveToStartOfLineAfterWhiteSpace(false)

/// Format the text lines in the specified line range
member x.FormatTextLines (range: SnapshotLineRange) preserveCaretPosition =

// Get formatting configuration values.
let autoIndent = _localSettings.AutoIndent
let textWidth =
if _localSettings.TextWidth = 0 then
VimConstants.DefaultFormatTextWidth
else
_localSettings.TextWidth
let comments = _localSettings.Comments
let tabStop = _localSettings.TabStop

// Extract the lines to be formatted and the first line.
let lines = range.Lines |> Seq.map SnapshotLineUtil.GetText
let firstLine = lines |> Seq.head

// Extract the leader string from a comment specification, e.g. "//".
let getLeaderFromSpec (spec: string) =
let colonIndex = spec.IndexOf(':')
if colonIndex = -1 then
spec
else
spec.Substring(colonIndex + 1)

// Get the leader pattern for a leader string.
let getLeaderPattern (leader: string) =
if leader = "" then
@"^\s*"
else
@"^\s*" + Regex.Escape(leader) + @"+\s*"

// Convert the comment specifications into leader patterns.
let patterns =
comments + ",:"
|> StringUtil.Split ','
|> Seq.map getLeaderFromSpec
|> Seq.map getLeaderPattern

// Check the first line against a potential comment pattern.
let checkPattern pattern =
let capture = Regex.Match(firstLine, pattern)
if capture.Success then
true, pattern, capture.Value
else
false, pattern, ""

// Choose a pattern and a leader.
let _, pattern, leader =
patterns
|> Seq.map checkPattern
|> Seq.filter (fun (matches, _, _) -> matches)
|> Seq.head

// Decide whether to use the leader for all lines.
let useLeaderForAllLines =
not (StringUtil.IsWhiteSpace leader) || autoIndent

// Strip the leader from a line.
let stripLeader (line: string) =
let capture = Regex.Match(line, pattern)
if capture.Success then
line.Substring(capture.Length)
else
line

// Strip the leader from all the lines.
let strippedLines =
lines
|> Seq.map stripLeader

// Split a line into words on whitespace.
let splitWords (line: string) =
if StringUtil.IsWhiteSpace line then
seq { yield "" }
else
Regex.Matches(line + " ", @"\S+\s+")
|> Seq.cast<Capture>
|> Seq.map (fun capture -> capture.Value)

// Concatenate a reversed list of words into a line.
let concatWords (words: string list) =
words
|> Seq.rev
|> String.concat ""
|> (fun line -> line.TrimEnd())

// Concatenate words into a line and prepend it to a list of lines.
let prependLine (words: string list) (lines: string list) =
if words.IsEmpty then
lines
else
concatWords words :: lines

// Calculate the length of the leader with tabs expanded.
let leaderLength =
StringUtil.ExpandTabsForColumn leader 0 tabStop
|> StringUtil.GetLength

// Aggregrate individual words into lines of limited length.
let takeWord ((column: int), (words: string list), (lines: string list)) (word: string) =

// Calculate the working limit for line length.
let limit =
if lines.IsEmpty || useLeaderForAllLines then
textWidth - leaderLength
else
textWidth

if word = "" then
0, List.Empty, "" :: prependLine words lines
elif column = 0 || column + word.TrimEnd().Length <= limit then
column + word.Length, word :: words, lines
else
word.Length, word :: List.Empty, concatWords words :: lines

// Add a leader to the formatted line if appropriate.
let addLeader (i: int) (line: string) =
if i = 0 || useLeaderForAllLines then
(leader + line).TrimEnd()
else
line

// Split the lines into words and then format them into lines using the aggregator.
let formattedLines =
let _, words, lines =
strippedLines
|> Seq.collect splitWords
|> Seq.fold takeWord (0, List.Empty, List.Empty)
prependLine words lines
|> Seq.rev
|> Seq.mapi addLeader

// Concatenate the formatted lines.
let newLine = EditUtil.NewLine _editorOptions
let replacement = formattedLines |> String.concat newLine

// Replace the old lines with the formatted lines.
_textBuffer.Replace(range.Extent.Span, replacement) |> ignore


// Place the cursor on the first non-blank character of the first line formatted.
let firstLine = SnapshotUtil.GetLine _textView.TextSnapshot range.StartLineNumber
TextViewUtil.MoveCaretToPoint _textView firstLine.Start
Expand Down Expand Up @@ -1887,7 +2033,8 @@ type internal CommonOperations
member x.EnsureAtPoint point viewFlags = x.EnsureAtPoint point viewFlags
member x.FillInVirtualSpace() = x.FillInVirtualSpace()
member x.FilterLines range command = x.FilterLines range command
member x.FormatLines range = x.FormatLines range
member x.FormatCodeLines range = x.FormatCodeLines range
member x.FormatTextLines range preserveCaretPosition = x.FormatTextLines range preserveCaretPosition
member x.GetRegister registerName = x.GetRegister registerName
member x.GetNewLineText point = x.GetNewLineText point
member x.GetNewLineIndent contextLine newLine = x.GetNewLineIndent contextLine newLine
Expand Down
5 changes: 5 additions & 0 deletions Src/VimCore/Constants.fs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ module VimConstants =
[<Literal>]
let DefaultHistoryLength = 20

/// The default text width that FormatTextLines will use if the 'textwidth'
/// setting is zero (see vim ':help gq')
[<Literal>]
let DefaultFormatTextWidth = 79

[<Literal>]
let IncrementalSearchTagName = "vsvim_incrementalsearch"

Expand Down
29 changes: 20 additions & 9 deletions Src/VimCore/CoreInterfaces.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2808,11 +2808,17 @@ type NormalCommand =
/// Create a fold over the specified motion
| FoldMotion of MotionData

/// Format the specified lines
| FormatLines
/// Format the code in the specified lines
| FormatCodeLines

/// Format the specified motion
| FormatMotion of MotionData
/// Format the code in the specified motion
| FormatCodeMotion of MotionData

/// Format the text in the specified lines, optionally preserving the caret position
| FormatTextLines of bool

/// Format the text in the specified motion
| FormatTextMotion of bool * MotionData

/// Go to the definition of hte word under the caret.
| GoToDefinition
Expand Down Expand Up @@ -3030,11 +3036,13 @@ type NormalCommand =

member private x.GetMotionDataCore() =
match x with
| NormalCommand.ChangeCaseMotion (changeCharacterKind, motion) -> Some ((fun motion -> NormalCommand.ChangeCaseMotion (changeCharacterKind, motion)), motion)
| NormalCommand.ChangeMotion motion -> Some (NormalCommand.ChangeMotion, motion)
| NormalCommand.DeleteMotion motion -> Some (NormalCommand.DeleteMotion, motion)
| NormalCommand.FilterMotion motion -> Some (NormalCommand.FilterMotion, motion)
| NormalCommand.FoldMotion motion -> Some (NormalCommand.FoldMotion, motion)
| NormalCommand.FormatMotion motion -> Some (NormalCommand.FormatMotion, motion)
| NormalCommand.FormatCodeMotion motion -> Some (NormalCommand.FormatCodeMotion, motion)
| NormalCommand.FormatTextMotion (preserveCaretPosition, motion) -> Some ((fun motion -> NormalCommand.FormatTextMotion (preserveCaretPosition, motion)), motion)
| NormalCommand.ShiftMotionLinesLeft motion -> Some (NormalCommand.ShiftMotionLinesLeft, motion)
| NormalCommand.ShiftMotionLinesRight motion -> Some (NormalCommand.ShiftMotionLinesRight, motion)
| NormalCommand.Yank motion -> Some (NormalCommand.Yank, motion)
Expand All @@ -3043,7 +3051,6 @@ type NormalCommand =
| NormalCommand.AddToWord _ -> None
| NormalCommand.ChangeCaseCaretLine _ -> None
| NormalCommand.ChangeCaseCaretPoint _ -> None
| NormalCommand.ChangeCaseMotion _ -> None
| NormalCommand.ChangeLines -> None
| NormalCommand.ChangeTillEndOfLine -> None
| NormalCommand.CloseAllFolds -> None
Expand All @@ -3062,7 +3069,8 @@ type NormalCommand =
| NormalCommand.DisplayCharacterCodePoint ->None
| NormalCommand.FilterLines -> None
| NormalCommand.FoldLines -> None
| NormalCommand.FormatLines -> None
| NormalCommand.FormatCodeLines -> None
| NormalCommand.FormatTextLines _ -> None
| NormalCommand.GoToDefinition -> None
| NormalCommand.GoToFileUnderCaret _ -> None
| NormalCommand.GoToGlobalDeclaration -> None
Expand Down Expand Up @@ -3175,8 +3183,11 @@ type VisualCommand =
/// Fold the current selected lines
| FoldSelection

/// Format the selected text
| FormatLines
/// Format the selected code lines
| FormatCodeLines

/// Format the selected text lines, optionally preserving the caret position
| FormatTextLines of bool

/// GoTo the file under the cursor in a new window
| GoToFileInSelectionInNewWindow
Expand Down
5 changes: 4 additions & 1 deletion Src/VimCore/MefInterfaces.fs
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,10 @@ type ICommonOperations =
abstract FilterLines: SnapshotLineRange -> command: string -> unit

/// Format the specified line range
abstract FormatLines: SnapshotLineRange -> unit
abstract FormatCodeLines: SnapshotLineRange -> unit

/// Format the specified line range
abstract FormatTextLines: SnapshotLineRange -> preserveCaretPosition: bool -> unit

/// Get the new line text which should be used for new lines at the given SnapshotPoint
abstract GetNewLineText: SnapshotPoint -> string
Expand Down
10 changes: 8 additions & 2 deletions Src/VimCore/Modes_Normal_NormalMode.fs
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,11 @@ type internal NormalMode
yield (".", CommandFlags.Special, NormalCommand.RepeatLastCommand)
yield ("<lt><lt>", CommandFlags.Repeatable, NormalCommand.ShiftLinesLeft)
yield (">>", CommandFlags.Repeatable, NormalCommand.ShiftLinesRight)
yield ("==", CommandFlags.Repeatable, NormalCommand.FormatLines)
yield ("==", CommandFlags.Repeatable, NormalCommand.FormatCodeLines)
yield ("gqgq", CommandFlags.Repeatable, NormalCommand.FormatTextLines false)
yield ("gqq", CommandFlags.Repeatable, NormalCommand.FormatTextLines false)
yield ("gwgw", CommandFlags.Repeatable, NormalCommand.FormatTextLines true)
yield ("gww", CommandFlags.Repeatable, NormalCommand.FormatTextLines true)
yield ("!!", CommandFlags.Repeatable, NormalCommand.FilterLines)
yield (":", CommandFlags.Special, NormalCommand.SwitchMode (ModeKind.Command, ModeArgument.None))
yield ("<C-^>", CommandFlags.None, NormalCommand.GoToRecentView)
Expand All @@ -189,7 +193,9 @@ type internal NormalMode
yield ("<lt>", CommandFlags.Repeatable ||| CommandFlags.ShiftLeft, NormalCommand.ShiftMotionLinesLeft)
yield (">", CommandFlags.Repeatable ||| CommandFlags.ShiftRight, NormalCommand.ShiftMotionLinesRight)
yield ("!", CommandFlags.Repeatable, NormalCommand.FilterMotion)
yield ("=", CommandFlags.Repeatable, NormalCommand.FormatMotion)
yield ("=", CommandFlags.Repeatable, NormalCommand.FormatCodeMotion)
yield ("gq", CommandFlags.Repeatable, (fun motion -> NormalCommand.FormatTextMotion (false, motion)))
yield ("gw", CommandFlags.Repeatable, (fun motion -> NormalCommand.FormatTextMotion (true, motion)))
} |> Seq.map (fun (str, flags, command) ->
let keyInputSet = KeyNotationUtil.StringToKeyInputSet str
CommandBinding.MotionBinding (keyInputSet, flags, command))
Expand Down
4 changes: 3 additions & 1 deletion Src/VimCore/Modes_Visual_VisualMode.fs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,9 @@ type internal VisualMode
yield ("<lt>", CommandFlags.Repeatable, VisualCommand.ShiftLinesLeft)
yield (">", CommandFlags.Repeatable, VisualCommand.ShiftLinesRight)
yield ("~", CommandFlags.Repeatable, VisualCommand.ChangeCase ChangeCharacterKind.ToggleCase)
yield ("=", CommandFlags.Repeatable, VisualCommand.FormatLines)
yield ("=", CommandFlags.Repeatable, VisualCommand.FormatCodeLines)
yield ("gq", CommandFlags.Repeatable, VisualCommand.FormatTextLines false)
yield ("gw", CommandFlags.Repeatable, VisualCommand.FormatTextLines true)
yield ("!", CommandFlags.Repeatable, VisualCommand.FilterLines)
} |> Seq.map (fun (str, flags, command) ->
let keyInputSet = KeyNotationUtil.StringToKeyInputSet str
Expand Down
2 changes: 2 additions & 0 deletions Src/VimCore/StringUtil.fs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ module internal StringUtil =

let Empty = System.String.Empty

let GetLength (input: string) = input.Length

let FindFirst (input:seq<char>) index del =
let found =
input
Expand Down
Loading

0 comments on commit 1a5bee0

Please sign in to comment.