You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 659d0e0
Browse filesBrowse the repository at this point in the historyBrowse files
Copy file name to clipboardExpand all lines: web/shell/README.md
+4-3Lines changed: 4 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -143,7 +143,7 @@ host translates a relocated mount back before a change reaches the review. `befo
143
143
runtime.folders(): Array<{ name: string; root: string; uri: string }>// live workspace folders, in order
144
144
runtime.mounts(): Array<{ ...folder; mount: string }>// where the engine sees each (§4 Mounts)
145
145
runtime.resolve(path: string): Uri|null// an ENGINE path (as a command prints it) -> the workspace Uri; null outside the mounts
146
-
runtime.stat(path: string): Promise<{ type: 'file'|'dir', size: number } |null>// bounded by fsMs; null for nothing / outside / error / timeout
146
+
runtime.stat(path: string): Promise<{ type: 'file'|'dir'|'other', size: number } |null>// bounded by fsMs; null for nothing / outside / error / timeout; 'other' = neither bit (FileType.Unknown, a link to nothing)
147
147
runtime.createSession(o?: { cwd?: string }): Session// a persistent terminal session
148
148
runtime.onAgentRun(listener: (e:AgentRunEvent) =>void): { dispose():void } // every run_command, for the mirror tab
149
149
runtime.dispose(): void
@@ -332,8 +332,9 @@ own values the rest.
332
332
through `forDisplay`, a one-line note through `forNote`. `sanitize.js` makes control, format (`Cf`), bidi and
333
333
unpaired-surrogate characters visible as `␛` / `[U+XXXX]`; the one exception is a single ZWNJ/ZWJ between two
334
334
letters of the **same** Arabic or Indic script (and the joiners of an emoji sequence, VS16 with an emoji), which
335
-
`forTerminal` keeps and `forDisplay` still shows. The line editor makes the same exception for what is **typed or
336
-
pasted**, and says once under the line what it did not type. Every function that sees untrusted text is linear
335
+
`forTerminal` keeps and `forDisplay` still shows. The line editor makes the same exception for what is **pasted,
336
+
completed or arrives whole in one chunk**, and says once under the line what it did not type; a key pressed alone
337
+
that types nothing the screen can show says nothing. Every function that sees untrusted text is linear
337
338
(`web/test/unit/helpers/shell-terminal-timing.js`: 23 shapes of 200,000 characters under 500 ms).
338
339
* A command never changes the protected paths (`.git`, `.vscode`, `.levelcode`, `.cursor`, `.husky`,
var __lcKeySpec=e=>{let t={},p=e.split(","),fl=(f,end)=>{f.includes("n")&&(t.numeric=!0),f.includes("r")&&(t.reverse=!0),f.includes("f")&&(t.ignoreCase=!0),f.includes("b")&&(t[end?"ignoreLeadingEnd":"ignoreLeading"]=!0),f.includes("h")&&(t.humanNumeric=!0),f.includes("V")&&(t.versionSort=!0),f.includes("d")&&(t.dictionaryOrder=!0),f.includes("M")&&(t.monthSort=!0)},pos=s=>{let m=/^(\\d*)(?:\\.(\\d*))?([A-Za-z]*)$/.exec(s);return m?{f:m[1]===""?NaN:parseInt(m[1],10),c:m[2]?parseInt(m[2],10):void 0,o:m[3]}:null},a=pos(p[0]);if(p.length>2||!a||!(a.f>=1))return null;if(t.startField=a.f,a.c>=1&&(t.startChar=a.c),fl(a.o,!1),p.length>1&&p[1]!==""){let b=pos(p[1]);if(!b)return null;b.f>=1&&(t.endField=b.f,b.c>=1&&(t.endChar=b.c)),fl(b.o,!0)}return t};
46
46
var __lcBytes=a=>__lcText.decodeBytes(Uint8Array.from(a));
47
47
var __lcByte=v=>{v&=255;return v<128?String.fromCharCode(v):String.fromCharCode(56320+v)};
48
48
var __lcHeal=s=>__lcText.joinEscapes(s);
49
49
var __lcErrno=l=>{let c=l&&l.code;return c==="ENOENT"?"No such file or directory":c==="EACCES"||c==="EPERM"?"Permission denied":c==="EISDIR"?"Is a directory":c==="ENOTDIR"?"Not a directory":c==="EROFS"?"Read-only file system":c==="ENOSPC"?"No space left on device":null};
50
+
var __lcRedirectName=async(e,n)=>{if(n==="")return"No such file or directory";if(n.length>1&&n.endsWith("/"))return"Is a directory";if(n.includes("..")||n.endsWith("/.")){let c=n[0]==="/"?"/":e.state.cwd,p=n.split("/").filter(s=>s!==""&&s!==".");for(let k=0;k<p.length;k++){let st;try{st=await e.fs.stat(c)}catch{return"No such file or directory"}if(!st.isDirectory)return"Not a directory";c=e.fs.resolvePath(c,p[k])}if(n.endsWith("/.")){try{if(!(await e.fs.stat(c)).isDirectory)return"Not a directory"}catch{return"No such file or directory"}}}return null};
51
+
var __lcCpLen=s=>{let n=0;for(let i=0;i<s.length;i++){let c=s.charCodeAt(i);if(c>=55296&&c<=56319&&i+1<s.length&&(s.charCodeAt(i+1)&64512)===56320)i++;n++}return n};
52
+
var __lcCpSub=(s,a,l)=>{let r="",k=0,i=0;for(;i<s.length;){let c=s.charCodeAt(i),w=c>=55296&&c<=56319&&i+1<s.length&&(s.charCodeAt(i+1)&64512)===56320?2:1;if(k>=a&&(l===void 0||k<a+l))r+=s.slice(i,i+w);k++;i+=w;if(l!==void 0&&k>=a+l)break}return r};
50
53
var __lcInherit=(h,e)=>{let q={...h};delete q.PWD;delete q.OLDPWD;return{...q,...e}};
51
54
var __lcIso=(p,u,a,d)=>{let f=p==="date"?"%Y-%m-%d":p==="hours"?"%Y-%m-%dT%H":p==="minutes"?"%Y-%m-%dT%H:%M":p==="ns"?"%Y-%m-%dT%H:%M:%S,000000000":"%Y-%m-%dT%H:%M:%S",s=As(f,u,a,d);if(p==="date")return s;let z=As("%z",u,a,d);return s+z.slice(0,3)+":"+z.slice(3)};
to: 'if(o==="}"){t+="\\\\}",n+=2,r=!1;continue}if((o==="+"||o==="?")&&!r){t+=o,n+=2;continue}t+=i+o,n+=2,r=o==="b"||o==="B"||o==="<"||o===">"||o==="`"||o==="\'";continue}',
471
474
},
475
+
{
476
+
name: 'grep, basic expression: an interval with no lower bound, \\{,2\\}, is \\{0,2\\}',
title: 'a command word that is an unquoted expansion is split into words (and globbed) as bash splits it (`c="ls -l"; $c` ran a command named "ls -l" and said "command not found"): the first word is the command and the rest are its first arguments',
695
+
title: 'a command word that is an expansion is split into words (and globbed) as bash splits it (`c="ls -l"; $c` ran a command named "ls -l" and said "command not found"): the first word is the command and the rest are its first arguments. A quoted list is words too (`"$@"`, `"${cmd[@]}"`, `"x$@"`: `run() { "$@"; }` could not run what it was given), and one that is empty is no command at all',
688
696
edits: [
689
697
{
690
698
name: 'simple command: the command name is expanded with field splitting when it is only expansions',
to: 'let __nm=t.name.parts.every(N=>N.type==="CommandSubstitution"||N.type==="ParameterExpansion"||N.type==="ArithmeticExpansion"),__ex=__nm?await dr(this.ctx,t.name):null,u=__ex?__ex.values[0]??"":await Be(this.ctx,t.name),f=[],d=[],p=(N,$)=>{',
700
+
to: 'let __nm=t.name.parts.every(N=>N.type==="CommandSubstitution"||N.type==="ParameterExpansion"||N.type==="ArithmeticExpansion"||N.type==="DoubleQuoted"&&N.parts.every(P=>P.type==="CommandSubstitution"||P.type==="ParameterExpansion"||P.type==="ArithmeticExpansion"||P.type==="Literal")),__ex=__nm?await dr(this.ctx,t.name):null,__z=__ex!==null&&__ex.values.length===0,u=__ex?__ex.values[0]??"":await Be(this.ctx,t.name),f=[],d=[],p=(N,$)=>{',
693
701
},
694
702
{
695
703
name: 'simple command: the words after the first one of such a name are the first arguments',
to: 'f.push(N),d.push($)};if(__ex)for(let __x of __ex.values.slice(1))p(__x,__ex.quoted);if(Jp(t.name,["local","declare","typeset","export","readonly"])&&(u==="local"',
698
706
},
707
+
{
708
+
name: 'simple command: an expansion that made no word at all (an empty `"$@"`) is no command, and the next word is the command, as for an unquoted one',
to: 'let m=__z||t.name.parts.every(N=>N.type==="CommandSubstitution"||N.type==="ParameterExpansion"||N.type==="ArithmeticExpansion");!u&&m&&f.length>0&&(u=f.shift(),d.shift());',
711
+
},
712
+
],
713
+
},
714
+
{
715
+
id: 'LC-32',
716
+
title: 'the target of an output redirection is looked at as it was typed: a name that ends in a slash is a directory (`echo x > f/` wrote a file named f), an empty one is no such file, and a path that goes up through a folder that is not there (`> x/../n`) or through a file is not there (`> x/../n` wrote `n`)',
717
+
edits: [
718
+
{
719
+
name: 'the check of an output target (exists? is a directory? noclobber?) first asks about the name as typed',
720
+
from: 'async function FO(e,t,n,r){try{if(!await e.fs.exists(t))return null;',
721
+
to: 'async function FO(e,t,n,r){try{let __m=await __lcRedirectName(e,n);if(__m)return`bash: ${n}: ${__m}\n`;if(!await e.fs.exists(t))return null;',
722
+
},
723
+
],
724
+
},
725
+
{
726
+
id: 'LC-33',
727
+
title: 'awk counts characters, not UTF-16 units: length, substr, index and the RSTART/RLENGTH of match are in code points (`length("😀")` was 2, GNU awk in a UTF-8 locale says 1)',
0 commit comments