home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / euphoria / ed.ex < prev    next >
Text File  |  1994-03-10  |  32KB  |  1,326 lines

  1.     ----------------------------------------------------------
  2.     --       This Euphoria Editor was developed by          --
  3.     --            Rapid Deployment Software.                --
  4.     --                                                      --
  5.     -- Permission is freely granted to anyone to modify     --
  6.     -- and/or redistribute this editor (ed.ex, syncolor.e). --
  7.     -- You may even sell it as it is, or with your          --
  8.     -- modifications.                                       --
  9.     ----------------------------------------------------------
  10.  
  11. without type_check -- makes it a bit faster
  12.  
  13. include graphics.e
  14.  
  15. constant TRUE = 1,
  16.      FALSE = 0
  17.  
  18. -- i/o devices
  19. constant KEYB = 0
  20. global constant SCREEN = 1
  21.  
  22. -- screen dimensions
  23. constant INIT_SCREEN_LENGTH = 25, -- 25, 28, 43, 50
  24.      SCREEN_WIDTH = 80
  25.  
  26. -- colors
  27. constant TOP_LINE_TEXT_COLOR = 0,
  28.      TOP_LINE_BACK_COLOR = 14,
  29.      BACKGROUND_COLOR = 7
  30.  
  31. -- colors needed by syncolor.e:
  32. -- Adjust to suit your monitor and your taste.
  33. global constant NORMAL_COLOR = 8,
  34.         COMMENT_COLOR = 4,
  35.         KEYWORD_COLOR = 1,
  36.         BUILTIN_COLOR = 5,
  37.         STRING_COLOR = 6 
  38. global constant bracket_color = {NORMAL_COLOR, 0, 14, 2, 15, 3, 9}
  39.  
  40. -- cursor style
  41. constant ED_CURSOR = UNDERLINE_CURSOR
  42.  
  43. constant TAB_WIDTH = 8
  44.  
  45. global constant BLANK_LINE = repeat('\t', SCREEN_WIDTH/TAB_WIDTH)
  46.  
  47. -- special input characters
  48. constant ESCAPE = 27,
  49.      CR = 13,
  50.      BS = 8,
  51.      HOME = 327,
  52.      PAGE_UP = 329,
  53.      END = 335,
  54.      PAGE_DOWN = 337,
  55.      INSERT = 338,
  56.      DELETE = 339,
  57.      CONTROL_DELETE = 403,
  58.      ARROW_LEFT = 331,
  59.      ARROW_RIGHT = 333,
  60.      ARROW_UP = 328,
  61.      ARROW_DOWN = 336
  62.  
  63. constant CONTROL_CHARS = {ESCAPE, BS, DELETE, PAGE_UP, PAGE_DOWN,
  64.               INSERT, CONTROL_DELETE,
  65.               ARROW_LEFT, ARROW_RIGHT, ARROW_UP, ARROW_DOWN,
  66.               HOME, END}
  67.  
  68. sequence buffer -- In-memory buffer where the file is manipulated.
  69. -- This is a sequence where each element is a sequence
  70. -- containing one line of text.
  71. -- Each line of text, except possibly the last one, ends with '\n'
  72.  
  73. type natural(integer x)
  74.     return x >= 0
  75. end type
  76.  
  77. type positive_int(integer x)
  78.     return x >= 1
  79. end type
  80.  
  81. positive_int screen_length
  82. screen_length = INIT_SCREEN_LENGTH
  83.  
  84. type screen_line(integer x)
  85. -- a valid line on the screen
  86.     return x >= 1 and x <= screen_length
  87. end type
  88.  
  89. type screen_col(integer x)
  90. -- a valid column on the screen
  91.     return x >= 1 and x <= SCREEN_WIDTH
  92. end type
  93.  
  94. type buffer_line(integer x)
  95. -- a valid buffer line
  96.     return x >= 1 and x <= length(buffer)
  97. end type
  98.  
  99. type char(integer x)
  100. -- a character
  101.     return x >= 0 and x <= 511
  102. end type
  103.  
  104. type extended_char(integer x)
  105.     return char(x) or x = -1
  106. end type
  107.  
  108. type boolean(integer x)
  109.     return x = TRUE or x = FALSE
  110. end type
  111.  
  112. type file_number(integer x)
  113.     return x >= -1
  114. end type
  115.  
  116. -- auto-completion of Euphoria statements ---------------------------
  117. boolean auto_complete
  118. auto_complete = TRUE  -- set to FALSE if you'd rather not have it
  119. ---------------------------------------------------------------------
  120.  
  121. sequence file_name   -- name of the file that we are editing
  122.  
  123. -- These are the critical variables that all editing operations
  124. -- must update:
  125. buffer_line  b_line  -- current line in buffer
  126. positive_int b_col   -- current character within line in buffer
  127. screen_line  s_line  -- line on screen corresponding to b_line
  128. screen_col   s_col   -- column on screen corresponding to b_col
  129.  
  130. boolean stop         -- indicates when to quit editing this file
  131.  
  132. sequence kill_buffer -- kill buffer of deleted lines or characters
  133. kill_buffer = {}
  134.  
  135. boolean adding_to_kill -- TRUE if still accumulating deleted lines/chars
  136.  
  137. boolean multi_color   -- use colors for keywords etc.
  138. boolean dot_e        -- TRUE if this is a .e/.ex file
  139. boolean modified     -- TRUE if file has been modified
  140. boolean view_only    -- binary file - view but don't save
  141. boolean top_line_set
  142.  
  143. natural start_line, start_col
  144.  
  145. sequence error_message
  146.  
  147. sequence config -- video configuration
  148.  
  149. procedure delay(atom n)
  150. -- an n second pause while a message is on the screen
  151.     atom t
  152.  
  153.     t = time()
  154.     while time() < t + n do
  155.     end while
  156. end procedure
  157.  
  158. function tab(positive_int pos)
  159. -- compute equivalent number of spaces for tab
  160.     return(floor((pos - 1) / TAB_WIDTH) + 1) * TAB_WIDTH + 1
  161. end function
  162.  
  163. -- color display of lines
  164. include syncolor.e
  165.  
  166. procedure DisplayLine(buffer_line line, screen_line sline, boolean all_clear)
  167. -- display a buffer line on a given line on the screen
  168. -- all_clear indicates that trailing whitespace is not necessary
  169.     sequence this_line
  170.     natural last
  171.  
  172.     this_line = buffer[line]
  173.     last = length(this_line)
  174.     if this_line[last] = '\n' then
  175.     last = last - 1
  176.     end if
  177.     position(sline, 1)
  178.     if multi_color then
  179.     -- color display
  180.     DisplayColorLine(this_line[1..last], all_clear)
  181.     else
  182.     -- monochrome display
  183.     if all_clear then
  184.         puts(SCREEN, this_line[1..last])
  185.     else
  186.         puts(SCREEN, this_line[1..last] & BLANK_LINE)
  187.     end if
  188.     end if
  189. end procedure
  190.  
  191. procedure DisplayScreen(positive_int bline, screen_line sline)
  192. -- print a series of buffer lines, starting at sline on screen
  193. -- and continue until the end of screen, or end of buffer
  194.     boolean all_clear
  195.  
  196.     if sline = 1 then
  197.     clear_screen() -- faster
  198.     all_clear = TRUE
  199.     else
  200.     all_clear = FALSE
  201.     end if
  202.     for b = bline to length(buffer) do
  203.     DisplayLine(b, sline, all_clear)
  204.     if sline = screen_length then
  205.         return
  206.     else
  207.         sline = sline + 1
  208.     end if
  209.     end for
  210.     -- blank any remaining screen lines after end of file
  211.     if not all_clear then
  212.     for s = sline to screen_length do
  213.         position(s, 1)
  214.         puts(SCREEN, BLANK_LINE)
  215.     end for
  216.     end if
  217. end procedure
  218.  
  219. function add_line(file_number file_no)
  220. -- add a new line to the buffer
  221.     object line
  222.  
  223.     line = gets(file_no)
  224.     if atom(line) then
  225.     return FALSE -- end of file
  226.     end if
  227.     if length(line) = 0 then
  228.     -- not a text file
  229.     line = "\n"
  230.     view_only = TRUE
  231.     end if
  232.     buffer = append(buffer, line)
  233.     return TRUE
  234. end function
  235.  
  236. procedure read_file(file_number file_no)
  237. -- read the entire file into buffer variable
  238.  
  239.     buffer = {}
  240.  
  241.     -- read and immediately display the first screenful
  242.     for i = 1 to screen_length do
  243.     if not add_line(file_no) then
  244.         exit
  245.     end if
  246.     end for
  247.     DisplayScreen(1, 1)
  248.  
  249.     -- read the rest
  250.     while add_line(file_no) do
  251.     end while
  252. end procedure
  253.  
  254. procedure blank_top_line()
  255.     position(1, 1)
  256.     puts(SCREEN, BLANK_LINE)
  257.     position(1, 1)
  258. end procedure
  259.  
  260. procedure reverse_video()
  261. -- start inverse video
  262.     text_color(TOP_LINE_TEXT_COLOR)
  263.     bk_color(TOP_LINE_BACK_COLOR)
  264. end procedure
  265.  
  266. procedure normal_video()
  267. -- end inverse video
  268.     text_color(NORMAL_COLOR)
  269.     bk_color(BACKGROUND_COLOR)
  270. end procedure
  271.  
  272. procedure set_top_line(sequence message)
  273.     -- set up message on top line
  274.     reverse_video()
  275.     blank_top_line()
  276.     puts(SCREEN, message)
  277.     top_line_set = TRUE
  278. end procedure
  279.  
  280. procedure clear_top_line()
  281.     -- restore top line
  282.     if length(buffer) = 0 then
  283.     blank_top_line()
  284.     elsif top_line_set then
  285.     DisplayLine(b_line - s_line + 1, 1, FALSE)
  286.     position(s_line, s_col)
  287.     end if
  288.     top_line_set = FALSE
  289. end procedure
  290.  
  291. procedure save_file(sequence file_name)
  292. -- write buffer back into the disk file
  293.     file_number file_no
  294.     
  295.     set_top_line("")
  296.     if view_only then
  297.     printf(SCREEN, "Can't save %s - it contains non-text characters",
  298.            {file_name})
  299.     delay(3)
  300.     stop = FALSE
  301.     return
  302.     end if
  303.     file_no = open(file_name, "w")
  304.     if file_no = -1 then
  305.     printf(SCREEN, "Can't save %s - write permission denied", 
  306.           {file_name})
  307.     delay(3)
  308.     stop = FALSE
  309.     return
  310.     end if
  311.     printf(SCREEN, "saving %s ...          ", {file_name})
  312.     for i = 1 to length(buffer) do
  313.     puts(file_no, buffer[i])
  314.     end for
  315.     close(file_no)
  316.     stop = TRUE
  317. end procedure
  318.  
  319. procedure arrow_right()
  320. -- action for right arrow key
  321.  
  322.     positive_int temp_col
  323.  
  324.     if s_col < SCREEN_WIDTH and b_col < length(buffer[b_line]) then
  325.     if buffer[b_line][b_col] = '\t' then
  326.         temp_col = tab(s_col)
  327.     else
  328.         temp_col = s_col + 1
  329.     end if
  330.     if temp_col > SCREEN_WIDTH then
  331.         return
  332.     end if
  333.     s_col = temp_col
  334.     b_col = b_col + 1
  335.     end if
  336. end procedure
  337.  
  338. procedure arrow_left()
  339. -- action for left arrow key
  340.  
  341.     positive_int old_b_col
  342.  
  343.     old_b_col = b_col
  344.     b_col = 1
  345.     s_col = 1
  346.     for i = 1 to old_b_col - 2 do
  347.     arrow_right()
  348.     end for
  349. end procedure
  350.  
  351. procedure arrow_up()
  352. -- action for up arrow key
  353.  
  354.     b_col = 1
  355.     s_col = 1
  356.     if b_line > 1 then
  357.     b_line = b_line - 1
  358.     if s_line > 1 then
  359.         s_line = s_line - 1
  360.         if s_line = 1 then
  361.         clear_top_line()
  362.         end if
  363.     else
  364.         -- move all lines down, display new line at top
  365.         scroll(-1)
  366.         DisplayLine(b_line, 1, TRUE)
  367.         position(1, 1)
  368.         s_line = 1
  369.     end if
  370.     end if
  371. end procedure
  372.  
  373. procedure arrow_down()
  374. -- action for down arrow key
  375.  
  376.     b_col = 1
  377.     s_col = 1
  378.     if b_line < length(buffer) then
  379.     b_line = b_line + 1
  380.     if s_line < screen_length then
  381.         s_line = s_line + 1
  382.     else
  383.         -- move all lines up, display new line at bottom
  384.         scroll(+1)
  385.         DisplayLine(b_line, screen_length, TRUE)
  386.     end if
  387.     end if
  388. end procedure
  389.  
  390. function numeric(sequence string)
  391. -- convert digit string to a number
  392.     natural n
  393.     positive_int i
  394.  
  395.     n = 0
  396.     i = 1
  397.     while string[i] >= '0' and string[i] <= '9' do
  398.     n = n * 10 + string[i] - '0'
  399.     i = i + 1
  400.     end while
  401.     return n
  402. end function
  403.  
  404. procedure goto_line(integer new_line, integer new_col)
  405. -- move to a specified line and column
  406. -- refresh screen if line is 0
  407.     integer new_s_line
  408.     boolean refresh
  409.  
  410.     if length(buffer) = 0 then
  411.     clear_screen()
  412.     return
  413.     end if
  414.     if new_line = 0 then
  415.     new_line = b_line
  416.     refresh = TRUE
  417.     else
  418.     refresh = FALSE
  419.     end if
  420.     if new_line < 1 then
  421.     new_line = 1
  422.     elsif new_line > length(buffer) then
  423.     new_line = length(buffer)
  424.     end if
  425.     new_s_line = new_line - b_line + s_line
  426.     b_line = new_line
  427.     if not refresh and screen_line(new_s_line) then
  428.     -- new line is on the screen
  429.     s_line = new_s_line
  430.     else
  431.     -- new line is off the screen, or refreshing
  432.     position(1, 1)
  433.     s_line = floor(screen_length/2)
  434.     if s_line > b_line or length(buffer) < screen_length then
  435.         s_line = b_line
  436.     elsif b_line > length(buffer) - screen_length + s_line then
  437.         s_line = screen_length - (length(buffer) - b_line)
  438.     end if
  439.     DisplayScreen(b_line - s_line + 1, 1)
  440.     end if
  441.     b_col = 1
  442.     s_col = 1
  443.     position(s_line, s_col)
  444.     for i = 1 to new_col-1 do
  445.     arrow_right()
  446.     end for
  447. end procedure
  448.  
  449. procedure page_down()
  450. -- action for page-down key
  451.     buffer_line prev_b_line
  452.  
  453.     if length(buffer) <= screen_length then
  454.     return
  455.     end if
  456.     prev_b_line = b_line
  457.     b_col = 1
  458.     s_col = 1
  459.     if b_line + screen_length + screen_length - s_line <= length(buffer) then
  460.     b_line = b_line + screen_length
  461.     else
  462.     b_line = length(buffer) - (screen_length - s_line)
  463.     end if
  464.     if b_line != prev_b_line then
  465.     DisplayScreen(b_line - s_line + 1, 1)
  466.     end if
  467. end procedure
  468.  
  469. procedure page_up()
  470. -- action for page-up key
  471.     buffer_line prev_b_line
  472.  
  473.     if length(buffer) <= screen_length then
  474.     return
  475.     end if
  476.     prev_b_line = b_line
  477.     b_col = 1
  478.     s_col = 1
  479.     if b_line - screen_length >= s_line then
  480.     b_line = b_line - screen_length
  481.     else
  482.     b_line = s_line
  483.     end if
  484.     if b_line != prev_b_line then
  485.     DisplayScreen(b_line - s_line + 1, 1)
  486.     end if
  487. end procedure
  488.  
  489. procedure new_screen_length()
  490. -- set new number of lines on screen
  491.     natural nlines
  492.  
  493.     set_top_line("")
  494.     puts(SCREEN, "How many lines on screen? (25, 28, 43, 50) ")
  495.     nlines = numeric(gets(KEYB))
  496.     if nlines then
  497.     screen_length = text_rows(nlines)
  498.     if screen_length != nlines then
  499.         sound(500)
  500.     end if
  501.     normal_video()
  502.     goto_line(0, b_col) -- refresh
  503.     if screen_length != nlines then
  504.         sound(0)
  505.     end if
  506.     end if
  507. end procedure
  508.  
  509. -- searching/replacing variables
  510. boolean searching, replacing
  511. searching = FALSE
  512. replacing = FALSE
  513.  
  514. sequence prev_string
  515. prev_string = ""
  516. sequence replace_string -- new string to replace with
  517.  
  518. procedure replace(sequence old_string)
  519. -- replace old string by new
  520. -- we are currently positioned at the start of old string
  521.     sequence line
  522.  
  523.     modified = TRUE
  524.     line = buffer[b_line]
  525.     line = line[1..b_col-1] & replace_string & line[b_col+length(old_string)..
  526.                         length(line)]
  527.     buffer[b_line] = line
  528.     DisplayLine(b_line, s_line, FALSE)
  529. end procedure
  530.  
  531. function search(boolean continue)
  532. -- find a string from here to the end of the file
  533. -- return TRUE if string is found
  534.     natural col
  535.     sequence old_string
  536.  
  537.     if length(buffer) = 0 then
  538.     puts(SCREEN, "buffer empty")
  539.     return FALSE
  540.     end if
  541.     set_top_line("")
  542.     if length(prev_string) = 0 then
  543.     puts(SCREEN, "searching for:")
  544.     else
  545.     printf(SCREEN, "searching for \"%s\":", {prev_string})
  546.     end if
  547.     if continue then
  548.     old_string = ""
  549.     else
  550.     old_string = gets(KEYB)
  551.     old_string = old_string[1..length(old_string)-1]
  552.     if replacing then
  553.         set_top_line("")
  554.         puts(SCREEN, "replace with:")
  555.         replace_string = gets(KEYB)
  556.         replace_string = replace_string[1..length(replace_string)-1]
  557.     end if
  558.     end if
  559.  
  560.     normal_video()
  561.     if length(old_string) = 0 then
  562.     old_string = prev_string
  563.     end if
  564.     if length(old_string) = 0 then
  565.     return FALSE
  566.     end if
  567.     prev_string = old_string
  568.     col = match(old_string, buffer[b_line][b_col+1..length(buffer[b_line])])
  569.     if col and s_col < SCREEN_WIDTH then
  570.     -- found it on this line after current position
  571.     for i = 1 to col do
  572.         arrow_right()
  573.     end for
  574.     if replacing then
  575.         replace(old_string)
  576.     end if
  577.     return TRUE
  578.     else
  579.     -- check lines following this one
  580.     for b = b_line+1 to length(buffer) do
  581.         col = match(old_string, buffer[b])
  582.         if col then
  583.         goto_line(b, 1)
  584.         for i = 1 to col - 1 do
  585.            arrow_right()
  586.         end for
  587.         if replacing and s_col < SCREEN_WIDTH then
  588.             replace(old_string)
  589.         end if
  590.         set_top_line("")
  591.         printf(SCREEN, "searching for \"%s\":", {prev_string})
  592.         return TRUE
  593.         end if
  594.     end for
  595.     set_top_line("")
  596.     printf(SCREEN, "\"%s\" not found", {old_string})
  597.     end if
  598.     return FALSE
  599. end function
  600.  
  601. procedure show_message()
  602. -- display error message from ex.err
  603.     if length(error_message) > 0 then
  604.     set_top_line(error_message)
  605.     normal_video()
  606.     if start_line = 1 then
  607.         delay(3)
  608.         clear_top_line()
  609.     end if
  610.     end if
  611.     position(s_line, s_col)
  612. end procedure
  613.  
  614. function get_err_line()
  615. -- try to get file name & line number from ex.err
  616. -- returns file_name, sets start_line, start_col, error_message
  617.  
  618.     file_number err_file
  619.     sequence file_name
  620.     sequence err_lines
  621.     object temp_line
  622.     natural colon_pos
  623.  
  624.     err_file = open("ex.err", "r")
  625.     if err_file = -1 then
  626.     error_message = ""
  627.     else
  628.     -- read the top of the ex.err error message file
  629.     err_lines = {}
  630.     while length(err_lines) < 5 do
  631.         temp_line = gets(err_file)
  632.         if atom(temp_line) then
  633.         exit
  634.         end if
  635.         err_lines = append(err_lines, temp_line)
  636.     end while
  637.     close(err_file)
  638.     -- look for file name, line, column and error message
  639.     if length(err_lines) > 0 then
  640.         if sequence(err_lines[1]) then
  641.         colon_pos = match(".e", err_lines[1])
  642.         if colon_pos then
  643.             if err_lines[1][colon_pos+2] = 'x' then
  644.             colon_pos = colon_pos + 1
  645.             end if
  646.             file_name = err_lines[1][1..colon_pos+1]
  647.             start_line = numeric(err_lines[1][colon_pos+3..
  648.                               length(err_lines[1])])
  649.             error_message = err_lines[2]
  650.             if length(err_lines) > 3 then
  651.             start_col = find('^', err_lines[length(err_lines)-1])
  652.             end if
  653.             return file_name
  654.         end if
  655.         end if
  656.     end if
  657.     end if
  658.     return ""
  659. end function
  660.  
  661. procedure shell(sequence command, boolean wait)
  662. -- run a DOS command
  663.     bk_color(0)
  664.     text_color(7)
  665.     clear_screen()
  666.     system(command, wait)
  667.     normal_video()
  668.     while get_key() != -1 do
  669.     end while
  670. end procedure
  671.  
  672. procedure first_bold(sequence string)
  673. -- highlight first char
  674.     text_color(TOP_LINE_TEXT_COLOR)
  675.     puts(SCREEN, string[1])
  676.     text_color(TOP_LINE_TEXT_COLOR + 8)
  677.     puts(SCREEN, string[2..length(string)])
  678. end procedure
  679.  
  680. procedure get_escape(boolean help)
  681. -- process escape command
  682.     sequence command, dos_command, answer
  683.     natural line
  684.  
  685.     cursor(ED_CURSOR)
  686.  
  687.     set_top_line("")
  688.     if help then
  689.     command = "h"
  690.     else
  691.     first_bold("help  ")
  692.     first_bold("quit  ")
  693.     first_bold("save  ")
  694.     first_bold("write  ")
  695.     if dot_e then
  696.         first_bold("ex  ")
  697.     end if
  698.     first_bold("dos  ")
  699.     first_bold("new  ")
  700.     first_bold("find  ")
  701.     first_bold("replace  ")
  702.     first_bold("lines  ")
  703.     text_color(TOP_LINE_TEXT_COLOR)
  704.     puts(SCREEN, "<ddd>  <cr>: ")
  705.     command = gets(KEYB)
  706.     if length(command) = 0 then
  707.         command = " "
  708.     end if
  709.     end if
  710.  
  711.     if command[1] = 'f' then
  712.     replacing = FALSE
  713.     searching = search(FALSE)
  714.  
  715.     elsif command[1] = 'r' then
  716.     replacing = TRUE
  717.     searching = search(FALSE)
  718.  
  719.     elsif command[1] = 'q' then
  720.     if modified then
  721.         set_top_line("quit without saving changes? ")
  722.         if match("y", gets(KEYB)) then
  723.         file_name = ""
  724.         stop = TRUE
  725.         end if
  726.     else
  727.         file_name = ""
  728.         stop = TRUE
  729.     end if
  730.  
  731.     elsif command[1] = 'n' then
  732.     stop = TRUE
  733.     if modified then
  734.         set_top_line("")
  735.         printf(SCREEN, "save changes to %s? ", {file_name})
  736.         if match("y", gets(KEYB)) then
  737.         save_file(file_name)
  738.         end if
  739.     end if
  740.     blank_top_line()
  741.     puts(SCREEN, "new file name? ")
  742.     file_name = gets(KEYB)
  743.     file_name = file_name[1..length(file_name)-1] -- drop the \n
  744.  
  745.     elsif command[1] = 'w' then
  746.     save_file(file_name)
  747.     modified = FALSE
  748.     stop = FALSE
  749.  
  750.     elsif command[1] = 's' then
  751.     save_file(file_name)
  752.     if stop then
  753.         file_name = ""
  754.     end if
  755.  
  756.     elsif command[1] = 'e' and dot_e then
  757.     if modified then
  758.         save_file(file_name)
  759.         modified = FALSE
  760.         stop = FALSE
  761.     end if
  762.     -- execute the current file & return
  763.     system("del ex.err > NUL", 0)
  764.     shell("ex " & file_name, TRUE)
  765.     goto_line(0, b_col)
  766.     if compare(file_name, get_err_line()) = 0 then
  767.         goto_line(start_line, start_col)
  768.         show_message()
  769.     end if
  770.  
  771.     elsif command[1] = 'd' then
  772.     set_top_line("")
  773.     puts(SCREEN, "DOS command? ")
  774.     dos_command = gets(KEYB)
  775.     dos_command = dos_command[1..length(dos_command)-1]
  776.     shell(dos_command, TRUE)
  777.     goto_line(0, b_col) -- refresh screen
  778.  
  779.     elsif command[1] = 'h' then
  780.     set_top_line("")
  781.     dos_command = getenv("EUDIR")
  782.     if atom(dos_command) then
  783.         dos_command = "ed C:\\EUPHORIA\\DOC"
  784.     else
  785.         dos_command = "ed " & dos_command & "\\DOC"
  786.     end if
  787.     if help then
  788.         puts(SCREEN,
  789.         "That key does nothing - do you want to view the help text? ")
  790.         answer = gets(KEYB)
  791.         if answer[1] != 'n' and answer[1] != 'N' then
  792.         answer = "e"
  793.         end if
  794.     else
  795.         puts(SCREEN, "Help text for ed, or for Euphoria? (e or E): ")
  796.         answer = gets(KEYB)
  797.     end if
  798.     if answer[1] = 'E' then
  799.         shell(dos_command & "\\REFMAN.DOC", FALSE)
  800.     elsif answer[1] = 'e' then
  801.         shell(dos_command & "\\ED.DOC", FALSE)
  802.     else
  803.         normal_video()
  804.     end if
  805.     goto_line(0, b_col)
  806.  
  807.     elsif command[1] = 'l' then
  808.     new_screen_length()
  809.  
  810.     elsif command[1] >= '0' and command[1] <= '9' then
  811.     line = numeric(command)
  812.     normal_video()
  813.     goto_line(line, 1)
  814.     if not buffer_line(line) then
  815.         set_top_line("")
  816.         printf(SCREEN, "lines are 1..%d", length(buffer))
  817.         if s_line = 1 then
  818.         delay(3)
  819.         end if
  820.     end if
  821.  
  822.     else
  823.     set_top_line("")
  824.     if length(buffer) = 0 then
  825.         puts(SCREEN, "empty buffer")
  826.         delay(3)
  827.     else
  828.         printf(SCREEN, "%s line %d of %d, column %d of %d, ",
  829.                {file_name, b_line, length(buffer), s_col,
  830.             SCREEN_WIDTH})
  831.         if modified then
  832.         puts(SCREEN, "modified")
  833.         else
  834.         puts(SCREEN, "not modified")
  835.         end if
  836.         if s_line = 1 then
  837.         delay(3)
  838.         end if
  839.     end if
  840.     end if
  841.  
  842.     normal_video()
  843.     if s_line = 1 or length(buffer) = 0 then
  844.     clear_top_line()
  845.     end if
  846. end procedure
  847.  
  848.  
  849. procedure insert(char key)
  850. -- insert a character into the current line at the current position
  851.  
  852.     sequence tail
  853.     positive_int new_col, b_col_save
  854.     screen_col s_col_save
  855.  
  856.     modified = TRUE
  857.     tail = buffer[b_line][b_col..length(buffer[b_line])]
  858.     if key = CR or key = '\n' then
  859.     -- truncate this line and create a new line using tail
  860.     buffer[b_line] = buffer[b_line][1..b_col-1] & '\n'
  861.     buffer = append(buffer[1..b_line], tail) &
  862.             buffer[b_line+1..length(buffer)]
  863.     if s_line = screen_length then
  864.         s_col_save = s_col
  865.         b_col_save = b_col
  866.         arrow_down()
  867.         arrow_up()
  868.         s_col = s_col_save
  869.         b_col = b_col_save
  870.         position(s_line, s_col)
  871.     end if
  872.     DisplayScreen(b_line, s_line)
  873.     b_line = b_line + 1
  874.     s_line = s_line + 1
  875.     s_col = 1
  876.     b_col = 1
  877.     else
  878.     if key = '\t' then
  879.         new_col = tab(s_col)
  880.     else
  881.         new_col = s_col + 1
  882.     end if
  883.     if new_col > SCREEN_WIDTH then
  884.         return
  885.     else
  886.         s_col = new_col
  887.     end if
  888.     buffer[b_line] = buffer[b_line][1..b_col-1] & key & tail
  889.     DisplayLine(b_line, s_line, TRUE)
  890.     b_col = b_col + 1
  891.     end if
  892.     position(s_line, s_col)
  893. end procedure
  894.  
  895. procedure insert_string(sequence text)
  896. -- insert a bunch of characters at the current position
  897.     natural save_line, save_col
  898.  
  899.     save_line = b_line
  900.     save_col = b_col
  901.     for i = 1 to length(text) do
  902.     if text[i] = CR or text[i] = '\n' then
  903.         insert(text[i])
  904.     else
  905.         buffer[b_line] = buffer[b_line][1..b_col-1] & text[i] &
  906.                  buffer[b_line][b_col..length(buffer[b_line])]
  907.         b_col = b_col + 1
  908.         if i = length(text) then
  909.         DisplayLine(b_line, s_line, TRUE)
  910.         end if
  911.     end if
  912.     end for
  913.     goto_line(save_line, save_col)
  914. end procedure
  915.  
  916. -- expandable words & corresponding text
  917. constant expand_word = {"if", "for", "while", "elsif",
  918.             "procedure", "type", "function"},
  919.  
  920.      expand_text = {" then", "=  to  by  do", " do", " then",
  921.             "()",
  922.             "()" & CR & "    return",
  923.             "()" & CR & "    return"}
  924.  
  925. procedure try_auto_complete()
  926. -- check for a keyword that can be automatically completed
  927.     sequence word, this_line, white_space
  928.     natural first_non_blank, wordnum
  929.  
  930.     insert(' ')
  931.     if not auto_complete then
  932.     return
  933.     end if
  934.     this_line = buffer[b_line]
  935.     white_space = this_line = ' ' or this_line = '\t'
  936.     first_non_blank = find(0, white_space)
  937.     if first_non_blank > 0 and first_non_blank < b_col - 2 then
  938.     if not find(0, white_space[b_col..length(white_space)-1]) then
  939.         word = this_line[first_non_blank..b_col - 2]
  940.         wordnum = find(word, expand_word)
  941.         if wordnum > 0 then
  942.         sound(1000)
  943.         -- expandable word (only word on line)
  944.         if compare(expand_word[wordnum], "elsif") = 0 then
  945.             insert_string(expand_text[wordnum])
  946.             delay(0.07) -- or beep is too short
  947.         else
  948.             insert_string(expand_text[wordnum] & CR &
  949.                   this_line[1..first_non_blank - 1] &
  950.                   "end " & expand_word[wordnum])
  951.         end if
  952.         sound(0)
  953.         end if
  954.     end if
  955.     end if
  956. end procedure
  957.  
  958. procedure insert_kill_buffer()
  959. -- insert the kill buffer at the current position
  960. -- kill buffer could be a sequence of lines or a sequence of characters
  961.  
  962.     if length(kill_buffer) = 0 then
  963.     return
  964.     end if
  965.     if atom(kill_buffer[1]) then
  966.     -- inserting a sequence of chars
  967.     insert_string(kill_buffer)
  968.     else
  969.     -- inserting a sequence of lines
  970.     modified = TRUE
  971.     buffer = buffer[1..b_line - 1] &
  972.          kill_buffer &
  973.          buffer[b_line..length(buffer)]
  974.     DisplayScreen(b_line, s_line)
  975.     arrow_up()
  976.     arrow_down()
  977.     end if
  978. end procedure
  979.  
  980. procedure delete_line(buffer_line dead_line)
  981. -- delete a line from the buffer and update the display if necessary
  982.  
  983.     integer x
  984.  
  985.     modified = TRUE
  986.     buffer = buffer[1..dead_line-1] & buffer[dead_line+1 .. length(buffer)]
  987.     x = dead_line - b_line + s_line
  988.     if screen_line(x) then
  989.     -- dead line is on the screen at line x
  990.     if x < screen_length/3 then
  991.         -- upper portion of screen
  992.         -- faster to scroll up, then reprint top few lines
  993.         -- (although top of screen will flicker a bit)
  994.         for i = x to 2 by -1 do
  995.         -- less noisy if we blank top lines before scrolling
  996.         position(i, 1)
  997.         puts(SCREEN, BLANK_LINE)
  998.         end for
  999.         scroll(+1)
  1000.         for i = x - 1 to 1 by -1 do
  1001.         -- redisplay top lines
  1002.         DisplayLine(b_line - s_line + i, i, TRUE)
  1003.         end for
  1004.         if length(buffer) - b_line >= screen_length - s_line then
  1005.         -- show new line at bottom
  1006.         DisplayLine(b_line + screen_length - s_line,
  1007.                 screen_length, TRUE)
  1008.         end if
  1009.     else
  1010.         -- lower portion of screen
  1011.         DisplayScreen(dead_line, x)
  1012.     end if
  1013.     end if
  1014.     if b_line = 1 then
  1015.     arrow_down()
  1016.     arrow_up()
  1017.     else
  1018.     arrow_up()
  1019.     arrow_down()
  1020.     end if
  1021.     adding_to_kill = TRUE
  1022. end procedure
  1023.  
  1024.  
  1025. procedure delete_char()
  1026. -- delete the character at the current position
  1027.     char dchar
  1028.     sequence head
  1029.     natural save_b_col
  1030.  
  1031.     modified = TRUE
  1032.     dchar = buffer[b_line][b_col]
  1033.     head = buffer[b_line][1..b_col - 1]
  1034.     if dchar = '\n' and b_line < length(buffer) then
  1035.     -- join this line with the next one and delete the next one
  1036.     buffer[b_line] = head & buffer[b_line+1]
  1037.     DisplayLine(b_line, s_line, FALSE)
  1038.     save_b_col = b_col
  1039.     delete_line(b_line + 1)
  1040.     for i = 1 to save_b_col - 1 do
  1041.         arrow_right()
  1042.     end for
  1043.     else
  1044.     buffer[b_line] = head & buffer[b_line][b_col+1..length(buffer[b_line])]
  1045.     if length(buffer[b_line]) = 0 then
  1046.         delete_line(b_line)
  1047.     else
  1048.         DisplayLine(b_line, s_line, FALSE)
  1049.         if b_col > length(buffer[b_line]) then
  1050.         arrow_left()
  1051.         end if
  1052.     end if
  1053.     end if
  1054.     adding_to_kill = TRUE
  1055. end procedure
  1056.  
  1057.  
  1058. function good(extended_char key)
  1059. -- return TRUE if key should be processed
  1060.     if find(key, CONTROL_CHARS & '\t' & CR) or (key >= ' ' and key <= 127) then
  1061.     return TRUE
  1062.     else
  1063.     return FALSE
  1064.     end if
  1065. end function
  1066.  
  1067. procedure edit_file()
  1068. -- edit the file in buffer
  1069.     extended_char key
  1070.  
  1071.     position(1, 1)
  1072.     s_line = 1
  1073.     s_col = 1
  1074.     b_line = 1
  1075.     b_col = 1
  1076.     if length(buffer) > 0 then
  1077.     if start_line > 0 then
  1078.         if start_line > length(buffer) then
  1079.         start_line = length(buffer)
  1080.         end if
  1081.         goto_line(start_line, start_col)
  1082.         show_message()
  1083.     end if
  1084.     end if
  1085.     cursor(ED_CURSOR)
  1086.     stop = FALSE
  1087.     while not stop do
  1088.     key = get_key()
  1089.  
  1090.     if good(key) then
  1091.         -- normal key
  1092.  
  1093.         -- hide cursor while we update the screen
  1094.         cursor(NO_CURSOR)
  1095.  
  1096.         if length(buffer) = 0 and key != ESCAPE then
  1097.         -- empty buffer
  1098.         -- only allowed action is to insert something
  1099.         if key = INSERT or not find(key, CONTROL_CHARS) then
  1100.             -- initialize buffer
  1101.             buffer = {{'\n'}} -- one line with \n
  1102.             b_line = 1
  1103.             b_col = 1
  1104.             s_line = 1
  1105.             s_col = 1
  1106.             if key = INSERT then
  1107.             insert_kill_buffer()
  1108.             else
  1109.             insert(key)
  1110.             end if
  1111.             DisplayLine(1, 1, FALSE)
  1112.         end if
  1113.  
  1114.         elsif key = DELETE then
  1115.         if not adding_to_kill then
  1116.             kill_buffer = {buffer[b_line][b_col]}
  1117.         elsif sequence(kill_buffer[1]) then
  1118.             -- we were building up deleted lines,
  1119.             -- but now we'll switch to chars
  1120.             kill_buffer = {buffer[b_line][b_col]}
  1121.         else
  1122.             kill_buffer = append(kill_buffer, buffer[b_line][b_col])
  1123.         end if
  1124.         delete_char()
  1125.  
  1126.         elsif key = CONTROL_DELETE then
  1127.         if not adding_to_kill then
  1128.             kill_buffer = {buffer[b_line]}
  1129.         elsif atom(kill_buffer[1]) then
  1130.             -- we were building up deleted chars,
  1131.             -- but now we'll switch to lines
  1132.             kill_buffer = {buffer[b_line]}
  1133.         else
  1134.             kill_buffer = append(kill_buffer, buffer[b_line])
  1135.         end if
  1136.         delete_line(b_line)
  1137.  
  1138.         else
  1139.         if key = ARROW_DOWN then
  1140.             arrow_down()
  1141.  
  1142.         elsif key = ARROW_UP then
  1143.             arrow_up()
  1144.  
  1145.         elsif key = INSERT then
  1146.             insert_kill_buffer()
  1147.  
  1148.         elsif key = ARROW_LEFT then
  1149.             arrow_left()
  1150.  
  1151.         elsif key = ARROW_RIGHT then
  1152.             arrow_right()
  1153.  
  1154.         elsif key = ' ' then
  1155.             try_auto_complete()
  1156.  
  1157.         elsif key = BS then
  1158.             arrow_left()
  1159.             delete_char()
  1160.  
  1161.         elsif key = PAGE_DOWN then
  1162.             page_down()
  1163.  
  1164.         elsif key = PAGE_UP then
  1165.             page_up()
  1166.  
  1167.         elsif key = HOME then
  1168.             goto_line(1, 1)
  1169.  
  1170.         elsif key = END then
  1171.             goto_line(length(buffer), 1)
  1172.  
  1173.         elsif key = ESCAPE then
  1174.             -- special command
  1175.             get_escape(FALSE)
  1176.  
  1177.         elsif key = CR then
  1178.             if searching then
  1179.             searching = search(TRUE)
  1180.             normal_video()
  1181.             searching = TRUE -- avoids accidental <CR> insertion
  1182.             else
  1183.             insert(key)
  1184.             end if
  1185.  
  1186.         else
  1187.             insert(key)
  1188.  
  1189.         end if
  1190.  
  1191.         adding_to_kill = FALSE
  1192.  
  1193.         end if
  1194.  
  1195.         if key != CR and key != ESCAPE then
  1196.         searching = FALSE
  1197.         end if
  1198.         position(s_line, s_col)
  1199.         cursor(ED_CURSOR)
  1200.     elsif key != -1 then
  1201.         -- illegal key pressed
  1202.         get_escape(TRUE)  -- give him some help
  1203.     end if
  1204.     end while
  1205. end procedure
  1206.  
  1207. function delete_trailing_white(sequence name)
  1208. -- get rid of blanks, tabs, newlines at end of string
  1209.     while find(name[length(name)], "\n\r\t ") do
  1210.     name = name[1..length(name)-1]
  1211.     if length(name) = 0 then
  1212.         exit
  1213.     end if
  1214.     end while
  1215.     return name
  1216. end function
  1217.  
  1218. function lower(sequence s)
  1219. -- convert to lower case
  1220.     for i = 1 to length(s) do
  1221.     if s[i] >= 'A' and s[i] <= 'Z' then
  1222.         s[i] = s[i] + 'a' - 'A' 
  1223.     end if
  1224.     end for
  1225.     return s
  1226. end function
  1227.  
  1228. procedure ed(sequence command)
  1229. -- editor main procedure
  1230. -- ed.ex is executed by ed.bat
  1231. -- command line will be:
  1232. --    ex ed.ex              - get filename etc. from ex.err
  1233. --    ex ed.ex filename     - filename specified
  1234.  
  1235.     file_number file_no
  1236.  
  1237.     start_line = 0
  1238.     start_col = 0
  1239.  
  1240.     if length(command) >= 3 then
  1241.     file_name = lower(command[3])
  1242.     else
  1243.     file_name = get_err_line()
  1244.     end if
  1245.     if length(file_name) = 0 then
  1246.     -- we still don't know the file name - so ask user
  1247.     puts(SCREEN, "file name? ")
  1248.     cursor(ED_CURSOR)
  1249.     file_name = gets(KEYB)
  1250.     end if
  1251.     file_name = delete_trailing_white(file_name)
  1252.     if length(file_name) = 0 then
  1253.     stop = TRUE
  1254.     return -- file_name was just whitespace - quit
  1255.     end if
  1256.     file_no = open(file_name, "r")
  1257.  
  1258.     -- turn off multi_color & auto_complete for non .e files
  1259.     multi_color = TRUE
  1260.     dot_e = FALSE
  1261.     if not config[VC_COLOR] then
  1262.     multi_color = FALSE -- mono monitor
  1263.     end if
  1264.     file_name = file_name & ' '
  1265.     dot_e = match(".ex ", file_name) or match(".e ", file_name) or
  1266.         match(".pro ", file_name)
  1267.     file_name = file_name[1..length(file_name)-1]
  1268.     if not dot_e then
  1269.     multi_color = FALSE
  1270.     auto_complete = FALSE
  1271.     end if
  1272.     if multi_color then
  1273.     init_class()
  1274.     end if
  1275.  
  1276.     top_line_set = FALSE
  1277.     adding_to_kill = FALSE
  1278.     modified = FALSE
  1279.     view_only = FALSE
  1280.     normal_video()
  1281.     wrap(0)
  1282.     if file_no = -1 then
  1283.     buffer = {}
  1284.     clear_screen()
  1285.     puts(SCREEN, "new file - ")
  1286.     puts(SCREEN, file_name)
  1287.     delay(1.5)
  1288.     position(1, 1)
  1289.     puts(SCREEN, BLANK_LINE)
  1290.     else
  1291.     position(1, 1)
  1292.     cursor(NO_CURSOR)
  1293.     read_file(file_no)
  1294.     close(file_no)
  1295.     end if
  1296.     edit_file()
  1297. end procedure
  1298.  
  1299. config = video_config()
  1300.  
  1301. if config[VC_XPIXELS] > 0 then
  1302.     if graphics_mode(3) then
  1303.     end if
  1304.     config = video_config()
  1305. end if
  1306.  
  1307. screen_length = config[VC_LINES]
  1308.  
  1309. ed(command_line())
  1310. while length(file_name) > 0 do
  1311.     ed({"ex", "ed.e" , file_name})
  1312. end while
  1313.  
  1314. -- exit editor
  1315. if screen_length != 25 then
  1316.     screen_length = text_rows(25)
  1317. end if
  1318. bk_color(0)
  1319. text_color(0)
  1320. position(screen_length, 1)
  1321. puts(SCREEN, BLANK_LINE)
  1322. position(screen_length, 1)
  1323. text_color(7)
  1324. puts(SCREEN, " \n")
  1325.  
  1326.