Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SourceSpan support #123

Merged
merged 19 commits into from
Apr 27, 2018
Merged
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
7 changes: 3 additions & 4 deletions src/WpfMath.Example/MainWindow.xaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Window x:Class="WpfMath.Example.MainWindow"
<Window x:Class="WpfMath.Example.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:WpfMath.Controls;assembly=WpfMath"
Expand All @@ -9,7 +9,6 @@
<Style TargetType="Button">
<Setter Property="Padding" Value="2" />
</Style>

</Window.Resources>
<Grid>

Expand All @@ -31,7 +30,7 @@
</Grid.ColumnDefinitions>
<Button Name="saveButton" Grid.Column="1" Margin="10,0,0,0" HorizontalAlignment="Right" VerticalAlignment="Stretch" Content="_Save" Click="saveButton_Click" />
</Grid>
<TextBox Name="inputTextBox" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
<TextBox Name="inputTextBox" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" SelectionChanged="inputTextBox_SelectionChanged" />
</DockPanel>

<Label Margin="10,0,10,10" Grid.Row="1" Grid.Column="0">
Expand Down Expand Up @@ -66,9 +65,9 @@
<Border Margin="10,0,10,10" BorderBrush="LightGray" BorderThickness="1" Grid.Row="3" Grid.Column="0">
<ScrollViewer Padding="4" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled">
<controls:FormulaControl x:Name="formula" Formula="{Binding Text, ElementName = inputTextBox, NotifyOnValidationError=True}"
SelectionBrush="LightBlue"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" SnapsToDevicePixels="True" />
</ScrollViewer>
</Border>

</Grid>
</Window>
10 changes: 9 additions & 1 deletion src/WpfMath.Example/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Microsoft.Win32;
using Microsoft.Win32;
using System;
using System.IO;
using System.Text;
Expand Down Expand Up @@ -62,6 +62,7 @@ private void saveButton_Click(object sender, RoutedEventArgs e)
using (var writer = new StreamWriter(stream))
writer.WriteLine(svgText);
break;

case 2:
var bitmap = renderer.RenderToBitmap(0, 0);
var encoder = new PngBitmapEncoder
Expand All @@ -70,6 +71,7 @@ private void saveButton_Click(object sender, RoutedEventArgs e)
};
encoder.Save(stream);
break;

default:
return;
}
Expand Down Expand Up @@ -101,5 +103,11 @@ private void Window_Closed(object sender, EventArgs e)
{
//
}

private void inputTextBox_SelectionChanged(object sender, RoutedEventArgs e)
{
formula.SelectionStart = inputTextBox.SelectionStart;
formula.SelectionLength = inputTextBox.SelectionLength;
}
}
}
41 changes: 39 additions & 2 deletions src/WpfMath.Tests/BoxTests.fs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
module WpfMath.Tests.BoxTests

open System

open FSharp.Core.Fluent
open DeepEqual.Syntax
open Xunit

Expand Down Expand Up @@ -37,10 +40,44 @@ let ``Box for \text{æ,} should be created successfully``() =
let ``ScriptsAtom should set Shift on the created box when creating box without any sub- or superscript``() =
Utils.initializeFontResourceLoading()

let baseAtom = CharAtom('x')
let scriptsAtom = ScriptsAtom(baseAtom, null, null)
let baseAtom = charSrc 'x' (src "x" 0 1)
let scriptsAtom = ScriptsAtom(null, baseAtom, null, null)

let box = scriptsAtom.CreateBox(environment)

let expectedShift = -(box.Height + box.Depth) / 2.0 - environment.MathFont.GetAxisHeight(environment.Style)
Assert.Equal(expectedShift, box.Shift)

[<Fact>]
let ``RowAtom creates boxes with proper sources``() =
let source = "2+2"
let src = src source
let parser = TexFormulaParser()
let formula = parser.Parse source
let box = formula.CreateBox environment :?> HorizontalBox
let chars = box.Children.filter(fun x -> x :? CharBox)
Assert.Collection(
chars,
Action<_>(fun (x : Box) -> Assert.Equal(src 0 1, x.Source)),
Action<_>(fun (x : Box) -> Assert.Equal(src 1 1, x.Source)),
Action<_>(fun (x : Box) -> Assert.Equal(src 2 1, x.Source)))

