-- Wee Euphoria Editor -- -- Copyright (c) 1998-2016 Pete Eberlein -- -- Permission is hereby granted, free of charge, to any person obtaining a copy -- of this software and associated documentation files (the "Software"), to deal -- in the Software without restriction, including without limitation the rights -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -- copies of the Software, and to permit persons to whom the Software is -- furnished to do so, subject to the following conditions: -- -- The above copyright notice and this permission notice shall be included in -- all copies or substantial portions of the Software. -- -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -- THE SOFTWARE. global constant window_title = "Wee Euphoria Editor", author = "Pete Eberlein ", version = "0.48", changelog = ` Version 0.48 - Bug fix: hang during replace all (thanks Andreas) - Auto-detect EOL characters when opening Version 0.47 - Bug fix: parser support for "loop until" statement (thanks dcuny) Version 0.46 - Bug fix: parser should allow case statements inside ifdef - Bug fix: crash in parser for non-existent namespace include (thanks Andreas) Version 0.45 - Help->Release Notes to view changelog - Bug fix: use EUDIR when locating help index - Bug fix: 'not' in ifdefs was flagged as an error - Bug fix: set default indentation for new tabs - Bug fix: clear insert chars after auto-indent Version 0.44 - ARM scintilla library (tested on Raspberry Pi 2) Version 0.43 - Bug fixed: open shell in current directory (UNIX) Version 0.42 - Run commands in a temporary script (Linux/OSX) Version 0.41 - Improved parsing speed Version 0.40 - Bug fixed: crashes on auto-completion and error parsing (thanks dcuny) Version 0.39 - Error indicators and mouse dwell message - Add namespaces to auto-complete (with 'n' icon) - Add EUDIR support - Bug fixed: mousewheel tab change on Windows Version 0.38 - Adding run options: background and test run (thanks KDR) - Bug fixed: crash on fail to open file during parsing (thanks sdpringle) - Bug fixed: crash on error while saving (thanks sdpringle) Version 0.37 - Filename first in window title - Tabs now have mouseover tooltip with full path (win32: only the active tab) - Bug fixed: crash when interpreter list is empty (thanks Irv) Version 0.36 - Windows icon built-in - Run Set Interpreter now lets you override the interpreter used Version 0.35 - Allow quoted include filename (thanks dcuny) Version 0.34 - Bug fix: crash on parsing "-type ..." (thanks dcuny) Version 0.33 - select next/prev tab using mouse wheel over tabs - close tab with middle mouse button - select next tab (Ctrl+Tab) and previous tab (Shift+Ctrl+Tab) Version 0.32 - 64-bit supported on Windows (joining Linux/OSX) - Bug fix: no message when opening saved tab whose file no longer exists Version 0.31 - Bug fix: cursor color changes based on background (thanks Andreas) - Bug fix: string color not loaded from config file (thanks Andreas) - Bug fix: bold options reversed for String and Number - Bug fix: incorrect tab or new tab selected when reopening tabs Version 0.30 - Options to disable auto-completing statements or braces - Auto-completion and syntax coloring disabled for non-Euphoria files - Bold options for syntax coloring - Bug fix: crash when typing character literals with calltip active - Bug fix: GTK version: close tab didn't prompt to save modified files Version 0.29 - Bug fix: crash + lose wee_conf when closing on Windows with reopen_tabs=1 Version 0.28 - Run menu choose which intepreter to use - Run will choose interpreter location (based on eu.cfg include path) - Option to reopen tabs next time Version 0.27 - autocompletion list shows icons for each type of declaration Version 0.26 - line wrap option - Bug fix: preserve selection during comment/uncomment lines Version 0.25 - fill in Find dialog with current selection - allow search backward in Find and Replace dialogs - quick search Find Next (Ctrl+G) Find Previous (Shift+Ctrl+G) - comment/uncomment selected lines (Ctrl+M) - go back (Esc) after doing View Declaration or Goto Error - if there is a selection, it will be deselected first Version 0.24 - drag'n'drop files Version 0.23 - Bug fix: run/bind/shroud/translate didn't like spaces in the path Version 0.22 - Bug fix: better at locating scintilla library when bound/compiled Version 0.21 - dialog to change syntax highlighting colors - popup menu on tab control Version 0.20 - run with arguments (Shift+F5) - move options to separate menu - option to sort subroutines in View Subroutines dialog - added Bind, Shroud and Translate to Run menu Version 0.19 - Linux/Mac (using EuGTK) - context-sensitive help (hit F1 on a word) Version 0.18 - store syntax highlighting colors in wee.conf Version 0.17 - updated: indent/unindent selection with tab/shift-tab Version 0.16 - updated: ex.err dialog with buttons: - and listbox with call stack or multiple undeclared references - Bug fix: weirdness with deletion autoinserted closing characters - Bug fix: calltip popup displaying incorrect highlight after first time - Bug fix: EOL mode on Linux/OSX - Bug fix: completions are now sorted - Bug fix: switch/case syntax highlight and autocomplete were missing - Bug fix: shift-ctrl-Z does redo Version 0.15 - hardcoded list of builtins and arguments in get_subroutine_arguents() Version 0.13 - auto-complete with include puts it near top of file - auto insert closing characters for ( { [ " ' - typing '(' after a subroutine will popup hints - Bug fix: autocomplete avoids activating inside comments and strings Version 0.12 - auto expansions for "if" "while" "for" and subroutines (thanks dcuny) - auto indent newlines - type ':' to autocomplete a namespace Version 0.11 - view completions will search std includes - Search->Find will remember last phrase - fixed tab names not updating when save-as - fixed "too many open files" error, wasn't closing files in view_error function - fixed parsing ifdefs - fixed crash after changing font (thanks euphoric) - reloading a file that is modified externally loads into wrong tab (thanks dcuny) Version 0.10 - Bug fix: ViewCompletions on whitespace crash Version 0.09 - Scintilla edit control (using Lua lexer :/) Version 0.08 - watch tabs for changes to files and ex.err - goto declaration (Ctrl+F2) and view completions (Ctrl+Space) Version 0.07 - tabs (can be selected using Alt+1..9) Older Versions - syntax coloring! - Bug fix: save mode changed to "wb" (Thanks Lucius) - replace all - goto error - hotkeys - cursor position - recent files list - saving window pos+size and recent files to "wee_conf.txt" - subroutines list dialog (F2) - choose font dialog (we suggest "Consolas" 11pt) - bugs fixed: - ViewDeclaration wrong cursor pos - audible ding when pressing Alt+1..9 - parser updates cache for modified include files - files are now opened in binary mode since scintilla can handle any line endings ` -- todo: -- bug with reloading files, first few characters get garbled -- expanding "for" should then tab between "=", "to", and "do" -- as it is now, its really annoying (but better with overtyping) -- fix brace highlighting in comments and strings -- put arguments after each subroutine completion (maybe) -- investigate Mike Duffy's scintilla Euphoria Lexer -- multicolored brackets and parens -- multiline comments and strings -- code-aware identifier rename -- add function to parser to get all instances of identifier in scope -- use multi-select to replace all instances in one shot -- limited to a single file (too difficult otherwise) -- macro recording (SCI_STARTRECORD/SCI_STOPRECORD/SCN_MACRORECORD) -- disable menu items when the action has no effect -- disable cut/copy when no selection, or paste with empty clipboard -- disable undo/redo when nothing to do -- disable go back when nowhere to go back to -- old calltips sometimes pop up after a calltip elsewhere is closed -- probably due to nesting calltips -- will crash by putting a function after sci_notify() (Linux/OSX 64-bit) -- triggered by using Alt+1-9 to change tabs -- Euphoria bug? file a ticket -- help->check for updates -- updating dll would require updater.ex -- option to bind/shround/recompile -- edit->tidy, to clean up indentation/formatting --without warning include parser.e include scintilla.e include std/get.e include std/filesys.e include std/sequence.e include std/text.e include std/machine.e include std/error.e include std/io.e include weeicon.e -- The ui includes are circular includes: -- wee.exw -> ui_win.e -> wee.exw -- The ui include is responsible for calling wee_init() and startup. ifdef WINDOWS then include ui_win.e elsedef include ui_gtk.e end ifdef -- all variables must be initialized in wee_init() due to circular include global constant max_recent_files = 5 -- these are configuration settings saved to wee_conf global atom x_pos, y_pos, x_size, y_size global sequence font_name, recent_files, terminal_program -- for GTK, runs programs in a terminal global integer font_height, line_numbers, -- boolean sorted_subs, -- boolean line_wrap, -- boolean reopen_tabs, -- boolean tab_width, -- usually 8 indentation_guides, -- boolean caret_width, -- in pixels complete_statements, -- when typing "if " -> "if | then\n \nend if" complete_braces, -- when typing '(' or '[' or '{' or ''' or '"' auto_namespace, -- when typing ':' auto_complete, -- when typing two word chars or backspace auto_arguments, -- when typing '(' after an identifier auto_indent, -- when creating a new line auto_indicator, -- underline words that are not in scope run_testrun, -- run exe after bind/shroud/translate run_background, -- for GTK, run programs/terminal in background run_waitkey, -- wait for keypress after running in a terminal keyword_color, builtin_color, string_color, comment_color, number_color, normal_color, background_color, linenumber_color, bracelight_color, bold_flags sequence tabs_to_open -- {{"filename", pos, topline},...} sequence file_types -- {{{"ext1","ext2",...}, SCLEX_x, {index, "keywords ...", ...}, {"match", "expand",...} global sequence file_name, run_file_name, ex_err_name global sequence find_phrase, replace_phrase, interpreter, wee_path -- local variables sequence tab_hedits, tab_file_names, tab_timestamps, recent_pos, tab_arguments, tab_pos_stack integer current_tab, modified, initial_tab atom hedit atom ex_err_timestamp integer expand_line, last_deleted_char sequence insert_chars sequence calltip_args -- {{start,end},...} integer calltip_pos sequence calltip_stack -- {calltip_pos, calltip_args...} sequence calltip_text -- "routine_name(type1 arg1, ...)" integer calltip_dwell -- flag indicating calltip from mouse dwell sequence wee_conf_filename sequence search_idx, search_dat sequence expansions sequence last_errors atom change_time -- when to perform error indicator checking -- shorthand helper function function ssm(integer m, object w=0, object l=0) return scintilla_send_message(hedit, m, w, l) end function function crash_cleanup(object x) ui_message_box_error(window_title, "Houston, Wee've had a problem.\n\n" & "The editor crashed and is closing now.\n") save_modified_tabs() if length(wee_conf_filename) then save_wee_conf(wee_conf_filename) end if return 0 end function constant -- colors of various syntax classes Black = #000000, Gray = #AAAAAA, DGray = #808080, Green = #00AA00, Yellow = #88FFFF, Magenta = #AA00AA, Cyan = #AAAA00, Red = #0000AA, Blue = #AA0000, LightBlue = #FFFFDD, White = #FFFFFF, BrightRed = #0000FF global procedure wee_init() sequence cmdline wee_conf_filename = "" crash_routine(routine_id("crash_cleanup")) recent_files = {} recent_pos = {} font_name = "" font_height = 10 line_numbers = 0 sorted_subs = 0 line_wrap = 0 file_name = "" hedit = 0 run_file_name = "" ex_err_name = "ex.err" ex_err_timestamp = get_timestamp(ex_err_name) interpreter = "" tab_hedits = {} tab_file_names = {} tab_timestamps = {} tab_arguments = {} tab_pos_stack = {} last_errors = {} modified = 0 expand_line = -1 insert_chars = "" last_deleted_char = 0 auto_namespace = 1 auto_complete = 1 complete_statements = 1 complete_braces = 1 auto_arguments = 1 auto_indent = 1 tab_width = 8 indentation_guides = 0 caret_width = 2 auto_indicator = 1 run_testrun = 0 run_background = 1 run_waitkey = 1 calltip_args = {} calltip_pos = -1 calltip_stack = {} calltip_text = "" calltip_dwell = 0 current_tab = 0 normal_color = Black background_color = White comment_color = DGray number_color = Black keyword_color = Green builtin_color = Magenta string_color = Blue bracelight_color = LightBlue linenumber_color = DGray bold_flags = #40 search_idx = {} search_dat = {} find_phrase = "" replace_phrase = "" terminal_program = "" reopen_tabs = 1 tabs_to_open = {} initial_tab = 1 cmdline = command_line() wee_path = canonical_path(dirname(cmdline[2])) if length(wee_path) and wee_path[$] = SLASH then wee_path = wee_path[1..$-1] end if file_types = { { -- extensions {"ex", "e", "exw", "ew", "exu", "eu"}, -- SCLEX_constant SCLEX_LUA, -- identifiers used with SCI_SETIDENTIFIERS {0, "procedure function type end and or xor not if then elsif else for to by do while "& "global constant include with without return exit "& -- OE4 "public export enum as namespace ifdef elsifdef elsedef "& "label entry break continue loop until routine switch case fallthru", 1, get_builtins() }, -- expansions used with auto_expand() {{"if", "", " then", "end if"}, {"elsif", "", " then"}, {"while", "", " do", "end while"}, {"for", " = to ", " do", "end for"}, {"case", "", " then"}, {"loop", "", "do", "end loop"}, {"switch", "", " do", "end switch"}, {0, "global ", "public ", "export "}, {"procedure", "()", "", "end procedure"}, {"function", "()", "", "end function"}, {"type", "()", "", "end type"} }, interpreter }, { -- extensions {"c", "h", "cpp", "hpp"}, -- SCLEX_constant SCLEX_CPP, -- identifiers used with SCI_SETKEYWORDS {0, "if else for do while switch case default "& "return break continue "& "enum typedef struct union static const ", 1, "void int char unsigned long sizeof" }, -- expansions used with auto_expand() {{"if", "()", " {", "}"}, {"} else if", "()", " { "}, {"while", "()", " {", "}"}, {"for", "(;;)", " {", "}"}, {"switch", "()", " {", "}"}, {"case", "", ":", "break;"} } }, { -- extensions {"htm", "html"}, -- SCLEX_constant SCLEX_HTML, -- identifiers used with SCI_SETKEYWORDS {}, -- expansions used with auto_expand() {} } } expansions = {} end procedure global procedure load_wee_conf(sequence wee_conf_file) integer f, eq, int_ok object l sequence key, val f = open(wee_conf_file, "r") if f = -1 then return end if wee_conf_filename = wee_conf_file l = gets(f) while sequence(l) do eq = find('=', l) if eq then key = l[1..eq-1] val = l[eq+1..length(l)-1] l = value(val) int_ok = (l[1] = 0 and integer(l[2])) if equal(key, "x_pos") and int_ok then x_pos = l[2] elsif equal(key, "y_pos") and int_ok then y_pos = l[2] elsif equal(key, "x_size") and int_ok then x_size = l[2] elsif equal(key, "y_size") and int_ok then y_size = l[2] elsif equal(key, "recent_file") then recent_files &= {val} elsif equal(key, "recent_pos") and int_ok then recent_pos &= l[2] elsif equal(key, "font_name") then font_name = val elsif equal(key, "font_height") and int_ok then font_height = l[2] if font_height < 0 then font_height = -floor((font_height*96+36)/72) end if elsif equal(key, "line_numbers") and int_ok then line_numbers = l[2] elsif equal(key, "normal_color") and int_ok then normal_color = l[2] elsif equal(key, "background_color") and int_ok then background_color = l[2] elsif equal(key, "comment_color") and int_ok then comment_color = l[2] elsif equal(key, "keyword_color") and int_ok then keyword_color = l[2] elsif equal(key, "builtin_color") and int_ok then builtin_color = l[2] elsif equal(key, "number_color") and int_ok then number_color = l[2] elsif equal(key, "string_color") and int_ok then string_color = l[2] elsif equal(key, "linenumber_color") and int_ok then linenumber_color = l[2] elsif equal(key, "bracelight_color") and int_ok then bracelight_color = l[2] elsif equal(key, "sorted_subs") and int_ok then sorted_subs = l[2] elsif equal(key, "line_wrap") and int_ok then line_wrap = l[2] elsif equal(key, "interpreter") then if not find(val, {"eui","euiw","ex","exw"}) then interpreter = val end if elsif equal(key, "reopen_tabs") and int_ok then reopen_tabs = l[2] elsif equal(key, "open_tab") then if length(l[2]) = 3 and sequence(l[2][1]) and integer(l[2][2]) and integer(l[2][3]) then tabs_to_open = append(tabs_to_open, l[2]) else tabs_to_open = append(tabs_to_open, {val, 0, 0}) end if elsif equal(key, "open_tab_pos") and int_ok then tabs_to_open[$][2] = l[2] elsif equal(key, "open_tab_line") and int_ok then tabs_to_open[$][3] = l[2] elsif equal(key, "initial_tab") and int_ok then initial_tab = l[2] elsif equal(key, "complete_statements") and int_ok then complete_statements = l[2] elsif equal(key, "complete_braces") and int_ok then complete_braces = l[2] elsif equal(key, "bold_flags") and int_ok then bold_flags = l[2] elsif equal(key, "tab_width") and int_ok then tab_width = l[2] elsif equal(key, "indentation_guides") and int_ok then indentation_guides = l[2] elsif equal(key, "caret_width") and int_ok then caret_width = l[2] elsif equal(key, "terminal_program") then terminal_program = val elsif equal(key, "run_testrun") and int_ok then run_testrun = l[2] elsif equal(key, "run_background") and int_ok then run_background = l[2] elsif equal(key, "run_waitkey") and int_ok then run_waitkey = l[2] end if end if l = gets(f) end while close(f) end procedure global procedure save_wee_conf(sequence wee_conf_file) integer f f = open(wee_conf_file, "w") printf(f, "x_pos=%d\ny_pos=%d\nx_size=%d\ny_size=%d\nline_numbers=%d\n", {x_pos, y_pos, x_size, y_size, line_numbers}) for i = 1 to length(recent_files) do puts(f, "recent_file="&recent_files[i]&"\n") printf(f, "recent_pos=%d\n", {recent_pos[i]}) end for if length(font_name) and font_height != 0 then printf(f, "font_name=%s\nfont_height=%d\n", {font_name, font_height}) end if printf(f, "normal_color=#%06x\n", {normal_color}) printf(f, "background_color=#%06x\n", {background_color}) printf(f, "keyword_color=#%06x\n", {keyword_color}) printf(f, "builtin_color=#%06x\n", {builtin_color}) printf(f, "comment_color=#%06x\n", {comment_color}) printf(f, "number_color=#%06x\n", {number_color}) printf(f, "string_color=#%06x\n", {string_color}) printf(f, "linenumber_color=#%06x\n", {linenumber_color}) printf(f, "bracelight_color=#%06x\n", {bracelight_color}) printf(f, "bold_flags=#%x\n", {bold_flags}) printf(f, "sorted_subs=%d\n", {sorted_subs}) printf(f, "line_wrap=%d\n", {line_wrap}) printf(f, "interpreter=%s\n", {interpreter}) printf(f, "reopen_tabs=%d\n", {reopen_tabs}) printf(f, "initial_tab=%d\n", {current_tab}) if reopen_tabs then for i = 1 to length(tab_file_names) do if length(tab_file_names[i]) then hedit = tab_hedits[i] printf(f, "open_tab=%s\nopen_tab_pos=%d\nopen_tab_line=%d\n", { tab_file_names[i], ssm(SCI_GETCURRENTPOS), ssm(SCI_GETFIRSTVISIBLELINE)}) end if end for end if printf(f, "complete_statements=%d\n", {complete_statements}) printf(f, "complete_braces=%d\n", {complete_braces}) printf(f, "tab_width=%d\n", {tab_width}) printf(f, "indentation_guides=%d\n", {indentation_guides}) printf(f, "caret_width=%d\n", {caret_width}) printf(f, "terminal_program=%s\n", {terminal_program}) printf(f, "run_testrun=%d\n", {run_testrun}) printf(f, "run_background=%d\n", {run_background}) printf(f, "run_waitkey=%d\n", {run_waitkey}) close(f) end procedure function sreplace(sequence text, sequence what, sequence replacement) integer i i = match(what, text) if i then return text[1..i-1] & replacement & text[i+length(what)..$] end if return text end function -- update the hedit lexer based on the file extension procedure update_lexer() sequence ext, keywords integer lexer if length(tab_file_names[current_tab]) then ext = lower(fileext(tab_file_names[current_tab])) else ext = "ex" -- New file end if lexer = SCLEX_NULL expansions = {} keywords = {} for i = 1 to length(file_types) do if find(ext, file_types[i][1]) then lexer = file_types[i][2] keywords = file_types[i][3] expansions = file_types[i][4] exit end if end for if lexer != ssm(SCI_GETLEXER) then ssm(SCI_SETLEXER, lexer) init_edit(hedit) for k = 1 to length(keywords) by 2 do ssm(SCI_SETKEYWORDS, keywords[k], keywords[k+1]) end for end if end procedure -- init edit, (or reinit all existing edits when hedit = 0) global procedure init_edit(atom edit) sequence font integer lexer hedit = edit font = font_name ifdef not WINDOWS then font = sreplace(font, "Medium", "") font = sreplace(font, "Thin", "") font = sreplace(font, "Bold", "") font = sreplace(font, "Italic", "") font = sreplace(font, "Condensed", "") font = rtrim(font) end ifdef ssm(SCI_STYLESETFONT, STYLE_DEFAULT, font) ifdef not WINDOWS then ssm(SCI_STYLESETBOLD, STYLE_DEFAULT, match(" Bold", font_name)) ssm(SCI_STYLESETITALIC, STYLE_DEFAULT, match(" Italic", font_name)) end ifdef ssm(SCI_STYLESETSIZE, STYLE_DEFAULT, font_height) ssm(SCI_STYLESETFORE, STYLE_DEFAULT, normal_color) ssm(SCI_STYLESETBACK, STYLE_DEFAULT, background_color) ssm(SCI_SETCARETFORE, xor_bits(background_color, #FFFFFF)) ssm(SCI_STYLESETBOLD, STYLE_DEFAULT, and_bits(bold_flags, 1)) ssm(SCI_STYLECLEARALL) lexer = ssm(SCI_GETLEXER) if lexer = SCLEX_LUA then ssm(SCI_STYLESETFORE, SCE_LUA_COMMENT, comment_color) ssm(SCI_STYLESETBOLD, SCE_LUA_COMMENT, 0 != and_bits(bold_flags, 2)) ssm(SCI_STYLESETFORE, SCE_LUA_COMMENTLINE, comment_color) ssm(SCI_STYLESETBOLD, SCE_LUA_COMMENTLINE, 0 != and_bits(bold_flags, 2)) ssm(SCI_STYLESETFORE, SCE_LUA_STRING, string_color) ssm(SCI_STYLESETBOLD, SCE_LUA_STRING, 0 != and_bits(bold_flags, 4)) ssm(SCI_STYLESETFORE, SCE_LUA_WORD, keyword_color) ssm(SCI_STYLESETBOLD, SCE_LUA_WORD, 0 != and_bits(bold_flags, 8)) ssm(SCI_STYLESETFORE, SCE_LUA_WORD2, builtin_color) ssm(SCI_STYLESETBOLD, SCE_LUA_WORD2, 0 != and_bits(bold_flags, #10)) ssm(SCI_STYLESETFORE, SCE_LUA_NUMBER, number_color) ssm(SCI_STYLESETBOLD, SCE_LUA_NUMBER, 0 != and_bits(bold_flags, #20)) elsif lexer = SCLEX_CPP then ssm(SCI_STYLESETFORE, SCE_C_COMMENT, comment_color) ssm(SCI_STYLESETBOLD, SCE_C_COMMENT, 0 != and_bits(bold_flags, 2)) ssm(SCI_STYLESETFORE, SCE_C_COMMENTLINE, comment_color) ssm(SCI_STYLESETBOLD, SCE_C_COMMENTLINE, 0 != and_bits(bold_flags, 2)) ssm(SCI_STYLESETFORE, SCE_C_STRING, string_color) ssm(SCI_STYLESETBOLD, SCE_C_STRING, 0 != and_bits(bold_flags, 4)) ssm(SCI_STYLESETFORE, SCE_C_WORD, keyword_color) ssm(SCI_STYLESETBOLD, SCE_C_WORD, 0 != and_bits(bold_flags, 8)) ssm(SCI_STYLESETFORE, SCE_C_WORD2, builtin_color) ssm(SCI_STYLESETBOLD, SCE_C_WORD2, 0 != and_bits(bold_flags, #10)) ssm(SCI_STYLESETFORE, SCE_C_NUMBER, number_color) ssm(SCI_STYLESETBOLD, SCE_C_NUMBER, 0 != and_bits(bold_flags, #20)) end if ssm(SCI_STYLESETBACK, STYLE_BRACELIGHT, bracelight_color) ssm(SCI_STYLESETBOLD, STYLE_BRACELIGHT, 0 != and_bits(bold_flags, #40)) ssm(SCI_STYLESETFORE, STYLE_LINENUMBER, linenumber_color) ssm(SCI_STYLESETBOLD, STYLE_LINENUMBER, 0 != and_bits(bold_flags, #80)) ssm(SCI_SETMARGINWIDTHN, 0, 48*line_numbers) -- line numbers margin visible ssm(SCI_SETMARGINWIDTHN, 1, 0) -- non-folding symbols margin hidden ssm(SCI_SETTABINDENTS, 1) ssm(SCI_SETBACKSPACEUNINDENTS, 1) ssm(SCI_SETINDENTATIONGUIDES, indentation_guides) ssm(SCI_SETTABWIDTH, tab_width) ssm(SCI_SETCARETWIDTH, caret_width) ssm(SCI_AUTOCSETSEPARATOR, '\n') ssm(SCI_AUTOCSTOPS, 0, " ") ssm(SCI_AUTOCSETFILLUPS, 0, "") --ssm(SCI_AUTOCSETORDER, SC_ORDER_CUSTOM) -- declaration order ssm(SCI_AUTOCSETORDER, SC_ORDER_PERFORMSORT) -- scintilla should sort ssm(SCI_AUTOCSETCANCELATSTART) -- call tips display above text ssm(SCI_CALLTIPSETPOSITION, 1) -- get modification events for deletetext ssm(SCI_SETMODEVENTMASK, SC_MOD_INSERTTEXT + SC_MOD_DELETETEXT + SC_MOD_BEFOREDELETE) ssm(SCI_SETWRAPMODE, line_wrap) ifdef WINDOWS then ssm(SCI_SETEOLMODE, SC_EOL_CRLF) elsedef ssm(SCI_SETEOLMODE, SC_EOL_LF) end ifdef ssm(SCI_REGISTERIMAGE, DECL_ATOM, a_xpm) ssm(SCI_REGISTERIMAGE, DECL_CONSTANT, c_xpm) ssm(SCI_REGISTERIMAGE, DECL_ENUM, e_xpm) ssm(SCI_REGISTERIMAGE, DECL_FUNCTION, f_xpm) ssm(SCI_REGISTERIMAGE, DECL_INTEGER, i_xpm) ssm(SCI_REGISTERIMAGE, DECL_NAMESPACE, n_xpm) ssm(SCI_REGISTERIMAGE, DECL_OBJECT, o_xpm) ssm(SCI_REGISTERIMAGE, DECL_PROCEDURE, p_xpm) ssm(SCI_REGISTERIMAGE, DECL_SEQUENCE, s_xpm) ssm(SCI_REGISTERIMAGE, DECL_TYPE, t_xpm) ssm(SCI_INDICSETSTYLE, 3, INDIC_SQUIGGLE) ssm(SCI_INDICSETFORE, 3, BrightRed) ssm(SCI_SETINDICATORCURRENT, 3) ssm(SCI_SETMOUSEDWELLTIME, 500) -- milliseconds end procedure global procedure reinit_all_edits() atom saved_hedit saved_hedit = hedit for i = 1 to length(tab_hedits) do init_edit(tab_hedits[i]) end for hedit = saved_hedit end procedure procedure auto_detect_indent(sequence text) integer i = 0, indent = 0, last = 0, tabs_used = 0, spaces_used = 0 integer cr = 0, crlf = 0, lf = 0 sequence indents = repeat(0, 8) while i < length(text) do i += 1 -- count spaces and tabs if text[i] = ' ' then indent += 1 if remainder(indent, tab_width) = 0 then spaces_used += 1 end if continue end if if text[i] = '\t' then indent += tab_width - remainder(indent, tab_width) tabs_used += 1 continue end if -- record positive indents if indent - last >= 1 and indent - last <= length(indents) then indents[indent - last] += 1 end if last = indent indent = 0 -- scan to end of line and count eol styles while i <= length(text) do if text[i] = '\r' then -- cr if i < length(text) and text[i+1] = '\n' then -- crlf i += 1 crlf += 1 else cr += 1 end if exit elsif text[i] = '\n' then -- lf lf += 1 if i < length(text) and text[i+1] = '\r' then -- lfcr - not counted i += 1 end if exit end if i += 1 end while end while -- pick the max used indent size indent = 1 for x = 2 to length(indents) do if indents[x] > indents[indent] then indent = x end if end for ssm(SCI_SETINDENT, indent) ssm(SCI_SETUSETABS, tabs_used > spaces_used) -- pick the most used eol style if cr > crlf and cr > lf then ssm(SCI_SETEOLMODE, SC_EOL_CR) elsif crlf > cr and crlf > lf then ssm(SCI_SETEOLMODE, SC_EOL_CRLF) elsif lf > cr and lf > crlf then ssm(SCI_SETEOLMODE, SC_EOL_LF) end if end procedure -- pos is integer position in current tab, or {"filename", pos} -- note: first character in document is at pos=1 global procedure goto_pos(object pos, integer len=0) sequence prev = {ssm(SCI_GETCURRENTPOS), ssm(SCI_GETFIRSTVISIBLELINE)} if sequence(pos) then sequence prev_file = file_name if open_file(pos[1], 0) = 0 then return -- file not found end if pos = pos[2] if not equal(file_name, prev_file) then -- open_file probably changed the current_tab prev &= {prev_file} end if end if tab_pos_stack[current_tab] &= {prev} pos -= 1 ssm(SCI_SETANCHOR, pos) ssm(SCI_SETCURRENTPOS, pos + len) set_top_line(-1) end procedure -- restore the previous cursor and scroll position, -- or clear the selection or autocompletion if there is one global procedure go_back() object pos = ssm(SCI_GETCURRENTPOS) -- clear any active autocomplete, and return -- cancel any active call tip, and return if ssm(SCI_AUTOCACTIVE) or ssm(SCI_CALLTIPACTIVE) then ssm(SCI_AUTOCCANCEL) ssm(SCI_CALLTIPCANCEL) calltip_pos = -1 calltip_stack = {} return end if -- clear any selection, and return if pos != ssm(SCI_GETANCHOR) then ssm(SCI_SETEMPTYSELECTION, pos) return end if if length(tab_pos_stack[current_tab]) = 0 then return -- empty stack end if -- get the last {pos,top_line,[filename]} on stack, and remove it pos = tab_pos_stack[current_tab][$] tab_pos_stack[current_tab] = tab_pos_stack[current_tab][1..$-1] if sequence(pos) and length(pos) >= 3 then if length(pos[3]) = 0 then -- fixme: pick first untitled tab integer tab = find("", tab_file_names) if tab = 0 then return end if select_tab(tab) elsif open_file(pos[3], 0) = 0 then return -- file not found or user cancelled end if end if ssm(SCI_SETEMPTYSELECTION, pos[1]) set_top_line(pos[2]) end procedure global function get_pos() return ssm(SCI_GETCURRENTPOS) end function global function get_selection() integer len atom buf sequence text len = ssm(SCI_GETSELTEXT) if len <= 1 then return "" end if buf = allocate(len) ssm(SCI_GETSELTEXT, 0, buf) text = peek({buf, len - 1}) free(buf) return text end function global procedure set_top_line(integer line) integer fv, los if line = -1 then line = ssm(SCI_LINEFROMPOSITION, get_pos()) end if fv = ssm(SCI_GETFIRSTVISIBLELINE) los = ssm(SCI_LINESONSCREEN) if line >= fv and line < fv + los-1 then return -- don't need to scroll end if line = ssm(SCI_SETFIRSTVISIBLELINE, line) end procedure global function get_modified() return ssm(SCI_GETMODIFY) end function global function get_line_length(integer line) if line = -1 then line = ssm(SCI_LINEFROMPOSITION, get_pos()) end if return ssm(SCI_LINELENGTH, line) end function global function get_line_end_position(integer line) if line = -1 then line = ssm(SCI_LINEFROMPOSITION, get_pos()) end if return ssm(SCI_GETLINEENDPOSITION, line) end function global function get_line_start_position(integer line) if line = -1 then line = ssm(SCI_LINEFROMPOSITION, get_pos()) end if return ssm(SCI_POSITIONFROMLINE, line) end function global function get_line(integer line) atom junk, len, buf sequence text if line = -1 then line = ssm(SCI_LINEFROMPOSITION, get_pos()) end if len = get_line_length(line) buf = allocate(len+1) ssm(SCI_GETLINE, line, buf) text = peek({buf, len}) free(buf) return text end function global function get_edit_text() atom junk, text_buffer, text_len sequence text text_len = ssm(SCI_GETTEXTLENGTH)+1 text_buffer = allocate(text_len) ssm(SCI_GETTEXT, text_len, text_buffer) text = peek({text_buffer, text_len-1}) free(text_buffer) return text end function global procedure update_status() atom pos, line, col pos = get_pos() line = 1+ssm(SCI_LINEFROMPOSITION, pos) col = 1+ssm(SCI_GETCOLUMN, pos) ui_update_status(sprintf("%d:%d ", {line, col})) if line-1 != expand_line then expand_line = -1 end if end procedure -------------------------------------- -- search and replace -------------------------------------- -- search for the phrase (sequence or pointer) and set the editor target -- returns 1 if found, otherwise 0 global function search_find(sequence phrase, integer backward = 0, integer in_replace_all = 0) if backward then ssm(SCI_SETTARGETSTART, ssm(SCI_GETSELECTIONSTART)) ssm(SCI_SETTARGETEND, 0) else ssm(SCI_SETTARGETSTART, ssm(SCI_GETSELECTIONEND)) ssm(SCI_SETTARGETEND, ssm(SCI_GETTEXTLENGTH)) end if if ssm(SCI_SEARCHINTARGET, length(phrase), phrase) < 0 then -- wrap around and search again if in_replace_all then return 0 elsif backward then ssm(SCI_SETTARGETSTART, ssm(SCI_GETTEXTLENGTH)) else ssm(SCI_SETTARGETSTART, 0) end if if ssm(SCI_SEARCHINTARGET, length(phrase), phrase) < 0 then -- clear the target so search_replace won't do bad things ssm(SCI_SETTARGETSTART, 0) ssm(SCI_SETTARGETEND, 0) return 0 end if end if -- scroll to left side first ssm(SCI_SETXOFFSET, 0) -- set selection from target ssm(SCI_SETSEL, ssm(SCI_GETTARGETSTART), ssm(SCI_GETTARGETEND)) return 1 end function -- replace the editor target with the phrase (sequence or pointer) -- returns 1 if replace made, otherwise 0 global function search_replace(sequence phrase) integer pos = ssm(SCI_GETTARGETSTART) if pos >= ssm(SCI_GETTARGETEND) then -- target not set if not equal(get_selection(), find_phrase) then return 0 end if SSM(hedit, SCI_TARGETFROMSELECTION) end if -- replace or replace_all ssm(SCI_REPLACETARGET, length(phrase), phrase) -- set the current pos to the end, so that the next -- find doesn't search within the replacement phrase ssm(SCI_SETSEL, pos, pos + length(phrase)) return 1 end function -- replace all in document, saving the cursor position -- returns count of replacements made global function search_replace_all(sequence what, sequence phrase) integer pos = ssm(SCI_GETCURRENTPOS), anchor = ssm(SCI_GETANCHOR), count = 0 -- save the current position to be restored later ssm(SCI_SETANCHOR, 0) ssm(SCI_SETCURRENTPOS, 0) -- search until not found ssm(SCI_BEGINUNDOACTION) while search_find(what, 0, 1) do search_replace(phrase) count += 1 end while ssm(SCI_ENDUNDOACTION) ssm(SCI_SETSEL, anchor, pos) return count end function -------------------------------------- -- tab management -------------------------------------- global function get_prev_tab() if current_tab <= 1 then return length(tab_hedits) end if return current_tab - 1 end function global function get_next_tab() if current_tab >= length(tab_hedits) then return 1 end if return current_tab + 1 end function global function make_tab_name() sequence name if length(file_name) = 0 then name = "New File" else name = file_name for i = length(name) to 1 by -1 do if name[i] = '\\' or name[i] = '/' then name = name[i+1..$] exit end if end for end if return name end function procedure update_tab_timestamp() if length(file_name) then tab_timestamps[current_tab] = get_timestamp(file_name) else tab_timestamps[current_tab] = -1 end if end procedure global procedure select_tab(integer tab) if tab < 1 or tab > length(tab_hedits) or tab = current_tab then return end if current_tab = tab file_name = tab_file_names[tab] hedit = tab_hedits[tab] modified = get_modified() expand_line = -1 insert_chars = "" calltip_args = {} calltip_pos = -1 calltip_stack = {} calltip_text = "" last_errors = {} ui_select_tab(tab) ui_update_window_title(make_tab_name()) update_status() update_lexer() change_time = time() + .5 end procedure global procedure update_tab_name() sequence name = make_tab_name() ui_update_window_title(name) if modified then name &= "*" end if ui_update_tab_name(current_tab, name) end procedure global procedure new_tab(sequence file_name) integer tab if equal(tab_file_names, {""}) and modified = 0 then -- unmodified new file, just reuse it and update the tab name tab = 1 tab_file_names[tab] = file_name update_tab_name() current_tab = 0 -- to force select_tab else hedit = ui_new_tab(make_tab_name()) tab_hedits = append(tab_hedits, hedit) tab_file_names = append(tab_file_names, file_name) tab_timestamps = append(tab_timestamps, -1) tab_arguments = append(tab_arguments, "") tab_pos_stack = append(tab_pos_stack, {}) tab = length(tab_hedits) end if select_tab(tab) init_edit(hedit) ssm(SCI_SETINDENT, 4) ssm(SCI_SETUSETABS, 0) end procedure global procedure close_tab() integer tab if save_if_modified(1) = 0 then return -- user cancelled the save end if tab = current_tab save_recent_pos() ui_close_tab(tab) tab_hedits = tab_hedits[1..tab-1] & tab_hedits[tab+1..$] tab_file_names = tab_file_names[1..tab-1] & tab_file_names[tab+1..$] tab_timestamps = tab_timestamps[1..tab-1] & tab_timestamps[tab+1..$] tab_arguments = tab_arguments[1..tab-1] & tab_arguments[tab+1..$] tab_pos_stack = tab_pos_stack[1..tab-1] & tab_pos_stack[tab+1..$] current_tab = 0 if length(tab_hedits) = 0 then new_file() -- must always have a edit available elsif tab > length(tab_hedits) then tab = length(tab_hedits) end if select_tab(tab) end procedure global function get_tab_arguments() return tab_arguments[current_tab] end function global procedure set_tab_arguments(sequence s) tab_arguments[current_tab] = s end procedure -------------------------------------- -- file open/save -------------------------------------- -- returns tab index of opened file, or 0 if cancelled global function open_file(sequence file_name, integer reload) atom result, text_buffer integer fn, tab, initial_pos = 0, first_visible_line = 0 object temp, s sequence text if length(file_name) = 0 then file_name = ui_get_open_file_name() if length(file_name) = 0 then return 0 end if if sequence(file_name[1]) then -- multiple open for i = 1 to length(file_name) do open_file(file_name[i], reload) end for return 0 end if elsif sequence(file_name[1]) then -- filename is {"file_name", initial_pos, first_visible_line} if length(file_name) >= 2 and integer(file_name[2]) then initial_pos = file_name[2] end if if length(file_name) >= 3 and integer(file_name[3]) then first_visible_line = file_name[3] end if file_name = file_name[1] end if file_name = canonical_path(file_name, 0, CORRECT) -- check if already existing tab tab = find(file_name, tab_file_names) if tab and reload = 0 then select_tab(tab) return tab end if text = "" fn = open(file_name, "rb") if fn = -1 then -- file couldn't be opened if ui_message_box_yes_no("Open", "Unable to open "&file_name&"\n\n"& "Do you want to create it?") = 0 then return 0 end if else -- read the contents of the file into text s = gets(fn) while sequence(s) do text &= s s = gets(fn) end while close(fn) end if if not tab then new_tab(file_name) tab = current_tab end if update_tab_timestamp() result = ssm(SCI_CLEARALL) result = ssm(SCI_SETTEXT, 0, text) result = ssm(SCI_SETSAVEPOINT) modified = 0 if tab then update_tab_name() end if result = ssm(SCI_SETFIRSTVISIBLELINE, first_visible_line) result = ssm(SCI_SETEMPTYSELECTION, initial_pos) result = ssm(SCI_EMPTYUNDOBUFFER) auto_detect_indent(text) add_recent_file(file_name) return tab end function -- open files on startup and saved tabs global procedure open_tabs() sequence cmdline -- open files from previous session for i = 1 to length(tabs_to_open) do if file_exists(tabs_to_open[i][1]) then open_file(tabs_to_open[i], 0) end if end for -- open files on command line cmdline = command_line() for i = 3 to length(cmdline) do open_file(cmdline[i], 0) end for if length(tab_file_names) = 0 and length(cmdline) < 3 then new_file() elsif length(cmdline) < 3 then -- select initial tab if no files on command line select_tab(initial_tab) end if end procedure -- returns 1 if ok, 0 on error function save_file() atom fn, junk sequence text ssm(SCI_SETSAVEPOINT, 0, 0) modified = 0 fn = open(file_name, "wb") if fn = -1 then ui_message_box_error("Save", "Unable to save file. Please make sure the file location is not read-only.") return 0 end if text = get_edit_text() puts(fn, text) junk = where(fn) close(fn) if junk != length(text) then ui_message_box_error("Save", "The file could not be written completely. Please make sure there is enough free space.") return 0 end if update_tab_name() update_tab_timestamp() return 1 end function global function save_file_as() -- returns 1 if ok, 0 if cancelled/error object temp temp = ui_get_save_file_name(file_name) if length(temp) = 0 then return 0 end if file_name = canonical_path(temp, 0, CORRECT) tab_file_names[current_tab] = file_name if save_file() = 0 then return 0 end if update_tab_name() update_lexer() return 1 end function global function save_if_modified(integer confirm) -- returns 1 if ok, 0 if cancelled/error atom result, junk sequence text if not get_modified() and (length(file_name) != 0 or confirm) then return 1 end if if confirm then text = "" if length(file_name) then text = " in " & file_name end if text = "The text"&text&" has changed.\n\nDo you want to save the changes?" result = ui_message_box_yes_no_cancel(window_title, text) if result != 1 then -- no or cancel return result + 1 end if end if if length(file_name) = 0 then return save_file_as() end if return save_file() end function global function save_modified_tabs() --returns 1 if ok, 0 if cancelled integer idx atom saved_hedit saved_hedit = hedit for tab = 1 to length(tab_hedits) do hedit = tab_hedits[tab] idx = find(tab_file_names[tab], recent_files) if idx then recent_pos[idx] = get_pos() end if if get_modified() then select_tab(tab) if save_if_modified(1) = 0 then return 0 -- cancelled end if saved_hedit = hedit end if end for hedit = saved_hedit return 1 end function global procedure new_file() atom junk new_tab("") ssm(SCI_SETTEXT, 0, "") ssm(SCI_SETSAVEPOINT, 0, 0) end procedure global procedure check_externally_modified_tabs() atom ts for i = 1 to length(tab_file_names) do if length(tab_file_names[i]) and tab_timestamps[i] != -1 then ts = get_timestamp(tab_file_names[i]) if ts != tab_timestamps[i] then -- clear the timestamp here so that don't repeat when the MessageBox retriggers WM_SETFOCUS tab_timestamps[i] = -1 if ui_message_box_yes_no(window_title, tab_file_names[i] & "\n\nThis file has been modified by another application. Do you want to reload it?") then integer pos select_tab(i) pos = get_pos() open_file(tab_file_names[i], 1) goto_pos(pos) else tab_timestamps[i] = ts end if end if end if end for end procedure global procedure add_recent_file(sequence filename) integer idx idx = find(filename, recent_files) if idx then -- move it to the top of the list recent_files = {recent_files[idx]} & recent_files[1..idx-1] & recent_files[idx+1..length(recent_files)] recent_pos = {recent_pos[idx]} & recent_pos[1..idx-1] & recent_pos[idx+1..length(recent_pos)] else idx = length(recent_files) if idx >= max_recent_files then idx -= 1 end if recent_files = {filename} & recent_files[1..idx] recent_pos = {0} & recent_pos[1..idx] end if ui_refresh_file_menu(recent_files) end procedure global procedure save_recent_pos() integer idx idx = find(file_name, recent_files) if idx then recent_pos[idx] = get_pos() end if end procedure global procedure open_recent(integer idx) atom junk integer tab if idx >= 1 and idx <= length(recent_files) then tab = open_file(recent_files[idx], 0) if tab then ssm(SCI_SETSEL, recent_pos[1], recent_pos[1]) set_top_line(-1) -- set the top visible line to the current line end if end if end procedure -------------------------------------- -- help routines -------------------------------------- sequence help_dir function tr(sequence s, sequence src, sequence dst) for i = 1 to length(s) do integer x = find(s[i], src) if x then s[i] = dst[x] end if end for return s end function global procedure context_help() sequence text, decls, word, name_space, path integer pos, junk object help text = get_edit_text() pos = get_pos() word = word_pos(text, pos) if length(word) < 2 then name_space = "" word = "" else name_space = word[2] word = word[1] end if -- load the search.dat file if length(search_dat) = 0 then sequence paths = include_search_paths for i = 1 to length(paths) do path = paths[i] if path[$] = SLASH then path = path[1..$-1] end if path &= join_path({"..","docs","html"}) & SLASH --puts(1, path&"\n") integer f = open(path & "js" & SLASH & "search.js","r") if f != -1 then help_dir = canonical_path(path) if help_dir[$] != SLASH then help_dir &= SLASH end if object line = gets(f) while sequence(line) do integer x = find(':', line) if x and line[1] = '"' then --puts(1, line[2..x-2]&" "&line[x+1..$]&"\n") search_idx = append(search_idx, line[2..x-2]) line = tr(line[x+1..$-1], "[]", "{}") line = value(line) search_dat = append(search_dat, line[2]) end if line = gets(f) end while close(f) exit end if end for if length(search_dat) = 0 then ui_message_box_error(window_title, "File not found: euphoria/docs/html/js/search.js") return end if end if -- search search.dat for matching entry help = {} for i = 1 to length(search_idx) do if equal(search_idx[i], word) then help = search_dat[i] exit end if end for if atom(help) or length(help) = 0 then ui_message_box_error("Help", "Didn't find any help on the topic: "&word& "\nPlease put the cursor over an Euphoria identifier"& "\nor standard library routine and try again.") return end if if length(help) > 1 then -- multiple help entries path = "" decls = get_declarations(parse(text, file_name), pos, name_space) for j = 1 to length(decls) by 2 do if equal(decls[j], word) then if atom(decls[j+1]) then path = file_name else path = decls[j+1][1] end if for i = 1 to length(help) do if match(sreplace(help[i][1],"_",{SLASH})&".e", path) then --show_help(help[i][1], help[i][2]) ui_show_uri("file://" & help_dir & help[i][1] & ".html#" & help[i][2]) return end if end for end if end for end if -- only one help entry, just show it --show_help(help[1][1], help[1][2]) ui_show_uri("file://" & help_dir & help[1][1] & ".html#" & help[1][2]) end procedure global procedure release_notes() new_file() ssm(SCI_SETLEXER, SCLEX_NULL) ssm(SCI_SETTEXT, 0, changelog) ssm(SCI_SETSAVEPOINT) end procedure global procedure open_tutorial() new_file() ssm(SCI_SETTEXT, 0, ` ---------------------------------------- -- Welcome to the Wee Euphoria Editor -- ---------------------------------------- -- Scintilla Keys: -- -- Text Size -- Magnify Control keypad + -- Reduce Control keypad - -- Normal Control keypad / -- -- Cursor Movement -- Go to start of document Control Home -- Go to end of document Control End -- Go to start of line Home -- Go to end of line End -- Go to previous paragraph Control Up -- Go to next paragraph Control Down -- Go to previous word Control Left -- Go to next word Control Right -- (shift extends selection) -- -- Delete Text -- To start of line Control Shift Backspace -- To start of word Control Backspace -- To end of word Control Delete -- -- Indent -- Indent block Tab -- Unindent block Shift Tab -- Comment/uncomment block Control M -- Editor Tab Management -- Switch to next tab Control PgDn / Control Tab -- Switch to previous tab Control PgUp / Shift Control Tab -- Switch tabs Mouse wheel -- Tab menu Right click -- Close tab Middle click -- Wee offers autocompletion for the following keywords. -- Type a space at the end of each keyword below to see the -- expansion. After the expansion, press Enter to jump over -- the "then" or "do" to the next line. if ------------------------- while ------------------------- switch ------------------------- procedure ------------------------- function ------------------------- type ------------------------- for -- The "for" keyword also inserts " = to " but is overtypable, -- meaning you can continue typing " = " and it will replace the -- existing characters. Or you can move your cursor using the -- arrow keys. -- Likewise, pair characters are inserted for typing the following -- characters: ( [ { " ' -- And you can overtype the closing character if you wish. -- Pressing "(" results in "()" -- Typing "123" results in "(123)" -- Pressing ")" results in "(123)" -- Try it! -- Here's a longer example demonstrating the overtype mechanism: -- type result -- ------- --------------------- -- foo[ foo[] -- 123 foo[123] -- ] foo[123] -- [ foo[123][] -- ( foo[123][()] -- i+1 foo[123][(i+1)] -- )*2 foo[123][(i+1)*2] -- ] foo[123][(i+1)*2] -- = { foo[123][(i+1)*2] = {} -- 1, { foo[123][(i+1)*2] = {1, {}} -- " foo[123][(i+1)*2] = {1, {""}} -- bar foo[123][(i+1)*2] = {1, {"bar"}} -- " foo[123][(i+1)*2] = {1, {"bar"}} -- } foo[123][(i+1)*2] = {1, {"bar"}} -- } foo[123][(i+1)*2] = {1, {"bar"}} -- If the initial character is deleted using backspace, the -- inserted pair will also be removed. (This does not work -- with quotation marks however, only parens and braces.) -- Pressing "(" results in "()" -- Pressing backspace results in "" -- Wee also knows about subroutines you've defined. -- Press F2 to see a list. If you've filled in names for the -- subroutine declarations above, they should appear. -- Pressing OK or Enter will move the cursor to the subroutine -- definition. If your cursor is on the name of an existing -- subroutine, it will be the highlighted entry. -- Try it! Type the name of a subroutine and hit F2. -- Autocompletion for any type of identifier is done with -- Control+Space. The standard library is also searched for -- completions, indicated by "--include" in the list entry, and -- when selected, the include statement will be inserted -- automatically near the top of the file. -- Typing ':' after a namespace identifier will show an auto- -- complete list for definitions within a specific namespace. -- Pressing Ctrl+F2 while on an identifier will move the cursor -- to the definition of that identifier, and select it. -- Pressing escape will deselect the text, and pressing escape -- again will return the cursor to the previous location, think -- of it as pressing Back in a web browser. -- Pressing Shift+F2 while on a subroutine identifier will display -- a calltips popup. The popup shows the types and names of the -- arguments to the subroutine, with default arguments enclosed in -- square brackets. -- Typing '(' after a subroutine identifier will show the calltips -- popup, and highlight the argument position as you type. -- Pressing F4 will open a dialog showing the most recent ex.err -- file, with the error message and a list of either: -- the call stack of the subroutines at the point of the crash, -- or a list of undefined symbols. -- Select an item in the list and press Goto Error to move the -- cursor to that location. -- That's all, have fun! `) ssm(SCI_SETSAVEPOINT) end procedure ------------------------------------------------------------------- -- ex.err file handling routines ------------------------------------------------------------------- -- returns {"filename:line", "message", "line1", "line2", "line3"...} global function get_ex_err() integer fn object line, msg, txt sequence result, tmp fn = open(ex_err_name, "r") if fn = -1 then return {} end if result = {} line = gets(fn) -- filename:line if not atom(line) then msg = gets(fn) result = {line, msg} if match("Errors resolving the following references", msg) then txt = gets(fn) while sequence(txt) and length(txt) > 1 do result = append(result, txt) txt = gets(fn) end while else result = append(result, line) tmp = "... called from " txt = gets(fn) while sequence(txt) and length(result) < 100 do if length(txt) > length(tmp) and equal(txt[1..length(tmp)], tmp) then result = append(result, txt[length(tmp)+1..$]) end if txt = gets(fn) end while end if end if close(fn) return result end function -- get the text between two delimeters, searching from end of text function text_between(sequence text, integer delim1, integer delim2) for i = length(text) to 1 by -1 do if text[i] = delim2 then for j = i-1 to 1 by -1 do if text[j] = delim1 then return text[j+1..i-1] end if end for exit end if end for return "" end function global procedure goto_error(sequence err, integer idx) integer a, b, c, line, tab, col sequence val, file, item, text if idx < 0 or idx > length(err)-2 then return end if text = err[idx+2] a = find('(', text) b = find(')', text) c = 0 for i = length(text) to 1 by -1 do if text[i] = ':' then c = i exit end if end for item = "" if a < c and c < b then -- 'blah' (filename:line) has not been declared file = dirname(ex_err_name) & SLASH & text[a+1..c-1] val = value(text[c+1..b-1]) item = text_between(text, '\'', '\'') elsif c then -- c:\path\to\filename.ext:line subroutine_name() file = text[1..c-1] val = value(text[c+1..$]) item = text_between(err[2], '\'', '\'') else return end if tab = open_file(file, 0) if tab and val[1] = GET_SUCCESS then line = val[2]-1 col = 0 if length(item) then col = match(item, get_line(line)) if col then col -= 1 else item = "" end if end if col += ssm(SCI_POSITIONFROMLINE, line) ssm(SCI_SETSEL, col, col + length(item)) set_top_line(line) end if end procedure global procedure reset_ex_err() if length(run_file_name) = 0 then return end if ex_err_name = dirname(run_file_name) & SLASH & "ex.err" ex_err_timestamp = get_timestamp(ex_err_name) end procedure global procedure check_ex_err() atom ts if length(run_file_name) = 0 then return end if ts = get_timestamp(ex_err_name) if ts > ex_err_timestamp then run_file_name = "" ui_view_error() end if ex_err_timestamp = ts end procedure ------------------------------------------------------------------- -- code syntax routines ------------------------------------------------------------------- -- move to the declaration of the word under the cursor global procedure view_declaration() sequence text, decls, word, name_space integer pos text = get_edit_text() pos = get_pos() word = word_pos(text, pos) if length(word) < 2 then return end if name_space = word[2] word = word[1] decls = get_declarations(parse(text, file_name), pos, name_space) for i = 1 to length(decls) do if equal(decls[i][1], word) then goto_pos(decls[i][2], length(word)) exit end if end for end procedure -- check the current document for errors, show underline indicators procedure do_error_indicators() if ssm(SCI_GETLEXER) != SCLEX_LUA then return end if sequence text = get_edit_text() sequence s = parse_errors(text, file_name) if equal(s, last_errors) then return end if last_errors = s ssm(SCI_INDICATORCLEARRANGE, 0, ssm(SCI_GETTEXTLENGTH)) for i = 1 to length(s) by 3 do ssm(SCI_INDICATORFILLRANGE, s[i]-1, s[i+1]) end for end procedure -- when the mouse hovers over an error, show the message in calltip procedure dwell_error(integer dwell_pos) integer pos, len for i = 1 to length(last_errors) by 3 do pos = last_errors[i]-1 len = last_errors[i+1] if dwell_pos >= pos and dwell_pos < pos + len then ssm(SCI_CALLTIPSHOW, pos, last_errors[i+2]) calltip_dwell = 1 return end if end for end procedure -- show a list of possible words when autocomplete is triggered global procedure view_completions() sequence text, word, ast, decls, name_space, suggestions integer pos, junk, style, ns = 0 if ssm(SCI_AUTOCACTIVE) then return -- autocomplete is already active end if pos = get_pos() style = ssm(SCI_GETSTYLEAT, pos-2) if style = SCE_LUA_STRING or style = SCE_LUA_COMMENTLINE then return -- no completion in strings or comments end if -- use scintilla's code completion text = get_edit_text() word = word_pos(text, pos) if length(word) < 4 then return end if ssm(SCI_SETSEL, word[4], word[4]) name_space = word[2] word = word[1] ast = parse(text, file_name) decls = get_declarations(ast, pos, name_space) if length(decls) = 0 or length(name_space) = 0 then suggestions = suggest_includes(word, name_space) if length(suggestions) = 0 and length(word) = 0 and length(name_space) > 0 then decls = get_declarations(ast, pos, "") suggestions = suggest_includes(name_space, "") ns = DECL_NAMESPACE word = name_space end if -- filter duplicate suggestions for i = 1 to length(suggestions) do sequence tmp = suggestions[i] if ns = 0 or tmp[3] = ns then integer found = 0 tmp = {tmp[1], tmp[1][1..find(' ', tmp[1])-1]} for j = 1 to length(decls) do if find(decls[j][1], tmp) then found = 1 exit end if end for if not found then decls = append(decls, suggestions[i]) end if end if end for end if text = "" for i = 1 to length(decls) do if (ns = 0 or decls[i][3] = ns) and length(decls[i][1]) >= length(word) and equal(decls[i][1][1..length(word)], word) then if length(text) then text &= "\n" end if text &= decls[i][1] & sprintf("?%d", {decls[i][3]}) end if end for if length(text) then ssm(SCI_AUTOCSHOW, length(word) * (ns = 0), text) end if end procedure -- show the subroutine arguments, usually triggered after a pressing '(' global procedure view_subroutine_arguments() sequence text, word, decls, name_space integer pos, end_pos, junk, calltip_save, style pos = get_pos() style = ssm(SCI_GETSTYLEAT, pos-2) if style = SCE_LUA_STRING or style = SCE_LUA_COMMENTLINE then return -- no completion in strings or comments end if calltip_save = calltip_pos if ssm(SCI_GETCHARAT, pos-1) = '(' then calltip_pos = pos pos -= 1 end if -- use scintilla's code completion text = get_edit_text() word = word_pos(text, pos) if length(word) < 4 or length(word[1]) = 0 then calltip_pos = calltip_save return end if pos = word[3] end_pos = word[4] name_space = word[2] word = word[1] decls = get_subroutine_arguments(parse(text, file_name), word, name_space) if length(decls) then decls = decls[$] -- overloaded? use the last one if calltip_save != -1 then calltip_stack &= {calltip_save, calltip_args, calltip_text} end if text = decls[1] & " " & decls[2] & "(" calltip_args = {} for i = 3 to length(decls) by 3 do if i != 3 then text &= ", " end if if decls[i+2] then text &= "[" end if calltip_args &= {{length(text), 0}} text &= decls[i] & " " & decls[i+1] calltip_args[$][2] = length(text) if decls[i+2] then text &= "]" end if end for text &= ")" --puts(1, text & "\n") calltip_text = text ssm(SCI_CALLTIPSHOW, pos, text) if length(calltip_args) then --ssm(SCI_CALLTIPSETHLT, calltip_args[1][1], calltip_args[1][2]) calltip_pos = end_pos+1 else calltip_pos = -1 -- no arguments, no need to update end if return end if -- decl not found, search includes instead decls = suggest_includes(word, name_space) text = "" for i = 1 to length(decls) by 2 do if length(decls[i]) >= length(word) and equal(decls[i][1..length(word)], word) then if length(text) then text &= '\n' end if text &= sreplace(decls[i], " --", "( --") end if end for if length(text) then ssm(SCI_GOTOPOS, end_pos+1) ssm(SCI_AUTOCSHOW, length(word)+1, text) end if end procedure procedure update_subroutine_arguments() sequence text integer pos, arg, ch pos = get_pos() ch = ssm(SCI_GETCHARAT, pos-1, 0) if ch != ',' and ch != '(' and ch != ')' and ssm(SCI_GETCHARAT, pos, 0) != ',' and last_deleted_char != ',' then -- this isn't reliable; backspace over comma, or delete selection containing comma, etc. return end if while 1 do while calltip_pos = -1 or pos < calltip_pos do if length(calltip_stack) = 0 then calltip_pos = -1 return end if calltip_pos = calltip_stack[$-2] calltip_args = calltip_stack[$-1] calltip_text = calltip_stack[$] ssm(SCI_CALLTIPSHOW, calltip_pos, calltip_text) calltip_stack = calltip_stack[1..$-3] end while -- get the text from after '(' up to the cursor text = repeat(0, pos - calltip_pos) for i = 1 to length(text) do text[i] = ssm(SCI_GETCHARAT, calltip_pos+i-1) end for arg = parse_argument_position(text) if arg = 0 then -- parsed closing ')' ssm(SCI_CALLTIPCANCEL) calltip_pos = -1 elsif arg > length(calltip_args) then -- parsed too many arguments ssm(SCI_CALLTIPSETHLT, 0, 0) exit else -- highlight argument at cursor position ssm(SCI_CALLTIPSETHLT, calltip_args[arg][1], calltip_args[arg][2]) exit end if end while end procedure function ltrim(sequence s) for i = 1 to length(s) do if not find(s[i], " \t\n\r") then return s[i..$] end if end for return "" end function function rtrim(sequence s) for i = length(s) to 1 by -1 do if not find(s[i], " \t\n\r") then return s[1..i] end if end for return "" end function -- expand "if " to "if then\n\nend if", etc. procedure auto_expand() sequence text, indent, indent_str integer pos, end_pos atom junk if length(expansions) = 0 then return end if -- make sure the current position is at the end of the line pos = get_pos() end_pos = get_line_end_position(-1) if pos != end_pos then return end if -- save the indentation and trim the line indent = get_line(-1) text = ltrim(indent) indent = "\n" & indent[1..$-length(text)] text = rtrim(text) -- try the expansions and get the text to insert for i = 1 to length(expansions) do if integer(expansions[i][1]) then -- try removing "global", "public", "export" for k = 2 to length(expansions[i]) do if match(expansions[i][k], text) = 1 then text = ltrim(text[length(expansions[i][k])..$]) exit end if end for elsif equal(text, expansions[i][1]) then text = expansions[i][2..$] insert_chars = reverse(text[1]) exit elsif i = length(expansions) then return end if end for indent_str = repeat(' ', ssm(SCI_GETINDENT)) if length(text) >= 3 then indent_str &= indent & text[3] end if text = text[1] & text[2] & indent & indent_str -- save the line for auto_indent() expand_line = ssm(SCI_LINEFROMPOSITION, pos) -- insert the text and restore cursor position ssm(SCI_INSERTTEXT, pos, text) end procedure procedure do_auto_indent() integer line, pos, end_pos sequence indent -- get the previous line and get the indentation pos = get_pos() line = ssm(SCI_LINEFROMPOSITION, pos) - 1 end_pos = get_line_end_position(line) if line = expand_line then -- delete the newline character and go to end of the next line ssm(SCI_DELETERANGE, end_pos, pos - end_pos) end_pos = get_line_end_position(line + 1) ssm(SCI_GOTOPOS, end_pos) expand_line = -1 insert_chars = {} return end if expand_line = -1 -- get the indentation of the previous line, and remove newline indent = get_line(line) for i = length(indent)-length(ltrim(indent)) to 0 by -1 do if i = 0 or (indent[i] != '\n' and indent[i] != '\r') then indent = indent[1..i] exit end if end for -- insert the indentation ssm(SCI_ADDTEXT, length(indent), indent) end procedure procedure do_brace_highlight(atom hedit) integer pos, brace, ch brace = -1 pos = get_pos()-1 if find(ssm(SCI_GETCHARAT, pos, 0), "{}()[]") then brace = ssm(SCI_BRACEMATCH, pos, 0) end if if brace = -1 then pos += 1 if find(ssm(SCI_GETCHARAT, pos, 0), "{}()[]") then brace = ssm(SCI_BRACEMATCH, pos, 0) end if if brace = -1 then pos = -1 end if end if brace = ssm(SCI_BRACEHIGHLIGHT, pos, brace) end procedure -- FIXME: this has a problem with brace chars within strings, for ex: {1, "}{", 2} procedure auto_complete_selection(integer pos, sequence text) integer inc, len, insert_line, arg, ns sequence line -- insert include statement if completion contains it inc = match(" --include", text) ns = find(':', text) if ns and ssm(SCI_GETCHARAT, pos-1) = ':' then line = "" while length(line) < ns do line = ssm(SCI_GETCHARAT, pos-length(line)-2) & line if equal(line, text[1..length(line)]) then ssm(SCI_DELETERANGE, pos-length(line)-1, length(line)+1) exit end if end while if not inc then ssm(SCI_ADDTEXT, length(text), text) ssm(SCI_AUTOCCANCEL, 0, 0) end if end if if inc then ssm(SCI_AUTOCCANCEL, 0, 0) len = get_pos() - pos arg = text[inc-1] = '(' if arg then -- delete the existing '(' since we are adding another one len -= 1 ssm(SCI_DELETERANGE, pos + len, 1) end if if len < 0 then len = 0 end if -- add in the selected text if len+1 <= inc then ssm(SCI_ADDTEXT, inc-1-len, text[len+1..inc]) end if text = text[inc+3..$] & "\n" -- search first few lines for include statements insert_line = -1 for i = 0 to 100 do line = ltrim(get_line(i)) if length(line) >= 8 and equal(line[1..8], "include ") then insert_line = i+1 elsif insert_line != -1 then exit end if end for if insert_line = -1 then -- insert after consecutive comment lines insert_line = 0 for i = 0 to 100 do line = ltrim(get_line(i)) if length(line) >= 2 and line[1]='-' and line[2]='-' then insert_line = i+1 else exit end if end for end if inc = ssm(SCI_POSITIONFROMLINE, insert_line, 0) if inc = -1 or inc > pos then inc = 0 end if ssm(SCI_INSERTTEXT, inc, text) if arg then view_subroutine_arguments() end if end if end procedure -- insert closing character when user types ( [ { ' " procedure insert_pair() integer pos, style, ch if not complete_braces then return end if pos = get_pos()-1 -- avoid inside strings and comments if pos != get_line_start_position(-1) then style = ssm(SCI_GETSTYLEAT, pos-1) if style = SCE_LUA_COMMENTLINE or (style = SCE_LUA_STRING and style = ssm(SCI_GETSTYLEAT, pos+1)) then return end if end if -- avoid inside words or numbers for i = pos+1 to get_line_end_position(-1) do ch = ssm(SCI_GETCHARAT, i) style = ssm(SCI_GETSTYLEAT, i) if find(ch, ",)}]\r\n+-*/=!<>#&\0") or style = SCE_LUA_WORD then exit elsif ch != ' ' and ch != '\t' then return end if end for ch = ssm(SCI_GETCHARAT, pos, 0) if ch = '(' then ch = ')' elsif ch = '{' then ch = '}' elsif ch = '[' then ch = ']' end if ssm(SCI_INSERTTEXT, pos+1, {ch}) insert_chars &= ch end procedure -- remove the characters inserted by insert_pair(), won't work for ' " tho procedure delete_pair() if length(insert_chars) = 0 then return end if if insert_chars[$] = last_deleted_char then insert_chars = insert_chars[1..$-1] elsif (last_deleted_char = '(' and insert_chars[$] = ')') or (last_deleted_char = '[' and insert_chars[$] = ']') or (last_deleted_char = '{' and insert_chars[$] = '}') then ssm(SCI_DELETERANGE, get_pos(), 1) insert_chars = insert_chars[1..$-1] end if end procedure -- open file(s) from a drag and drop operation procedure open_uri(sequence uri) uri = split(uri, "\r\n") for i = 1 to length(uri) do if match("file://", uri[i]) = 1 then open_file(uri[i][8..$], 0) end if end for end procedure -- toggle the comment at the start of each line in selection global procedure toggle_comment() integer pos = ssm(SCI_GETCURRENTPOS), anchor = ssm(SCI_GETANCHOR), line_pos = ssm(SCI_LINEFROMPOSITION, pos), line_anchor = ssm(SCI_LINEFROMPOSITION, anchor), col_pos = pos - ssm(SCI_POSITIONFROMLINE, line_pos), col_anchor = anchor - ssm(SCI_POSITIONFROMLINE, line_anchor), line_start = ssm(SCI_LINEFROMPOSITION, ssm(SCI_GETSELECTIONSTART)), line_end = ssm(SCI_LINEFROMPOSITION, ssm(SCI_GETSELECTIONEND)-1) integer uncomment = 1 if line_end < line_start then line_end = line_start end if -- check to see if all lines are commented for i = line_start to line_end do pos = ssm(SCI_POSITIONFROMLINE, i) if ssm(SCI_GETCHARAT, pos) != '-' or ssm(SCI_GETCHARAT, pos+1) != '-' then uncomment = 0 -- non comment detected exit end if end for -- insert/remove comment markers ssm(SCI_BEGINUNDOACTION) for i = line_start to line_end do pos = ssm(SCI_POSITIONFROMLINE, i) if uncomment then ssm(SCI_DELETERANGE, pos, 2) else ssm(SCI_INSERTTEXT, pos, "--") end if end for ssm(SCI_ENDUNDOACTION) -- adjust the selection to be the same column as it was before pos = ssm(SCI_POSITIONFROMLINE, line_pos) + col_pos anchor = ssm(SCI_POSITIONFROMLINE, line_anchor) + col_anchor if col_pos != 0 then pos -= uncomment*4-2 end if if col_anchor != 0 then anchor -= uncomment*4-2 end if ssm(SCI_SETSEL, anchor, pos) end procedure ------------------------------------------------------------------- -- eu.cfg handling ------------------------------------------------------------------- -- returns a list of interpreters that exist on disk global function get_interpreters() sequence paths, try, exe, bin, result = {} integer index if find(fileext(file_name), {"ew","exw"}) then bin = {"euiw", "eui", "exw", "ex"} else bin = {"eui", "euiw", "ex", "exw"} end if ifdef UNIX then bin = append(bin, "exu") end ifdef paths = parse_eu_cfg(dirname(file_name) & SLASH & "eu.cfg") paths &= include_search_paths -- fallback to running interpreter for i = 1 to length(paths) do try = paths[i] if try[$] = SLASH then try = try[1..$-1] end if for j = 1 to length(bin) do exe = try & join_path({"..", "bin", bin[j]}) ifdef WINDOWS then exe &= ".exe" end ifdef exe = canonical_path(exe) if not find(exe, result) and file_exists(exe) then result = append(result, exe) end if end for end for if length(interpreter) and not find(interpreter, result) then result = prepend(result, interpreter) end if return result end function -- if bin="" returns an interpreter from one of these locations: -- interpreter global variable if set -- some interpreter from the paths in eu.cfg -- the running interpreter's include_paths -- if bin is "eubind" "eushroud" or "euc" then use the same search -- paths as above to locate it global function get_eu_bin(sequence bin) sequence paths, try, exe if length(bin) = 0 then if length(interpreter) != 0 then return interpreter end if if find(fileext(file_name), {"ew","exw"}) then bin = {"euiw", "eui", "exw", "ex"} else bin = {"eui", "euiw", "ex", "exw"} end if ifdef UNIX then bin = append(bin, "exu") end ifdef else bin = {bin} end if paths = parse_eu_cfg(dirname(file_name) & SLASH & "eu.cfg") paths &= include_search_paths -- fallback to running interpreter try = dirname(interpreter) if length(try) then paths = prepend(paths, try) end if for i = 1 to length(paths) do try = paths[i] if try[$] = SLASH then try = try[1..$-1] end if for j = 1 to length(bin) do exe = try & join_path({"..", "bin", bin[j]}) ifdef WINDOWS then exe &= ".exe" end ifdef exe = canonical_path(exe) if file_exists(exe) then return exe end if end for end for return bin[1] end function -- returns text in quotes if it contains spaces global function quote_spaces(sequence text) if find(' ', text) then for i = length(text) to 1 by -1 do if text[i] = '"' or text[i] = '\\' then text = text[1..i-1] & '\\' & text[i..$] end if end for return '"' & text & '"' end if return text end function ------------------------------------------------------------------- -- scintilla editor notifications ------------------------------------------------------------------- procedure log_notification(atom notification) integer code, pos, ch, modifiers code = peek4u(notification+8) pos = peek4u(notification+12) if code = SCN_STYLENEEDED then puts(1, "SCN_STYLENEEDED\n") elsif code = SCN_CHARADDED then ch = peek4s(notification+16) printf(1, "SCN_CHARADDED ch=%d '%s'\n", {ch, {ch}}) elsif code = SCN_SAVEPOINTREACHED then puts(1, "SCN_SAVEPOINTREACHED\n") elsif code = SCN_SAVEPOINTLEFT then puts(1, "SCN_SAVEPOINTLEFT\n") elsif code = SCN_MODIFYATTEMPTRO then puts(1, "SCN_MODIFYATTEMPTRO\n") elsif code = SCN_KEY then ch = peek4s(notification+16) modifiers = peek4s(notification+20) printf(1, "SCN_KEY ch=%d modifiers=#%x\n", {ch, modifiers}) elsif code = SCN_DOUBLECLICK then puts(1, "SCN_DOUBLECLICK\n") elsif code = SCN_UPDATEUI then printf(1, "SCN_UPDATEUI updated=#%x\n", {peek4s(notification+88)}) elsif code = SCN_MODIFIED then puts(1, "SCN_MODIFIED\n") elsif code = SCN_MACRORECORD then puts(1, "SCN_MACRORECORD\n") elsif code = SCN_MARGINCLICK then puts(1, "SCN_MARGINCLICK\n") elsif code = SCN_NEEDSHOWN then puts(1, "SCN_NEEDSHOWN\n") elsif code = SCN_PAINTED then --gets sent after each caret blink --puts(1, "SCN_PAINTED\n") elsif code = SCN_USERLISTSELECTION then puts(1, "SCN_USERLISTSELECTION\n") elsif code = SCN_URIDROPPED then puts(1, "SCN_URIDROPPED\n") elsif code = SCN_DWELLSTART then puts(1, "SCN_DWELLSTART\n") elsif code = SCN_DWELLEND then puts(1, "SCN_DWELLEND\n") elsif code = SCN_ZOOM then puts(1, "SCN_ZOOM\n") elsif code = SCN_HOTSPOTCLICK then puts(1, "SCN_HOTSPOTCLICK\n") elsif code = SCN_HOTSPOTDOUBLECLICK then puts(1, "SCN_HOTSPOTDOUBLECLICK\n") elsif code = SCN_HOTSPOTRELEASECLICK then puts(1, "SCN_HOTSPOTRELEASECLICK\n") elsif code = SCN_INDICATORCLICK then puts(1, "SCN_INDICATORCLICK\n") elsif code = SCN_INDICATORRELEASE then puts(1, "SCN_INDICATORRELEASE\n") elsif code = SCN_CALLTIPCLICK then puts(1, "SCN_CALLTIPCLICK\n") elsif code = SCN_AUTOCSELECTION then puts(1, "SCN_AUTOCSELECTION\n") elsif code = SCN_AUTOCCANCELLED then puts(1, "SCN_AUTOCCANCELLED\n") elsif code = SCN_AUTOCCHARDELETED then puts(1, "SCN_AUTOCCHARDELETED\n") elsif code = SCN_FOCUSIN then puts(1, "SCN_FOCUSIN\n") elsif code = SCN_FOCUSOUT then puts(1, "SCN_FOCUSOUT\n") end if end procedure ifdef BITS64 then constant NOTIFICATION_CODE = 16, NOTIFICATION_POS = 24, NOTIFICATION_CH = 28, NOTIFICATION_MODIFIERS = 32, NOTIFICATION_MODIFICATIONTYPE = 36, NOTIFICATION_TEXT = 40, NOTIFICATION_LENGTH = 48, NOTIFICATION_UPDATED = 116 elsedef constant NOTIFICATION_CODE = 8, NOTIFICATION_POS = 12, NOTIFICATION_CH = 16, NOTIFICATION_MODIFIERS = 20, NOTIFICATION_MODIFICATIONTYPE = 24, NOTIFICATION_TEXT = 28, NOTIFICATION_LENGTH = 32, NOTIFICATION_UPDATED = 88 end ifdef function notification_text(atom notification) ifdef BITS64 then return peek_string(peek8u(notification+NOTIFICATION_TEXT)) elsedef return peek_string(peek4u(notification+NOTIFICATION_TEXT)) end ifdef end function global function sci_notify(atom hedit, atom data, atom notification, atom userdata) integer code, pos, ch, modifiers, updated, len if notification = 0 then return 0 end if --log_notification(notification) code = peek4u(notification+NOTIFICATION_CODE) if code = SCN_UPDATEUI then updated = peek4u(notification+NOTIFICATION_UPDATED) if ssm(SCI_CALLTIPACTIVE) then update_subroutine_arguments() end if if and_bits(updated, SC_UPDATE_SELECTION+SC_UPDATE_CONTENT) = SC_UPDATE_SELECTION then -- selection moved and content was not changed insert_chars = "" elsif last_deleted_char then delete_pair() last_deleted_char = 0 end if if and_bits(updated, SC_UPDATE_SELECTION) then update_status() end if do_brace_highlight(hedit) elsif code = SCN_SAVEPOINTREACHED or code = SCN_SAVEPOINTLEFT then modified = (code = SCN_SAVEPOINTLEFT) update_tab_name() elsif code = SCN_KEY then ch = peek4s(notification+NOTIFICATION_CH) modifiers = peek4s(notification+NOTIFICATION_MODIFIERS) -- on OSX X11, META and SHIFT are the only modifiers we receive if ch = 'Z' and and_bits(modifiers, SCMOD_CTRL+SCMOD_META) then if and_bits(modifiers, SCMOD_SHIFT) then ssm(SCI_REDO, 0, 0) else ssm(SCI_UNDO, 0, 0) end if elsif ch >= '1' and ch <= '9' and and_bits(modifiers, SCMOD_ALT+SCMOD_META) then select_tab(ch - '0') elsif ch < 128 then --printf(1, "SCN_KEY ch=%d '%s' modifiers=%d\n", {ch, {ch}, modifiers}) else --printf(1, "SCN_KEY ch=#%x modifiers=%d\n", {ch, modifiers}) end if elsif code = SCN_CHARADDED then ch = peek4s(notification+NOTIFICATION_CH) if length(insert_chars) and ch = insert_chars[$] then -- the user retyped the insert character, so delete it ssm(SCI_DELETERANGE, get_pos(), 1) -- the UPDATEUI will remove the insert_chars[$] elsif ch = ':' and auto_namespace then view_completions() elsif ch = ' ' and complete_statements then auto_expand() elsif ch = '\n' and auto_indent then do_auto_indent() elsif ch = '(' then -- check for displaying subroutine arguments if complete_braces then insert_pair() end if if auto_arguments then view_subroutine_arguments() end if elsif ch = '{' or ch = '[' and complete_braces then insert_pair() elsif ch = '"' or ch = '\'' and complete_braces then insert_pair() end if elsif code = SCN_AUTOCSELECTION then -- check for "--include" in selection, then add include statement near top of file -- or after the first non-commented line, or ideally with other includes pos = peek4s(notification+NOTIFICATION_POS) auto_complete_selection(pos, notification_text(notification)) elsif code = SCN_MODIFIED then modifiers = peek4u(notification+NOTIFICATION_MODIFICATIONTYPE) if and_bits(modifiers, SC_MOD_BEFOREDELETE) then pos = peek4s(notification+NOTIFICATION_POS) len = peek4u(notification+NOTIFICATION_LENGTH) if len = 1 then last_deleted_char = ssm(SCI_GETCHARAT, pos) end if end if if auto_indicator and ssm(SCI_GETLEXER) = SCLEX_LUA and and_bits(modifiers, SC_MOD_INSERTTEXT + SC_MOD_DELETETEXT) then change_time = time() + .5 end if elsif code = SCN_URIDROPPED then open_uri(notification_text(notification)) elsif code = SCN_DWELLSTART then pos = peek4s(notification+NOTIFICATION_POS) if change_time = 0 then dwell_error(pos) end if elsif code = SCN_DWELLEND then if calltip_dwell then ssm(SCI_CALLTIPCANCEL) calltip_dwell = 0 end if elsif code = SCN_PAINTED then -- could run some background processing during painted, every time cursor blinks if auto_indicator and change_time and change_time <= time() then do_error_indicators() change_time = 0 end if end if return 0 end function