Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
83c9e24
Add code editor settings UI
silverwind Feb 4, 2026
27a8fbe
Update web_src/js/features/codeeditor.ts
silverwind Feb 4, 2026
7f6547c
Apply suggestion from @silverwind
silverwind Feb 5, 2026
963e4a7
Apply suggestion from @silverwind
silverwind Feb 5, 2026
b9a69aa
Rename editor options template to settings
silverwind Feb 5, 2026
7fc1b99
fmt
silverwind Feb 5, 2026
75d75c5
Use Fomantic dropdowns for editor option selects
silverwind Feb 10, 2026
92210b3
Fix first menu item border-radius for selection dropdowns
silverwind Feb 10, 2026
5f70d2e
Reset font styles on editor options container
silverwind Feb 10, 2026
9151a8d
Merge branch 'main' into editoropts
silverwind Feb 10, 2026
baa4e3e
Reduce editor option dropdown size to match small buttons
silverwind Feb 10, 2026
096e534
Merge branch 'main' into editoropts
wxiaoguang Feb 11, 2026
9a2342a
temp
wxiaoguang Feb 11, 2026
b472cf8
use native select
wxiaoguang Feb 11, 2026
49a1926
clean up
wxiaoguang Feb 11, 2026
d89ab2c
use optgroups over tooltip
silverwind Feb 11, 2026
450f372
fix js, fix select event
wxiaoguang Feb 11, 2026
39f34fc
reset font size by tailwind
wxiaoguang Feb 11, 2026
f9b2135
use mask technique
silverwind Feb 11, 2026
85a38ac
remove unneeded svgs
silverwind Feb 11, 2026
7a49255
restore pointer-events
silverwind Feb 11, 2026
518f02b
fix lint
silverwind Feb 11, 2026
6657a32
Apply suggestion from @silverwind
silverwind Feb 11, 2026
68c549d
Apply suggestion from @silverwind
silverwind Feb 11, 2026
4b1b6de
Merge branch 'main' into editoropts
silverwind Feb 11, 2026
e6f179b
use top/translate
silverwind Feb 11, 2026
d188d69
Only set tabSize when EditorConfig defines indentation
silverwind Feb 11, 2026
74a2498
Add aria-labels to editor option selects for accessibility
silverwind Feb 11, 2026
3f811fb
fix
wxiaoguang Feb 11, 2026
cc81066
fix CodeEditorConfig
wxiaoguang Feb 11, 2026
4934181
fix
wxiaoguang Feb 11, 2026
93d320e
Update web_src/fomantic/build/components/dropdown.js
wxiaoguang Feb 11, 2026
1cb2d6d
Rename CSS variable --gitea-select-arrows to --select-arrows
silverwind Feb 11, 2026
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
2 changes: 2 additions & 0 deletions modules/templates/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"fmt"
"html/template"
"net/url"
"path"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -42,6 +43,7 @@ func NewFuncMap() template.FuncMap {

"PathEscape": url.PathEscape,
"PathEscapeSegments": util.PathEscapeSegments,
"PathExt": path.Ext,
Comment thread
wxiaoguang marked this conversation as resolved.
Outdated

// utils
"StringUtils": NewStringUtils,
Expand Down
7 changes: 7 additions & 0 deletions options/locale/locale_en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,13 @@
"filter.private": "Private",
"no_results_found": "No results found.",
"internal_error_skipped": "Internal error occurred but is skipped: %s",
"characters_spaces": "Spaces",
"characters_tabs": "Tabs",
"text_indent_style": "Indent style",
"text_indent_size": "Indent size",
"text_line_wrap": "Wrap",
"text_line_nowrap": "No wrap",
"text_line_wrap_mode": "Line wrap mode",
"search.search": "Search…",
"search.type_tooltip": "Search type",
"search.fuzzy": "Fuzzy",
Expand Down
17 changes: 14 additions & 3 deletions routers/web/repo/editor.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ func prepareEditorPageFormOptions(ctx *context.Context, editorAction string) *co
ctx.Data["CommitFormOptions"] = commitFormOptions

// for online editor
ctx.Data["PreviewableExtensions"] = strings.Join(markup.PreviewableExtensions(), ",")
ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",")
ctx.Data["PreviewableExtensions"] = markup.PreviewableExtensions()
ctx.Data["LineWrapExtensions"] = setting.Repository.Editor.LineWrapExtensions
ctx.Data["IsEditingFileOnly"] = ctx.FormString("return_uri") != ""
ctx.Data["ReturnURI"] = ctx.FormString("return_uri")

Expand Down Expand Up @@ -321,7 +321,18 @@ func EditFile(ctx *context.Context) {
}
}