[<Fact>]
let ``BigOperatorAtom creates a box with proper sources``() =
let source = @"\int_a^b"
let src = src source
let parser = TexFormulaParser()
let formula = parser.Parse source
let box = formula.CreateBox environment :?> VerticalBox

let charBoxes =
box.Children
.filter(fun x -> x :? HorizontalBox)
.collect(fun x -> x.Children.filter(fun y -> y :? CharBox))
.toList()

Assert.Collection(
charBoxes,
Action<_>(fun (x : Box) -> Assert.Equal(src 7 1, x.Source)),
Action<_>(fun (x : Box) -> Assert.Equal(src 1 3, x.Source)),
Action<_>(fun (x : Box) -> Assert.Equal(src 5 1, x.Source)))
42 changes: 10 additions & 32 deletions src/WpfMath.Tests/ParserTests.fs
Original file line number Diff line number Diff line change
@@ -1,35 +1,15 @@
module WpfMath.Tests.ParserTests

open System

open DeepEqual.Syntax
open Xunit

open WpfMath
open WpfMath.Exceptions
open WpfMath.Tests.Utils

let assertParseResult formula expected =
let parser = TexFormulaParser()
let result = parser.Parse(formula)
result.WithDeepEqual(expected)
.ExposeInternalsOf<TexFormula>()
.ExposeInternalsOf<FencedAtom>()
.Assert()

let assertParseThrows<'ex when 'ex :> exn> formula =
let parser = TexFormulaParser()
Assert.Throws<'ex>(Func<obj>(fun () -> upcast parser.Parse(formula)))

let textStyle = "text"
let rmStyle = "mathrm"
let itStyle = "mathit"
let calStyle = "mathcal"

let ``2+2`` = row [char '2'; symbol "plus"; char '2']
let ``\mathrm{2+2}`` = row [styledChar('2', rmStyle); symbol "plus"; styledChar('2', rmStyle)]
let ``\lim`` = row [styledChar('l', rmStyle); styledChar('i', rmStyle); styledChar('m', rmStyle)]
let ``\sin`` = row [styledChar('s', rmStyle); styledChar('i', rmStyle); styledChar('n', rmStyle)]
let ``\mathrm{2+2}`` = row [styledChar '2' rmStyle; symbol "plus"; styledChar '2' rmStyle]
let ``\lim`` = row [styledChar 'l' rmStyle; styledChar 'i' rmStyle; styledChar 'm' rmStyle]
let ``\sin`` = row [styledChar 's' rmStyle; styledChar 'i' rmStyle; styledChar 'n' rmStyle]

[<Fact>]
let ``2+2 should be parsed properly`` () =
Expand Down Expand Up @@ -92,14 +72,12 @@ let ``\text command should be supported`` () =

[<Fact>]
let ``Spaces in \text shouldn't be ignored`` () =
let textChar c = styledChar (c, textStyle)
assertParseResult
<| @"\text{a b c}"
<| (formula <| row [textChar 'a'; space; textChar 'b'; space; textChar 'c'])

[<Fact>]
let ``\text should support Cyrillic`` () =
let textChar c = styledChar (c, textStyle)
assertParseResult
<| @"\text{абв}"
<| (formula <| styledString textStyle "абв")
Expand All @@ -108,7 +86,7 @@ let ``\text should support Cyrillic`` () =
let ``\mathrm should be parsed properly`` () =
assertParseResult
<| @"\mathrm{sin}"
<| (formula <| row [styledChar('s', rmStyle); styledChar('i', rmStyle); styledChar('n', rmStyle)])
<| (formula <| row [styledChar 's' rmStyle; styledChar 'i' rmStyle; styledChar 'n' rmStyle])

[<Fact>]
let ``\mathrm should be parsed properly for complex eqs`` () =
Expand All @@ -122,14 +100,14 @@ let ``\mathrm should be parsed properly for complex eqs`` () =
let ``\mathit should be parsed properly`` () =
assertParseResult
<| @"\mathit{sin}"
<| (formula <| row [styledChar('s', itStyle); styledChar('i', itStyle); styledChar('n', itStyle)])
<| (formula <| row [styledChar 's' itStyle; styledChar 'i' itStyle; styledChar 'n' itStyle])


