mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-26 12:27:06 +00:00 
			
		
		
		
	Backport #25184 by @jtran Fixes #25160. `data-source-position` of checkboxes in a task list was incorrect whenever there was YAML front matter. This would result in issue content or PR descriptions getting corrupted with random `x` or space characters when a user checked or unchecked a task. Co-authored-by: Jonathan Tran <jon@allspice.io>
This commit is contained in:
		| @@ -76,7 +76,8 @@ func IsSummary(node ast.Node) bool { | ||||
| // TaskCheckBoxListItem is a block that represents a list item of a markdown block with a checkbox | ||||
| type TaskCheckBoxListItem struct { | ||||
| 	*ast.ListItem | ||||
| 	IsChecked bool | ||||
| 	IsChecked      bool | ||||
| 	SourcePosition int | ||||
| } | ||||
|  | ||||
| // KindTaskCheckBoxListItem is the NodeKind for TaskCheckBoxListItem | ||||
| @@ -86,6 +87,7 @@ var KindTaskCheckBoxListItem = ast.NewNodeKind("TaskCheckBoxListItem") | ||||
| func (n *TaskCheckBoxListItem) Dump(source []byte, level int) { | ||||
| 	m := map[string]string{} | ||||
| 	m["IsChecked"] = strconv.FormatBool(n.IsChecked) | ||||
| 	m["SourcePosition"] = strconv.FormatInt(int64(n.SourcePosition), 10) | ||||
| 	ast.DumpHelper(n, source, level, m, nil) | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -177,6 +177,11 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa | ||||
| 					newChild := NewTaskCheckBoxListItem(listItem) | ||||
| 					newChild.IsChecked = taskCheckBox.IsChecked | ||||
| 					newChild.SetAttributeString("class", []byte("task-list-item")) | ||||
| 					segments := newChild.FirstChild().Lines() | ||||
| 					if segments.Len() > 0 { | ||||
| 						segment := segments.At(0) | ||||
| 						newChild.SourcePosition = rc.metaLength + segment.Start | ||||
| 					} | ||||
| 					v.AppendChild(v, newChild) | ||||
| 				} | ||||
| 			} | ||||
| @@ -457,12 +462,7 @@ func (r *HTMLRenderer) renderTaskCheckBoxListItem(w util.BufWriter, source []byt | ||||
| 		} else { | ||||
| 			_, _ = w.WriteString("<li>") | ||||
| 		} | ||||
| 		_, _ = w.WriteString(`<input type="checkbox" disabled=""`) | ||||
| 		segments := node.FirstChild().Lines() | ||||
| 		if segments.Len() > 0 { | ||||
| 			segment := segments.At(0) | ||||
| 			_, _ = w.WriteString(fmt.Sprintf(` data-source-position="%d"`, segment.Start)) | ||||
| 		} | ||||
| 		fmt.Fprintf(w, `<input type="checkbox" disabled="" data-source-position="%d"`, n.SourcePosition) | ||||
| 		if n.IsChecked { | ||||
| 			_, _ = w.WriteString(` checked=""`) | ||||
| 		} | ||||
|   | ||||
| @@ -178,6 +178,9 @@ func actualRender(ctx *markup.RenderContext, input io.Reader, output io.Writer) | ||||
| 	} | ||||
| 	buf = giteautil.NormalizeEOL(buf) | ||||
|  | ||||
| 	// Preserve original length. | ||||
| 	bufWithMetadataLength := len(buf) | ||||
|  | ||||
| 	rc := &RenderConfig{ | ||||
| 		Meta: renderMetaModeFromString(string(ctx.RenderMetaAs)), | ||||
| 		Icon: "table", | ||||
| @@ -185,6 +188,12 @@ func actualRender(ctx *markup.RenderContext, input io.Reader, output io.Writer) | ||||
| 	} | ||||
| 	buf, _ = ExtractMetadataBytes(buf, rc) | ||||
|  | ||||
| 	metaLength := bufWithMetadataLength - len(buf) | ||||
| 	if metaLength < 0 { | ||||
| 		metaLength = 0 | ||||
| 	} | ||||
| 	rc.metaLength = metaLength | ||||
|  | ||||
| 	pc.Set(renderConfigKey, rc) | ||||
|  | ||||
| 	if err := converter.Convert(buf, lw, parser.WithContext(pc)); err != nil { | ||||
|   | ||||
| @@ -520,3 +520,40 @@ func TestMathBlock(t *testing.T) { | ||||
|  | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestTaskList(t *testing.T) { | ||||
| 	testcases := []struct { | ||||
| 		testcase string | ||||
| 		expected string | ||||
| 	}{ | ||||
| 		{ | ||||
| 			// data-source-position should take into account YAML frontmatter. | ||||
| 			`--- | ||||
| foo: bar | ||||
| --- | ||||
| - [ ] task 1`, | ||||
| 			`<details><summary><i class="icon table"></i></summary><table> | ||||
| <thead> | ||||
| <tr> | ||||
| <th>foo</th> | ||||
| </tr> | ||||
| </thead> | ||||
| <tbody> | ||||
| <tr> | ||||
| <td>bar</td> | ||||
| </tr> | ||||
| </tbody> | ||||
| </table> | ||||
| </details><ul> | ||||
| <li class="task-list-item"><input type="checkbox" disabled="" data-source-position="19"/>task 1</li> | ||||
| </ul> | ||||
| `, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for _, test := range testcases { | ||||
| 		res, err := RenderString(&markup.RenderContext{Ctx: git.DefaultContext}, test.testcase) | ||||
| 		assert.NoError(t, err, "Unexpected error in testcase: %q", test.testcase) | ||||
| 		assert.Equal(t, test.expected, res, "Unexpected result in testcase %q", test.testcase) | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -20,6 +20,9 @@ type RenderConfig struct { | ||||
| 	TOC      string // "false": hide,  "side"/empty: in sidebar,  "main"/"true": in main view | ||||
| 	Lang     string | ||||
| 	yamlNode *yaml.Node | ||||
|  | ||||
| 	// Used internally.  Cannot be controlled by frontmatter. | ||||
| 	metaLength int | ||||
| } | ||||
|  | ||||
| func renderMetaModeFromString(s string) markup.RenderMetaMode { | ||||
|   | ||||
| @@ -29,6 +29,14 @@ export function initMarkupTasklist() { | ||||
|  | ||||
|         const encoder = new TextEncoder(); | ||||
|         const buffer = encoder.encode(oldContent); | ||||
|         // Indexes may fall off the ends and return undefined. | ||||
|         if (buffer[position - 1] !== '['.codePointAt(0) || | ||||
|           buffer[position] !== ' '.codePointAt(0) && buffer[position] !== 'x'.codePointAt(0) || | ||||
|           buffer[position + 1] !== ']'.codePointAt(0)) { | ||||
|           // Position is probably wrong.  Revert and don't allow change. | ||||
|           checkbox.checked = !checkbox.checked; | ||||
|           throw new Error(`Expected position to be space or x and surrounded by brackets, but it's not: position=${position}`); | ||||
|         } | ||||
|         buffer.set(encoder.encode(checkboxCharacter), position); | ||||
|         const newContent = new TextDecoder().decode(buffer); | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Giteabot
					Giteabot