Loading tools/internal/doc/docx/code.go +64 −8 Changes for tools/internal/doc/docx/code.go: 64 added lines, 8 removed lines. Original line number Diff line number Diff line Loading @@ -9,13 +9,16 @@ import ( "tools/internal/doc" ) // Code runs are Courier New 8pt, the PL style. Keywords are bold and comments // are italic, which is the highlighting used for syntactic descriptions in the // working documents. Colours are not added. // Code runs are Courier New 8pt, the PL style. Keywords are bold. TTCN-3 // comments are italic grey, and string and numeric literals are tinted. // EBNF comments and nonterminals stay italic, without colour. var ( codePlain = props{mono: true, size: 16} codeBold = props{mono: true, bold: true, size: 16} codeItalic = props{mono: true, italic: true, size: 16} codeComment = props{mono: true, italic: true, size: 16, color: "595959"} codeString = props{mono: true, size: 16, color: "803000"} codeNumber = props{mono: true, size: 16, color: "003B8F"} ) // An empty Normal paragraph between two listings keeps them from becoming one Loading Loading @@ -99,16 +102,16 @@ func (w *writer) ttcn3Segment(s string, inComment *bool) { for i := 0; i < len(s); { if *inComment { if j := strings.Index(s[i:], "*/"); j >= 0 { w.text(s[i:i+j+2], codeItalic) w.text(s[i:i+j+2], codeComment) i += j + 2 *inComment = false continue } w.text(s[i:], codeItalic) w.text(s[i:], codeComment) return } if strings.HasPrefix(s[i:], "//") { w.text(s[i:], codeItalic) w.text(s[i:], codeComment) return } if strings.HasPrefix(s[i:], "/*") { Loading @@ -117,7 +120,16 @@ func (w *writer) ttcn3Segment(s string, inComment *bool) { } if s[i] == '"' || s[i] == '\'' { j := scanQuoted(s, i) w.text(s[i:j], codePlain) if s[i] == '\'' && j < len(s) && isStringSuffix(s[j]) { j++ } w.text(s[i:j], codeString) i = j continue } if isDigit(s[i]) { j := scanNumber(s, i) w.text(s[i:j], codeNumber) i = j continue } Loading @@ -135,7 +147,7 @@ func (w *writer) ttcn3Segment(s string, inComment *bool) { continue } j := i + 1 for j < len(s) && !codeSpecial(s, j) { for j < len(s) && !ttcn3Special(s, j) { j++ } w.text(s[i:j], codePlain) Loading Loading @@ -281,6 +293,50 @@ func codeSpecial(s string, i int) bool { return strings.HasPrefix(s[i:], "//") || strings.HasPrefix(s[i:], "/*") } func ttcn3Special(s string, i int) bool { return codeSpecial(s, i) || i < len(s) && isDigit(s[i]) } func isDigit(c byte) bool { return c >= '0' && c <= '9' } // isStringSuffix reports whether c is a bitstring, hexstring, or octetstring suffix. func isStringSuffix(c byte) bool { switch c { case 'B', 'b', 'H', 'h', 'O', 'o': return true default: return false } } // scanNumber returns the index past a decimal integer or float starting at i. func scanNumber(s string, i int) int { j := i for j < len(s) && isDigit(s[j]) { j++ } if j+1 < len(s) && s[j] == '.' && isDigit(s[j+1]) { j++ for j < len(s) && isDigit(s[j]) { j++ } } if j < len(s) && (s[j] == 'e' || s[j] == 'E') { k := j + 1 if k < len(s) && (s[k] == '+' || s[k] == '-') { k++ } start := k for k < len(s) && isDigit(s[k]) { k++ } if k > start { j = k } } return j } func scanQuoted(s string, i int) int { q := s[i] i++ Loading tools/internal/doc/docx/write.go +8 −4 Changes for tools/internal/doc/docx/write.go: 8 added lines, 4 removed lines. Original line number Diff line number Diff line Loading @@ -90,9 +90,9 @@ const ( // and a tab before the title. Notes use NO, example captions use EX, and code // uses PL. Bullets use B1+ and ordered lists use BN. Code marked test is // omitted, and so are HTML comments. An example caption is "EXAMPLE n: title", // counted again from 1 in each clause. TTCN-3 keywords are bold and comments // are italic. A defined EBNF name is italic, and a use of it is a hyperlink // to the first definition. // counted again from 1 in each clause. TTCN-3 keywords are bold, comments are // italic grey, and string and numeric literals are coloured. A defined EBNF // name is italic, and a use of it is a hyperlink to the first definition. func Write(w io.Writer, d *doc.Document) error { if d == nil { return fmt.Errorf("docx: nil document") Loading Loading @@ -163,6 +163,7 @@ type props struct { bold, italic, mono bool size int // half-points; 0 inherits style string color string // RRGGBB; empty inherits } func (w *writer) document() []byte { Loading Loading @@ -609,7 +610,7 @@ func (w *writer) tab(p props) { func (w *writer) br() { w.body.WriteString(`<w:r><w:br/></w:r>`) } func (p props) write(b *strings.Builder) { if p.style == "" && !p.mono && !p.bold && !p.italic && p.size == 0 { if p.style == "" && !p.mono && !p.bold && !p.italic && p.size == 0 && p.color == "" { return } b.WriteString(`<w:rPr>`) Loading @@ -625,6 +626,9 @@ func (p props) write(b *strings.Builder) { if p.italic { b.WriteString(`<w:i/><w:iCs/>`) } if p.color != "" { fmt.Fprintf(b, `<w:color w:val="%s"/>`, p.color) } if p.size > 0 { fmt.Fprintf(b, `<w:sz w:val="%d"/><w:szCs w:val="%d"/>`, p.size, p.size) } Loading tools/internal/doc/docx/write_test.go +37 −1 Changes for tools/internal/doc/docx/write_test.go: 37 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -106,7 +106,7 @@ func TestWriteRoundTrip(t *testing.T) { `w:name="ebnf_Expr"`, `w:name="ebnf_Term"`, `<w:b/><w:bCs/><w:sz w:val="16"/><w:szCs w:val="16"/></w:rPr><w:t>var</w:t>`, `<w:i/><w:iCs/><w:sz w:val="16"/><w:szCs w:val="16"/></w:rPr><w:t>// note</w:t>`, `<w:i/><w:iCs/><w:color w:val="595959"/><w:sz w:val="16"/><w:szCs w:val="16"/></w:rPr><w:t>// note</w:t>`, } { if !strings.Contains(docXML, want) { t.Errorf("document missing %s", want) Loading Loading @@ -220,6 +220,42 @@ func TestWriteRoundTrip(t *testing.T) { } } func TestTTCN3Highlight(t *testing.T) { d := &doc.Document{Sections: []*doc.Section{{ Number: "1", Level: 1, Title: []doc.Inline{doc.Text("Literals")}, Blocks: []doc.Block{ &doc.Code{Lang: "ttcn3", Lines: []string{ `var integer x := 1; // note`, `x := "hi" & '01'B; /* block`, `still */ y := 1.5E-2;`, }}, &doc.Code{Lang: "ebnf", Lines: []string{"Expr ::= Term. // keep"}}, }, }}} var buf bytes.Buffer if err := Write(&buf, d); err != nil { t.Fatal(err) } docXML := zipPart(t, buf.Bytes(), "word/document.xml") for _, want := range []string{ `<w:b/><w:bCs/><w:sz w:val="16"/><w:szCs w:val="16"/></w:rPr><w:t>var</w:t>`, `<w:color w:val="003B8F"/><w:sz w:val="16"/><w:szCs w:val="16"/></w:rPr><w:t>1</w:t>`, `<w:i/><w:iCs/><w:color w:val="595959"/><w:sz w:val="16"/><w:szCs w:val="16"/></w:rPr><w:t>// note</w:t>`, `<w:color w:val="803000"/><w:sz w:val="16"/><w:szCs w:val="16"/></w:rPr><w:t>"hi"</w:t>`, `<w:color w:val="803000"/><w:sz w:val="16"/><w:szCs w:val="16"/></w:rPr><w:t>'01'B</w:t>`, `<w:i/><w:iCs/><w:color w:val="595959"/><w:sz w:val="16"/><w:szCs w:val="16"/></w:rPr><w:t>/* block</w:t>`, `<w:i/><w:iCs/><w:color w:val="595959"/><w:sz w:val="16"/><w:szCs w:val="16"/></w:rPr><w:t>still */</w:t>`, `<w:color w:val="003B8F"/><w:sz w:val="16"/><w:szCs w:val="16"/></w:rPr><w:t>1.5E-2</w:t>`, `<w:i/><w:iCs/><w:sz w:val="16"/><w:szCs w:val="16"/></w:rPr><w:t>// keep</w:t>`, } { if !strings.Contains(docXML, want) { t.Errorf("document missing %s", want) } } } func TestListContinuation(t *testing.T) { d := &doc.Document{Sections: []*doc.Section{{ Number: "1", Loading Loading
tools/internal/doc/docx/code.go +64 −8 Changes for tools/internal/doc/docx/code.go: 64 added lines, 8 removed lines. Original line number Diff line number Diff line Loading @@ -9,13 +9,16 @@ import ( "tools/internal/doc" ) // Code runs are Courier New 8pt, the PL style. Keywords are bold and comments // are italic, which is the highlighting used for syntactic descriptions in the // working documents. Colours are not added. // Code runs are Courier New 8pt, the PL style. Keywords are bold. TTCN-3 // comments are italic grey, and string and numeric literals are tinted. // EBNF comments and nonterminals stay italic, without colour. var ( codePlain = props{mono: true, size: 16} codeBold = props{mono: true, bold: true, size: 16} codeItalic = props{mono: true, italic: true, size: 16} codeComment = props{mono: true, italic: true, size: 16, color: "595959"} codeString = props{mono: true, size: 16, color: "803000"} codeNumber = props{mono: true, size: 16, color: "003B8F"} ) // An empty Normal paragraph between two listings keeps them from becoming one Loading Loading @@ -99,16 +102,16 @@ func (w *writer) ttcn3Segment(s string, inComment *bool) { for i := 0; i < len(s); { if *inComment { if j := strings.Index(s[i:], "*/"); j >= 0 { w.text(s[i:i+j+2], codeItalic) w.text(s[i:i+j+2], codeComment) i += j + 2 *inComment = false continue } w.text(s[i:], codeItalic) w.text(s[i:], codeComment) return } if strings.HasPrefix(s[i:], "//") { w.text(s[i:], codeItalic) w.text(s[i:], codeComment) return } if strings.HasPrefix(s[i:], "/*") { Loading @@ -117,7 +120,16 @@ func (w *writer) ttcn3Segment(s string, inComment *bool) { } if s[i] == '"' || s[i] == '\'' { j := scanQuoted(s, i) w.text(s[i:j], codePlain) if s[i] == '\'' && j < len(s) && isStringSuffix(s[j]) { j++ } w.text(s[i:j], codeString) i = j continue } if isDigit(s[i]) { j := scanNumber(s, i) w.text(s[i:j], codeNumber) i = j continue } Loading @@ -135,7 +147,7 @@ func (w *writer) ttcn3Segment(s string, inComment *bool) { continue } j := i + 1 for j < len(s) && !codeSpecial(s, j) { for j < len(s) && !ttcn3Special(s, j) { j++ } w.text(s[i:j], codePlain) Loading Loading @@ -281,6 +293,50 @@ func codeSpecial(s string, i int) bool { return strings.HasPrefix(s[i:], "//") || strings.HasPrefix(s[i:], "/*") } func ttcn3Special(s string, i int) bool { return codeSpecial(s, i) || i < len(s) && isDigit(s[i]) } func isDigit(c byte) bool { return c >= '0' && c <= '9' } // isStringSuffix reports whether c is a bitstring, hexstring, or octetstring suffix. func isStringSuffix(c byte) bool { switch c { case 'B', 'b', 'H', 'h', 'O', 'o': return true default: return false } } // scanNumber returns the index past a decimal integer or float starting at i. func scanNumber(s string, i int) int { j := i for j < len(s) && isDigit(s[j]) { j++ } if j+1 < len(s) && s[j] == '.' && isDigit(s[j+1]) { j++ for j < len(s) && isDigit(s[j]) { j++ } } if j < len(s) && (s[j] == 'e' || s[j] == 'E') { k := j + 1 if k < len(s) && (s[k] == '+' || s[k] == '-') { k++ } start := k for k < len(s) && isDigit(s[k]) { k++ } if k > start { j = k } } return j } func scanQuoted(s string, i int) int { q := s[i] i++ Loading
tools/internal/doc/docx/write.go +8 −4 Changes for tools/internal/doc/docx/write.go: 8 added lines, 4 removed lines. Original line number Diff line number Diff line Loading @@ -90,9 +90,9 @@ const ( // and a tab before the title. Notes use NO, example captions use EX, and code // uses PL. Bullets use B1+ and ordered lists use BN. Code marked test is // omitted, and so are HTML comments. An example caption is "EXAMPLE n: title", // counted again from 1 in each clause. TTCN-3 keywords are bold and comments // are italic. A defined EBNF name is italic, and a use of it is a hyperlink // to the first definition. // counted again from 1 in each clause. TTCN-3 keywords are bold, comments are // italic grey, and string and numeric literals are coloured. A defined EBNF // name is italic, and a use of it is a hyperlink to the first definition. func Write(w io.Writer, d *doc.Document) error { if d == nil { return fmt.Errorf("docx: nil document") Loading Loading @@ -163,6 +163,7 @@ type props struct { bold, italic, mono bool size int // half-points; 0 inherits style string color string // RRGGBB; empty inherits } func (w *writer) document() []byte { Loading Loading @@ -609,7 +610,7 @@ func (w *writer) tab(p props) { func (w *writer) br() { w.body.WriteString(`<w:r><w:br/></w:r>`) } func (p props) write(b *strings.Builder) { if p.style == "" && !p.mono && !p.bold && !p.italic && p.size == 0 { if p.style == "" && !p.mono && !p.bold && !p.italic && p.size == 0 && p.color == "" { return } b.WriteString(`<w:rPr>`) Loading @@ -625,6 +626,9 @@ func (p props) write(b *strings.Builder) { if p.italic { b.WriteString(`<w:i/><w:iCs/>`) } if p.color != "" { fmt.Fprintf(b, `<w:color w:val="%s"/>`, p.color) } if p.size > 0 { fmt.Fprintf(b, `<w:sz w:val="%d"/><w:szCs w:val="%d"/>`, p.size, p.size) } Loading
tools/internal/doc/docx/write_test.go +37 −1 Changes for tools/internal/doc/docx/write_test.go: 37 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -106,7 +106,7 @@ func TestWriteRoundTrip(t *testing.T) { `w:name="ebnf_Expr"`, `w:name="ebnf_Term"`, `<w:b/><w:bCs/><w:sz w:val="16"/><w:szCs w:val="16"/></w:rPr><w:t>var</w:t>`, `<w:i/><w:iCs/><w:sz w:val="16"/><w:szCs w:val="16"/></w:rPr><w:t>// note</w:t>`, `<w:i/><w:iCs/><w:color w:val="595959"/><w:sz w:val="16"/><w:szCs w:val="16"/></w:rPr><w:t>// note</w:t>`, } { if !strings.Contains(docXML, want) { t.Errorf("document missing %s", want) Loading Loading @@ -220,6 +220,42 @@ func TestWriteRoundTrip(t *testing.T) { } } func TestTTCN3Highlight(t *testing.T) { d := &doc.Document{Sections: []*doc.Section{{ Number: "1", Level: 1, Title: []doc.Inline{doc.Text("Literals")}, Blocks: []doc.Block{ &doc.Code{Lang: "ttcn3", Lines: []string{ `var integer x := 1; // note`, `x := "hi" & '01'B; /* block`, `still */ y := 1.5E-2;`, }}, &doc.Code{Lang: "ebnf", Lines: []string{"Expr ::= Term. // keep"}}, }, }}} var buf bytes.Buffer if err := Write(&buf, d); err != nil { t.Fatal(err) } docXML := zipPart(t, buf.Bytes(), "word/document.xml") for _, want := range []string{ `<w:b/><w:bCs/><w:sz w:val="16"/><w:szCs w:val="16"/></w:rPr><w:t>var</w:t>`, `<w:color w:val="003B8F"/><w:sz w:val="16"/><w:szCs w:val="16"/></w:rPr><w:t>1</w:t>`, `<w:i/><w:iCs/><w:color w:val="595959"/><w:sz w:val="16"/><w:szCs w:val="16"/></w:rPr><w:t>// note</w:t>`, `<w:color w:val="803000"/><w:sz w:val="16"/><w:szCs w:val="16"/></w:rPr><w:t>"hi"</w:t>`, `<w:color w:val="803000"/><w:sz w:val="16"/><w:szCs w:val="16"/></w:rPr><w:t>'01'B</w:t>`, `<w:i/><w:iCs/><w:color w:val="595959"/><w:sz w:val="16"/><w:szCs w:val="16"/></w:rPr><w:t>/* block</w:t>`, `<w:i/><w:iCs/><w:color w:val="595959"/><w:sz w:val="16"/><w:szCs w:val="16"/></w:rPr><w:t>still */</w:t>`, `<w:color w:val="003B8F"/><w:sz w:val="16"/><w:szCs w:val="16"/></w:rPr><w:t>1.5E-2</w:t>`, `<w:i/><w:iCs/><w:sz w:val="16"/><w:szCs w:val="16"/></w:rPr><w:t>// keep</w:t>`, } { if !strings.Contains(docXML, want) { t.Errorf("document missing %s", want) } } } func TestListContinuation(t *testing.T) { d := &doc.Document{Sections: []*doc.Section{{ Number: "1", Loading