ctx.Data["EditorconfigJson"] = getContextRepoEditorConfig(ctx, ctx.Repo.TreePath)
ecJSON, ecDef := getContextRepoEditorConfig(ctx, ctx.Repo.TreePath)
ctx.Data["EditorconfigJson"] = ecJSON
ctx.Data["EditorconfigIndentStyle"] = "space"
Comment thread
silverwind marked this conversation as resolved.
Outdated
ctx.Data["EditorconfigIndentSize"] = "4"
Comment thread
silverwind marked this conversation as resolved.
Outdated
if ecDef != nil {
if ecDef.IndentStyle != "" {
ctx.Data["EditorconfigIndentStyle"] = ecDef.IndentStyle
}
if ecDef.IndentSize != "" && ecDef.IndentSize != "tab" {
ctx.Data["EditorconfigIndentSize"] = ecDef.IndentSize
}
}
ctx.HTML(http.StatusOK, tplEditFile)
Comment thread
wxiaoguang marked this conversation as resolved.
}

Expand Down
10 changes: 6 additions & 4 deletions routers/web/repo/editor_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import (
"code.gitea.io/gitea/modules/log"
repo_module "code.gitea.io/gitea/modules/repository"
context_service "code.gitea.io/gitea/services/context"

"github.com/editorconfig/editorconfig-core-go/v2"
)

// getUniquePatchBranchName Gets a unique branch name for a new patch branch
Expand Down Expand Up @@ -62,17 +64,17 @@ func getClosestParentWithFiles(gitRepo *git.Repository, branchName, originTreePa
return f(originTreePath, commit)
}

