diff --git a/modules/templates/helper.go b/modules/templates/helper.go index a2cc166de9755..a63e4dfa0ca5b 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -9,6 +9,7 @@ import ( "html" "html/template" "net/url" + "path" "strings" "time" @@ -49,6 +50,7 @@ func NewFuncMap() template.FuncMap { "PathEscape": url.PathEscape, "PathEscapeSegments": util.PathEscapeSegments, + "PathExt": path.Ext, // utils "StringUtils": NewStringUtils, diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 85d2c71ec7181..d80be1a244c53 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -163,6 +163,14 @@ 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... type_tooltip = Search type diff --git a/routers/web/repo/cherry_pick.go b/routers/web/repo/cherry_pick.go index 33d941c9d886c..e2f0271d703eb 100644 --- a/routers/web/repo/cherry_pick.go +++ b/routers/web/repo/cherry_pick.go @@ -56,7 +56,7 @@ func CherryPick(ctx *context.Context) { } ctx.Data["new_branch_name"] = GetUniquePatchBranchName(ctx) ctx.Data["last_commit"] = ctx.Repo.CommitID - ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",") + ctx.Data["LineWrapExtensions"] = setting.Repository.Editor.LineWrapExtensions ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL() ctx.HTML(200, tplCherryPick) @@ -84,7 +84,7 @@ func CherryPickPost(ctx *context.Context) { ctx.Data["commit_choice"] = form.CommitChoice ctx.Data["new_branch_name"] = form.NewBranchName ctx.Data["last_commit"] = ctx.Repo.CommitID - ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",") + ctx.Data["LineWrapExtensions"] = setting.Repository.Editor.LineWrapExtensions ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL() if ctx.HasError() { diff --git a/routers/web/repo/editor.go b/routers/web/repo/editor.go index 85f407ab8d795..420f50ee43cc0 100644 --- a/routers/web/repo/editor.go +++ b/routers/web/repo/editor.go @@ -28,6 +28,8 @@ import ( "code.gitea.io/gitea/services/context/upload" "code.gitea.io/gitea/services/forms" files_service "code.gitea.io/gitea/services/repository/files" + + "github.com/editorconfig/editorconfig-core-go/v2" ) const ( @@ -190,9 +192,14 @@ func editFile(ctx *context.Context, isNewFile bool) { } ctx.Data["new_branch_name"] = GetUniquePatchBranchName(ctx) ctx.Data["last_commit"] = ctx.Repo.CommitID - ctx.Data["PreviewableExtensions"] = strings.Join(markup.PreviewableExtensions(), ",") - ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",") - ctx.Data["EditorconfigJson"] = GetEditorConfig(ctx, treePath) + ctx.Data["PreviewableExtensions"] = markup.PreviewableExtensions() + ctx.Data["LineWrapExtensions"] = setting.Repository.Editor.LineWrapExtensions + + ecDef := GetEditorConfig(ctx, treePath) + ecBytes, _ := json.Marshal(ecDef) + ctx.Data["EditorconfigJson"] = string(ecBytes) + ctx.Data["EditorconfigIndentStyle"] = ecDef.IndentStyle + ctx.Data["EditorconfigIndentSize"] = ecDef.IndentSize ctx.Data["IsEditingFileOnly"] = ctx.FormString("return_uri") != "" ctx.Data["ReturnURI"] = ctx.FormString("return_uri") @@ -200,17 +207,17 @@ func editFile(ctx *context.Context, isNewFile bool) { ctx.HTML(http.StatusOK, tplEditFile) } -// GetEditorConfig returns a editorconfig JSON string for given treePath or "null" -func GetEditorConfig(ctx *context.Context, treePath string) string { +// GetEditorConfig returns the editorconfig definition for given treePath or nil +func GetEditorConfig(ctx *context.Context, treePath string) *editorconfig.Definition { ec, _, err := ctx.Repo.GetEditorconfig() if err == nil { def, err := ec.GetDefinitionForFilename(treePath) if err == nil { - jsonStr, _ := json.Marshal(def) - return string(jsonStr) + return def } + return nil } - return "null" + return nil } // EditFile render edit file page @@ -244,8 +251,8 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b ctx.Data["commit_choice"] = form.CommitChoice ctx.Data["new_branch_name"] = form.NewBranchName ctx.Data["last_commit"] = ctx.Repo.CommitID - 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["EditorconfigJson"] = GetEditorConfig(ctx, form.TreePath) if ctx.HasError() { diff --git a/routers/web/repo/patch.go b/routers/web/repo/patch.go index 4d47a705d6e94..520451ed97427 100644 --- a/routers/web/repo/patch.go +++ b/routers/web/repo/patch.go @@ -36,7 +36,7 @@ func NewDiffPatch(ctx *context.Context) { } ctx.Data["new_branch_name"] = GetUniquePatchBranchName(ctx) ctx.Data["last_commit"] = ctx.Repo.CommitID - ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",") + ctx.Data["LineWrapExtensions"] = setting.Repository.Editor.LineWrapExtensions ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL() ctx.HTML(200, tplPatchFile) @@ -59,7 +59,7 @@ func NewDiffPatchPost(ctx *context.Context) { ctx.Data["commit_choice"] = form.CommitChoice ctx.Data["new_branch_name"] = form.NewBranchName ctx.Data["last_commit"] = ctx.Repo.CommitID - ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",") + ctx.Data["LineWrapExtensions"] = setting.Repository.Editor.LineWrapExtensions if ctx.HasError() { ctx.HTML(200, tplPatchFile) diff --git a/templates/repo/editor/edit.tmpl b/templates/repo/editor/edit.tmpl index 577a2be9ad38e..bafe77b4adbbc 100644 --- a/templates/repo/editor/edit.tmpl +++ b/templates/repo/editor/edit.tmpl @@ -30,19 +30,24 @@
-
+ data-previewable-extensions="{{StringUtils.Join .PreviewableExtensions ","}}" + data-line-wrap-extensions="{{StringUtils.Join .LineWrapExtensions ","}}">{{.FileContent}}
diff --git a/templates/repo/editor/options.tmpl b/templates/repo/editor/options.tmpl new file mode 100644 index 0000000000000..6a48a9bdfc164 --- /dev/null +++ b/templates/repo/editor/options.tmpl @@ -0,0 +1,20 @@ + + +{{$mode := (Iif (SliceUtils.Contains .LineWrapExtensions (PathExt .TreePath)) "on" "off")}} + diff --git a/templates/repo/editor/patch.tmpl b/templates/repo/editor/patch.tmpl index 33a7c2a89d8c7..ce17d1b714e58 100644 --- a/templates/repo/editor/patch.tmpl +++ b/templates/repo/editor/patch.tmpl @@ -28,7 +28,7 @@
diff --git a/web_src/js/features/codeeditor.ts b/web_src/js/features/codeeditor.ts index af9830a4db232..f217bab8af448 100644 --- a/web_src/js/features/codeeditor.ts +++ b/web_src/js/features/codeeditor.ts @@ -38,6 +38,7 @@ const baseOptions: MonacoOpts = { scrollbar: {horizontalScrollbarSize: 6, verticalScrollbarSize: 6}, scrollBeyondLastLine: false, automaticLayout: true, + indentSize: 'tabSize', }; function getEditorconfig(input: HTMLInputElement): EditorConfig | null { @@ -163,6 +164,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('.js-indent-style-select').addEventListener('change', (e) => { + const insertSpaces = (e.target as HTMLSelectElement).value === 'space'; + editor.updateOptions({insertSpaces, useTabStops: !insertSpaces}); + }); + form.querySelector('.js-indent-size-select').addEventListener('change', (e) => { + const tabSize = Number((e.target as HTMLSelectElement).value); + editor.updateOptions({tabSize}); + }); + form.querySelector('.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'); @@ -225,12 +240,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 + opts.tabSize = Number(ec.indent_size) || Number(ec.tab_width) || 4; + if ('max_line_length' in ec) { opts.rulers = [Number(ec.max_line_length)]; }