emode1.red 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198
  1. %
  2. % EMODE1.RED - Screen editor for PSL
  3. %
  4. % Authors: W. Galway, M. Griss, R. Armantrout
  5. % Symbolic Computation Group
  6. % Computer Science Dept.
  7. % University of Utah
  8. % Date: 8 June 1982
  9. % Copyright (c) 1982 University of Utah
  10. %
  11. % This file is the main body of code for the screen oriented editor
  12. % EMODE. This editor is patterned after EMACS from MIT and also after EM
  13. % written by Robert Armantrout for use on small Unix systems.
  14. FLUID '(
  15. Two_window_midpoint % Gives location (roughly) of dividing line for two
  16. % window mode.
  17. FirstCall % NIL means re-entering EMODE, T means first time.
  18. kill_opers % list of (names of) dispatch routines that kill
  19. % text. NEEDS MORE DOCUMENTATION!
  20. kill_buffer_ring % Vector of vectors of strings--holds recently
  21. % deleted text.
  22. kill_ring_index % Pointer to the most recent "kill buffer".
  23. last_yank_point % Vector of [buffer lineindex point], giving location
  24. % where last "yank" occured.
  25. last_operation % The "last" routine dispatched to (before the
  26. % "current operation").
  27. runflag % EMODE continues READ/DISPATCH/REDISPLAY until NIL
  28. SelfInsertCharacter % The last character typed (dispatched on?)
  29. last_buffername % Name (a string) of the last buffer visited.
  30. !*DBG % T for debugging (not really implemented).
  31. );
  32. FirstCall := 'T; % To force init of all structures
  33. last_buffername := "MAIN"; % Set up default, NEEDS more thought?
  34. !*DBG := NIL; % No debug
  35. % 8 entries in the kill ring.
  36. kill_buffer_ring := MkVect(7);
  37. kill_ring_index := 0;
  38. kill_opers :=
  39. '(
  40. kill_line
  41. kill_region
  42. kill_forward_word
  43. kill_backward_word
  44. kill_forward_sexpr
  45. kill_backward_sexpr
  46. );
  47. Symbolic Procedure DBG1(x);
  48. If !*DBG then Print LIST("-> ",x);
  49. Symbolic Procedure DBG2(x);
  50. If !*DBG then Print LIST("<- ",x);
  51. FLUID '(UserSetupRoutine);
  52. UserSetupRoutine := NIL;
  53. Symbolic Procedure EMODE();
  54. % Rebind channels to use "EMODE buffers", then return. Use function
  55. % "OldFACE" to switch back to original channels. (OldFace is typically
  56. % bound to M-C-Z.)
  57. begin scalar chnl;
  58. if FirstCall then
  59. <<
  60. FirstCall := NIL;
  61. % Why doesn't ALL this code go into EMODEinitialize? Sigh.
  62. EMODEinitialize();
  63. % Any ideas where best to place the following call?
  64. % ANSWER is, GET RID OF IT, it's not a proper method to allow
  65. % customizations, since multiple users can't use it.
  66. % Current practice is for UserSetupRoutine to be a fluid--set to name
  67. % of procedure to execute inside user's initialization routine, NIL
  68. % outside of that scope.
  69. if not null UserSetupRoutine then
  70. Apply(UserSetupRoutine,NIL);
  71. % Open up special channel for buffer I/O. Arguments are
  72. % expressions to be evaluated to get name of input buffer, name of
  73. % output buffer, and a window to "pop up" for the output buffer.
  74. EmodeBufferChannel :=
  75. OpenBufferChannel('CurrentBufferName,
  76. ''OUT_WINDOW,
  77. NIL
  78. );
  79. >>;
  80. EchoOff();
  81. !*EMODE := T; % HERE??? Set FLUID flag to show "EMODE running".
  82. % ErrorSet could be used to make sure echos get turned back on.
  83. % Use system's idea of backtrace
  84. ERRORSET('(FullRefresh), T, !*BACKTRACE);
  85. % (Need to do something if an error!)
  86. SelectEmodeChannels();
  87. end;
  88. % Save old channels at load (compile) time?
  89. OldStdIn := STDIN!*;
  90. OldStdOut := STDOUT!*;
  91. OldErrOut := ErrOut!*;
  92. Symbolic Procedure EMODEinitialize();
  93. % Sets up data structures for starting up EMODE. DOESN'T affect terminal
  94. % mode.
  95. begin
  96. SetScreen(); % Initialise Screen Space
  97. SetupInitialBufferStructure();
  98. % A kludge (!?) to implement a pop-up break window.
  99. % Create the window to look into the "break" buffer.
  100. BreakWindow :=
  101. FramedWindowDescriptor('BREAK,
  102. % Starts at column 39, Near top of screen
  103. Coords(39,1),
  104. % Dimensions are roughly 40 wide by 10 high.
  105. Coords(39,9));
  106. % Very carefully (?) redefine the break handler.
  107. if FUnBoundP('pre_emode_break) then
  108. % Work with !*usermode OFF, so no objection is made as we redefine
  109. % Break. Also !*REDEFMSG OFF so that it happens "quietly".
  110. begin scalar !*USERMODE, !*REDEFMSG;
  111. CopyD('pre_emode_break,'Break);
  112. CopyD('Break, 'EMODEbreak);
  113. end;
  114. OneWindow(); % Initialize in one-window mode.
  115. end;
  116. Symbolic Procedure EMODEbreak();
  117. % Redefined break handler for EMODE.
  118. Begin Scalar Oldwindow;
  119. Oldwindow:=CurrentWindowdescriptor;
  120. SelectWindow BreakWindow;
  121. !$BeginningOfBuffer(); % Place point at start of buffer.
  122. % Transfer control to the original break handler. Catch may be
  123. % overkill, but is more certain to catch errors and stuff.
  124. Catch(NIL, pre_emode_break() );
  125. % When finished, "clean" our screen off.
  126. remove_current_view();
  127. SelectWindow Oldwindow; % Back to the window we originally had.
  128. end;
  129. Symbolic Procedure OldFACE();
  130. % Causes sytem to quit using "Rlisp Interface" mode, go back to "normal mode".
  131. <<
  132. SelectOldChannels();
  133. EchoOn();
  134. !*EMODE := NIL; % HERE???
  135. leave_dispatch_loop(); % Set flag to cause EMODE to exit.
  136. >>;
  137. Symbolic Procedure SelectEmodeChannels();
  138. % Select channels that read from and write to EMODE buffers.
  139. <<
  140. % Most channels just default to these? ErrOut!* is an exception, so
  141. % fix it.
  142. STDIN!* := EmodeBufferChannel;
  143. STDOUT!* := EmodeBufferChannel;
  144. ErrOut!* := EmodeBufferChannel;
  145. RDS STDIN!*; % Select the channels, "EMODE1" is called when read
  146. % routines invoke the "editor routine" for the newly
  147. % selected channels.
  148. WRS STDOUT!*;
  149. >>;
  150. Symbolic Procedure OldEMODE();
  151. % "Old fashioned" version of invoking EMODE. "New" version invokes "Rlisp
  152. % interface" instead. This version is being kept for documentation--it's
  153. % basically obsolete.
  154. <<
  155. If FirstCall then
  156. <<
  157. EMODEinitialize();
  158. FirstCall := NIL;
  159. >>;
  160. % Any ideas where best to place the following call?
  161. % Current practice is for UserSetupRoutine to be a fluid--set to name
  162. % of procedure to execute inside user's initialization routine, NIL
  163. % outside of that scope.
  164. if not null UserSetupRoutine then
  165. Apply(UserSetupRoutine,NIL);
  166. % A bit of a kludge to make sure echos get turned back on.
  167. ECHOoff();
  168. % Do full refresh on restart, clean up junk on screen.
  169. ERRORSET('(FullRefresh), T, !*BACKTRACE);
  170. ERRORSET('(EMODE1 ""),T,!*BACKTRACE); % Use system's idea of backtrace
  171. ECHOon();
  172. >>;
  173. Symbolic Procedure EMODE1(msg);
  174. % "msg" is an initial message to put into the "message window".
  175. begin
  176. show_message(msg);
  177. EMODEdispatchLoop(); % Execute read/dispatch/refresh loop until
  178. % "done"
  179. end;
  180. Symbolic Procedure EMODEdispatchLoop();
  181. % Execute read/dispatch/refresh loop while fluid "runflag" is true.
  182. begin scalar runflag;
  183. runflag := T;
  184. while runflag do
  185. <<
  186. % Note that it's actually a refresh/read/dispatch loop.
  187. optional_refresh();
  188. % READ and then dispatch on character
  189. ERRORSET('(DISPATCHER),T,T);
  190. % Refresh screen (if no user input is pending).
  191. >>;
  192. PutLine(); % Make sure everything's put away!
  193. end;
  194. Symbolic Procedure FreshEMODE(); % Force Full Init
  195. <<
  196. FirstCall := T;
  197. EMODE()
  198. >>;
  199. %. --------------- EMODE error handles
  200. Symbolic Procedure EMODEerror(x);
  201. Error(666," *** EMODE *** " . x);
  202. %. ---------- Buffer Management ----------
  203. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  204. %
  205. FLUID '(
  206. BufferNames % Buffer names are kept on the fluid association
  207. % list "BufferNames", associated with a list of
  208. % variable bindings (an "environment") for that
  209. % buffer.
  210. % Buffers are described by the following "per buffer" variables. (The
  211. % bindings of the variables depend on the current "buffer" environment.)
  212. CurrentBufferText % Vector of lines making up the buffer.
  213. % (CurrentLine is magic, see below.)
  214. CurrentBufferSize % Number of lines actually within buffer
  215. CurrentLine % The contents (text) of current line--as a linked
  216. % list of character codes. (Takes precedence over
  217. % whatever is contained in the text vector.)
  218. CurrentLineIndex % Index of "current line" within buffer.
  219. point % Number of chars to the left of point within
  220. % CurrentLine.
  221. );
  222. %
  223. % Associated with a Buffer should be:
  224. % Its MODE (or is this WINDOW attribute?)
  225. % names of referencing windows (if any)?
  226. % Associated File (or is this WINDOW attribute?)
  227. %.------------- Basic Buffer Structure ----------------
  228. Symbolic Procedure SetBufferText(i,text);
  229. % Store text into buffer at i. (Text is a string.)
  230. CurrentBufferText[i] := text;
  231. Symbolic Procedure GetBufferText(i);
  232. % Return the text stored in buffer at i.
  233. CurrentBufferText[i];
  234. % Perhaps this is carrying "modularity" a bit too far? [But, I think not.
  235. % WFG]
  236. Symbolic Procedure NextIndex(i);
  237. % Put in bounds checking?
  238. i + 1;
  239. Symbolic Procedure PreviousIndex(i);
  240. i - 1;
  241. Symbolic Procedure SetupInitialBufferStructure();
  242. % Creates initial buffers for EMODE. Should be done at loadtime?
  243. <<
  244. BufferNames := NIL; % Association list of (Name . BufferDescriptor)
  245. CurrentBufferName := NIL;
  246. % Second argument does the actual work of creating the buffer.
  247. CreateBuffer('MAIN, 'create_rlisp_buffer);
  248. CreateBuffer('OUT_WINDOW, 'create_rlisp_buffer);
  249. % Not clear what the appropriate mode is, sure to change depending on
  250. % what's prompted for.
  251. CreateBuffer('PROMPT_BUFFER, 'create_rlisp_buffer);
  252. % Perhaps a "null" mode makes more sense here, but it's dangerous,
  253. % since if person edits this buffer, there's no character defined to
  254. % get out. Needs more thought (as usual)!
  255. CreateBuffer('MESSAGE_BUFFER, 'create_rlisp_buffer);
  256. % Create the BREAK (input) buffer. (I anticipate a break output
  257. % buffer one of these days.)
  258. CreateBuffer('BREAK, 'create_rlisp_buffer);
  259. % Set up the buffer text.
  260. SelectBuffer 'BREAK;
  261. % Include semicolons in the text so that both the Lisp and Rlisp
  262. % readers can handle the break buffer.
  263. Insert_string("A ;% To abort");
  264. !$CRLF();
  265. Insert_string("Q ;% To quit");
  266. !$CRLF();
  267. Insert_string("T ;% To traceback");
  268. !$CRLF();
  269. Insert_string("I ;% Trace interpreted stuff");
  270. !$CRLF();
  271. Insert_string("R ;% Retry");
  272. !$CRLF();
  273. Insert_string("C ;% Continue, using last value");
  274. !$CRLF();
  275. Insert_string("? ;% For more help");
  276. !$CRLF();
  277. % Start by editing in the MAIN buffer.
  278. SelectBuffer('MAIN);
  279. EstablishCurrentMode();
  280. >>;
  281. Symbolic Procedure SelectBuffer(BufferName);
  282. % Select a buffer. (Restore its environment after saving old.)
  283. % (Some confusing subtle points have to be resolved, concerning selecting a
  284. % buffer "BufferName", where "BufferName" equals "CurrentBufferName". Current
  285. % "solution" is a kludge?)
  286. % As an example of the sort of thing that can happen--it would seem
  287. % unnecesary to restore the environment if we are selecting the
  288. % CurrentBufferName. BUT, that's not the case in the current
  289. % implementation, since (for example) the REFRESH algorithm will select a
  290. % window--which restores the "CurrentBufferName", and after selecting
  291. % window, it continues to call select the buffer. (Attempted cure for this
  292. % is to store the CurrentBufferName under some other ID in the window
  293. % environment. Ultimate cure for this is to refer to buffers, and windows,
  294. % by their values (environment association lists or whatever), rather than
  295. % by some name.)
  296. begin scalar BufferEnv;
  297. If BufferName neq CurrentBufferName then
  298. <<
  299. if (BufferEnv := atsoc(BufferName,BufferNames)) then
  300. % (The environment part of (name . env) pair.)
  301. BufferEnv := cdr BufferEnv
  302. else
  303. return
  304. EMODEError list("Buffer ", BufferName, " can't be selected");
  305. if CurrentBufferName then
  306. DeSelectBuffer CurrentBufferName;
  307. RestoreEnv BufferEnv; % Restore environment for buffer
  308. CurrentBufferName := BufferName;
  309. >>;
  310. end;
  311. Symbolic Procedure DeSelectBuffer(BufferName);
  312. begin scalar BufferEnv;
  313. if null (BufferEnv := assoc(BufferName,BufferNames)) then
  314. Return Prin2t LIST("Buffer doesn't exist to deselect:",BufferName);
  315. SaveEnv(cdr BufferEnv); % Save current buffer bindings (uses RPLACD)
  316. CurrentBufferName := NIL;
  317. end;
  318. %. ------------ Line and Char Counting ----------------
  319. % Count lines from P1 to P2 (0 if P1 = P2).
  320. Symbolic Procedure CountLinesFrom(P1,P2);
  321. P2 - P1; % This was harder when a linked list was
  322. % used (in the past) to represent buffers.
  323. % Returns number of lines in current buffer.
  324. Symbolic Procedure CountAllLines;
  325. CurrentBufferSize;
  326. % Returns number of lines from current line (inclusive) to end of buffer.
  327. Symbolic Procedure CountLinesLeft;
  328. CurrentBufferSize - CurrentLineIndex;
  329. % Returns number of lines before the current line.
  330. Symbolic Procedure CountLinesBefore;
  331. CurrentLineIndex; % zero origin indexing
  332. % -----------CHARACTER Lines (line contents)---------
  333. % Some lines are currently represented as a linked list of ASCII characters .
  334. % Insert SelfInsertCharacter into the current line, update point.
  335. Symbolic Procedure InsertSelfCharacter();
  336. InsertCharacter SelfInsertCharacter;
  337. Symbolic Procedure InsertCharacter(ch);
  338. <<
  339. if ch = char EOL then
  340. !$CRLF()
  341. else
  342. <<
  343. CurrentLine := InsertListEntry(CurrentLine,Point,ch);
  344. Point := Point + 1;
  345. >>;
  346. >>;
  347. Symbolic Procedure transpose_characters();
  348. % Transpose the last two characters, if we're at the end of the line, or if
  349. % a character was just inserted. Otherwise, transpose the characters on
  350. % either side of point.
  351. begin scalar ch1, ch2;
  352. if point = length CurrentLine OR
  353. last_operation eq 'InsertSelfCharacter
  354. then
  355. !$BackwardCharacter();
  356. % Gripe if not enough to the left. (??)
  357. if point < 1 then
  358. return Ding();
  359. ch2 := CurrentCharacter();
  360. !$BackwardCharacter();
  361. ch1 := CurrentCharacter();
  362. DeleteCharacter();
  363. DeleteCharacter();
  364. InsertCharacter(ch2);
  365. InsertCharacter(ch1);
  366. end;
  367. Symbolic Procedure AppendLine(contents, PreviousLine);
  368. % Append line with "contents" just past "PreviousLine"
  369. begin integer putindx;
  370. CurrentBufferSize := CurrentBufferSize + 1;
  371. % Grow the buffer if necessary.
  372. if CurrentBufferSize > size(CurrentBufferText) then
  373. CurrentBufferText := concat(CurrentBufferText, MkVect(63));
  374. putindx := CurrentBufferSize - 1; % Shuffle from the back
  375. while putindx > PreviousLine + 1 do
  376. <<
  377. SetBufferText(putindx, GetBufferText(putindx - 1));
  378. putindx := putindx - 1;
  379. >>;
  380. % Put new line just past "PreviousLine".
  381. SetBufferText(putindx, contents);
  382. end;
  383. Symbolic Procedure Insert_string(strng);
  384. % Insert a string into the buffer, starting at point, update point to be
  385. % just past string.
  386. begin scalar newline;
  387. PutLine(); % Pack the current line in (as a string)
  388. newline := GetBufferText(CurrentLineIndex); % Grab it back.
  389. newline := nary!-concat(
  390. sub(newline,0,point-1), % head of old string
  391. strng, % new string
  392. % and tail of old string.
  393. sub(newline, point, size(newline) - point)
  394. );
  395. % Update point
  396. point := point + size(strng) + 1;
  397. % Put away the new line
  398. SetBufferText(CurrentLineIndex, newline);
  399. GetLine(CurrentLineIndex); % Get it back (I know, wierd!)
  400. end;
  401. Procedure append_line(s);
  402. % Append string as a new line in the current buffer.
  403. <<
  404. !$CRLF();
  405. insert_string(s);
  406. >>;
  407. Symbolic Procedure InsertLine(linetext);
  408. % Insert line before current line, then position past newly inserted line.
  409. % (An efficiency crock?)
  410. % "linetext" is a linked list of character codes (for now).
  411. <<
  412. !$BeginningOfLine();
  413. !$CRLF();
  414. !$BackwardLine();
  415. CurrentLine := linetext;
  416. PutLine();
  417. !$ForwardLine();
  418. >>;
  419. Symbolic Procedure insert_kill_buffer();
  420. % Insert the "kill_buffer" into the current location (i.e. "yank"). Record
  421. % location of "point" after the yank, so that unkill_previous can avoid
  422. % doing stuff if not at the last yank point.
  423. % (This code isn't very efficient, it's an order(M*N) algorithm, when it
  424. % should really be order(N)--should be reworked.)
  425. begin scalar kill_buffer;
  426. % Avoid doing anything if kill_buffer not set up.
  427. kill_buffer := kill_buffer_ring[kill_ring_index];
  428. if kill_buffer then
  429. <<
  430. SetMark();
  431. PutLine();
  432. Insert_string(kill_buffer[0]);
  433. if size(kill_buffer) > 0 then
  434. <<
  435. GetLine(CurrentLineIndex);
  436. !$CRLF();
  437. !$BackwardLine();
  438. for i := 1 : size(kill_buffer) - 1 do
  439. <<
  440. AppendLine(kill_buffer[i], CurrentLineIndex);
  441. CurrentLineIndex := NextIndex(CurrentLineIndex);
  442. >>;
  443. CurrentLineIndex := NextIndex(CurrentLineIndex);
  444. GetLine(CurrentLineIndex); % KLUDGE!
  445. point := 0; % More kludge
  446. Insert_string(kill_buffer[size(kill_buffer)]);
  447. >>;
  448. GetLine(CurrentLineIndex);
  449. >>;
  450. % Note precise location of this yank, create the pointer if NIL.
  451. if null last_yank_point then
  452. last_yank_point := MkVect(2);
  453. last_yank_point[0] := CurrentBufferName;
  454. last_yank_point[1] := CurrentLineIndex;
  455. last_yank_point[2] := point;
  456. end;
  457. Symbolic Procedure unkill_previous();
  458. % Delete (without saving away) the current region, and then unkill (yank)
  459. % the "previous" entry in the kill ring. "Ding" if not at location of last
  460. % yank.
  461. if null last_yank_point
  462. OR not(CurrentBufferName eq last_yank_point[0])
  463. OR not(CurrentLineIndex equal last_yank_point[1])
  464. OR not(point equal last_yank_point[2])
  465. then
  466. Ding()
  467. else
  468. <<
  469. Delete_or_copy(T, CurrentLineIndex, point, MarkLineIndex, MarkPoint);
  470. rotate_kill_index(-1);
  471. insert_kill_buffer();
  472. >>;
  473. Symbolic Procedure InsertListEntry(oldlist,pos,val);
  474. % Insert val into oldlist at position pos (or at end of list if pos too big)
  475. if null oldlist then list(val)
  476. else if pos = 0 then cons( val , oldlist )
  477. else cons( car oldlist ,
  478. InsertListEntry( cdr oldlist , pos-1 , val ));
  479. % Delete character at point in current line
  480. Symbolic Procedure DeleteCharacter();
  481. CurrentLine := DeleteListEntry(CurrentLine,Point);
  482. % Delete list entry at pos (or do nothing if pos past end of list)
  483. Symbolic Procedure DeleteListEntry(oldlist,pos);
  484. if null oldlist then NIL
  485. else if pos = 0 then cdr oldlist
  486. else cons(car oldlist,
  487. DeleteListEntry(cdr oldlist , pos-1 ));
  488. % Return character at point in current line.
  489. Symbolic Procedure CurrentCharacter();
  490. begin scalar linetail;
  491. linetail := Tail(CurrentLine,point);
  492. return if null linetail then
  493. char EOL
  494. else
  495. car linetail;
  496. end;
  497. % Return first n entries at head of x.
  498. Symbolic Procedure Head(x,n);
  499. if null x then
  500. NIL
  501. else if n = 0 then
  502. NIL
  503. else
  504. cons(car x , Head(cdr x,n-1));
  505. Symbolic Procedure PackLine(lst);
  506. % Pack a list of character codes into a string.
  507. List2String lst;
  508. Symbolic Procedure UnpackLine(str);
  509. % Unpack a string, or NIL, into a list of character codes.
  510. if null str then
  511. NIL % SPECIAL CASE
  512. else
  513. String2List str;
  514. Symbolic Procedure PutLine();
  515. % Put away the magical current line (may want to check for necessity?)
  516. SetBufferText(CurrentLineIndex, PackLine CurrentLine);
  517. Symbolic Procedure GetLine(x);
  518. % "UNPACK" line pointed to by x
  519. <<
  520. CurrentLine := UnpackLine GetBufferText(x);
  521. CurrentLineIndex := x;
  522. >>;
  523. Symbolic Procedure SelectLine(x);
  524. % Select a new current line at location x.
  525. if (x neq CurrentLineIndex) then % If a non-trivial operation
  526. <<
  527. PutLine(); % Put away the old line
  528. GetLine(x); % and fetch the new one.
  529. >>;
  530. Symbolic Procedure delete_or_copy(del_flg, line1,point1, line2, point2);
  531. % Delete (if del_flg is non-NIL) or copy (otherwise) the text between
  532. % line1, point1 (column) through line2, point2, inclusive. Return the
  533. % deleted (or copied) text as a pair of ((direction_of_deletion) .
  534. % (vector_of_strings)). The "direction" is +1 if (line1, point1) <=
  535. % (line2, point2), and -1 otherwise. Update (CurrentLineIndex, point) if
  536. % it lies within the deleted region.
  537. begin scalar deleted_text,dir , text_length, indx, tmp, tmp2;
  538. PutLine();
  539. dir := 1; % Default
  540. % Make sure that (line1, point1) comes first.
  541. if line2 < line1 then
  542. <<
  543. dir := -1;
  544. tmp := line2;
  545. line2 := line1;
  546. line1 := tmp;
  547. tmp := point2;
  548. point2 := point1;
  549. point1 := tmp;
  550. >>
  551. else if (line1 = line2) and (point2 < point1) then
  552. <<
  553. dir := -1;
  554. tmp := point2;
  555. point2 := point1;
  556. point1 := tmp;
  557. >>;
  558. % Update (CurrentLineIndex, point), if it lies in deleted region.
  559. if
  560. del_flg
  561. and
  562. ((line1 < CurrentLineIndex)
  563. or ((line1 = CurrentLineIndex) and (point1 < point)))
  564. and
  565. ((CurrentLineIndex < line2)
  566. or ((CurrentLineIndex = line2) and (point <= point2)))
  567. then
  568. <<
  569. CurrentLineIndex := line1;
  570. point := point1;
  571. >>;
  572. % Similarly for "mark". (A kludge, this should at least be a macro.)
  573. if
  574. del_flg
  575. and
  576. ((line1 < MarkLineIndex)
  577. or ((line1 = MarkLineIndex) and (point1 < MarkPoint)))
  578. and
  579. ((MarkLineIndex < line2)
  580. or ((MarkLineIndex = line2) and (MarkPoint <= point2)))
  581. then
  582. <<
  583. MarkLineIndex := line1;
  584. MarkPoint := point1;
  585. >>;
  586. % Get length of deleted text, in lines, suitable for 0 indexing (i.e. 0
  587. % is "length" for one line of text).
  588. text_length := line2 - line1;
  589. deleted_text := MkVect(text_length);
  590. tmp := GetBufferText(line1); % Grab first line of region to delete.
  591. % Things are simple if deletion all on the same line.
  592. if text_length = 0 then
  593. <<
  594. if del_flg then
  595. SetBufferText(line1,
  596. concat(sub(tmp, 0, point1-1),
  597. sub(tmp, point2, size(tmp) - point2)));
  598. % Refetch "current line".
  599. GetLine(CurrentLineIndex);
  600. deleted_text[0] := sub(tmp, point1, point2-point1-1);
  601. return dir . deleted_text;
  602. >>;
  603. % deleted_text[0] gets everything on line1 to the right of point1, and
  604. % the new line gets everything to the left (with more to be tacked on
  605. % later).
  606. deleted_text[0] := sub(tmp, point1, size(tmp) - point1);
  607. % Store away the deleted part of the last line of the region.
  608. tmp2 := GetBufferText(line2);
  609. deleted_text[text_length] := sub(tmp2, 0, point2-1);
  610. % and tack the tail onto the head of undeleted line1.
  611. if del_flg then
  612. SetBufferText(line1, concat(sub(tmp, 0, point1 - 1),
  613. sub(tmp2, point2, size(tmp2)-point2)));
  614. % Copy rest of text into deleted_text.
  615. for i := line1+1 : line2-1 do
  616. deleted_text[i-line1] := GetBufferText(i);
  617. % Shuffle all the text, deleting the lines between line1 and line2.
  618. if del_flg then
  619. <<
  620. indx := 1;
  621. while not EndOfBufferP(line2+indx) do
  622. <<
  623. SetBufferText(line1+indx, GetBufferText(line2 + indx));
  624. indx := indx + 1;
  625. >>;
  626. % Note size change (but don't bother to decrease the actual size of the
  627. % vector holding the text, for now).
  628. CurrentBufferSize := CurrentBufferSize - (line2 - line1);
  629. >>;
  630. % Refetch "current line".
  631. GetLine(CurrentLineIndex);
  632. return dir . deleted_text;
  633. end;
  634. Symbolic Procedure DeleteTextEntry(x);
  635. % Delete the line at x (delete entry from vector of lines).
  636. % Depends on CurrentLine being "put away".
  637. <<
  638. if not EndOfBufferP(x) then
  639. <<
  640. x := x+1; % Shuffle the elements down one entry.
  641. while not EndOfBufferP(x) do
  642. <<
  643. SetBufferText(x-1, GetBufferText(x));
  644. x := x+1;
  645. >>;
  646. CurrentBufferSize := CurrentBufferSize - 1; % Note size change
  647. % (But don't bother to decrease actual size of line vector.)
  648. >>;
  649. GetLine(CurrentLineIndex);
  650. >>;
  651. %. ------------- Basic Dispatch Callable Control Procedures
  652. Symbolic Procedure leave_dispatch_loop();
  653. % Set flag to cause exit from read/dispatch/refresh loop.
  654. <<
  655. PutLine(); % Make sure current line "put away".
  656. runflag := NIL; % (Set flag to be detected by "main loop".)
  657. >>;
  658. Symbolic Procedure !$DeleteBuffer();
  659. % Delete entire contents of buffer (similar to creating new buffer)
  660. <<
  661. % Initial vector allows only one line. (Should really be parameterized.)
  662. CurrentBufferText := MkVect(1);
  663. CurrentBufferSize := 1; % Start with one line of text (but
  664. % zero characters in the line!)
  665. CurrentLine := NIL;
  666. CurrentLineIndex := 0;
  667. point := 0;
  668. >>;
  669. % Move to beginning of buffer
  670. Symbolic Procedure !$BeginningOfBuffer();
  671. <<
  672. SelectLine(0);
  673. point := 0;
  674. >>;
  675. % Move to end of buffer
  676. Symbolic Procedure !$EndOfBuffer();
  677. <<
  678. SelectLine(CurrentBufferSize - 1);
  679. point := length(CurrentLine);
  680. >>;
  681. Symbolic Procedure SetMark();
  682. % Set "mark" pointer from "point".
  683. <<
  684. MarkLineIndex := CurrentLineIndex;
  685. MarkPoint := point;
  686. >>;
  687. Symbolic Procedure ExchangePointAndMark();
  688. begin scalar tmp;
  689. tmp := point;
  690. point := MarkPoint;
  691. MarkPoint := tmp;
  692. tmp := CurrentLineIndex; % NOTE, it doesn't work to just set
  693. % CurrentLineIndex := MarkLineIndex.
  694. SelectLine(MarkLineIndex);
  695. MarkLineIndex := tmp;
  696. end;
  697. % NOTE, there is a vague asymmetry about EndOfBufferP and
  698. % BeginningOfBufferP. These folks need more thought to avoid off by one
  699. % errors. (Should work in terms of characters, not lines?)
  700. Symbolic Procedure EndOfBufferP(i);
  701. % Return T if i is at end of buffer (past the last line in the buffer).
  702. i >= CurrentBufferSize;
  703. Symbolic Procedure BeginningOfBufferP(i);
  704. % Return T if i at beginning (first line) of buffer.
  705. i <= 0; % Use <= for robustness
  706. % Insert a CRLF at point (new line character (or end of line character
  707. % if you prefer))
  708. Symbolic Procedure !$CRLF();
  709. <<
  710. % Store away the head of the current line (at the current line)
  711. SetBufferText(CurrentLineIndex , PackLine Head(CurrentLine,Point) );
  712. % Append the tail end of the line just past the current line, and point
  713. % to it.
  714. CurrentLine := Tail(CurrentLine,Point);
  715. AppendLine(PackLine CurrentLine , CurrentLineIndex);
  716. CurrentLineIndex := NextIndex(CurrentLineIndex);
  717. Point := 0;
  718. >>;
  719. % Move to beginning of current line
  720. Symbolic Procedure !$BeginningOfLine();
  721. Point := 0;
  722. % Move to end of current line
  723. Symbolic Procedure !$EndOfLine();
  724. Point := length(CurrentLine);
  725. % Move up a line (attempting to stay in same column), dont move past; % start of buffer:=
  726. Symbolic Procedure !$BackwardLine();
  727. if BeginningOfBufferP(CurrentLineIndex) then
  728. Ding()
  729. else
  730. <<
  731. SelectLine(PreviousIndex(CurrentLineIndex));
  732. if Point > Length CurrentLine then
  733. Point := Length(CurrentLine)
  734. >>;
  735. Symbolic Procedure !$ForwardLine();
  736. % Move down a line (attempting to stay in same column), don't move past
  737. % end of buffer.
  738. if EndOfBufferP(NextIndex CurrentLineIndex) then
  739. Ding()
  740. else
  741. <<
  742. SelectLine(NextIndex CurrentLineIndex);
  743. % DO WE REALLY want to change point? WFG
  744. If point > Length(CurrentLine) then
  745. point := Length CurrentLine
  746. >>;
  747. % Move back a character, to previous line if at start of current line.
  748. Symbolic Procedure !$BackwardCharacter();
  749. if point = 0 then
  750. if BeginningOfBufferP(CurrentLineIndex) then
  751. Ding()
  752. else
  753. <<
  754. SelectLine(PreviousIndex(CurrentLineIndex));
  755. point := Length(CurrentLine);
  756. >>
  757. else
  758. point := point - 1;
  759. % Move forward a character, to Next line if at end of current line.
  760. Symbolic Procedure !$ForwardCharacter();
  761. % NOTE use of "length" function, assumption of list for CurrentLine.
  762. if point = length(Currentline) then
  763. if EndOfBufferP(NextIndex CurrentLineIndex) then Ding()
  764. else
  765. <<
  766. SelectLine(NextIndex(CurrentLineIndex));
  767. Point := 0;
  768. >>
  769. else point := point+1;
  770. % Delete character before point.
  771. Symbolic Procedure !$DeleteBackwardCharacter();
  772. <<
  773. if point = 0 and BeginningOfBufferP(CurrentLineIndex) then
  774. Ding()
  775. else
  776. <<
  777. !$BackwardCharacter();
  778. !$DeleteForwardCharacter();
  779. >>;
  780. >>;
  781. % Delete character after point
  782. Symbolic Procedure !$DeleteForwardCharacter();
  783. if point = length(Currentline) then
  784. if EndOfBufferP(CurrentLineIndex) or % Complain if at (or near)
  785. EndOfBufferP(NextIndex CurrentLineIndex) % end of buffer.
  786. then
  787. Ding()
  788. else
  789. <<
  790. % non-destructively append Next line to this line
  791. CurrentLine :=
  792. Append(CurrentLine,
  793. UnpackLine GetBufferText(NextIndex(CurrentLineIndex)));
  794. PutLine();
  795. DeleteTextEntry NextIndex CurrentLineIndex;
  796. >>
  797. else
  798. DeleteCharacter();
  799. Symbolic Procedure rotate_kill_index(N);
  800. % Step the kill_ring_index by N, modulo the ring size.
  801. begin scalar ring_size;
  802. kill_ring_index := kill_ring_index + N;
  803. % Now do "cheap and dirty" modulus function.
  804. % Get number of entries in ring, compensate for 0 indexing.
  805. ring_size := size(kill_buffer_ring) +1;
  806. while kill_ring_index >= ring_size do
  807. kill_ring_index := kill_ring_index - ring_size;
  808. while kill_ring_index < 0 do
  809. kill_ring_index := kill_ring_index + ring_size;
  810. end;
  811. Symbolic Procedure update_kill_buffer(killed_text);
  812. % Update the "kill buffer", either appending/prepending to the current
  813. % buffer, or "pushing" the kill ring, as appropriate. killed_text is a
  814. % pair, the car of which is +1 if the text was "forward killed", and -1 if
  815. % "backwards killed". The cdr is the actual text (a vector of strings).
  816. begin scalar new_entry, tmp, tmp1, tmp2;
  817. % If last operation wasn't a kill, then "push" the new text.
  818. if not (last_operation memq kill_opers) then
  819. <<
  820. rotate_kill_index(1); % Move to a new kill buffer.
  821. kill_buffer_ring[kill_ring_index] := cdr killed_text;
  822. >>
  823. else
  824. % Otherwise, append or prepend the text, as appropriate.
  825. <<
  826. tmp1 := kill_buffer_ring[kill_ring_index]; % The old text.
  827. tmp2 := cdr killed_text; % The new text to tack on.
  828. % Swap the two pieces of text if deletion was "backwards".
  829. if car killed_text < 0 then
  830. <<
  831. tmp := tmp1;
  832. tmp1 := tmp2;
  833. tmp2 := tmp;
  834. >>;
  835. % Allocate space for the new "kill buffer". (A bit tricky due to 0
  836. % indexing and fact that the last line of tmp1 is concatenated with
  837. % first line of tmp2.)
  838. new_entry := MkVect(size(tmp1) + size(tmp2));
  839. tmp := 0; % Now tmp serves as index into the new buffer.
  840. for i := 0 : size(tmp1) - 1 do
  841. <<
  842. new_entry[tmp] := tmp1[i];
  843. tmp := tmp + 1;
  844. >>;
  845. % Concatenate last line of tmp1 with first line of tmp2.
  846. new_entry[tmp] := concat(tmp1[size tmp1], tmp2[0]);
  847. tmp := tmp + 1;
  848. % Tack on the rest of tmp2.
  849. for i := 1 : size(tmp2) do
  850. <<
  851. new_entry[tmp] := tmp2[i];
  852. tmp := tmp + 1;
  853. >>;
  854. kill_buffer_ring[kill_ring_index] := new_entry;
  855. >>;
  856. end;
  857. Symbolic Procedure kill_region();
  858. % Kill (and save in kill buffer) the region between point and mark.
  859. <<
  860. update_kill_buffer
  861. delete_or_copy(T, CurrentLineIndex, point, MarkLineIndex, MarkPoint);
  862. >>;
  863. Symbolic Procedure copy_region();
  864. % (Should this be counted as a "kill_oper"? How about previous kills?)
  865. <<
  866. update_kill_buffer
  867. delete_or_copy(NIL, CurrentLineIndex, point, MarkLineIndex, MarkPoint);
  868. >>;
  869. % Kill current line from point onwards, or delete "CRLF" if at end of line.
  870. Symbolic Procedure kill_line();
  871. begin scalar cline, cpoint;
  872. cline := CurrentLineIndex;
  873. cpoint := point;
  874. % Move over region to kill, then kill it.
  875. if point = length(CurrentLine) then % Delete CRLF at end of line.
  876. !$ForwardCharacter() % (Skip over CRLF.)
  877. else
  878. !$EndOfLine();
  879. update_kill_buffer
  880. delete_or_copy(T, cline, cpoint, CurrentLineIndex, point);
  881. end;
  882. Symbolic Procedure kill_forward_word();
  883. begin scalar cline, cpoint;
  884. cline := CurrentLineIndex;
  885. cpoint := point;
  886. % Move over region to kill, then kill it.
  887. forward_word();
  888. update_kill_buffer
  889. delete_or_copy(T, cline, cpoint, CurrentLineIndex, point);
  890. end;
  891. Symbolic Procedure kill_backward_word();
  892. begin scalar cline, cpoint;
  893. cline := CurrentLineIndex;
  894. cpoint := point;
  895. % Move over region to kill, then kill it.
  896. backward_word();
  897. update_kill_buffer
  898. delete_or_copy(T, cline, cpoint, CurrentLineIndex, point);
  899. end;
  900. Symbolic Procedure kill_forward_sexpr();
  901. begin scalar cline, cpoint;
  902. cline := CurrentLineIndex;
  903. cpoint := point;
  904. % Move over region to kill, then kill it.
  905. forward_sexpr();
  906. update_kill_buffer
  907. delete_or_copy(T, cline, cpoint, CurrentLineIndex, point);
  908. end;
  909. Symbolic Procedure kill_backward_sexpr();
  910. begin scalar cline, cpoint;
  911. cline := CurrentLineIndex;
  912. cpoint := point;
  913. % Move over region to kill, then kill it.
  914. backward_sexpr();
  915. update_kill_buffer
  916. delete_or_copy(T, cline, cpoint, CurrentLineIndex, point);
  917. end;
  918. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  919. Symbolic Procedure Print1Dispatch(ch1, ch2, fname);
  920. % Print out the dispatch routine for a (possibly "extended") character.
  921. % (Second "character" is NIL for unextended characters.)
  922. % Don't print anything if it's a self inserting character, or "undefined".
  923. <<
  924. if not(fname memq '(InsertSelfCharacter Ding)) then
  925. PrintF("%w %w %w%n", character_name ch1,
  926. character_name ch2, fname);
  927. >>;
  928. Symbolic Procedure PrintAllDispatch;
  929. % Print out the current dispatch table.
  930. % Need a "mode" that dumps stuff in a form appropriate for SCRIBE?
  931. <<
  932. % First, list the routines bound to single characters.
  933. for ch := 0:255 do
  934. Print1Dispatch(ch, NIL, getv(MainDispatch, ch));
  935. % next, list all the C-X bindings
  936. for each x in cdr atsoc(char cntrl X, PrefixAssociationLists) do
  937. Print1Dispatch(char cntrl X, car x, cdr x);
  938. >>;
  939. Symbolic Procedure GetInternalName(ch,DispatchTable);
  940. if pairp DispatchTable then
  941. if(ch := atsoc(ch,DispatchTable)) then cdr ch else 'Ding
  942. else getv(DispatchTable,ch);
  943. fluid '(character_name_table);
  944. % An association list of (character code . name), used by procedure
  945. % character_name.
  946. character_name_table :=
  947. '(
  948. (8#7 . "Bell")
  949. (8#10 . "Backspace")
  950. (8#11 . "Tab")
  951. (8#12 . "Linefeed")
  952. (8#15 . "Return")
  953. (8#33 . "Escape")
  954. (8#40 . "Blank")
  955. (8#177 . "Rubout")
  956. );
  957. Symbolic Procedure character_name(ch);
  958. % Return a string giving the name for a character code, return "" if "ch"
  959. % not a number. Names for control characters are typically "C-...", names
  960. % for meta characters are "M-...". Printing characters name themselves.
  961. begin scalar name;
  962. % Typically ch will be NIL if it isn't a number.
  963. if not numberp ch then
  964. return "";
  965. name := MkString(0,0); % A one character string
  966. if ch > char BLANK and ch <= char '!~ then
  967. name[0] := ch % A "printing" character
  968. else if LAND(ch, 8#200) neq 0 then % Meta bit set
  969. name := concat("M-", character_name LAND(ch,8#177))
  970. else if name := atsoc(ch, character_name_table) then
  971. name := cdr name % association list catches wild cards.
  972. else if ch < char BLANK then
  973. name := concat("C-",
  974. if ch = 8#37 then character_name(char RUBOUT)
  975. else character_name(ch + 8#100))
  976. else
  977. EMODEerror list(ch, " is bad character code for routine `character_name'");
  978. return name;
  979. end;
  980. Symbolic Procedure !$HelpDispatch();
  981. % Give a little information on the routine bound to a keyboard character
  982. % (or characters, in the case of prefixed things).
  983. % We need to do a better job of merging this code with PrintAllDispatch,
  984. % AND the code that actually dispatches.
  985. begin scalar ch1, ch2, fname;
  986. ch1 := prompt_for_character("Function of character: ");
  987. if ch1 = char ESC then % Treat as meta character
  988. <<
  989. ch1 := LOR( 8#200, GetNextCommandCharacter());
  990. fname := GetInternalName(ch1, MainDispatch)
  991. >>
  992. else if ch1 = char meta X OR ch1 = char cntrl X then
  993. <<
  994. ch2 := GetNextCommandCharacter();
  995. fname := GetInternalName(ch2,atsoc(ch1, PrefixAssociationLists))
  996. >>
  997. else
  998. fname := GetInternalName(ch1,MainDispatch);
  999. show_message BldMsg("%w %w %w", character_name ch1,
  1000. character_name ch2, fname);
  1001. end;
  1002. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1003. Symbolic Procedure OpenLine();
  1004. % Insert a NEWLINE (or EOL) at POINT, keep POINT before newline
  1005. <<
  1006. InsertCharacter(char EOL);
  1007. !$BackwardCharacter();
  1008. >>;