// getContextRepoEditorConfig returns the editorconfig JSON string for given treePath or "null"
func getContextRepoEditorConfig(ctx *context_service.Context, treePath string) string {
// getContextRepoEditorConfig returns the editorconfig JSON string and definition for given treePath
func getContextRepoEditorConfig(ctx *context_service.Context, treePath string) (string, *editorconfig.Definition) {
Comment thread
wxiaoguang marked this conversation as resolved.
Outdated
ec, _, err := ctx.Repo.GetEditorconfig()
if err == nil {
def, err := ec.GetDefinitionForFilename(treePath)
if err == nil {
jsonStr, _ := json.Marshal(def)
return string(jsonStr)
return string(jsonStr), def
}
}
return "null"
return "null", nil
}

// getParentTreeFields returns list of parent tree names and corresponding tree paths based on given treePath.
Expand Down
19 changes: 11 additions & 8 deletions templates/repo/editor/edit.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,22 @@
{{if not .NotEditableReason}}
<div class="field">
<div class="ui top attached header">
<div class="ui compact small menu small-menu-items repo-editor-menu">
<a class="active item" data-tab="write">{{svg "octicon-code"}} {{if .IsNewFile}}{{ctx.Locale.Tr "repo.editor.new_file"}}{{else}}{{ctx.Locale.Tr "repo.editor.edit_file"}}{{end}}</a>
<a class="item" data-tab="preview" data-preview-url="{{.Repository.Link}}/markup" data-preview-context-ref="{{.RepoLink}}/src/{{.RefTypeNameSubURL}}">{{svg "octicon-eye"}} {{ctx.Locale.Tr "preview"}}</a>
{{if not .IsNewFile}}
<a class="item" data-tab="diff" hx-params="context,content" hx-vals='{"context":"{{.BranchLink}}"}' hx-include="#edit_area" hx-swap="innerHTML" hx-target=".tab[data-tab='diff']" hx-indicator=".tab[data-tab='diff']" hx-post="{{.RepoLink}}/_preview/{{.BranchName | PathEscapeSegments}}/{{.TreePath | PathEscapeSegments}}">{{svg "octicon-diff"}} {{ctx.Locale.Tr "repo.editor.preview_changes"}}</a>
{{end}}
<div class="tw-w-100 tw-flex tw-justify-between tw-flex-col md:tw-flex-row tw-gap-2 tw-items-stretch">
<div class="ui compact small menu small-menu-items repo-editor-menu tw-self-start">
<a class="active item" data-tab="write">{{svg "octicon-code"}} {{if .IsNewFile}}{{ctx.Locale.Tr "repo.editor.new_file"}}{{else}}{{ctx.Locale.Tr "repo.editor.edit_file"}}{{end}}</a>
<a class="item" data-tab="preview" data-preview-url="{{.Repository.Link}}/markup" data-preview-context-ref="{{.RepoLink}}/src/{{.RefTypeNameSubURL}}">{{svg "octicon-eye"}} {{ctx.Locale.Tr "preview"}}</a>
{{if not .IsNewFile}}
<a class="item" data-tab="diff" hx-params="context,content" hx-vals='{"context":"{{.BranchLink}}"}' hx-include="#edit_area" hx-swap="innerHTML" hx-target=".tab[data-tab='diff']" hx-indicator=".tab[data-tab='diff']" hx-post="{{.RepoLink}}/_preview/{{.BranchName | PathEscapeSegments}}/{{.TreePath | PathEscapeSegments}}">{{svg "octicon-diff"}} {{ctx.Locale.Tr "repo.editor.preview_changes"}}</a>
{{end}}
</div>
{{template "repo/editor/options" .}}
</div>
</div>
<div class="ui bottom attached segment tw-p-0">
<div class="ui active tab tw-rounded-b" data-tab="write">
<textarea id="edit_area" name="content" class="tw-hidden" data-id="repo-{{.Repository.Name}}-{{.TreePath}}"
data-previewable-extensions="{{.PreviewableExtensions}}"
data-line-wrap-extensions="{{.LineWrapExtensions}}">{{.FileContent}}</textarea>
data-previewable-extensions="{{StringUtils.Join .PreviewableExtensions ","}}"
data-line-wrap-extensions="{{StringUtils.Join .LineWrapExtensions ","}}">{{.FileContent}}</textarea>
<div class="editor-loading is-loading"></div>
</div>
<div class="ui tab tw-px-4 tw-py-3" data-tab="preview">
Expand Down
25 changes: 25 additions & 0 deletions templates/repo/editor/options.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{{$indentStyle := (or .EditorconfigIndentStyle "space")}}
{{$indentSize := (or .EditorconfigIndentSize "4")}}
{{$treePath := (or .TreePath "")}}
<div class="editor-options tw-flex tw-gap-2">
<select class="js-indent-style-select tw-w-auto">
<optgroup label="{{ctx.Locale.Tr "text_indent_style"}}">
<option{{if eq $indentStyle "space"}} selected{{end}} value="space">{{ctx.Locale.Tr "characters_spaces"}}</option>
<option{{if eq $indentStyle "tab"}} selected{{end}} value="tab">{{ctx.Locale.Tr "characters_tabs"}}</option>
</optgroup>
</select>
<select class="js-indent-size-select tw-w-auto">
<optgroup label="{{ctx.Locale.Tr "text_indent_size"}}">
<option{{if eq $indentSize "2"}} selected{{end}} value="2">2</option>
<option{{if eq $indentSize "4"}} selected{{end}} value="4">4</option>
<option{{if eq $indentSize "8"}} selected{{end}} value="8">8</option>
</optgroup>
</select>
{{$mode := (Iif (SliceUtils.Contains .LineWrapExtensions (PathExt $treePath)) "on" "off")}}
<select class="js-line-wrap-select tw-w-auto">
<optgroup label="{{ctx.Locale.Tr "text_line_wrap_mode"}}">
<option{{if eq $mode "on"}} selected{{end}} value="on">{{ctx.Locale.Tr "text_line_wrap"}}</option>
<option{{if eq $mode "off"}} selected{{end}} value="off">{{ctx.Locale.Tr "text_line_nowrap"}}</option>
</optgroup>
</select>
</div>
21 changes: 14 additions & 7 deletions templates/repo/editor/patch.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,22 @@
</div>
</div>
<div class="field">
<div class="ui compact small menu small-menu-items repo-editor-menu">
<a class="active item" data-tab="write">{{svg "octicon-code" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.editor.new_patch"}}</a>
<div class="ui top attached header">
<div class="tw-w-100 tw-flex tw-justify-between tw-flex-col md:tw-flex-row tw-gap-2 tw-items-stretch">
<div class="ui compact small menu small-menu-items repo-editor-menu tw-self-start">
<a class="active item" data-tab="write">{{svg "octicon-code" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.editor.new_patch"}}</a>
</div>
{{template "repo/editor/options" .}}
</div>
</div>
<div class="ui active tab segment tw-rounded tw-p-0" data-tab="write">
<textarea id="edit_area" name="content" class="tw-hidden" data-id="repo-{{.Repository.Name}}-patch"
data-context="{{.RepoLink}}"
data-line-wrap-extensions="{{.LineWrapExtensions}}">
<div class="ui bottom attached segment tw-p-0">
<div class="ui active tab tw-rounded-b" data-tab="write">
<textarea id="edit_area" name="content" class="tw-hidden" data-id="repo-{{.Repository.Name}}-patch"
data-context="{{.RepoLink}}"
data-line-wrap-extensions="{{StringUtils.Join .LineWrapExtensions ","}}">
{{.FileContent}}</textarea>
<div class="editor-loading is-loading"></div>
<div class="editor-loading is-loading"></div>
</div>
</div>
</div>
{{template "repo/editor/commit_form" .}}
Expand Down
17 changes: 9 additions & 8 deletions templates/repo/settings/githook_edit.tmpl
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
{{template "repo/settings/layout_head" (dict "ctxData" . "pageClass" "repository settings edit githook")}}
<div class="repo-setting-content">
<h4 class="ui top attached header">
{{ctx.Locale.Tr "repo.settings.githooks"}}
</h4>
<div class="ui attached segment">
<p>{{ctx.Locale.Tr "repo.settings.githook_edit_desc"}}</p>
<form class="ui form" action="{{.Link}}" method="post">
<form class="ui form" action="{{.Link}}" method="post">
Comment thread
wxiaoguang marked this conversation as resolved.
<h4 class="ui top attached header tw-flex tw-justify-between tw-items-center">
{{ctx.Locale.Tr "repo.settings.githooks"}}
{{template "repo/editor/options" .}}
</h4>
<div class="ui attached segment">
<p>{{ctx.Locale.Tr "repo.settings.githook_edit_desc"}}</p>
{{with .Hook}}
<div class="inline field">
<label>{{ctx.Locale.Tr "repo.settings.githook_name"}}</label>
Expand All @@ -20,7 +21,7 @@
<button class="ui primary button">{{ctx.Locale.Tr "repo.settings.update_githook"}}</button>
</div>
{{end}}
</form>
</div>
</div>
</form>
</div>
{{template "repo/settings/layout_footer" .}}
26 changes: 19 additions & 7 deletions web_src/js/features/codeeditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const baseOptions: MonacoOpts = {
scrollbar: {horizontalScrollbarSize: 6, verticalScrollbarSize: 6, alwaysConsumeMouseWheel: false},
scrollBeyondLastLine: false,
automaticLayout: true,
indentSize: 'tabSize',
wrappingIndent: 'none',
wordWrapBreakAfterCharacters: '',
wordWrapBreakBeforeCharacters: '',
Expand Down Expand Up @@ -152,6 +153,7 @@ export async function createMonaco(textarea: HTMLTextAreaElement, filename: stri
const editor = monaco.editor.create(container, {
model,
theme: 'gitea',
...baseOptions,
...other,
});

Expand All @@ -167,6 +169,20 @@ export async function createMonaco(textarea: HTMLTextAreaElement, filename: stri
textarea.dispatchEvent(new Event('change')); // seems to be needed for jquery-are-you-sure
});

const form = textarea.closest('form');
form?.querySelector<HTMLSelectElement>('.js-indent-style-select')?.addEventListener('change', (e) => {
Comment thread
silverwind marked this conversation as resolved.
Outdated
const insertSpaces = (e.target as HTMLSelectElement).value === 'space';
editor.updateOptions({insertSpaces, useTabStops: !insertSpaces});
});
form?.querySelector<HTMLSelectElement>('.js-indent-size-select')?.addEventListener('change', (e) => {
Comment thread
silverwind marked this conversation as resolved.
Outdated
const tabSize = Number((e.target as HTMLSelectElement).value);
editor.updateOptions({tabSize});
});
form?.querySelector<HTMLSelectElement>('.js-line-wrap-select')?.addEventListener('change', (e) => {
const wordWrap = (e.target as HTMLSelectElement).value as IEditorOptions['wordWrap'];
editor.updateOptions({wordWrap});
});

exportEditor(editor);

const loading = document.querySelector('.editor-loading');
Expand Down Expand Up @@ -208,7 +224,6 @@ export async function createCodeEditor(textarea: HTMLTextAreaElement, filenameIn
togglePreviewDisplay(isPreviewable);

const {monaco, editor} = await createMonaco(textarea, filename, {
...baseOptions,
...getFileBasedOptions(filenameInput.value, lineWrapExts),
...getEditorConfigOptions(editorConfig),
});
Expand All @@ -229,12 +244,9 @@ function getEditorConfigOptions(ec: EditorConfig | null): MonacoOpts {
const opts: MonacoOpts = {};
opts.detectIndentation = !('indent_style' in ec) || !('indent_size' in ec);

if ('indent_size' in ec) {
opts.indentSize = Number(ec.indent_size);
}
if ('tab_width' in ec) {
opts.tabSize = Number(ec.tab_width) || Number(ec.indent_size);
}
// we use indentSize='tabSize' so these numbers always match
Comment thread
silverwind marked this conversation as resolved.
Outdated
opts.tabSize = Number(ec.tab_width) || Number(ec.indent_size) || 4;

if ('max_line_length' in ec) {
opts.rulers = [Number(ec.max_line_length)];
}
Expand Down