[<Fact>]
let ``\mathcal should be parsed properly`` () =
assertParseResult
<| @"\mathcal{sin}"
<| (formula <| row [styledChar('s', calStyle); styledChar('i', calStyle); styledChar('n', calStyle)])
<| (formula <| row [styledChar 's' calStyle; styledChar 'i' calStyle; styledChar 'n' calStyle])

[<Fact>]
let ``\mathrm{} should throw exn`` () =
Expand All @@ -142,7 +120,7 @@ let ``\lim should be parsed properly`` () =
assertParseResult
<| @"\lim_{n} x"
<| (formula <| row [
opWithScripts ``\lim`` (char 'n') null (System.Nullable true);
opWithScripts ``\lim`` (char 'n') null (Some true)
char 'x'
])

Expand All @@ -151,7 +129,7 @@ let ``{\lim} x should be parsed properly`` () =
assertParseResult
<| @"{\lim} x"
<| (formula <| row [
group (op ``\lim`` (System.Nullable true));
group (op ``\lim`` (Some true))
char 'x'
])

Expand All @@ -160,7 +138,7 @@ let ``\sin should be parsed properly`` () =
assertParseResult
<| @"\sin^{n} x"
<| (formula <| row [
opWithScripts ``\sin`` null (char 'n') (System.Nullable false);
opWithScripts ``\sin`` null (char 'n') (Some false)
char 'x'
])

Expand All @@ -169,7 +147,7 @@ let ``\int f should be parser properly`` () =
assertParseResult
<| @"\int f"
<| (formula <| row [
op (symbolOp "int") (System.Nullable ())
op (symbolOp "int") (None)
char 'f'
])

Expand Down
31 changes: 31 additions & 0 deletions src/WpfMath.Tests/SourceDetectionTests.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
module WpfMath.Tests.SourceDetectionTests

open Xunit

open WpfMath
open WpfMath.Tests.Utils

[<Fact>]
let ``2+2 should be parsed properly`` () =
let source = "2+2"
let src = src source
let tree = rowSrc [ charSrc '2' (src 0 1)
symbolSrc "plus" TexAtomType.BinaryOperator (src 1 1)
charSrc '2' (src 2 1) ]
(src 0 3)
assertParseResultWithSource
<| source
<| formula tree

[<Fact>]
let ``integral expression should be parsed properly`` () =
let source = @"\int_a^b"
let src = src source
let tree = opWithScriptsSrc (symbolSrc "int" TexAtomType.BigOperator (src 1 3))
(charSrc 'a' (src 5 1))
(charSrc 'b' (src 7 1))
None
(src 0 8)
assertParseResultWithSource
<| source
<| formula tree
80 changes: 64 additions & 16 deletions src/WpfMath.Tests/Utils.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,17 @@
open System
open System.Windows

open FSharp.Linq.RuntimeHelpers
open DeepEqual.Syntax
open Xunit

open WpfMath

let textStyle = "text"
let rmStyle = "mathrm"
let itStyle = "mathit"
let calStyle = "mathcal"

let initializeFontResourceLoading =
let monitor = obj()
fun () ->
Expand All @@ -15,26 +24,65 @@ let initializeFontResourceLoading =
let formula (root : Atom) : TexFormula =
TexFormula(RootAtom = root)

let space = SpaceAtom()
let char (c : char) : CharAtom = CharAtom(c)
let styledChar (c : char, style:string) : CharAtom = CharAtom(c, style)
let op (baseAtom : Atom) (useVertScripts : System.Nullable<bool>) : BigOperatorAtom = BigOperatorAtom(baseAtom, null, null, useVertScripts)
let opWithScripts (baseAtom : Atom) (subscript : Atom) (superscript : Atom) (useVertScripts : System.Nullable<bool>)
: BigOperatorAtom = BigOperatorAtom(baseAtom, subscript, superscript, useVertScripts)
let scripts (baseAtom : Atom) (subscript : Atom) (superscript : Atom)
: ScriptsAtom = ScriptsAtom(baseAtom, subscript, superscript)
let group (groupedAtom: Atom) : TypedAtom = TypedAtom(groupedAtom, TexAtomType.Ordinary, TexAtomType.Ordinary)
let symbol (name : string) : SymbolAtom = SymbolAtom(name, TexAtomType.BinaryOperator, false)
let symbolOp (name : string) : SymbolAtom = SymbolAtom(name, TexAtomType.BigOperator, false)
let row (children : Atom seq) : RowAtom =
let private createComparer formula expected =
let parser = TexFormulaParser()
let result = parser.Parse(formula)
result.WithDeepEqual(expected)
.ExposeInternalsOf<TexFormula>()
.ExposeInternalsOf<FencedAtom>()

