Fossil SCM
Changes line-numbered output to make line numbers selectable, as discussed in [https://fossil-scm.org/forum/forumpost/dc3da10590]. A couple decisions are needed before deciding whether to merge.
Commit
ec73edd4d09dc7c9f2f3e766d2fa4274b32bf7dcb31739c2d77511b1ab0d93c4
Parent
39a5289b9b7c20a…
10 files changed
+2
-1
+1
-1
+52
+1
-1
+40
+40
-6
+1
+1
-1
+1
+2
+2
-1
| --- src/ajax.c | ||
| +++ src/ajax.c | ||
| @@ -130,11 +130,12 @@ | ||
| 130 | 130 | wiki_render_by_mimetype(pContent, zMime); |
| 131 | 131 | break; |
| 132 | 132 | default:{ |
| 133 | 133 | const char *zContent = blob_str(pContent); |
| 134 | 134 | if(AJAX_PREVIEW_LINE_NUMBERS & flags){ |
| 135 | - output_text_with_line_numbers(zContent, "on"); | |
| 135 | + output_text_with_line_numbers(zContent, | |
| 136 | + blob_size(pContent), "on"); | |
| 136 | 137 | }else{ |
| 137 | 138 | const char *zExt = strrchr(zName,'.'); |
| 138 | 139 | if(zExt && zExt[1]){ |
| 139 | 140 | CX("<pre><code class='language-%s'>%h</code></pre>", |
| 140 | 141 | zExt+1, zContent); |
| 141 | 142 |
| --- src/ajax.c | |
| +++ src/ajax.c | |
| @@ -130,11 +130,12 @@ | |
| 130 | wiki_render_by_mimetype(pContent, zMime); |
| 131 | break; |
| 132 | default:{ |
| 133 | const char *zContent = blob_str(pContent); |
| 134 | if(AJAX_PREVIEW_LINE_NUMBERS & flags){ |
| 135 | output_text_with_line_numbers(zContent, "on"); |
| 136 | }else{ |
| 137 | const char *zExt = strrchr(zName,'.'); |
| 138 | if(zExt && zExt[1]){ |
| 139 | CX("<pre><code class='language-%s'>%h</code></pre>", |
| 140 | zExt+1, zContent); |
| 141 |
| --- src/ajax.c | |
| +++ src/ajax.c | |
| @@ -130,11 +130,12 @@ | |
| 130 | wiki_render_by_mimetype(pContent, zMime); |
| 131 | break; |
| 132 | default:{ |
| 133 | const char *zContent = blob_str(pContent); |
| 134 | if(AJAX_PREVIEW_LINE_NUMBERS & flags){ |
| 135 | output_text_with_line_numbers(zContent, |
| 136 | blob_size(pContent), "on"); |
| 137 | }else{ |
| 138 | const char *zExt = strrchr(zName,'.'); |
| 139 | if(zExt && zExt[1]){ |
| 140 | CX("<pre><code class='language-%s'>%h</code></pre>", |
| 141 | zExt+1, zContent); |
| 142 |
+1
-1
| --- src/attach.c | ||
| +++ src/attach.c | ||
| @@ -617,11 +617,11 @@ | ||
| 617 | 617 | const char *z; |
| 618 | 618 | content_get(ridSrc, &attach); |
| 619 | 619 | blob_to_utf8_no_bom(&attach, 0); |
| 620 | 620 | z = blob_str(&attach); |
| 621 | 621 | if( zLn ){ |
| 622 | - output_text_with_line_numbers(z, zLn); | |
| 622 | + output_text_with_line_numbers(z, blob_size(&attach), zLn); | |
| 623 | 623 | }else{ |
| 624 | 624 | @ <pre> |
| 625 | 625 | @ %h(z) |
| 626 | 626 | @ </pre> |
| 627 | 627 | } |
| 628 | 628 |
| --- src/attach.c | |
| +++ src/attach.c | |
| @@ -617,11 +617,11 @@ | |
| 617 | const char *z; |
| 618 | content_get(ridSrc, &attach); |
| 619 | blob_to_utf8_no_bom(&attach, 0); |
| 620 | z = blob_str(&attach); |
| 621 | if( zLn ){ |
| 622 | output_text_with_line_numbers(z, zLn); |
| 623 | }else{ |
| 624 | @ <pre> |
| 625 | @ %h(z) |
| 626 | @ </pre> |
| 627 | } |
| 628 |
| --- src/attach.c | |
| +++ src/attach.c | |
| @@ -617,11 +617,11 @@ | |
| 617 | const char *z; |
| 618 | content_get(ridSrc, &attach); |
| 619 | blob_to_utf8_no_bom(&attach, 0); |
| 620 | z = blob_str(&attach); |
| 621 | if( zLn ){ |
| 622 | output_text_with_line_numbers(z, blob_size(&attach), zLn); |
| 623 | }else{ |
| 624 | @ <pre> |
| 625 | @ %h(z) |
| 626 | @ </pre> |
| 627 | } |
| 628 |
+52
| --- src/default.css | ||
| +++ src/default.css | ||
| @@ -445,10 +445,12 @@ | ||
| 445 | 445 | div.selectedText { |
| 446 | 446 | font-weight: bold; |
| 447 | 447 | color: blue; |
| 448 | 448 | background-color: #d5d5ff; |
| 449 | 449 | border: 1px blue solid; |
| 450 | + border-top: none; | |
| 451 | + border-bottom: none; | |
| 450 | 452 | } |
| 451 | 453 | p.missingPriv { |
| 452 | 454 | color: blue; |
| 453 | 455 | } |
| 454 | 456 | span.wikiruleHead { |
| @@ -1138,5 +1140,55 @@ | ||
| 1138 | 1140 | .input-with-label > label { |
| 1139 | 1141 | font-weight: initial; |
| 1140 | 1142 | margin: 0 0.25em 0 0.25em; |
| 1141 | 1143 | vertical-align: middle; |
| 1142 | 1144 | } |
| 1145 | + | |
| 1146 | +table.numbered-lines td { | |
| 1147 | + font-family: monospace; | |
| 1148 | + line-height: 2.8ex; | |
| 1149 | + white-space: pre; | |
| 1150 | + margin: 0; | |
| 1151 | + white-space: nowrap; | |
| 1152 | + vertical-align: top; | |
| 1153 | + padding: 1em 0 0 0 /*prevents slight overlap at top */; | |
| 1154 | +} | |
| 1155 | +table.numbered-lines td:nth-of-type(1) > span { | |
| 1156 | + display: block; | |
| 1157 | + margin: 0; | |
| 1158 | + padding: 0; | |
| 1159 | + line-height: inherit; | |
| 1160 | + font-size: inherit; | |
| 1161 | + font-family: inherit; | |
| 1162 | + cursor: pointer; | |
| 1163 | + white-space: pre; | |
| 1164 | +} | |
| 1165 | +table.numbered-lines td:nth-of-type(1) > span:hover { | |
| 1166 | + background-color: #777; | |
| 1167 | + opacity: 0.25; | |
| 1168 | +} | |
| 1169 | +table.numbered-lines td:nth-of-type(2) { | |
| 1170 | + padding-left: 1em; | |
| 1171 | +} | |
| 1172 | +table.numbered-lines td:nth-of-type(2) > pre { | |
| 1173 | + margin: 0; | |
| 1174 | + padding: 0; | |
| 1175 | + overflow-x: auto; | |
| 1176 | + overflow-y: hidden /* apparently not needed, but eases my mind */; | |
| 1177 | + padding: 0 0 1em 0 /*prevents a slight underlap at bottom from triggering a scrollar */; | |
| 1178 | +} | |
| 1179 | +table.numbered-lines td:nth-of-type(2) > pre > code { | |
| 1180 | + margin: 0; | |
| 1181 | + padding: 0; | |
| 1182 | + white-space: pre; | |
| 1183 | + line-height: inherit; | |
| 1184 | + font-size: inherit; | |
| 1185 | + font-family: inherit; | |
| 1186 | +} | |
| 1187 | +table.numbered-lines td:nth-of-type(2) > pre > code > * { | |
| 1188 | + box-sizing: border-box; | |
| 1189 | +} | |
| 1190 | +/* if div.selectedText has a top/bottom border, we need to subtract those | |
| 1191 | + from the top margin here... */ | |
| 1192 | +/*table.numbered-lines td:nth-of-type(2) > pre > code > div.selectedText { | |
| 1193 | + margin-top: -2px; | |
| 1194 | +}*/ | |
| 1143 | 1195 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -445,10 +445,12 @@ | |
| 445 | div.selectedText { |
| 446 | font-weight: bold; |
| 447 | color: blue; |
| 448 | background-color: #d5d5ff; |
| 449 | border: 1px blue solid; |
| 450 | } |
| 451 | p.missingPriv { |
| 452 | color: blue; |
| 453 | } |
| 454 | span.wikiruleHead { |
| @@ -1138,5 +1140,55 @@ | |
| 1138 | .input-with-label > label { |
| 1139 | font-weight: initial; |
| 1140 | margin: 0 0.25em 0 0.25em; |
| 1141 | vertical-align: middle; |
| 1142 | } |
| 1143 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -445,10 +445,12 @@ | |
| 445 | div.selectedText { |
| 446 | font-weight: bold; |
| 447 | color: blue; |
| 448 | background-color: #d5d5ff; |
| 449 | border: 1px blue solid; |
| 450 | border-top: none; |
| 451 | border-bottom: none; |
| 452 | } |
| 453 | p.missingPriv { |
| 454 | color: blue; |
| 455 | } |
| 456 | span.wikiruleHead { |
| @@ -1138,5 +1140,55 @@ | |
| 1140 | .input-with-label > label { |
| 1141 | font-weight: initial; |
| 1142 | margin: 0 0.25em 0 0.25em; |
| 1143 | vertical-align: middle; |
| 1144 | } |
| 1145 | |
| 1146 | table.numbered-lines td { |
| 1147 | font-family: monospace; |
| 1148 | line-height: 2.8ex; |
| 1149 | white-space: pre; |
| 1150 | margin: 0; |
| 1151 | white-space: nowrap; |
| 1152 | vertical-align: top; |
| 1153 | padding: 1em 0 0 0 /*prevents slight overlap at top */; |
| 1154 | } |
| 1155 | table.numbered-lines td:nth-of-type(1) > span { |
| 1156 | display: block; |
| 1157 | margin: 0; |
| 1158 | padding: 0; |
| 1159 | line-height: inherit; |
| 1160 | font-size: inherit; |
| 1161 | font-family: inherit; |
| 1162 | cursor: pointer; |
| 1163 | white-space: pre; |
| 1164 | } |
| 1165 | table.numbered-lines td:nth-of-type(1) > span:hover { |
| 1166 | background-color: #777; |
| 1167 | opacity: 0.25; |
| 1168 | } |
| 1169 | table.numbered-lines td:nth-of-type(2) { |
| 1170 | padding-left: 1em; |
| 1171 | } |
| 1172 | table.numbered-lines td:nth-of-type(2) > pre { |
| 1173 | margin: 0; |
| 1174 | padding: 0; |
| 1175 | overflow-x: auto; |
| 1176 | overflow-y: hidden /* apparently not needed, but eases my mind */; |
| 1177 | padding: 0 0 1em 0 /*prevents a slight underlap at bottom from triggering a scrollar */; |
| 1178 | } |
| 1179 | table.numbered-lines td:nth-of-type(2) > pre > code { |
| 1180 | margin: 0; |
| 1181 | padding: 0; |
| 1182 | white-space: pre; |
| 1183 | line-height: inherit; |
| 1184 | font-size: inherit; |
| 1185 | font-family: inherit; |
| 1186 | } |
| 1187 | table.numbered-lines td:nth-of-type(2) > pre > code > * { |
| 1188 | box-sizing: border-box; |
| 1189 | } |
| 1190 | /* if div.selectedText has a top/bottom border, we need to subtract those |
| 1191 | from the top margin here... */ |
| 1192 | /*table.numbered-lines td:nth-of-type(2) > pre > code > div.selectedText { |
| 1193 | margin-top: -2px; |
| 1194 | }*/ |
| 1195 |
+1
-1
| --- src/diff.c | ||
| +++ src/diff.c | ||
| @@ -125,11 +125,11 @@ | ||
| 125 | 125 | ** in the count even if it lacks the \n terminator. If an empty string |
| 126 | 126 | ** is specified, the number of lines is zero. For the purposes of this |
| 127 | 127 | ** function, a string is considered empty if it contains no characters |
| 128 | 128 | ** -OR- it contains only NUL characters. |
| 129 | 129 | */ |
| 130 | -static int count_lines( | |
| 130 | +int count_lines( | |
| 131 | 131 | const char *z, |
| 132 | 132 | int n, |
| 133 | 133 | int *pnLine |
| 134 | 134 | ){ |
| 135 | 135 | int nLine; |
| 136 | 136 | |
| 137 | 137 | ADDED src/fossil.numbered-lines.js |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -125,11 +125,11 @@ | |
| 125 | ** in the count even if it lacks the \n terminator. If an empty string |
| 126 | ** is specified, the number of lines is zero. For the purposes of this |
| 127 | ** function, a string is considered empty if it contains no characters |
| 128 | ** -OR- it contains only NUL characters. |
| 129 | */ |
| 130 | static int count_lines( |
| 131 | const char *z, |
| 132 | int n, |
| 133 | int *pnLine |
| 134 | ){ |
| 135 | int nLine; |
| 136 | |
| 137 | DDED src/fossil.numbered-lines.js |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -125,11 +125,11 @@ | |
| 125 | ** in the count even if it lacks the \n terminator. If an empty string |
| 126 | ** is specified, the number of lines is zero. For the purposes of this |
| 127 | ** function, a string is considered empty if it contains no characters |
| 128 | ** -OR- it contains only NUL characters. |
| 129 | */ |
| 130 | int count_lines( |
| 131 | const char *z, |
| 132 | int n, |
| 133 | int *pnLine |
| 134 | ){ |
| 135 | int nLine; |
| 136 | |
| 137 | DDED src/fossil.numbered-lines.js |
| --- a/src/fossil.numbered-lines.js | ||
| +++ b/src/fossil.numbered-lines.js | ||
| @@ -0,0 +1,40 @@ | ||
| 1 | +(function callee(arg){ | |
| 2 | + /* JSelse if(!arg){tion callee(arg){ | |
| 3 | +e(arg){ | |
| 4 | + /* | |
| 5 | + JSfossil', | |
| 6 | + ppend(function1M@Y5,1:rx@ZS,1: | |
| 7 | +G@eW,1p@_~,11:unselect line*/ | |
| 8 | + //console.debug("Unselected line #"+ln); | |
| 9 | +Q@eU,Z@cT,2g@dG,c://console.debug("Selected range: ",rng)J@cU,K:2; | |
| 10 | + } | |
| 11 | + ; | |
| 12 | + else if(tbl.length>1){ | |
| 13 | +f(1l@kP,n@mBreturn; | |
| 14 | + }else{ | |
| 15 | +gs =urn y - this.e.crgs =urn y - this.e.clientHeight/2; | |
| 16 | + console.debug("(function callee(arg){ | |
| 17 | + /* | |
| 18 | + JSelse if(!arg){tion callee(arg){ | |
| 19 | + /* | |
| 20 | + JS(function callee(arg){ | |
| 21 | + /* | |
| 22 | + JSfossil', | |
| 23 | + (F.toast("Copied: ",D.append(function callee(arg){ | |
| 24 | + /* | |
| 25 | + JSelse if(!arg){tion callee(artooltip>x, | |
| 26 | + adjustY: (y)=>y9F@Lz,1M@Y5,1:rx@ZS,1: | |
| 27 | +G@eW,1p@_~,11:unselect line*/ | |
| 28 | + //console.debug("Unselected line #"+ln); | |
| 29 | +Q@eU,Z@cT,2g@dG,c://console.debug("Selected range: ",rng)J@cU,K:2; | |
| 30 | + } | |
| 31 | + } | |
| 32 | + h@WA,9:.forEach(7@ED,p@XB,K:); | |
| 33 | + if(f.mode>0){y@j0,7:lineTipa@qr,D: const spansU@W8,D:'); | |
| 34 | + if(1l@kP,n@mB,17@m~,7:if(i===G@bW,1t@oz,2:; | |
| 35 | +H@f0,N@RT,Q@r~,1IHlU4;function(xreturn x + 20; | |
| 36 | + }, | |
| 37 | + adjustY: function(y){ | |
| 38 | + return y - this.e.clientHeight/2; | |
| 39 | + }addClass(D.span(), 'copy-button'); | |
| 40 | + const link = D.attr(D.span(), 'id', 'fossil-ln-link'lineTip.show(ev.clientX, ev.clientY |
| --- a/src/fossil.numbered-lines.js | |
| +++ b/src/fossil.numbered-lines.js | |
| @@ -0,0 +1,40 @@ | |
| --- a/src/fossil.numbered-lines.js | |
| +++ b/src/fossil.numbered-lines.js | |
| @@ -0,0 +1,40 @@ | |
| 1 | (function callee(arg){ |
| 2 | /* JSelse if(!arg){tion callee(arg){ |
| 3 | e(arg){ |
| 4 | /* |
| 5 | JSfossil', |
| 6 | ppend(function1M@Y5,1:rx@ZS,1: |
| 7 | G@eW,1p@_~,11:unselect line*/ |
| 8 | //console.debug("Unselected line #"+ln); |
| 9 | Q@eU,Z@cT,2g@dG,c://console.debug("Selected range: ",rng)J@cU,K:2; |
| 10 | } |
| 11 | ; |
| 12 | else if(tbl.length>1){ |
| 13 | f(1l@kP,n@mBreturn; |
| 14 | }else{ |
| 15 | gs =urn y - this.e.crgs =urn y - this.e.clientHeight/2; |
| 16 | console.debug("(function callee(arg){ |
| 17 | /* |
| 18 | JSelse if(!arg){tion callee(arg){ |
| 19 | /* |
| 20 | JS(function callee(arg){ |
| 21 | /* |
| 22 | JSfossil', |
| 23 | (F.toast("Copied: ",D.append(function callee(arg){ |
| 24 | /* |
| 25 | JSelse if(!arg){tion callee(artooltip>x, |
| 26 | adjustY: (y)=>y9F@Lz,1M@Y5,1:rx@ZS,1: |
| 27 | G@eW,1p@_~,11:unselect line*/ |
| 28 | //console.debug("Unselected line #"+ln); |
| 29 | Q@eU,Z@cT,2g@dG,c://console.debug("Selected range: ",rng)J@cU,K:2; |
| 30 | } |
| 31 | } |
| 32 | h@WA,9:.forEach(7@ED,p@XB,K:); |
| 33 | if(f.mode>0){y@j0,7:lineTipa@qr,D: const spansU@W8,D:'); |
| 34 | if(1l@kP,n@mB,17@m~,7:if(i===G@bW,1t@oz,2:; |
| 35 | H@f0,N@RT,Q@r~,1IHlU4;function(xreturn x + 20; |
| 36 | }, |
| 37 | adjustY: function(y){ |
| 38 | return y - this.e.clientHeight/2; |
| 39 | }addClass(D.span(), 'copy-button'); |
| 40 | const link = D.attr(D.span(), 'id', 'fossil-ln-link'lineTip.show(ev.clientX, ev.clientY |
+40
-6
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -2011,27 +2011,30 @@ | ||
| 2011 | 2011 | manifest_destroy(pManifest); |
| 2012 | 2012 | return rid; |
| 2013 | 2013 | } |
| 2014 | 2014 | |
| 2015 | 2015 | /* |
| 2016 | -** The "z" argument is a string that contains the text of a source code | |
| 2017 | -** file. This routine appends that text to the HTTP reply with line numbering. | |
| 2016 | +** The "z" argument is a string that contains the text of a source | |
| 2017 | +** code file and nZ is its length in bytes. This routine appends that | |
| 2018 | +** text to the HTTP reply with line numbering. | |
| 2018 | 2019 | ** |
| 2019 | 2020 | ** zLn is the ?ln= parameter for the HTTP query. If there is an argument, |
| 2020 | 2021 | ** then highlight that line number and scroll to it once the page loads. |
| 2021 | 2022 | ** If there are two line numbers, highlight the range of lines. |
| 2022 | 2023 | ** Multiple ranges can be highlighed by adding additional line numbers |
| 2023 | 2024 | ** separated by a non-digit character (also not one of [-,.]). |
| 2024 | 2025 | */ |
| 2025 | 2026 | void output_text_with_line_numbers( |
| 2026 | 2027 | const char *z, |
| 2028 | + int nZ, | |
| 2027 | 2029 | const char *zLn |
| 2028 | 2030 | ){ |
| 2029 | 2031 | int iStart, iEnd; /* Start and end of region to highlight */ |
| 2030 | 2032 | int n = 0; /* Current line number */ |
| 2031 | 2033 | int i = 0; /* Loop index */ |
| 2032 | 2034 | int iTop = 0; /* Scroll so that this line is on top of screen. */ |
| 2035 | + int nLine = 0; | |
| 2033 | 2036 | Stmt q; |
| 2034 | 2037 | |
| 2035 | 2038 | iStart = iEnd = atoi(zLn); |
| 2036 | 2039 | db_multi_exec( |
| 2037 | 2040 | "CREATE TEMP TABLE lnos(iStart INTEGER PRIMARY KEY, iEnd INTEGER)"); |
| @@ -2058,11 +2061,17 @@ | ||
| 2058 | 2061 | iEnd = db_column_int(&q, 1); |
| 2059 | 2062 | iTop = iStart - 15 + (iEnd-iStart)/4; |
| 2060 | 2063 | if( iTop>iStart - 2 ) iTop = iStart-2; |
| 2061 | 2064 | } |
| 2062 | 2065 | db_finalize(&q); |
| 2063 | - @ <pre> | |
| 2066 | + CX("<table class='numbered-lines'><tbody><tr><td>"); | |
| 2067 | + count_lines(z, nZ, &nLine); | |
| 2068 | + for(i=0; i < nLine; ++i){ | |
| 2069 | + CX("<span>%6d</span>", i+1); | |
| 2070 | + } | |
| 2071 | + CX("</td><td><pre><code>"); | |
| 2072 | + assert(!n); | |
| 2064 | 2073 | while( z[0] ){ |
| 2065 | 2074 | n++; |
| 2066 | 2075 | db_prepare(&q, |
| 2067 | 2076 | "SELECT min(iStart), max(iEnd) FROM lnos" |
| 2068 | 2077 | " WHERE iStart <= %d AND iEnd >= %d", n, n); |
| @@ -2074,11 +2083,10 @@ | ||
| 2074 | 2083 | for(i=0; z[i] && z[i]!='\n'; i++){} |
| 2075 | 2084 | if( n==iTop ) cgi_append_content("<span id=\"scrollToMe\">", -1); |
| 2076 | 2085 | if( n==iStart ){ |
| 2077 | 2086 | cgi_append_content("<div class=\"selectedText\">",-1); |
| 2078 | 2087 | } |
| 2079 | - cgi_printf("%6d ", n); | |
| 2080 | 2088 | if( i>0 ){ |
| 2081 | 2089 | char *zHtml = htmlize(z, i); |
| 2082 | 2090 | cgi_append_content(zHtml, -1); |
| 2083 | 2091 | fossil_free(zHtml); |
| 2084 | 2092 | } |
| @@ -2087,16 +2095,42 @@ | ||
| 2087 | 2095 | else cgi_append_content("\n", 1); |
| 2088 | 2096 | z += i; |
| 2089 | 2097 | if( z[0]=='\n' ) z++; |
| 2090 | 2098 | } |
| 2091 | 2099 | if( n<iEnd ) cgi_printf("</div>"); |
| 2092 | - @ </pre> | |
| 2100 | + CX("</code></pre></td></tr></tbody></table>\n"); | |
| 2093 | 2101 | if( db_int(0, "SELECT EXISTS(SELECT 1 FROM lnos)") ){ |
| 2094 | 2102 | builtin_request_js("scroll.js"); |
| 2095 | 2103 | } |
| 2104 | + builtin_request_js("fossil.numbered-lines.js"); | |
| 2096 | 2105 | } |
| 2097 | 2106 | |
| 2107 | +/* | |
| 2108 | +** COMMAND: test-line-numbers | |
| 2109 | +** | |
| 2110 | +** Usage: %fossil test-line-numbers FILE ?LN-SPEC? | |
| 2111 | +** | |
| 2112 | +*/ | |
| 2113 | +void cmd_test_line_numbers(void){ | |
| 2114 | + Blob content = empty_blob; | |
| 2115 | + const char * zLn = ""; | |
| 2116 | + const char * zFilename = 0; | |
| 2117 | + | |
| 2118 | + if(g.argc < 3){ | |
| 2119 | + usage("FILE"); | |
| 2120 | + }else if(g.argc>3){ | |
| 2121 | + zLn = g.argv[3]; | |
| 2122 | + } | |
| 2123 | + db_find_and_open_repository(0,0); | |
| 2124 | + zFilename = g.argv[2]; | |
| 2125 | + fossil_print("%s %s\n", zFilename, zLn); | |
| 2126 | + | |
| 2127 | + blob_read_from_file(&content, zFilename, ExtFILE); | |
| 2128 | + output_text_with_line_numbers(blob_str(&content), blob_size(&content), zLn); | |
| 2129 | + blob_reset(&content); | |
| 2130 | + fossil_print("%b\n", cgi_output_blob()); | |
| 2131 | +} | |
| 2098 | 2132 | |
| 2099 | 2133 | /* |
| 2100 | 2134 | ** WEBPAGE: artifact |
| 2101 | 2135 | ** WEBPAGE: file |
| 2102 | 2136 | ** WEBPAGE: whatis |
| @@ -2398,11 +2432,11 @@ | ||
| 2398 | 2432 | " WHERE filename.fnid=mlink.fnid" |
| 2399 | 2433 | " AND mlink.fid=%d", |
| 2400 | 2434 | rid); |
| 2401 | 2435 | zExt = zFileName ? strrchr(zFileName, '.') : 0; |
| 2402 | 2436 | if( zLn ){ |
| 2403 | - output_text_with_line_numbers(z, zLn); | |
| 2437 | + output_text_with_line_numbers(z, blob_size(&content), zLn); | |
| 2404 | 2438 | }else if( zExt && zExt[1] ){ |
| 2405 | 2439 | @ <pre> |
| 2406 | 2440 | @ <code class="language-%s(zExt+1)">%h(z)</code> |
| 2407 | 2441 | @ </pre> |
| 2408 | 2442 | }else{ |
| 2409 | 2443 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -2011,27 +2011,30 @@ | |
| 2011 | manifest_destroy(pManifest); |
| 2012 | return rid; |
| 2013 | } |
| 2014 | |
| 2015 | /* |
| 2016 | ** The "z" argument is a string that contains the text of a source code |
| 2017 | ** file. This routine appends that text to the HTTP reply with line numbering. |
| 2018 | ** |
| 2019 | ** zLn is the ?ln= parameter for the HTTP query. If there is an argument, |
| 2020 | ** then highlight that line number and scroll to it once the page loads. |
| 2021 | ** If there are two line numbers, highlight the range of lines. |
| 2022 | ** Multiple ranges can be highlighed by adding additional line numbers |
| 2023 | ** separated by a non-digit character (also not one of [-,.]). |
| 2024 | */ |
| 2025 | void output_text_with_line_numbers( |
| 2026 | const char *z, |
| 2027 | const char *zLn |
| 2028 | ){ |
| 2029 | int iStart, iEnd; /* Start and end of region to highlight */ |
| 2030 | int n = 0; /* Current line number */ |
| 2031 | int i = 0; /* Loop index */ |
| 2032 | int iTop = 0; /* Scroll so that this line is on top of screen. */ |
| 2033 | Stmt q; |
| 2034 | |
| 2035 | iStart = iEnd = atoi(zLn); |
| 2036 | db_multi_exec( |
| 2037 | "CREATE TEMP TABLE lnos(iStart INTEGER PRIMARY KEY, iEnd INTEGER)"); |
| @@ -2058,11 +2061,17 @@ | |
| 2058 | iEnd = db_column_int(&q, 1); |
| 2059 | iTop = iStart - 15 + (iEnd-iStart)/4; |
| 2060 | if( iTop>iStart - 2 ) iTop = iStart-2; |
| 2061 | } |
| 2062 | db_finalize(&q); |
| 2063 | @ <pre> |
| 2064 | while( z[0] ){ |
| 2065 | n++; |
| 2066 | db_prepare(&q, |
| 2067 | "SELECT min(iStart), max(iEnd) FROM lnos" |
| 2068 | " WHERE iStart <= %d AND iEnd >= %d", n, n); |
| @@ -2074,11 +2083,10 @@ | |
| 2074 | for(i=0; z[i] && z[i]!='\n'; i++){} |
| 2075 | if( n==iTop ) cgi_append_content("<span id=\"scrollToMe\">", -1); |
| 2076 | if( n==iStart ){ |
| 2077 | cgi_append_content("<div class=\"selectedText\">",-1); |
| 2078 | } |
| 2079 | cgi_printf("%6d ", n); |
| 2080 | if( i>0 ){ |
| 2081 | char *zHtml = htmlize(z, i); |
| 2082 | cgi_append_content(zHtml, -1); |
| 2083 | fossil_free(zHtml); |
| 2084 | } |
| @@ -2087,16 +2095,42 @@ | |
| 2087 | else cgi_append_content("\n", 1); |
| 2088 | z += i; |
| 2089 | if( z[0]=='\n' ) z++; |
| 2090 | } |
| 2091 | if( n<iEnd ) cgi_printf("</div>"); |
| 2092 | @ </pre> |
| 2093 | if( db_int(0, "SELECT EXISTS(SELECT 1 FROM lnos)") ){ |
| 2094 | builtin_request_js("scroll.js"); |
| 2095 | } |
| 2096 | } |
| 2097 | |
| 2098 | |
| 2099 | /* |
| 2100 | ** WEBPAGE: artifact |
| 2101 | ** WEBPAGE: file |
| 2102 | ** WEBPAGE: whatis |
| @@ -2398,11 +2432,11 @@ | |
| 2398 | " WHERE filename.fnid=mlink.fnid" |
| 2399 | " AND mlink.fid=%d", |
| 2400 | rid); |
| 2401 | zExt = zFileName ? strrchr(zFileName, '.') : 0; |
| 2402 | if( zLn ){ |
| 2403 | output_text_with_line_numbers(z, zLn); |
| 2404 | }else if( zExt && zExt[1] ){ |
| 2405 | @ <pre> |
| 2406 | @ <code class="language-%s(zExt+1)">%h(z)</code> |
| 2407 | @ </pre> |
| 2408 | }else{ |
| 2409 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -2011,27 +2011,30 @@ | |
| 2011 | manifest_destroy(pManifest); |
| 2012 | return rid; |
| 2013 | } |
| 2014 | |
| 2015 | /* |
| 2016 | ** The "z" argument is a string that contains the text of a source |
| 2017 | ** code file and nZ is its length in bytes. This routine appends that |
| 2018 | ** text to the HTTP reply with line numbering. |
| 2019 | ** |
| 2020 | ** zLn is the ?ln= parameter for the HTTP query. If there is an argument, |
| 2021 | ** then highlight that line number and scroll to it once the page loads. |
| 2022 | ** If there are two line numbers, highlight the range of lines. |
| 2023 | ** Multiple ranges can be highlighed by adding additional line numbers |
| 2024 | ** separated by a non-digit character (also not one of [-,.]). |
| 2025 | */ |
| 2026 | void output_text_with_line_numbers( |
| 2027 | const char *z, |
| 2028 | int nZ, |
| 2029 | const char *zLn |
| 2030 | ){ |
| 2031 | int iStart, iEnd; /* Start and end of region to highlight */ |
| 2032 | int n = 0; /* Current line number */ |
| 2033 | int i = 0; /* Loop index */ |
| 2034 | int iTop = 0; /* Scroll so that this line is on top of screen. */ |
| 2035 | int nLine = 0; |
| 2036 | Stmt q; |
| 2037 | |
| 2038 | iStart = iEnd = atoi(zLn); |
| 2039 | db_multi_exec( |
| 2040 | "CREATE TEMP TABLE lnos(iStart INTEGER PRIMARY KEY, iEnd INTEGER)"); |
| @@ -2058,11 +2061,17 @@ | |
| 2061 | iEnd = db_column_int(&q, 1); |
| 2062 | iTop = iStart - 15 + (iEnd-iStart)/4; |
| 2063 | if( iTop>iStart - 2 ) iTop = iStart-2; |
| 2064 | } |
| 2065 | db_finalize(&q); |
| 2066 | CX("<table class='numbered-lines'><tbody><tr><td>"); |
| 2067 | count_lines(z, nZ, &nLine); |
| 2068 | for(i=0; i < nLine; ++i){ |
| 2069 | CX("<span>%6d</span>", i+1); |
| 2070 | } |
| 2071 | CX("</td><td><pre><code>"); |
| 2072 | assert(!n); |
| 2073 | while( z[0] ){ |
| 2074 | n++; |
| 2075 | db_prepare(&q, |
| 2076 | "SELECT min(iStart), max(iEnd) FROM lnos" |
| 2077 | " WHERE iStart <= %d AND iEnd >= %d", n, n); |
| @@ -2074,11 +2083,10 @@ | |
| 2083 | for(i=0; z[i] && z[i]!='\n'; i++){} |
| 2084 | if( n==iTop ) cgi_append_content("<span id=\"scrollToMe\">", -1); |
| 2085 | if( n==iStart ){ |
| 2086 | cgi_append_content("<div class=\"selectedText\">",-1); |
| 2087 | } |
| 2088 | if( i>0 ){ |
| 2089 | char *zHtml = htmlize(z, i); |
| 2090 | cgi_append_content(zHtml, -1); |
| 2091 | fossil_free(zHtml); |
| 2092 | } |
| @@ -2087,16 +2095,42 @@ | |
| 2095 | else cgi_append_content("\n", 1); |
| 2096 | z += i; |
| 2097 | if( z[0]=='\n' ) z++; |
| 2098 | } |
| 2099 | if( n<iEnd ) cgi_printf("</div>"); |
| 2100 | CX("</code></pre></td></tr></tbody></table>\n"); |
| 2101 | if( db_int(0, "SELECT EXISTS(SELECT 1 FROM lnos)") ){ |
| 2102 | builtin_request_js("scroll.js"); |
| 2103 | } |
| 2104 | builtin_request_js("fossil.numbered-lines.js"); |
| 2105 | } |
| 2106 | |
| 2107 | /* |
| 2108 | ** COMMAND: test-line-numbers |
| 2109 | ** |
| 2110 | ** Usage: %fossil test-line-numbers FILE ?LN-SPEC? |
| 2111 | ** |
| 2112 | */ |
| 2113 | void cmd_test_line_numbers(void){ |
| 2114 | Blob content = empty_blob; |
| 2115 | const char * zLn = ""; |
| 2116 | const char * zFilename = 0; |
| 2117 | |
| 2118 | if(g.argc < 3){ |
| 2119 | usage("FILE"); |
| 2120 | }else if(g.argc>3){ |
| 2121 | zLn = g.argv[3]; |
| 2122 | } |
| 2123 | db_find_and_open_repository(0,0); |
| 2124 | zFilename = g.argv[2]; |
| 2125 | fossil_print("%s %s\n", zFilename, zLn); |
| 2126 | |
| 2127 | blob_read_from_file(&content, zFilename, ExtFILE); |
| 2128 | output_text_with_line_numbers(blob_str(&content), blob_size(&content), zLn); |
| 2129 | blob_reset(&content); |
| 2130 | fossil_print("%b\n", cgi_output_blob()); |
| 2131 | } |
| 2132 | |
| 2133 | /* |
| 2134 | ** WEBPAGE: artifact |
| 2135 | ** WEBPAGE: file |
| 2136 | ** WEBPAGE: whatis |
| @@ -2398,11 +2432,11 @@ | |
| 2432 | " WHERE filename.fnid=mlink.fnid" |
| 2433 | " AND mlink.fid=%d", |
| 2434 | rid); |
| 2435 | zExt = zFileName ? strrchr(zFileName, '.') : 0; |
| 2436 | if( zLn ){ |
| 2437 | output_text_with_line_numbers(z, blob_size(&content), zLn); |
| 2438 | }else if( zExt && zExt[1] ){ |
| 2439 | @ <pre> |
| 2440 | @ <code class="language-%s(zExt+1)">%h(z)</code> |
| 2441 | @ </pre> |
| 2442 | }else{ |
| 2443 |
+1
| --- src/main.mk | ||
| +++ src/main.mk | ||
| @@ -225,10 +225,11 @@ | ||
| 225 | 225 | $(SRCDIR)/forum.js \ |
| 226 | 226 | $(SRCDIR)/fossil.bootstrap.js \ |
| 227 | 227 | $(SRCDIR)/fossil.confirmer.js \ |
| 228 | 228 | $(SRCDIR)/fossil.dom.js \ |
| 229 | 229 | $(SRCDIR)/fossil.fetch.js \ |
| 230 | + $(SRCDIR)/fossil.numbered-lines.js \ | |
| 230 | 231 | $(SRCDIR)/fossil.page.fileedit.js \ |
| 231 | 232 | $(SRCDIR)/fossil.page.forumpost.js \ |
| 232 | 233 | $(SRCDIR)/fossil.page.wikiedit.js \ |
| 233 | 234 | $(SRCDIR)/fossil.storage.js \ |
| 234 | 235 | $(SRCDIR)/fossil.tabs.js \ |
| 235 | 236 |
| --- src/main.mk | |
| +++ src/main.mk | |
| @@ -225,10 +225,11 @@ | |
| 225 | $(SRCDIR)/forum.js \ |
| 226 | $(SRCDIR)/fossil.bootstrap.js \ |
| 227 | $(SRCDIR)/fossil.confirmer.js \ |
| 228 | $(SRCDIR)/fossil.dom.js \ |
| 229 | $(SRCDIR)/fossil.fetch.js \ |
| 230 | $(SRCDIR)/fossil.page.fileedit.js \ |
| 231 | $(SRCDIR)/fossil.page.forumpost.js \ |
| 232 | $(SRCDIR)/fossil.page.wikiedit.js \ |
| 233 | $(SRCDIR)/fossil.storage.js \ |
| 234 | $(SRCDIR)/fossil.tabs.js \ |
| 235 |
| --- src/main.mk | |
| +++ src/main.mk | |
| @@ -225,10 +225,11 @@ | |
| 225 | $(SRCDIR)/forum.js \ |
| 226 | $(SRCDIR)/fossil.bootstrap.js \ |
| 227 | $(SRCDIR)/fossil.confirmer.js \ |
| 228 | $(SRCDIR)/fossil.dom.js \ |
| 229 | $(SRCDIR)/fossil.fetch.js \ |
| 230 | $(SRCDIR)/fossil.numbered-lines.js \ |
| 231 | $(SRCDIR)/fossil.page.fileedit.js \ |
| 232 | $(SRCDIR)/fossil.page.forumpost.js \ |
| 233 | $(SRCDIR)/fossil.page.wikiedit.js \ |
| 234 | $(SRCDIR)/fossil.storage.js \ |
| 235 | $(SRCDIR)/fossil.tabs.js \ |
| 236 |
+1
-1
| --- src/scroll.js | ||
| +++ src/scroll.js | ||
| @@ -1,2 +1,2 @@ | ||
| 1 | 1 | /* Cause the page to scroll so that the #scrollToMe is visible */ |
| 2 | -document.getElementById('scrollToMe').scrollIntoView(true); | |
| 2 | +(document.getElementById('scrollToMe')||document.body).scrollIntoView(true); | |
| 3 | 3 |
| --- src/scroll.js | |
| +++ src/scroll.js | |
| @@ -1,2 +1,2 @@ | |
| 1 | /* Cause the page to scroll so that the #scrollToMe is visible */ |
| 2 | document.getElementById('scrollToMe').scrollIntoView(true); |
| 3 |
| --- src/scroll.js | |
| +++ src/scroll.js | |
| @@ -1,2 +1,2 @@ | |
| 1 | /* Cause the page to scroll so that the #scrollToMe is visible */ |
| 2 | (document.getElementById('scrollToMe')||document.body).scrollIntoView(true); |
| 3 |
+1
| --- win/Makefile.mingw | ||
| +++ win/Makefile.mingw | ||
| @@ -637,10 +637,11 @@ | ||
| 637 | 637 | $(SRCDIR)/forum.js \ |
| 638 | 638 | $(SRCDIR)/fossil.bootstrap.js \ |
| 639 | 639 | $(SRCDIR)/fossil.confirmer.js \ |
| 640 | 640 | $(SRCDIR)/fossil.dom.js \ |
| 641 | 641 | $(SRCDIR)/fossil.fetch.js \ |
| 642 | + $(SRCDIR)/fossil.numbered-lines.js \ | |
| 642 | 643 | $(SRCDIR)/fossil.page.fileedit.js \ |
| 643 | 644 | $(SRCDIR)/fossil.page.forumpost.js \ |
| 644 | 645 | $(SRCDIR)/fossil.page.wikiedit.js \ |
| 645 | 646 | $(SRCDIR)/fossil.storage.js \ |
| 646 | 647 | $(SRCDIR)/fossil.tabs.js \ |
| 647 | 648 |
| --- win/Makefile.mingw | |
| +++ win/Makefile.mingw | |
| @@ -637,10 +637,11 @@ | |
| 637 | $(SRCDIR)/forum.js \ |
| 638 | $(SRCDIR)/fossil.bootstrap.js \ |
| 639 | $(SRCDIR)/fossil.confirmer.js \ |
| 640 | $(SRCDIR)/fossil.dom.js \ |
| 641 | $(SRCDIR)/fossil.fetch.js \ |
| 642 | $(SRCDIR)/fossil.page.fileedit.js \ |
| 643 | $(SRCDIR)/fossil.page.forumpost.js \ |
| 644 | $(SRCDIR)/fossil.page.wikiedit.js \ |
| 645 | $(SRCDIR)/fossil.storage.js \ |
| 646 | $(SRCDIR)/fossil.tabs.js \ |
| 647 |
| --- win/Makefile.mingw | |
| +++ win/Makefile.mingw | |
| @@ -637,10 +637,11 @@ | |
| 637 | $(SRCDIR)/forum.js \ |
| 638 | $(SRCDIR)/fossil.bootstrap.js \ |
| 639 | $(SRCDIR)/fossil.confirmer.js \ |
| 640 | $(SRCDIR)/fossil.dom.js \ |
| 641 | $(SRCDIR)/fossil.fetch.js \ |
| 642 | $(SRCDIR)/fossil.numbered-lines.js \ |
| 643 | $(SRCDIR)/fossil.page.fileedit.js \ |
| 644 | $(SRCDIR)/fossil.page.forumpost.js \ |
| 645 | $(SRCDIR)/fossil.page.wikiedit.js \ |
| 646 | $(SRCDIR)/fossil.storage.js \ |
| 647 | $(SRCDIR)/fossil.tabs.js \ |
| 648 |
+2
| --- win/Makefile.msc | ||
| +++ win/Makefile.msc | ||
| @@ -558,10 +558,11 @@ | ||
| 558 | 558 | "$(SRCDIR)\forum.js" \ |
| 559 | 559 | "$(SRCDIR)\fossil.bootstrap.js" \ |
| 560 | 560 | "$(SRCDIR)\fossil.confirmer.js" \ |
| 561 | 561 | "$(SRCDIR)\fossil.dom.js" \ |
| 562 | 562 | "$(SRCDIR)\fossil.fetch.js" \ |
| 563 | + "$(SRCDIR)\fossil.numbered-lines.js" \ | |
| 563 | 564 | "$(SRCDIR)\fossil.page.fileedit.js" \ |
| 564 | 565 | "$(SRCDIR)\fossil.page.forumpost.js" \ |
| 565 | 566 | "$(SRCDIR)\fossil.page.wikiedit.js" \ |
| 566 | 567 | "$(SRCDIR)\fossil.storage.js" \ |
| 567 | 568 | "$(SRCDIR)\fossil.tabs.js" \ |
| @@ -1152,10 +1153,11 @@ | ||
| 1152 | 1153 | echo "$(SRCDIR)\forum.js" >> $@ |
| 1153 | 1154 | echo "$(SRCDIR)\fossil.bootstrap.js" >> $@ |
| 1154 | 1155 | echo "$(SRCDIR)\fossil.confirmer.js" >> $@ |
| 1155 | 1156 | echo "$(SRCDIR)\fossil.dom.js" >> $@ |
| 1156 | 1157 | echo "$(SRCDIR)\fossil.fetch.js" >> $@ |
| 1158 | + echo "$(SRCDIR)\fossil.numbered-lines.js" >> $@ | |
| 1157 | 1159 | echo "$(SRCDIR)\fossil.page.fileedit.js" >> $@ |
| 1158 | 1160 | echo "$(SRCDIR)\fossil.page.forumpost.js" >> $@ |
| 1159 | 1161 | echo "$(SRCDIR)\fossil.page.wikiedit.js" >> $@ |
| 1160 | 1162 | echo "$(SRCDIR)\fossil.storage.js" >> $@ |
| 1161 | 1163 | echo "$(SRCDIR)\fossil.tabs.js" >> $@ |
| 1162 | 1164 |
| --- win/Makefile.msc | |
| +++ win/Makefile.msc | |
| @@ -558,10 +558,11 @@ | |
| 558 | "$(SRCDIR)\forum.js" \ |
| 559 | "$(SRCDIR)\fossil.bootstrap.js" \ |
| 560 | "$(SRCDIR)\fossil.confirmer.js" \ |
| 561 | "$(SRCDIR)\fossil.dom.js" \ |
| 562 | "$(SRCDIR)\fossil.fetch.js" \ |
| 563 | "$(SRCDIR)\fossil.page.fileedit.js" \ |
| 564 | "$(SRCDIR)\fossil.page.forumpost.js" \ |
| 565 | "$(SRCDIR)\fossil.page.wikiedit.js" \ |
| 566 | "$(SRCDIR)\fossil.storage.js" \ |
| 567 | "$(SRCDIR)\fossil.tabs.js" \ |
| @@ -1152,10 +1153,11 @@ | |
| 1152 | echo "$(SRCDIR)\forum.js" >> $@ |
| 1153 | echo "$(SRCDIR)\fossil.bootstrap.js" >> $@ |
| 1154 | echo "$(SRCDIR)\fossil.confirmer.js" >> $@ |
| 1155 | echo "$(SRCDIR)\fossil.dom.js" >> $@ |
| 1156 | echo "$(SRCDIR)\fossil.fetch.js" >> $@ |
| 1157 | echo "$(SRCDIR)\fossil.page.fileedit.js" >> $@ |
| 1158 | echo "$(SRCDIR)\fossil.page.forumpost.js" >> $@ |
| 1159 | echo "$(SRCDIR)\fossil.page.wikiedit.js" >> $@ |
| 1160 | echo "$(SRCDIR)\fossil.storage.js" >> $@ |
| 1161 | echo "$(SRCDIR)\fossil.tabs.js" >> $@ |
| 1162 |
| --- win/Makefile.msc | |
| +++ win/Makefile.msc | |
| @@ -558,10 +558,11 @@ | |
| 558 | "$(SRCDIR)\forum.js" \ |
| 559 | "$(SRCDIR)\fossil.bootstrap.js" \ |
| 560 | "$(SRCDIR)\fossil.confirmer.js" \ |
| 561 | "$(SRCDIR)\fossil.dom.js" \ |
| 562 | "$(SRCDIR)\fossil.fetch.js" \ |
| 563 | "$(SRCDIR)\fossil.numbered-lines.js" \ |
| 564 | "$(SRCDIR)\fossil.page.fileedit.js" \ |
| 565 | "$(SRCDIR)\fossil.page.forumpost.js" \ |
| 566 | "$(SRCDIR)\fossil.page.wikiedit.js" \ |
| 567 | "$(SRCDIR)\fossil.storage.js" \ |
| 568 | "$(SRCDIR)\fossil.tabs.js" \ |
| @@ -1152,10 +1153,11 @@ | |
| 1153 | echo "$(SRCDIR)\forum.js" >> $@ |
| 1154 | echo "$(SRCDIR)\fossil.bootstrap.js" >> $@ |
| 1155 | echo "$(SRCDIR)\fossil.confirmer.js" >> $@ |
| 1156 | echo "$(SRCDIR)\fossil.dom.js" >> $@ |
| 1157 | echo "$(SRCDIR)\fossil.fetch.js" >> $@ |
| 1158 | echo "$(SRCDIR)\fossil.numbered-lines.js" >> $@ |
| 1159 | echo "$(SRCDIR)\fossil.page.fileedit.js" >> $@ |
| 1160 | echo "$(SRCDIR)\fossil.page.forumpost.js" >> $@ |
| 1161 | echo "$(SRCDIR)\fossil.page.wikiedit.js" >> $@ |
| 1162 | echo "$(SRCDIR)\fossil.storage.js" >> $@ |
| 1163 | echo "$(SRCDIR)\fossil.tabs.js" >> $@ |
| 1164 |