Download CommonLua/X/XTextEditorCodePlugin.lua from sirnii/myspace: direct link, hf CLI and curl.
- Browser
- Download file 14.5 kB
-
https://huggingface.co/spaces/sirnii/myspace/resolve/main/CommonLua/X/XTextEditorCodePlugin.lua
- Command line
-
hf download hf://spaces/sirnii/myspace/CommonLua/X/XTextEditorCodePlugin.lua
-
curl -L -o XTextEditorCodePlugin.lua https://huggingface.co/spaces/sirnii/myspace/resolve/main/CommonLua/X/XTextEditorCodePlugin.lua
14.5 kB
| local MeasureText = UIL.MeasureText | |
| local MeasureToCharStart = UIL.MeasureToCharStart | |
| local StretchText = UIL.StretchText | |
| local DrawSolidRect = UIL.DrawSolidRect | |
| DefineClass.XCodeEditorPlugin = { | |
| __parents = { "XTextEditorPlugin" }, | |
| SelectionColor = RGB(78, 140, 187), | |
| KeywordColor = RGB(75, 105, 198), | |
| CommentColor = RGB(0, 128, 0), | |
| QuoteColor = RGB(190, 150, 150), | |
| NumberColor = RGB(255, 141, 141), | |
| ErrorColor = RGB(255, 0, 0), | |
| SingleInstance = false, | |
| highlighted_text = false, | |
| highlight_positions = {}, | |
| error_line = false, | |
| error_text = false, | |
| error_needs_drawn = false, -- runtime state while drawing | |
| dim_text = false, | |
| color_string = false, | |
| comment_out = false, | |
| comment_close_string = false, | |
| } | |
| function XCodeEditorPlugin:SetError(line, text) | |
| self.error_line = line | |
| self.error_text = text | |
| end | |
| function XCodeEditorPlugin:VerticalSpaceAfterLine(edit, line) | |
| if self.error_line and line == Min(self.error_line, #edit.lines) then | |
| return edit.font_height | |
| end | |
| end | |
| function XCodeEditorPlugin:OnBeginDraw(edit) | |
| self.color_string = false | |
| self.comment_out = false | |
| self.comment_close_string = false | |
| self.error_needs_drawn = self.error_line ~= false | |
| end | |
| function XCodeEditorPlugin:OnDrawText(edit, line_idx, text, target_box, font, text_color) | |
| if self.error_line and line_idx == Min(self.error_line, #edit.lines) then | |
| local width, height = MeasureText(self.error_text, font) | |
| local pt = target_box:min() + point(0, edit:GetFontHeight()) | |
| if pt:y() + height <= edit.content_box:maxy() then | |
| StretchText(self.error_text, sizebox(pt + point(5, 0), point(width, height)), font, self.ErrorColor) | |
| local sx = target_box:sizex() | |
| DrawSolidRect(sizebox(pt, point(sx, 1)), self.ErrorColor) | |
| DrawSolidRect(sizebox(pt + point( 0, -3), point(1, 3)), self.ErrorColor) | |
| DrawSolidRect(sizebox(pt + point(sx - 1, -3), point(1, 3)), self.ErrorColor) | |
| self.error_needs_drawn = false | |
| end | |
| end | |
| end | |
| function XCodeEditorPlugin:OnEndDraw(edit) | |
| -- the error is outside of the viewport | |
| if self.error_needs_drawn then | |
| local win_box = edit.box | |
| local height = edit:GetFontHeight() | |
| local start_idx = edit:LineIdxFromScreenY(win_box:miny()) | |
| local target_box | |
| local padding_x1, padding_y1, padding_x2, padding_y2 = ScaleXY(edit.scale, edit.Padding:xyxy()) | |
| local size = point(win_box:sizex() + padding_x1 + padding_x2, height) | |
| if self.error_line < start_idx then | |
| target_box = sizebox(win_box:min() - point(padding_x1, padding_y1), size + point(0, padding_y1)) | |
| else | |
| target_box = sizebox(point(win_box:minx() - padding_x1, win_box:maxy() - height), size + point(0, padding_y2)) | |
| end | |
| -- force fit the box with the error message in the UI viewport | |
| local win = edit | |
| while win do | |
| target_box = FitBoxInBox(target_box, win.box) | |
| win = win.parent | |
| end | |
| local color = GetDarkModeSetting() and const.clrWhite or const.clrBlack | |
| DrawSolidRect(target_box, InterpolateRGB(edit.Background, color, 1, 10)) | |
| -- draw error | |
| local font = edit:GetFontId() | |
| local width, height = MeasureText(self.error_text, font) | |
| StretchText(self.error_text, sizebox(target_box:min() + point(5, 0), point(width, height)), font, self.ErrorColor) | |
| self.error_needs_drawn = false | |
| end | |
| end | |
| function XCodeEditorPlugin:ModColor(color) | |
| if self.dim_text then | |
| local r, g, b = GetRGB(color) | |
| local apply_modifier = GetDarkModeSetting() and | |
| function(v) return v * 75 / 100 end or | |
| function(v) return v + (255 - v) * 40 / 100 end | |
| return RGB(apply_modifier(r), apply_modifier(g), apply_modifier(b)) | |
| end | |
| return color | |
| end | |
| function XCodeEditorPlugin:SetDimText(dim) | |
| self.dim_text = dim | |
| end | |
| local var_or_func_pattern = "([%a%d_]*)([^%a%d_]*)" | |
| local lua_keywords = { | |
| ["goto"] = true, | |
| ["and"] = true, | |
| ["break"] = true, | |
| ["do"] = true, | |
| ["else"] = true, | |
| ["elseif"] = true, | |
| ["end"] = true, | |
| ["false"] = true, | |
| ["for"] = true, | |
| ["function"] = true, | |
| ["if"] = true, | |
| ["in"] = true, | |
| ["local"] = true, | |
| ["nil"] = true, | |
| ["not"] = true, | |
| ["or"] = true, | |
| ["repeat"] = true, | |
| ["return"] = true, | |
| ["then"] = true, | |
| ["true"] = true, | |
| ["until"] = true, | |
| ["while"] = true, | |
| } | |
| function XCodeEditorPlugin:PreProcessCharacter(other, character, comment_position, current, num_bytes) | |
| if not self.comment_out then | |
| if character == '"' then | |
| if not self.color_string then self.color_string = '"' | |
| elseif self.color_string == '"' then self.color_string = false end | |
| elseif character == "'" then | |
| if not self.color_string then self.color_string = "'" | |
| elseif self.color_string == "'" then self.color_string = false end | |
| end | |
| if comment_position and num_bytes == comment_position then | |
| local pos = comment_position + 2 | |
| if other:sub(pos, pos) == "[" then | |
| pos = pos + 1 | |
| local gap = "" | |
| while other:sub(pos, pos) == "=" do | |
| gap = gap .. "=" | |
| pos = pos + 1 | |
| end | |
| if other:sub(pos, pos) == "[" then | |
| self.comment_close_string = string.format("]%s]", gap) | |
| end | |
| end | |
| self.comment_out = true | |
| end | |
| end | |
| end | |
| function XCodeEditorPlugin:PostProcessCharacter(other, character, comment_position, current, num_bytes) | |
| local comment_close = self.comment_close_string | |
| if character == "]" and comment_close and current >= #comment_close and other:sub(current - #comment_close + 1):starts_with(comment_close) then | |
| comment_position = other:find_lower("--", current) | |
| self.comment_close_string = false | |
| self.comment_out = false | |
| end | |
| end | |
| function XCodeEditorPlugin:OnDrawLineOutsideView(edit, line_idx, text, above_view) | |
| if above_view then | |
| for word, other in text:gmatch(var_or_func_pattern) do | |
| if other and other ~= " " then | |
| local comment_position = other:find_lower("--") | |
| local current, num_bytes = 1, 0 | |
| local character = utf8.sub(other, current, current) | |
| while #character > 0 do | |
| num_bytes = num_bytes + #character | |
| self:PreProcessCharacter(other, character, comment_position, current, num_bytes) | |
| self:PostProcessCharacter(other, character, comment_position, current, num_bytes) | |
| current = current + 1 | |
| character = utf8.sub(other, current, current) | |
| end | |
| end | |
| end | |
| end | |
| end | |
| function XCodeEditorPlugin:OnBeforeDrawText(edit, line_idx, text, target_box, font, text_color) | |
| for idx, pos in ipairs(self.highlight_positions[line_idx]) do | |
| local x1 = MeasureToCharStart(text, font, pos + 1) | |
| local x2 = MeasureToCharStart(text, font, pos + utf8.len(self.highlighted_text) + 1) | |
| DrawSolidRect(box(target_box:minx() + x1, target_box:miny(), target_box:minx() + x2, target_box:maxy()), self.SelectionColor) | |
| end | |
| if not self.comment_close_string then | |
| self.comment_out = false | |
| end | |
| local pos = 0 | |
| local text_start = target_box:minx() | |
| for word, other in text:gmatch(var_or_func_pattern) do | |
| local len_word = utf8.len(word) | |
| local len_other = utf8.len(other) | |
| local x1 = MeasureToCharStart(text, font, pos + 1) | |
| local x2 = MeasureToCharStart(text, font, pos + len_word + 1) | |
| local word_box = box(text_start + x1, target_box:miny(), text_start + x2, target_box:maxy()) | |
| local new_text_color | |
| if not self.comment_out and not self.color_string then | |
| if other and other:starts_with('(') and word ~= "function" then | |
| new_text_color = RGB(160, 170, 150) | |
| elseif tonumber(word) ~= nil then | |
| new_text_color = self.NumberColor | |
| else | |
| new_text_color = lua_keywords[word] and self.KeywordColor or text_color | |
| end | |
| end | |
| StretchText(word, word_box, font, self:ModColor(self.comment_out and self.CommentColor or self.color_string and self.QuoteColor or new_text_color)) | |
| if other and other ~= " " then | |
| local comment_position = other:find_lower("--") | |
| local current, num_bytes = 1, 0 | |
| local character = utf8.sub(other, current, current) | |
| while #character > 0 do | |
| num_bytes = num_bytes + #character | |
| self:PreProcessCharacter(other, character, comment_position, current, num_bytes) | |
| local x3 = MeasureToCharStart(text, font, pos + len_word + current) | |
| local other_box = box(text_start + x3, target_box:miny(), text_start + x3, target_box:maxy()) | |
| if character == "." and tonumber(word) ~= nil then | |
| StretchText(character, other_box, font, self:ModColor(self.NumberColor)) | |
| else | |
| StretchText(character, other_box, font, self:ModColor(self.comment_out and self.CommentColor or self.color_string and self.QuoteColor or (character == '"' or character == "'") and self.QuoteColor or text_color)) | |
| end | |
| self:PostProcessCharacter(other, character, comment_position, current, num_bytes) | |
| current = current + 1 | |
| character = utf8.sub(other, current, current) | |
| end | |
| end | |
| pos = pos + len_word + len_other | |
| end | |
| return true | |
| end | |
| function XCodeEditorPlugin:OnWordSelection(edit, word_to_mark) | |
| self.highlighted_text = word_to_mark | |
| self.highlight_positions = {} | |
| for idx, line in ipairs(edit.lines) do | |
| self.highlight_positions[idx] = {} | |
| local pos = 0 | |
| for word, other in line:gmatch(var_or_func_pattern) do | |
| local len = utf8.len(word) | |
| if word == word_to_mark then | |
| local current_pos = #(self.highlight_positions[idx] or empty_table) | |
| self.highlight_positions[idx][current_pos + 1] = pos | |
| end | |
| pos = pos + len + utf8.len(other) | |
| end | |
| end | |
| end | |
| function XCodeEditorPlugin:OnSelectHighlight(edit, search_text, ignore_case) | |
| self.highlighted_text = search_text | |
| self.highlight_positions = {} | |
| for idx, line in ipairs(edit.lines) do | |
| self.highlight_positions[idx] = {} | |
| local pos, len = 1, utf8.len(line) | |
| local text = ignore_case and line:lower() or line | |
| local end_pos | |
| while pos <= len do | |
| pos, end_pos = string.find(text, search_text, pos, true) | |
| if not pos then break end | |
| local current_pos = #(self.highlight_positions[idx] or empty_table) | |
| self.highlight_positions[idx][current_pos + 1] = pos - 1 | |
| pos = end_pos + 1 | |
| end | |
| end | |
| end | |
| function XCodeEditorPlugin:ClearHighlights() | |
| self.highlight_positions = {} | |
| self.highlighted_text = false | |
| end | |
| XCodeEditorPlugin.OnTextChanged = XCodeEditorPlugin.ClearHighlights | |
| XCodeEditorPlugin.OnKillFocus = XCodeEditorPlugin.ClearHighlights | |
| function XCodeEditorPlugin:OnShortcut(edit, shortcut, source, ...) | |
| local start_line, start_char, end_line, end_char = edit:GetSelectionSortedBounds() | |
| if start_line and end_line then | |
| if shortcut == "Tab" and edit.AllowTabs then | |
| edit:SetCursor(start_line, 0, false) | |
| if end_char == 0 then | |
| end_line = end_line - 1 | |
| end | |
| edit:SetCursor(end_line, utf8.len(edit.lines[end_line]), "select", "include last endline") | |
| local new_text = "" | |
| for i = start_line, end_line do | |
| new_text = new_text.."\t"..edit.lines[i] | |
| end | |
| edit:EditOperation(new_text, nil, nil, "keep_selection") | |
| return "break" | |
| elseif shortcut == "Shift-Tab" then | |
| edit:SetCursor(start_line, 0, false) | |
| if end_char == 0 then | |
| end_line = end_line - 1 | |
| end | |
| edit:SetCursor(end_line, utf8.len(edit.lines[end_line]), "select", "include last endline") | |
| local new_text = "" | |
| local has_changes = false | |
| for i = start_line, end_line do | |
| if string.sub(edit.lines[i], 1, 1) == "\t" then | |
| new_text = new_text..string.sub(edit.lines[i], 2, edit.lines[i].length) | |
| has_changes = true | |
| else | |
| new_text = new_text..edit.lines[i] | |
| end | |
| end | |
| if has_changes then | |
| edit:EditOperation(new_text, nil, nil, "keep_selection") | |
| return "break" | |
| end | |
| end | |
| end | |
| if shortcut == "Ctrl-F" and edit.Multiline then | |
| self:ActivateSearch(edit) | |
| return "break" | |
| elseif shortcut == "Ctrl-L" then | |
| local line = edit.cursor_line | |
| edit:SetCursor(line, 0) | |
| edit:SetCursor(line, utf8.len(edit.lines[line]), "select", "include last endline") | |
| edit:EditOperation() | |
| return "break" | |
| elseif shortcut == "Ctrl-Shift-Up" then | |
| local line1, char1, line2, char2 = edit:GetSelectionSortedBounds() | |
| local line = line1 or edit.cursor_line | |
| if line > 1 then | |
| if line1 then -- move selection | |
| if char2 ~= 0 then line2 = line2 + 1 end | |
| edit:ExchangeLines(line1 - 1, line1, line2, line1) | |
| edit:SetCursor(line1 - 1, 0) | |
| edit:SetCursor(line2 - 1, 0, "select") | |
| else -- move current line | |
| edit:ExchangeLines(line - 1, line, line + 1, line) | |
| end | |
| end | |
| return "break" | |
| elseif shortcut == "Ctrl-Shift-Down" then | |
| local line1, char1, line2, char2 = edit:GetSelectionSortedBounds() | |
| local line = line2 or edit.cursor_line | |
| if line < #edit.lines or char2 == 0 then | |
| if line2 then -- move selection | |
| if char2 ~= 0 then line2 = line2 + 1 end | |
| edit:ExchangeLines(line1, line2, line2 + 1, line1) | |
| edit:SetCursor(line1 + 1, 0) | |
| edit:SetCursor(line2 + 1, 0, "select") | |
| else -- move current line | |
| edit:ExchangeLines(line, line + 1, line + 2, line) | |
| end | |
| end | |
| return "break" | |
| elseif shortcut == "Escape" then | |
| self:ClearHighlights() | |
| edit:ClearSelection() | |
| return "break" | |
| end | |
| end | |
| function XCodeEditorPlugin:ActivateSearch(edit) | |
| local search_box = XEdit:new({ | |
| Margins = box(5, 5, 5, 5), | |
| MinWidth = 150, | |
| MaxWidth = 150, | |
| HAlign = "right", | |
| VAlign = "top", | |
| BorderWidth = 1, | |
| AutoSelectAll = true, | |
| -- close with Escape and Enter | |
| OnKbdChar = function(self, char, ...) | |
| if char == "\r" then | |
| self:SetFocus(false) | |
| return "break" | |
| end | |
| return XEdit.OnKbdChar(self, char, ...) | |
| end, | |
| OnShortcut = function(self, shortcut, ...) | |
| if shortcut == "Escape" then | |
| self:SetFocus(false) | |
| return "break" | |
| end | |
| return XEdit.OnShortcut(self, shortcut, ...) | |
| end, | |
| OnKillFocus = function(self) | |
| if self.window_state ~= "destroying" then | |
| self:Close() | |
| end | |
| end, | |
| -- search and highlight | |
| OnTextChanged = function(search_box) | |
| local text = search_box:GetText() | |
| if text ~= "" then | |
| edit:SelectFirstOccurence(text, "ignore_case") | |
| else | |
| self:ClearHighlights() | |
| end | |
| end, | |
| -- hack(s) to leave the search box in the top-right corner regardless of scroll | |
| parent_offset = false, | |
| OnLayoutComplete = function(self) | |
| self.parent_offset = self.box:min() + point(edit.OffsetX, edit.OffsetY) - edit.box:min() | |
| self:SetBox() | |
| end, | |
| SetBox = function(self, ...) | |
| if self.parent_offset then | |
| local x, y = (edit.box:min() + self.parent_offset):xy() | |
| local w, h = self.box:sizexyz() | |
| XEdit.SetBox(self, x, y, w, h) | |
| else | |
| XEdit.SetBox(self, ...) | |
| end | |
| end, | |
| SetLayoutSpace = function(self, ...) | |
| self.parent_offset = false | |
| return XEdit.SetLayoutSpace(self, ...) | |
| end | |
| }, edit) | |
| local text = edit:GetSelectedText() | |
| local idx = text.find(text, "[\r\n]") | |
| if idx then | |
| text = text:sub(1, idx - 1) | |
| end | |
| search_box:Open() | |
| search_box:SetText(text) | |
| search_box:SetFocus(true) | |
| Msg("XWindowRecreated", search_box) -- dark mode stuff | |
| end | |