let assertParseResultWithSource (formula : string) (expected : TexFormula) : unit =
(createComparer formula expected).Assert()

let assertParseResult (formula : string) (expected : TexFormula) : unit =
let toExpression = LeafExpressionConverter.QuotationToLambdaExpression
(createComparer formula expected)
.IgnoreProperty<Atom>(toExpression(<@ Func<Atom, obj>(fun a -> upcast a.Source) @>))
.Assert()

let assertParseThrows<'ex when 'ex :> exn> formula =
let parser = TexFormulaParser()
Assert.Throws<'ex>(Func<obj>(fun () -> upcast parser.Parse(formula)))

let src (string : string) (start : int) (len : int) = SourceSpan(string, start, len)

let private toNullable = function
| Some x -> Nullable x
| None -> Nullable()

let charSrc (c : char) (source : SourceSpan) : CharAtom = CharAtom(source, c)
let symbolSrc (name : string) (aType : TexAtomType) (source : SourceSpan) : SymbolAtom =
SymbolAtom(source, name, aType, false)
let opWithScriptsSrc (baseAtom : Atom)
(subscript : Atom)
(superscript : Atom)
(useVertScripts : bool option)
(source : SourceSpan)
: BigOperatorAtom = BigOperatorAtom(source, baseAtom, subscript, superscript, toNullable useVertScripts)
let rowSrc (children : Atom seq) (source : SourceSpan) : RowAtom =
children
|> Seq.fold (fun row atom -> row.Add atom) (RowAtom())
let fenced left body right : FencedAtom = FencedAtom(body, left, right)
|> Seq.fold (fun row atom -> row.Add atom) (RowAtom(source))

let space = SpaceAtom(null)
let char (c : char) : CharAtom = charSrc c null
let styledChar (c : char) (style : string) : CharAtom = CharAtom(null, c, style)
let textChar c = styledChar c textStyle
let opWithScripts (baseAtom : Atom) (subscript : Atom) (superscript : Atom) (useVertScripts : bool option)
: BigOperatorAtom = opWithScriptsSrc baseAtom subscript superscript useVertScripts null
let op (baseAtom : Atom) (useVertScripts : bool option) : BigOperatorAtom =
opWithScripts baseAtom null null useVertScripts
let scripts (baseAtom : Atom) (subscript : Atom) (superscript : Atom)
: ScriptsAtom = ScriptsAtom(null, baseAtom, subscript, superscript)
let group (groupedAtom: Atom) : TypedAtom = TypedAtom(null, groupedAtom, TexAtomType.Ordinary, TexAtomType.Ordinary)
let symbol (name : string) : SymbolAtom = symbolSrc name TexAtomType.BinaryOperator null
let symbolOp (name : string) : SymbolAtom = SymbolAtom(null, name, TexAtomType.BigOperator, false)
let row (children : Atom seq) : RowAtom = rowSrc children null
let fenced left body right : FencedAtom = FencedAtom(null, body, left, right)
let styledString (style : string) (text : string) : RowAtom =
text
|> Seq.map (fun c -> styledChar (c, style) :> Atom)
|> Seq.map (fun c -> styledChar c style :> Atom)
|> row

let brace (name : string) (braceType : TexAtomType) : SymbolAtom = SymbolAtom(name, braceType, true)
let brace (name : string) (braceType : TexAtomType) : SymbolAtom = SymbolAtom(null, name, braceType, true)
let openBrace (name : string) : SymbolAtom = brace name TexAtomType.Opening
let closeBrace (name : string) : SymbolAtom = brace name TexAtomType.Closing
Loading