static void real_pos(const editor_t *editor, vector_size_t *x, text_line_size_t *y) { *x = 1; for (vector_size_t i = editor->allow_edit_begin; i < editor->current_line; ++i) { if (contains_newline(editor_line(editor, i))) ++*x; } *y = editor->screen_pos + 1; if (editor->current_line) { for (vector_size_t i = editor->current_line - 1; i >= editor->allow_edit_begin;) { const text_line_t *tl = editor_line(editor, i); if (contains_newline(tl)) break; else *y += display_width(tl->buf, tl->size); if (i) --i; else break; } } }
/* ** The pTkt object is a ticket change artifact. Output a detailed ** description of this object. */ void ticket_output_change_artifact(Manifest *pTkt, const char *zListType){ int i; int wikiFlags = WIKI_NOBADLINKS; const char *zBlock = "<blockquote>"; const char *zEnd = "</blockquote>"; if( P("plaintext")!=0 ){ wikiFlags |= WIKI_LINKSONLY; zBlock = "<blockquote><pre class='verbatim'>"; zEnd = "</pre></blockquote>"; } if( zListType==0 ) zListType = "1"; cgi_printf("<ol type=\"%s\">\n",(zListType)); for(i=0; i<pTkt->nField; i++){ Blob val; const char *z; z = pTkt->aField[i].zName; blob_set(&val, pTkt->aField[i].zValue); if( z[0]=='+' ){ cgi_printf("<li>Appended to %h:%s\n",(&z[1]),(zBlock)); wiki_convert(&val, 0, wikiFlags); cgi_printf("%s</li>\n",(zEnd)); }else if( blob_size(&val)>50 || contains_newline(&val) ){ cgi_printf("<li>Change %h to:%s\n",(z),(zBlock)); wiki_convert(&val, 0, wikiFlags); cgi_printf("%s</li>\n",(zEnd)); }else{ cgi_printf("<li>Change %h to \"%h\"</li>\n",(z),(blob_str(&val))); } blob_reset(&val); } cgi_printf("</ol>\n"); }
static void display(editor_t *editor, bool ansi) { int line = 0, lines = screen_lines() - 1; for (vector_size_t i = editor->window_top; i < editor->allow_edit_end && line < lines; ++i, ++line) { text_line_t *tl = editor_line(editor, i); if (editor->redraw || tl->redraw) { screen_move_clear(line); if (ansi) { screen_puts(tl->buf, tl->size); } else { bool marked = i >= editor->mark_begin && i < editor->mark_end; if (marked) screen_puts("\033[7m", 4); const char *last = tl->buf, *end = tl->buf + tl->size; for (const char *ptr = tl->buf; ptr < end; ++ptr) { if (*ptr == '\033') { if (ptr > last) screen_puts(last, ptr - last); screen_puts("\033[33m*", 0); if (marked) screen_puts("\033[37m", 0); else screen_puts("\033[m", 0); last = ptr + 1; } } if (end > last) { size_t size = end - last - contains_newline(tl); if (size) screen_puts(last, size); } if (marked) screen_puts("\033[m", 3); } tl->redraw = false; } } if (editor->redraw) { for (; line < lines; ++line) { screen_move_clear(line); screen_puts("\033[33m~\033[m", 0); } } show_status_line(editor); screen_move(editor->current_line - editor->window_top, editor->screen_pos); editor->redraw = false; return; }
/** * 从编辑器当前光标所在行开始自动折行 * @param editor 编辑器 * @return 折行次数 */ static int wrap_long_lines(editor_t *editor) { vector_size_t saved_line, line; saved_line = line = editor->current_line; text_line_size_t saved_pos = editor->buffer_pos; while (1) { text_line_t *tl = editor_line(editor, line); const char *p = seek_to_display_width(tl->buf, tl->size, WRAP_MARGIN); if (p >= tl->buf + tl->size) break; editor->redraw = true; // 如果当前光标被折行,记录下新位置 if (saved_line == line && p <= tl->buf + saved_pos) { saved_pos -= p - tl->buf; saved_line = line + 1; } ++line; text_line_t *next_tl = editor_line(editor, line); if (contains_newline(tl) || !next_tl || line >= editor->allow_edit_end) { text_line_t new_tl = { .buf = NULL }; vector_insert(&editor->lines, line, &new_tl); tl = editor_line(editor, line - 1); ++editor->allow_edit_end; } ++editor->current_line; editor->buffer_pos = 0; if (raw_insert(editor, p, tl->size - (p - tl->buf))) tl->size = p - tl->buf; else break; }
/* ** COMMAND: ticket* ** Usage: %fossil ticket SUBCOMMAND ... ** ** Run various subcommands to control tickets ** ** %fossil ticket show (REPORTTITLE|REPORTNR) ?TICKETFILTER? ?options? ** ** options can be: ** ?-l|--limit LIMITCHAR? ** ?-q|--quote? ** ?-R|--repository FILE? ** ** Run the ticket report, identified by the report format title ** used in the gui. The data is written as flat file on stdout, ** using TAB as separator. The separator can be changed using ** the -l or --limit option. ** ** If TICKETFILTER is given on the commandline, the query is ** limited with a new WHERE-condition. ** example: Report lists a column # with the uuid ** TICKETFILTER may be [#]='uuuuuuuuu' ** example: Report only lists rows with status not open ** TICKETFILTER: status != 'open' ** If the option -q|--quote is used, the tickets are encoded by ** quoting special chars(space -> \\s, tab -> \\t, newline -> \\n, ** cr -> \\r, formfeed -> \\f, vtab -> \\v, nul -> \\0, \\ -> \\\\). ** Otherwise, the simplified encoding as on the show report raw ** page in the gui is used. This has no effect in JSON mode. ** ** Instead of the report title its possible to use the report ** number. Using the special report number 0 list all columns, ** defined in the ticket table. ** ** %fossil ticket list fields ** ** list all fields, defined for ticket in the fossil repository ** ** %fossil ticket list reports ** ** list all ticket reports, defined in the fossil repository ** ** %fossil ticket set TICKETUUID (FIELD VALUE)+ ?-q|--quote? ** %fossil ticket change TICKETUUID (FIELD VALUE)+ ?-q|--quote? ** ** change ticket identified by TICKETUUID and set the value of ** field FIELD to VALUE. ** ** Field names as defined in the TICKET table. By default, these ** names include: type, status, subsystem, priority, severity, foundin, ** resolution, title, and comment, but other field names can be added ** or substituted in customized installations. ** ** If you use +FIELD, the VALUE Is appended to the field FIELD. ** You can use more than one field/value pair on the commandline. ** Using -q|--quote enables the special character decoding as ** in "ticket show". So it's possible, to set multiline text or ** text with special characters. ** ** %fossil ticket add FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote? ** ** like set, but create a new ticket with the given values. ** ** %fossil ticket history TICKETUUID ** ** Show the complete change history for the ticket ** ** The values in set|add are not validated against the definitions ** given in "Ticket Common Script". */ void ticket_cmd(void){ int n; const char *zUser; const char *zDate; const char *zTktUuid; /* do some ints, we want to be inside a checkout */ db_find_and_open_repository(0, 0); user_select(); zUser = find_option("user-override",0,1); if( zUser==0 ) zUser = g.zLogin; zDate = find_option("date-override",0,1); if( zDate==0 ) zDate = "now"; zDate = date_in_standard_format(zDate); zTktUuid = find_option("uuid-override",0,1); if( zTktUuid && (strlen(zTktUuid)!=40 || !validate16(zTktUuid,40)) ){ fossil_fatal("invalid --uuid-override: must be 40 characters of hex"); } /* ** Check that the user exists. */ if( !db_exists("SELECT 1 FROM user WHERE login=%Q", zUser) ){ fossil_fatal("no such user: %s", zUser); } if( g.argc<3 ){ usage("add|change|list|set|show|history"); } n = strlen(g.argv[2]); if( n==1 && g.argv[2][0]=='s' ){ /* set/show cannot be distinguished, so show the usage */ usage("add|change|list|set|show|history"); } if( strncmp(g.argv[2],"list",n)==0 ){ if( g.argc==3 ){ usage("list fields|reports"); }else{ n = strlen(g.argv[3]); if( !strncmp(g.argv[3],"fields",n) ){ /* simply show all field names */ int i; /* read all available ticket fields */ getAllTicketFields(); for(i=0; i<nField; i++){ printf("%s\n",aField[i].zName); } }else if( !strncmp(g.argv[3],"reports",n) ){ rpt_list_reports(); }else{ fossil_fatal("unknown ticket list option '%s'!",g.argv[3]); } } }else{ /* add a new ticket or set fields on existing tickets */ tTktShowEncoding tktEncoding; tktEncoding = find_option("quote","q",0) ? tktFossilize : tktNoTab; if( strncmp(g.argv[2],"show",n)==0 ){ if( g.argc==3 ){ usage("show REPORTNR"); }else{ const char *zRep = 0; const char *zSep = 0; const char *zFilterUuid = 0; zSep = find_option("limit","l",1); zRep = g.argv[3]; if( !strcmp(zRep,"0") ){ zRep = 0; } if( g.argc>4 ){ zFilterUuid = g.argv[4]; } rptshow( zRep, zSep, zFilterUuid, tktEncoding ); } }else{ /* add a new ticket or update an existing ticket */ enum { set,add,history,err } eCmd = err; int i = 0; Blob tktchng, cksum; /* get command type (set/add) and get uuid, if needed for set */ if( strncmp(g.argv[2],"set",n)==0 || strncmp(g.argv[2],"change",n)==0 || strncmp(g.argv[2],"history",n)==0 ){ if( strncmp(g.argv[2],"history",n)==0 ){ eCmd = history; }else{ eCmd = set; } if( g.argc==3 ){ usage("set|change|history TICKETUUID"); } zTktUuid = db_text(0, "SELECT tkt_uuid FROM ticket WHERE tkt_uuid GLOB '%s*'", g.argv[3] ); if( !zTktUuid ){ fossil_fatal("unknown ticket: '%s'!",g.argv[3]); } i=4; }else if( strncmp(g.argv[2],"add",n)==0 ){ eCmd = add; i = 3; if( zTktUuid==0 ){ zTktUuid = db_text(0, "SELECT lower(hex(randomblob(20)))"); } } /* none of set/add, so show the usage! */ if( eCmd==err ){ usage("add|fieldlist|set|show|history"); } /* we just handle history separately here, does not get out */ if( eCmd==history ){ Stmt q; int tagid; if ( i != g.argc ){ fossil_fatal("no other parameters expected to %s!",g.argv[2]); } tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'", zTktUuid); if( tagid==0 ){ fossil_fatal("no such ticket %h", zTktUuid); } db_prepare(&q, "SELECT datetime(mtime%s), objid, uuid, NULL, NULL, NULL" " FROM event, blob" " WHERE objid IN (SELECT rid FROM tagxref WHERE tagid=%d)" " AND blob.rid=event.objid" " UNION " "SELECT datetime(mtime%s), attachid, uuid, src, " " filename, user" " FROM attachment, blob" " WHERE target=(SELECT substr(tagname,5) FROM tag WHERE tagid=%d)" " AND blob.rid=attachid" " ORDER BY 1 DESC", timeline_utc(), tagid, timeline_utc(), tagid ); while( db_step(&q)==SQLITE_ROW ){ Manifest *pTicket; char zShort[12]; const char *zDate = db_column_text(&q, 0); int rid = db_column_int(&q, 1); const char *zChngUuid = db_column_text(&q, 2); const char *zFile = db_column_text(&q, 4); memcpy(zShort, zChngUuid, 10); zShort[10] = 0; if( zFile!=0 ){ const char *zSrc = db_column_text(&q, 3); const char *zUser = db_column_text(&q, 5); if( zSrc==0 || zSrc[0]==0 ){ fossil_print("Delete attachment %s\n", zFile); }else{ fossil_print("Add attachment %s\n", zFile); } fossil_print(" by %s on %s\n", zUser, zDate); }else{ pTicket = manifest_get(rid, CFTYPE_TICKET, 0); if( pTicket ){ int i; fossil_print("Ticket Change by %s on %s:\n", pTicket->zUser, zDate); for(i=0; i<pTicket->nField; i++){ Blob val; const char *z; z = pTicket->aField[i].zName; blob_set(&val, pTicket->aField[i].zValue); if( z[0]=='+' ){ fossil_print(" Append to "); z++; }else{ fossil_print(" Change "); } fossil_print("%h: ",z); if( blob_size(&val)>50 || contains_newline(&val)) { fossil_print("\n ",blob_str(&val)); comment_print(blob_str(&val),4,79); }else{ fossil_print("%s\n",blob_str(&val)); } blob_reset(&val); } } manifest_destroy(pTicket); } } db_finalize(&q); return; } /* read all given ticket field/value pairs from command line */ if( i==g.argc ){ fossil_fatal("empty %s command aborted!",g.argv[2]); } getAllTicketFields(); /* read commandline and assign fields in the aField[].zValue array */ while( i<g.argc ){ char *zFName; char *zFValue; int j; int append = 0; zFName = g.argv[i++]; if( i==g.argc ){ fossil_fatal("missing value for '%s'!",zFName); } zFValue = g.argv[i++]; if( tktEncoding == tktFossilize ){ zFValue=mprintf("%s",zFValue); defossilize(zFValue); } append = (zFName[0] == '+'); if (append){ zFName++; } j = fieldId(zFName); if( j == -1 ){ fossil_fatal("unknown field name '%s'!",zFName); }else{ if (append) { aField[j].zAppend = zFValue; } else { aField[j].zValue = zFValue; } } } /* now add the needed artifacts to the repository */ blob_zero(&tktchng); /* add the time to the ticket manifest */ blob_appendf(&tktchng, "D %s\n", zDate); /* append defined elements */ for(i=0; i<nField; i++){ char *zValue = 0; char *zPfx; if (aField[i].zAppend && aField[i].zAppend[0] ){ zPfx = " +"; zValue = aField[i].zAppend; } else if( aField[i].zValue && aField[i].zValue[0] ){ zPfx = " "; zValue = aField[i].zValue; } else { continue; } if( memcmp(aField[i].zName, "private_", 8)==0 ){ zValue = db_conceal(zValue, strlen(zValue)); blob_appendf(&tktchng, "J%s%s %s\n", zPfx, aField[i].zName, zValue); }else{ blob_appendf(&tktchng, "J%s%s %#F\n", zPfx, aField[i].zName, strlen(zValue), zValue); } } blob_appendf(&tktchng, "K %s\n", zTktUuid); blob_appendf(&tktchng, "U %F\n", zUser); md5sum_blob(&tktchng, &cksum); blob_appendf(&tktchng, "Z %b\n", &cksum); if( ticket_put(&tktchng, zTktUuid, 0) ){ fossil_fatal("%s\n", g.zErrMsg); }else{ fossil_print("ticket %s succeeded for %s\n", (eCmd==set?"set":"add"),zTktUuid); } } } }