/* TwoPartSubstitute - goes from current line to current line minus 1 * doing substitute in 2 parts if it has to, that * appear as 1 */ vi_rc TwoPartSubstitute( char *find, char *replace, int prompt, int wrap ) { vi_rc rc; long changecnt, linecnt; linenum end_line; char *cmd = MemAlloc( MAX_INPUT_LINE ); StartUndoGroup( UndoStack ); // search from current position forward to end of doc sprintf( cmd, "/%s/%s/g%c", find, replace, ( prompt ) ? 'i' : '\0' ); end_line = CurrentFile->fcbs.tail->end_line; rc = Substitute( CurrentPos.line, end_line, cmd ); changecnt = LastChangeCount; linecnt = LastLineCount; if( wrap && !LastSubstituteCancelled && CurrentPos.line != 1 && rc == ERR_NO_ERR ) { // search from beginning of do to here sprintf( cmd, "/%s/%s/g%c", find, replace, ( prompt ) ? 'i' : '\0' ); rc = Substitute( 1, CurrentPos.line - 1, cmd ); linecnt += LastLineCount; changecnt += LastChangeCount; } if( rc == ERR_NO_ERR ) { Message1( "%l changes on %l lines", changecnt, linecnt ); } EndUndoGroup( UndoStack ); MemFree( cmd ); return( rc ); } /* TwoPartSubstitute */
/* * DoneCurrentInsert - finished current insertion of text */ void DoneCurrentInsert( bool trim ) { if( EditFlags.InsertModeActive ) { EditFlags.InsertModeActive = FALSE; if( trim ) { trimWorkLine(); } if( CurrentPos.column > WorkLine->len ) { if( EditFlags.Modeless ) { GoToColumnOK( WorkLine->len + 1 ); } else { GoToColumnOK( WorkLine->len ); } } doneWithCurrentLine(); EndUndoGroup( UndoStack ); if( !EditFlags.Modeless ) { NewCursor( CurrentWindow, EditVars.NormalCursorType ); SetWindowCursor(); } EditFlags.EscapedInsertChar = FALSE; EditFlags.NoReplaceSearchString = FALSE; } } /* DoneCurrentInsert */
/* * EnterHexKey - enter a hexidecimal key stroke and insert it into the text */ vi_rc EnterHexKey( void ) { int i; char st[MAX_STR], val; vi_rc rc; const char *ptr; rc = ModificationTest(); if( rc != ERR_NO_ERR ) { return( rc ); } if( CurrentLine->len >= EditVars.MaxLine - 1 ) { return( ERR_LINE_FULL ); } rc = PromptForString( "Enter the number of char to insert:", st, sizeof( st ) - 1, NULL ); if( rc != ERR_NO_ERR ) { if( rc == NO_VALUE_ENTERED ) { return( ERR_NO_ERR ); } return( rc ); } /* * get value */ ptr = SkipLeadingSpaces( st ); val = (char)strtol( ptr, NULL, 0 ); if( val == '\0' ) { return( ERR_INVALID_VALUE ); } /* * build undo record */ StartUndoGroup( UndoStack ); CurrentLineReplaceUndoStart(); CurrentLineReplaceUndoEnd( true ); EndUndoGroup( UndoStack ); /* * add the char */ GetCurrentLine(); for( i = WorkLine->len; i >= CurrentPos.column - 1; i-- ) { WorkLine->data[i + 1] = WorkLine->data[i]; } WorkLine->data[CurrentPos.column - 1] = val; WorkLine->len++; DisplayWorkLine( true ); if( CurrentPos.column < WorkLine->len ) { GoToColumn( CurrentPos.column + 1, WorkLine->len + 1 ); } ReplaceCurrentLine(); EditFlags.Dotable = true; return( ERR_NO_ERR ); } /* EnterHexKey */
/* * doneWithCurrentLine - update current line, and update undo record */ static void doneWithCurrentLine( void ) { startNewLineUndo(); ReplaceCurrentLine(); if( currLineRepUndo ) { ConditionalCurrentLineReplaceUndoEnd(); currLineRepUndo = FALSE; } else { EndUndoGroup( UndoStack ); } } /* doneWithCurrentLine */
/* * TryEndUndoGroup - try to end undo group in last specified stack */ void TryEndUndoGroup( undo_stack *cstack ) { info *cinfo; for( cinfo = InfoHead; cinfo != NULL; cinfo = cinfo->next ) { if( cstack == cinfo->UndoStack ) { EndUndoGroup( cstack ); break; } } } /* TryEndUndoGroup */
/* * UndoReplaceLines - undo the replacement of a group of lines */ vi_rc UndoReplaceLines( linenum sline, linenum eline ) { vi_rc rc; fcb_list fcblist; if( !EditFlags.Undo || UndoStack == NULL ) { return( ERR_NO_ERR ); } rc = GetCopyOfLineRange( sline, eline, &fcblist ); if( rc != ERR_NO_ERR ) { return( rc ); } StartUndoGroup( UndoStack ); UndoDeleteFcbs( sline - 1, &fcblist, UndoStack ); UndoInsert( sline, eline, UndoStack ); EndUndoGroup( UndoStack ); return( ERR_NO_ERR ); } /* UndoReplaceLines */
/* * JoinCurrentLineToNext */ vi_rc JoinCurrentLineToNext( void ) { int i, j; vi_rc rc; rc = ModificationTest(); if( rc != ERR_NO_ERR ) { return( rc ); } i = (int) GetRepeatCount(); StartUndoGroup( UndoStack ); for( j = 0; j < i; j++ ) { rc = GenericJoinCurrentLineToNext( true ); if( rc != ERR_NO_ERR ) { break; } } EndUndoGroup( UndoStack ); return( rc ); } /* JoinCurrentLineToNext */
/* * finiSource - release language variables */ static void finiSource( labels *lab, vlist *vl, sfile *sf, undo_stack *atomic ) { sfile *curr, *tmp; info *cinfo; if( lab != NULL ) { MemFreeList( lab->cnt, lab->name ); MemFree( lab->pos ); } VarListDelete( vl ); curr = sf; while( curr != NULL ) { tmp = curr->next; MemFree( curr->data ); MemFree( curr->arg1 ); MemFree( curr->arg2 ); MemFree( curr ); curr = tmp; } /* * make sure this undo stack is still around */ if( atomic != NULL ) { for( cinfo = InfoHead; cinfo != NULL; cinfo = cinfo->next ) { if( atomic == cinfo->UndoStack ) { break; } } if( cinfo != NULL ) { EndUndoGroup( atomic ); } } } /* finiSource */
// insert separator feature void CSpacerView::MouseDown(BPoint where) { static const char *bigsep = "\n/*\nvoid c------------------------------() {}\n*/\n"; static const char *smallsep = "// ---------------------------------------\n"; EditView *ev = editor.curev; uint32 buttons; if (!ev) return; LockWindow(); int y = ev->scroll.y + ((int)where.y / editor.font_height); if (y < 0) y = 0; if (y >= ev->nlines) y = (ev->nlines - 1); clLine *line = ev->GetLineHandle(y); if (!line) { UnlockWindow(); return; } GetMouse(&where, &buttons); const char *sep = (buttons & B_SECONDARY_MOUSE_BUTTON) ? smallsep : bigsep; BeginUndoGroup(ev); ev->action_insert_string(0, y, sep, NULL, NULL); EndUndoGroup(ev); ev->RedrawView(); if (sep == bigsep) FunctionList->ScanIfNeeded(); UnlockWindow(); }
/* * DeleteAndInsertText - delete text range, then insert at beginning */ vi_rc DeleteAndInsertText( int scol, int ecol ) { int startcol; vi_rc rc; StartUndoGroup( UndoStack ); CurrentLineReplaceUndoStart(); currLineRepUndo = TRUE; if( ecol >= 0 ) { if( CurrentLine->len > 0 ) { rc = DeleteBlockFromCurrentLine( scol, ecol, FALSE ); if( rc == ERR_NO_ERR ) { startcol = CurrentPos.column; if( scol > ecol ) { startcol = ecol + 1; } if( startcol > WorkLine->len ) { startcol = WorkLine->len + 1; } DisplayWorkLine( TRUE ); ReplaceCurrentLine(); rc = GoToColumnOK( startcol ); } if( rc != ERR_NO_ERR ) { CurrentLineReplaceUndoCancel(); EndUndoGroup( UndoStack ); return( rc ); } } else { ReplaceCurrentLine(); } } continueInsertText( CurrentPos.column, FALSE ); return( ERR_NO_ERR ); } /* DeleteAndInsertText */
/* * InsertTextForSpecialKey - insert text for ^O, ALT_O */ void InsertTextForSpecialKey( vi_key event, char *buff ) { linenum line; int type; if( CurrentFile == NULL ) { return; } line = CurrentPos.line; type = INSERT_BEFORE; if( event == VI_KEY( CTRL_O ) ) { type = INSERT_AFTER; line += 1; } Modified( true ); StartUndoGroup( UndoStack ); UndoInsert( line, line, UndoStack ); AddNewLineAroundCurrent( buff, strlen( buff ), type ); EndUndoGroup( UndoStack ); DCDisplayAllLines(); DCUpdate(); } /* InsertTextForSpecialKey */
// process the key void ProcessKey(EditView *ev, int key) { uint flags = GetKeyAttr(key); char MergeToPrior; // free xseek mode if (ev->cursor.xseekmode != CM_FREE) { switch(key) { // these need to maintain the state of the CM_WANT_SCREEN_COORD coordinate case B_UP_ARROW: case B_DOWN_ARROW: case B_PAGE_UP: case B_PAGE_DOWN: break; default: ev->cursor.set_mode(CM_FREE); } } // commands which delete selection & contents if a selection is present if (flags & KF_SELDEL) { if (ev->selection.present) { ev->SelDel(); if (flags & KF_SELDEL_ONLY) return; } } // create new undo group before executing keys which modify the document if (flags & KF_UNDOABLE) { if (flags & KF_UNDO_MERGEABLE) { MergeToPrior = undo_can_merge(ev, ev->cursor.x, ev->cursor.y, key); if (!MergeToPrior) BeginUndoGroup(ev); } else { MergeToPrior = 0; BeginUndoGroup(ev); } } if (flags & KF_AFFECTS_SELECTION) // key can create/remove selection { if (IsShiftDown() && !ev->selection.present) selection_create(ev); } switch(key) { case B_ESCAPE: if (editor.settings.esc_quits_immediately) // a testmode { MainWindow->fDoingInstantQuit = true; be_app->PostMessage(B_QUIT_REQUESTED); } break; case B_LEFT_ARROW: ev->cursor.left(); break; case B_RIGHT_ARROW: ev->cursor.right(); break; case B_UP_ARROW: ev->cursor.up(); break; case B_DOWN_ARROW: ev->cursor.down(); break; case B_PAGE_DOWN: ev->cursor.pgdn(); break; case B_PAGE_UP: ev->cursor.pgup(); break; case B_HOME: DoHome(ev); break; case B_END: DoEnd(ev); break; case KEY_MOUSEWHEEL_DOWN: ev->scroll_down(3); break; case KEY_MOUSEWHEEL_UP: ev->scroll_up(3); break; case B_ENTER: DoEnter(ev); editor.stats.CRs_typed++; editor.stats.keystrokes_typed++; break; case B_TAB: { if (IsShiftDown()) { DoShiftTab(ev); break; } if (DoTabIndent(ev)) break; ev->SelDel(); ev->action_insert_char(ev->cursor.x, ev->cursor.y, TAB); ev->cursor.x++; editor.stats.keystrokes_typed++; } break; // BKSP is equivalent to left followed by del case B_BACKSPACE: if (!ev->cursor.y && !ev->cursor.x) break; ev->cursor.left(); undo_SetMergeMode(ev, MERGE_BKSP, MergeToPrior); ev->action_delete_right(ev->cursor.x, ev->cursor.y, 1); editor.stats.keystrokes_typed++; break; case B_DELETE: undo_SetMergeMode(ev, MERGE_DEL, MergeToPrior); ev->action_delete_right(ev->cursor.x, ev->cursor.y, 1); editor.stats.keystrokes_typed++; break; // typing default: { // ignore non-printable keystrokes if (key > 127 || key < 9) break; if (editor.InOverwriteMode && \ ev->cursor.x < ev->curline->GetLength()) { // less than ideal: i wasn't planning on Overwrite Mode when writing // the undo feature, so it can't merge undo records that contain both a // delete and a insertion. OVR mode isn't used much and undo still works, // just one char at a time, so I think it's ok for now but eventually should // be looked at. if (MergeToPrior) { MergeToPrior = false; BeginUndoGroup(ev); } ev->action_delete_right(ev->cursor.x, ev->cursor.y, 1); } else { undo_SetMergeMode(ev, MERGE_TYPING, MergeToPrior); } ev->action_insert_char(ev->cursor.x, ev->cursor.y, key); ev->cursor.x++; editor.stats.keystrokes_typed++; } break; } // smart indent (for close quotes) if (key == '}' && editor.settings.smart_indent_on_close) CloseSmartIndent(ev); if (flags & KF_AFFECTS_SELECTION) ev->ExtendOrDropSel(key); if (flags & KF_UNDOABLE) { if (MergeToPrior) UpdateMergedUndoGroup(ev); else EndUndoGroup(ev); } if (!(flags & KF_NO_VIEW_TO_CURSOR)) { ev->MakeCursorVisible(); } }
/* * InsertLines - insert a set of lines after specified number in current file */ vi_rc InsertLines( linenum s, fcb_list *fcblist, undo_stack *us ) { fcb *sfcb, *cfcb; linenum l, e; vi_rc rc; rc = ModificationTest(); if( rc != ERR_NO_ERR ) { return( rc ); } if( s < 0 ) { return( ERR_NO_SUCH_LINE ); } /* * find the number of lines inserted */ e = 0; for( cfcb = fcblist->head; cfcb != NULL; cfcb = cfcb->next ) { e += cfcb->end_line - cfcb->start_line + 1; } e += s; /* * see if there is a null fcb at the head; if so, ditch * the null fcb and then reset line ranges */ if( CurrentFile->fcbs.head->nullfcb ) { FreeEntireFcb( CurrentFile->fcbs.head ); CurrentFile->fcbs = *fcblist; e = e - s; s = 0; /* * if we are to insert after line 0, then make this block of * fcbs the first set in the text */ } else if( s == 0 ) { fcblist->tail->next = CurrentFile->fcbs.head; CurrentFile->fcbs.head->prev = fcblist->tail; CurrentFile->fcbs.head = fcblist->head; } else { /* * if we are inserting after the last fcb in the file, * make this block of fcbs the last set in the text */ rc = FindFcbWithLine( s + 1, CurrentFile, &sfcb ); if( rc != ERR_NO_ERR ) { if( rc != ERR_NO_SUCH_LINE ) { return( rc ); } fcblist->head->prev = CurrentFile->fcbs.tail; CurrentFile->fcbs.tail->next = fcblist->head; CurrentFile->fcbs.tail = fcblist->tail; /* * put the lines after the line to be have text inserted * into a new fcb, then insert the block of fcb's after * the fcb containing the line to have text inserted */ } else { rc = SplitFcbAtLine( s + 1, CurrentFile, sfcb ); if( rc > ERR_NO_ERR ) { return( rc ); } /* * line we want to split at is already the first line * in the fcb, so chain the new fcb block before * the fcb containg the line we want to split at */ if( rc == NO_SPLIT_CREATED_AT_START_LINE ) { if( sfcb->prev != NULL ) { sfcb->prev->next = fcblist->head; } fcblist->head->prev = sfcb->prev; sfcb->prev = fcblist->tail; fcblist->tail->next = sfcb; /* * chain the new fcb block after the fcb that used * to containing the line we wanted to split at, and * before the new fcb containing the line we want * to split at */ } else { if( rc == NO_SPLIT_CREATED_AT_END_LINE ) { // Die( "Impossible, can't be past last line"); } if( sfcb->next != NULL ) { sfcb->next->prev = fcblist->tail; } fcblist->tail->next = sfcb->next; sfcb->next = fcblist->head; fcblist->head->prev = sfcb; } } } /* * now, resequence line numbers and set proper file ptr */ for( cfcb = fcblist->head; cfcb != NULL; cfcb = cfcb->next ) { cfcb->f = CurrentFile; l = cfcb->end_line - cfcb->start_line; if( cfcb->prev != NULL ) { cfcb->start_line = cfcb->prev->end_line + 1; } else { cfcb->start_line = 1; } cfcb->end_line = cfcb->start_line + l; } /* * finish up: collect fcbs, point to corrent line, and * build undo for operation */ rc = MergeAllFcbs( &CurrentFile->fcbs ); if( rc != ERR_NO_ERR ) { return( rc ); } StartUndoGroup( us ); rc = ValidateCurrentLine(); if( rc != ERR_NO_ERR ) { return( rc ); } Modified( true ); UndoInsert( s + 1, e, us ); EndUndoGroup( us ); return( ERR_NO_ERR ); } /* InsertLines */
/* * Global - perform global command */ vi_rc Global( linenum n1, linenum n2, const char *data, int dmt ) { char *sstr, *linedata; bool match; vi_rc rc; vi_rc rc1; long changecnt = 0; linenum ll; fcb *cfcb; line *cline; regexp crx; i_mark pos; /* * get search string and command */ rc = ModificationTest(); if( rc != ERR_NO_ERR ) { return( rc ); } sstr = alloca( MAX_INPUT_LINE ); if( sstr == NULL ) { return( ERR_NO_STACK ); } data = SkipLeadingSpaces( data ); data = GetNextWord( data, sstr, SingleSlash ); if( *sstr == '\0' ) { return( ERR_INVALID_GLOBAL_CMD ); } if( *data == '/' ) ++data; // skip one slash character data = SkipLeadingSpaces( data ); /* * verify last line */ if( n2 > CurrentFile->fcbs.tail->end_line ) { rc = CFindLastLine( &ll ); if( rc != ERR_NO_ERR ) { return( rc ); } if( n2 > ll ) { return( ERR_INVALID_LINE_RANGE ); } } /* * set for start of search */ if( EditFlags.Verbose && EditFlags.EchoOn ) { ClearWindow( MessageWindow ); } rc = CurrentRegComp( sstr ); if( rc != ERR_NO_ERR ) { return( rc ); } SaveCurrentFilePos(); StartUndoGroup( UndoStack ); EditFlags.DisplayHold = true; /* * pass one - find all matches */ for( pos.line = n1; pos.line <= n2; pos.line++ ) { /* * go thorugh file, marking global lines */ pos.column = 0; rc = FindRegularExpression( NULL, &pos, &linedata, n2, 0 ); if( rc != ERR_NO_ERR ) { if( rc == ERR_FIND_PAST_TERM_LINE || rc == ERR_FIND_NOT_FOUND || rc == ERR_FIND_END_OF_FILE ) { break; } RestoreCurrentFilePos(); EditFlags.DisplayHold = false; return( rc ); } if( pos.line > n2 ) { break; } /* * go to appropriate spot in file */ rc = GoToLineNoRelCurs( pos.line ); if( rc != ERR_NO_ERR ) { RestoreCurrentFilePos(); EditFlags.DisplayHold = false; return( rc ); } /* * mark fcb and line for a match */ CurrentFcb->globalmatch = true; CurrentLine->u.ld.globmatch = true; if( EditFlags.Verbose && EditFlags.EchoOn ) { // WPrintfLine( MessageWindow,1,"Match on line %l",clineno ); Message1( "Match on line %l", pos.line ); } } /* * negate range, if needed */ if( dmt ) { /* * run through each line, flipping globmatch flag on lines */ CGimmeLinePtr( n1, &CurrentFcb, &CurrentLine ); match = false; for( CurrentPos.line = n1; CurrentPos.line <= n2; CurrentPos.line++ ) { if( CurrentLine->u.ld.globmatch ) { CurrentLine->u.ld.globmatch = false; } else { match = true; CurrentLine->u.ld.globmatch = true; } CurrentLine = CurrentLine->next; if( CurrentLine == NULL ) { CurrentFcb->globalmatch = match; CurrentFcb = CurrentFcb->next; FetchFcb( CurrentFcb ); CurrentLine = CurrentFcb->lines.head; match = false; } } } /* * Pass 2: do all changes */ rc = ERR_NO_ERR; EditFlags.GlobalInProgress = true; memcpy( &crx, CurrentRegularExpression, sizeof( crx ) ); for( CurrentFcb = CurrentFile->fcbs.head; CurrentFcb != NULL; CurrentFcb = CurrentFcb->next ) { if( !CurrentFcb->globalmatch ) continue; FetchFcb( CurrentFcb ); CurrentPos.line = CurrentFcb->start_line; for( CurrentLine = CurrentFcb->lines.head; CurrentLine != NULL; CurrentLine = CurrentLine->next, CurrentPos.line++ ) { if( !CurrentLine->u.ld.globmatch ) continue; CurrentLine->u.ld.globmatch = false; changecnt++; CurrentPos.column = 1; ProcessingMessage( CurrentPos.line ); /* * build command line */ rc = RunCommandLine( data ); if( rc > ERR_NO_ERR ) { break; } } if( rc > ERR_NO_ERR ) { break; } CurrentFcb->globalmatch = false; } /* * we have an error, so fix up fcbs */ if( rc > ERR_NO_ERR ) { for( cfcb = CurrentFile->fcbs.head; cfcb != NULL; cfcb = cfcb->next ) { if( cfcb->globalmatch ) { cfcb->globalmatch = false; cfcb->non_swappable = false; for( cline = cfcb->lines.head; cline != NULL; cline = cline->next ) { cline->u.ld.globmatch = false; } } } } /* * display results */ EditFlags.GlobalInProgress = false; EditFlags.DisplayHold = false; EndUndoGroup( UndoStack ); RestoreCurrentFilePos(); rc1 = SetCurrentLine( CurrentPos.line ); if( rc1 != ERR_NO_ERR ) { if( rc1 == ERR_NO_SUCH_LINE ) { SetCurrentLine( 1 ); } else { return( rc1 ); } } Message1( "%l matches found",changecnt ); DCDisplayAllLines(); return( rc ); } /* Global */
/* * GenericJoinCurrentLineToNext */ vi_rc GenericJoinCurrentLineToNext( bool remsp ) { line *nline = CurrentLine; fcb *nfcb = CurrentFcb; int i, j, k; vi_rc rc; /* * get next line data */ rc = CGimmeNextLinePtr( &nfcb, &nline ); if( rc != ERR_NO_ERR ) { return( rc ); } if( CurrentLine->len + nline->len + 1 >= EditVars.MaxLine ) { return( ERR_LINE_FULL ); } /* * now, copy in the data */ StartUndoGroup( UndoStack ); CurrentLineReplaceUndoStart(); CurrentLineReplaceUndoEnd( true ); GetCurrentLine(); if( remsp ) { while( WorkLine->len > 0 && WorkLine->data[WorkLine->len - 1] == ' ' ) { WorkLine->data[WorkLine->len - 1] = 0; WorkLine->len--; } j = FindStartOfALine( nline ) - 1; k = 0; if( !(j == 0 && nline->data[0] == ' ') ) { if( WorkLine->len != 0 ) { WorkLine->data[WorkLine->len] = ' '; k = WorkLine->len + 1; } for( i = j; i <= nline->len; i++ ) { WorkLine->data[k + i - j] = nline->data[i]; } } } else { k = WorkLine->len; for( i = 0; i <= nline->len; i++ ) { WorkLine->data[k + i] = nline->data[i]; } } WorkLine->len = strlen( WorkLine->data ); ReplaceCurrentLine(); /* * delete next line */ rc = DeleteLineRange( CurrentPos.line + 1, CurrentPos.line + 1, 0 ); if( rc != ERR_NO_ERR ) { return( rc ); } EndUndoGroup( UndoStack ); if( remsp ) { if( k < 2 ) { k = 2; } rc = GoToColumn( k - 1, CurrentLine->len ); if( rc != ERR_NO_ERR ) { return( rc ); } } DCDisplayAllLines(); return( ERR_NO_ERR ); } /* GenericJoinCurrentLineToNext */
/* * InsertLinesAtCursor - insert a set of lines at current pos. in file */ vi_rc InsertLinesAtCursor( fcb_list *fcblist, undo_stack *us ) { fcb *cfcb; linenum e; int lastLineLen; char *source; line *tLine; vi_rc rc; rc = ModificationTest(); if( rc != ERR_NO_ERR ) { return( rc ); } /* * find the number of lines inserted */ e = 0; for( cfcb = fcblist->head; cfcb != NULL; cfcb = cfcb->next ) { e += (cfcb->end_line - cfcb->start_line + 1); } // add chars from right of cursor to end of last line of buffer source = CurrentLine->data + CurrentPos.column - 1; lastLineLen = fcblist->tail->lines.tail->len; fcblist->tail->lines.tail->len += CurrentLine->len - CurrentPos.column + 1; fcblist->tail->byte_cnt += CurrentLine->len - CurrentPos.column + 1; tLine = fcblist->tail->lines.tail->prev; fcblist->tail->lines.tail = MemReAlloc( fcblist->tail->lines.tail, sizeof( line ) + fcblist->tail->lines.tail->len + 1 ); if( tLine ) { tLine->next = fcblist->tail->lines.tail; } strcpy( fcblist->tail->lines.tail->data + lastLineLen, source ); StartUndoGroup( us ); // create new current line in work line CurrentLineReplaceUndoStart(); GetCurrentLine(); WorkLine->len = CurrentPos.column + fcblist->head->lines.head->len - 1; strcpy( WorkLine->data + CurrentPos.column - 1, fcblist->head->lines.head->data ); // replace current line ReplaceCurrentLine(); CurrentLineReplaceUndoEnd( true ); // remove first line of buffer FetchFcb( fcblist->head ); fcblist->head->non_swappable = true; fcblist->head->start_line++; fcblist->head->byte_cnt -= fcblist->head->lines.head->len + 1; tLine = fcblist->head->lines.head; fcblist->head->lines.head = fcblist->head->lines.head->next; fcblist->head->lines.head->prev = NULL; MemFree( tLine ); fcblist->head->non_swappable = false; // add rest of lines of buffer & done if( fcblist->head->lines.head) { InsertLines( CurrentPos.line, fcblist, us ); } EndUndoGroup( us ); // if are indeed linebased, move cursor as well if( !EditFlags.LineBased ) { GoToLineNoRelCurs( CurrentPos.line + e - 1 ); GoToColumnOnCurrentLine( lastLineLen + 1 ); } return( ERR_NO_ERR ); } /* InsertLinesAtCursor */
/* * ProcessEx - process an ex command */ vi_rc ProcessEx( linenum n1, linenum n2, bool n2f, int tkn, const char *data ) { vi_rc rc = ERR_INVALID_COMMAND, i; char word[MAX_STR]; linenum addr, tlines; fcb *cfcb; line *cline; fcb_list fcblist; GetNextWord1( data, word ); data = word; if( GetAddress( &data, &addr ) != ERR_NO_ERR ) { addr = -1; } tlines = n2 - n1 + 1; switch( tkn ) { case EX_T_APPEND: if( !EditFlags.ExMode ) { return( ERR_ONLY_VALID_IN_EX_MODE ); } if( !n2f ) { rc = Append( n1, true ); } break; case EX_T_CHANGE: if( !EditFlags.ExMode ) { return( ERR_ONLY_VALID_IN_EX_MODE ); } StartUndoGroup( UndoStack ); rc = DeleteLineRange( n1, n2, 0 ); if( rc != ERR_NO_ERR ) { EndUndoGroup( UndoStack ); break; } rc = Append( n1 - 1, false ); if( rc != ERR_NO_ERR ) { EndUndoGroup( UndoStack ); break; } break; case EX_T_COPY: if( addr < 0 || IsPastLastLine( addr ) ) { return( ERR_INVALID_ADDRESS ); } i = GetCopyOfLineRange( n1, n2, &fcblist ); if( i ) { break; } rc = InsertLines( addr, &fcblist, UndoStack ); GoToLineNoRelCurs( addr ); if( rc == ERR_NO_ERR ) { Message1( strCmmsg, tlines, "copied", addr ); } break; case EX_T_INSERT: if( !EditFlags.ExMode ) { return( ERR_ONLY_VALID_IN_EX_MODE ); } if( !n2f ) { rc = Append( n1 - 1, true ); } break; case EX_T_JOIN: if( SaveAndResetFilePos( n1 ) != ERR_NO_ERR ) { rc = ERR_NO_SUCH_LINE; break; } if( tlines == 1 ) { n2 = n1 + 1; tlines = 2; } SetRepeatCount( tlines - 1 ); rc = JoinCurrentLineToNext(); RestoreCurrentFilePos(); GoToLineNoRelCurs( n1 ); if( rc == ERR_NO_ERR ) { Message1( "lines %l to %l joined", n1, n2 ); } break; case EX_T_LIST: if( !EditFlags.ExMode ) { return( ERR_ONLY_VALID_IN_EX_MODE ); } for( rc = CGimmeLinePtr( n1, &cfcb, &cline ); rc == ERR_NO_ERR; rc = CGimmeNextLinePtr( &cfcb, &cline ) ) { if( EditFlags.LineNumbers ) { MyPrintf( "%M %s\n", n1, cline->data ); } else { MyPrintf( "%s\n", cline->data ); } if( n1 >= n2 ) { break; } n1++; } break; case EX_T_MARK: rc = SetGenericMark( n1, 1, C2VIKEY( word[0] ) ); break; case EX_T_MOVE: if( addr < 0 || IsPastLastLine( addr ) ) { return( ERR_INVALID_ADDRESS ); } SavebufNumber = WORK_SAVEBUF; StartUndoGroup( UndoStack ); rc = DeleteLineRange( n1, n2, SAVEBUF_FLAG ); if( SavebufNumber != WORK_SAVEBUF ) { /* if this changes, the command will fail * this could be caused by checking out a read-only file * so fix the deleted text and give an error message */ DoUndo(); return( ERR_INVALID_COMMAND ); } if( rc != ERR_NO_ERR ) { EndUndoGroup( UndoStack ); break; } if( addr > n2 ) { addr -= tlines; } else if( addr >= n1 && addr <= n2 ) { addr = n1; } rc = InsertLines( addr, &WorkSavebuf->u.fcbs, UndoStack ); EndUndoGroup( UndoStack ); GoToLineNoRelCurs( addr ); if( rc == ERR_NO_ERR ) { Message1( strCmmsg, tlines, "moved", addr ); } break; case EX_T_UNDO: rc = DoUndo(); break; case EX_T_UNDO_DMT: rc = DoUndoUndo(); break; case EX_T_EQUALS: Message1( "%l", n1 ); rc = ERR_NO_ERR; break; case EX_T_VERSION: rc = DoVersion(); break; case EX_T_VISUAL: case EX_T_VISUAL_DMT: if( EditFlags.LineDisplay ) { ScreenPage( -1 ); EditFlags.ExMode = false; EditFlags.LineDisplay = false; EditFlags.ClockActive = true; ReDisplayScreen(); DoVersion(); } if( word[0] != '\0' ) { rc = EditFile( word, ( tkn == EX_T_VISUAL_DMT ) ); } else { rc = ERR_NO_ERR; } break; } return( rc ); } /* ProcessEx */
/* * DoGenericFilter - filter some crap */ vi_rc DoGenericFilter( linenum s, linenum e, const char *cmd ) { fcb *cfcb, *tfcb; line *cline; vi_rc rc; char filtin[_MAX_PATH], filtout[_MAX_PATH]; fcb_list fcblist; int fh; rc = ModificationTest(); if( rc != ERR_NO_ERR ) { return( rc ); } /* * filter on a line */ rc = GetCopyOfLineRange( s, e, &fcblist ); if( rc != ERR_NO_ERR ) { return( rc ); } /* * get file */ MakeTmpPath( filtin, FILTER_FILE_NAME ); fh = mkstemp( filtin ); if( fh == -1 ) return( ERR_FILE_OPEN ); /* * now, dump this crap to a tmp file */ for( cfcb = fcblist.head; cfcb != NULL; cfcb = tfcb ) { FetchFcb( cfcb ); for( cline = cfcb->lines.head; cline != NULL; cline = cline->next ) { write( fh, cline->data, strlen( cline->data ) ); #if defined( __UNIX__ ) write( fh, "\n", 1 ); #else write( fh, "\r\n", 2 ); #endif } tfcb = cfcb->next; FcbFree( cfcb ); } close( fh ); MakeTmpPath( filtout, FILTER_FILE_NAME ); fh = mkstemp( filtout ); if( fh == -1 ) { remove( filtin ); return( ERR_FILE_OPEN ); } close( fh ); /* * shell out to the given command */ ExecCmd( filtin, filtout, cmd ); StartUndoGroup( UndoStack ); rc = DeleteLineRange( s, e, 0 ); if( rc == ERR_NO_ERR ) { ReadAFile( s - 1, filtout ); Message1( "%l lines filtered through %s", e - s + 1, cmd ); } EndUndoGroup( UndoStack ); /* * cleanup */ if( rc == ERR_NO_ERR ) { rc = DO_NOT_CLEAR_MESSAGE_WINDOW; } remove( filtin ); remove( filtout ); return( rc ); } /* DoGenericFilter */
/* * Substitute - perform substitution */ vi_rc Substitute( linenum n1, linenum n2, char *data ) { char *sstr, *rstr, *newr; char flag[20], *linedata; bool iflag = false; bool gflag = false; bool undoflag = false; bool restline = false; bool splitpending = false; bool undoline = false; int i, rlen, slen; bool splitme; long changecnt = 0, linecnt = 0; linenum llineno, ll, lastline = 0, extra; i_mark pos; vi_rc rc; LastSubstituteCancelled = 0; LastChangeCount = 0; LastLineCount = 0; sstr = alloca( MAX_INPUT_LINE ); if( sstr == NULL ) { return( ERR_NO_STACK ); } strcpy( sstr, data ); rc = ModificationTest(); if( rc != ERR_NO_ERR ) { return( rc ); } strcpy( data, sstr ); rstr = alloca( MAX_INPUT_LINE ); if( rstr == NULL ) { return( ERR_NO_STACK ); } if( NextWordSlash( data, sstr ) < 0 ) { return( ERR_INVALID_SUBS_CMD ); } if( NextWordSlash( data, rstr ) < 0 ) { return( ERR_INVALID_SUBS_CMD ); } slen = NextWord1( data, flag ); for( i = 0; i < slen; i++ ) { switch( flag[i] ) { case 'g': gflag = true; break; case 'i': case 'c': iflag = true; break; } } rc = CurrentRegComp( sstr ); if( rc != ERR_NO_ERR ) { return( rc ); } /* * verify last line */ if( n2 > CurrentFile->fcbs.tail->end_line ) { rc = CFindLastLine( &ll ); if( rc != ERR_NO_ERR ) { return( rc ); } if( n2 > ll ) { return( ERR_INVALID_LINE_RANGE ); } } /* * set for start of search */ if( EditFlags.Verbose && EditFlags.EchoOn ) { ClearWindow( MessageWindow ); } SaveCurrentFilePos(); llineno = n1 - 1; EditFlags.AllowRegSubNewline = true; newr = StaticAlloc(); for( pos.column = 0, pos.line = n1; pos.line <= n2; nextSearchStartPos( &pos, gflag, rlen ) ) { /* * get regular expression, and build replacement string */ rc = FindRegularExpression( NULL, &pos, &linedata, n2, 0 ); if( rc != ERR_NO_ERR || pos.line > n2 ) { break; } slen = GetCurrRegExpLength(); splitme = RegSub( CurrentRegularExpression, rstr, newr, pos.line ); rlen = strlen( newr ); ProcessingMessage( pos.line ); /* * if in global mode, see if we already have an undo for * this line */ if( gflag ) { if( lastline != pos.line ) { undoline = false; } } /* * interactive mode? yes, then display text and ask to change */ if( iflag ) { change_resp rsp; if( !restline ) { ClearWindow( MessageWindow ); } restline = true; GoToLineNoRelCurs( pos.line ); if( EditFlags.GlobalInProgress ) { EditFlags.DisplayHold = false; DCDisplayAllLines(); EditFlags.DisplayHold = true; } HilightSearchString( &pos, slen ); rsp = ChangePrompt(); if( rsp == CHANGE_NO ) { ResetDisplayLine(); rlen = 1; continue; } else if( rsp == CHANGE_CANCEL ) { ResetDisplayLine(); LastSubstituteCancelled = 1; break; } else if( rsp == CHANGE_ALL ) { ResetDisplayLine(); iflag = false; } } /* * set up for global undo if we haven't already */ if( !undoflag ) { StartUndoGroup( UndoStack ); undoflag = true; } /* * bump change counts */ changecnt++; if( llineno != pos.line ) { if( splitpending ) { splitpending = false; extra = SplitUpLine( llineno ); n2 += extra; pos.line += extra; } linecnt++; llineno = pos.line; } /* * get copy of line, and verify that new stuff fits */ CurrentPos.line = pos.line; rc = CGimmeLinePtr( pos.line, &CurrentFcb, &CurrentLine ); if( rc != ERR_NO_ERR ) { break; } if( CurrentLine->len + rlen - slen >= EditVars.MaxLine ) { rc = ERR_LINE_FULL; break; } /* * now build the individual undo */ CurrentFcb->non_swappable = true; if( !undoline ) { CurrentLineReplaceUndoStart(); CurrentLineReplaceUndoEnd( true ); if( gflag ) { undoline = true; lastline = pos.line; } } /* * remove the old string */ GetCurrentLine(); WorkLine->len = ReplaceSubString( WorkLine->data, WorkLine->len, pos.column, pos.column + slen - 1, newr, rlen ); if( iflag ) { DisplayWorkLine( true ); } ReplaceCurrentLine(); /* * if not global, only do this change on this line */ if( splitme ) { splitpending = true; } CurrentFcb->non_swappable = false; } StaticFree( newr ); EditFlags.AllowRegSubNewline = false; /* * is there still a split line pending? */ if( splitpending ) { SplitUpLine( llineno ); } RestoreCurrentFilePos(); if( restline ) { SetCurrentLine( CurrentPos.line ); GoToColumnOK( CurrentPos.column ); } if( undoflag ) { EndUndoGroup( UndoStack ); } switch( rc ) { case ERR_NO_ERR: case ERR_LINE_FULL: case ERR_FIND_PAST_TERM_LINE: case ERR_FIND_NOT_FOUND: case ERR_FIND_END_OF_FILE: /* * display results */ if( rc == ERR_LINE_FULL ) { Message1( "Stopped at line %l - line full", pos.line ); } else { Message1( "%l changes on %l lines", changecnt, linecnt ); LastLineCount = linecnt; LastChangeCount = changecnt; } DCDisplayAllLines(); rc = ERR_NO_ERR; break; default: break; } return( rc ); } /* Substitute */
/* * CurrentLineReplaceUndoEnd - actually add the undo */ void CurrentLineReplaceUndoEnd( int endgrp ) { fcb *cfcb, *nfcb; undo *top, *delrec; fcb_list fcblist; if( !EditFlags.Undo || UndoStack == NULL ) { return; } /* * see if we can merge this with the last undo record * (provided we are in an open undo group) * * we need the following undo sequence: * END_UNDO_GROUP * UNDO_INSERT_LINES * - must have end line one less than the current line * UNDO_DELETE_FCBS * - must have last line to insert being two less * than the current (since then the undo for * the current would be on the line ONE less than * the current); */ if( endgrp && UndoStack->OpenUndo > 0 ) { top = UndoStack->stack[UndoStack->current]; if( top != NULL && top->type == END_UNDO_GROUP ) { top = top->next; if( top != NULL && top->type == UNDO_INSERT_LINES ) { if( top->data.del_range.end == CurrentPos.line - 1 ) { delrec = top; top = top->next; if( top != NULL && top->type == UNDO_DELETE_FCBS ) { cfcb = top->data.fcbs.tail; if( cfcb->end_line == CurrentPos.line - 2 ) { /* * FINALLY, we can add it. either * add to current fcb or add a new * fcb */ if( (FcbSize( cfcb ) + lineSave->len + 4) <= MAX_IO_BUFFER ) { FetchFcb( cfcb ); InsertLLItemAfter( (ss **)&cfcb->lines.tail, (ss *)cfcb->lines.tail, (ss *)lineSave ); cfcb->byte_cnt += lineSave->len + 1; cfcb->end_line++; } else { nfcb = singleLineFcb(); nfcb->start_line = nfcb->end_line = cfcb->end_line + 1; InsertLLItemAfter( (ss **)&(top->data.fcbs.tail), (ss *)cfcb, (ss *)nfcb ); nfcb->non_swappable = FALSE; } delrec->data.del_range.end++; Modified( TRUE ); return; } } } } } } /* * create an fcb with a single line */ cfcb = singleLineFcb(); /* * build undo action */ fcblist.head = cfcb; fcblist.tail = cfcb; StartUndoGroupWithPosition( UndoStack, cLine, pageTop, cCol ); UndoDeleteFcbs( CurrentPos.line - 1, &fcblist, UndoStack ); UndoInsert( CurrentPos.line, CurrentPos.line, UndoStack ); if( endgrp ) { EndUndoGroup( UndoStack ); } Modified( TRUE ); cfcb->non_swappable = FALSE; } /* CurrentLineReplaceUndoEnd */
vi_rc Change( range *r ) { int scol, ecol; int tmp; vi_rc rc; vi_key key; #ifndef __WIN__ int vecol; #endif /* * change line ranges */ if( r->start.line != r->end.line ) { StartUndoGroup( UndoStack ); if( !r->line_based ) { rc = Cut( r->start.line, r->start.column, r->end.line, r->end.column, true ); r->end.column = -1; scol = -1; ecol = -1; } else { if( r->start.line == CurrentPos.line ) { r->start.line++; } else { r->end.line--; } if( r->start.line <= r->end.line ) { rc = DeleteLineRange( r->start.line, r->end.line, 0 ); if( rc != ERR_NO_ERR ) { EndUndoGroup( UndoStack ); return( rc ); } } scol = FindStartOfCurrentLine() - 1; ecol = CurrentLine->len - 1; } DCDisplayAllLines(); rc = DeleteAndInsertText( scol, ecol ); EndUndoGroup( UndoStack ); return( rc ); } /* * change text on current line */ rc = ERR_NO_ERR; GoToLineNoRelCurs( r->start.line ); ecol = r->end.column; scol = r->start.column; #ifdef __WIN__ // GetCurrentLine(); strcpy( WorkLine->data, CurrentLine->data ); tmp = WorkLine->data[ecol]; WorkLine->data[ecol] = '$'; #else vecol = VirtualColumnOnCurrentLine( ecol + 1 ); vecol--; ExpandTabsInABuffer( CurrentLine->data, CurrentLine->len, WorkLine->data, EditVars.MaxLine + 1 ); WorkLine->len = strlen( WorkLine->data ); tmp = WorkLine->data[vecol]; WorkLine->data[vecol] = '$'; #endif if( WorkLine->len == 0 ) { WorkLine->data[1] = '\0'; } EditFlags.InsertModeActive = true; GoToColumn( scol + 1, CurrentLine->len ); EditFlags.InsertModeActive = false; DisplayWorkLine( true ); UnselectRegion(); DCUpdate(); #ifndef __WIN__ HiliteAColumnRange( CurrentPos.line, scol, ecol ); #endif /* * now, get ready to do change */ key = GetNextEvent( false ); #ifdef __WIN__ WorkLine->data[ecol] = tmp; #else WorkLine->data[vecol] = tmp; #endif DisplayWorkLine( true ); if( key == VI_KEY( ESC ) && !EditFlags.ChangeLikeVI ) { WorkLine->len = -1; GoToColumn( scol + 1, CurrentLine->len ); } else { KeyAdd( key ); rc = DeleteAndInsertText( scol, ecol ); } return( rc ); } /* Change */