SourcesTextEditor.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. export class SourcesTextEditor extends TextEditor.CodeMirrorTextEditor{constructor(delegate,codeMirrorOptions){const defaultCodeMirrorOptions={lineNumbers:true,lineWrapping:false,bracketMatchingSetting:Common.moduleSetting('textEditorBracketMatching'),padBottom:true};if(codeMirrorOptions){Object.assign(defaultCodeMirrorOptions,codeMirrorOptions);}
  2. super(defaultCodeMirrorOptions);this.codeMirror().addKeyMap({'Enter':'smartNewlineAndIndent','Esc':'sourcesDismiss'});this._delegate=delegate;this.codeMirror().on('cursorActivity',this._cursorActivity.bind(this));this.codeMirror().on('gutterClick',this._gutterClick.bind(this));this.codeMirror().on('scroll',this._scroll.bind(this));this.codeMirror().on('focus',this._focus.bind(this));this.codeMirror().on('blur',this._blur.bind(this));this.codeMirror().on('beforeSelectionChange',this._fireBeforeSelectionChanged.bind(this));this.element.addEventListener('contextmenu',this._contextMenu.bind(this),false);this._gutterMouseMove=event=>{this.element.classList.toggle('CodeMirror-gutter-hovered',event.clientX<this.codeMirror().getGutterElement().getBoundingClientRect().right);};this._gutterMouseOut=event=>{this.element.classList.toggle('CodeMirror-gutter-hovered',false);};this.codeMirror().addKeyMap(_BlockIndentController);this._tokenHighlighter=new TokenHighlighter(this,this.codeMirror());this._gutters=[lineNumbersGutterType];this.codeMirror().setOption('gutters',this._gutters.slice());this.codeMirror().setOption('electricChars',false);this.codeMirror().setOption('smartIndent',false);function updateAnticipateJumpFlag(value){this._isHandlingMouseDownEvent=value;}
  3. this.element.addEventListener('mousedown',updateAnticipateJumpFlag.bind(this,true),true);this.element.addEventListener('mousedown',updateAnticipateJumpFlag.bind(this,false),false);Common.moduleSetting('textEditorIndent').addChangeListener(this._onUpdateEditorIndentation,this);Common.moduleSetting('textEditorAutoDetectIndent').addChangeListener(this._onUpdateEditorIndentation,this);Common.moduleSetting('showWhitespacesInEditor').addChangeListener(this._updateWhitespace,this);Common.moduleSetting('textEditorCodeFolding').addChangeListener(this._updateCodeFolding,this);this._updateCodeFolding();this._autocompleteConfig={isWordChar:TextUtils.TextUtils.isWordChar};Common.moduleSetting('textEditorAutocompletion').addChangeListener(this._updateAutocomplete,this);this._updateAutocomplete();this._onUpdateEditorIndentation();this._setupWhitespaceHighlight();this._infoBarDiv=null;}
  4. attachInfobar(infobar){if(!this._infoBarDiv){this._infoBarDiv=createElementWithClass('div','flex-none');UI.ARIAUtils.markAsAlert(this._infoBarDiv);this.element.insertBefore(this._infoBarDiv,this.element.firstChild);}
  5. this._infoBarDiv.appendChild(infobar.element);infobar.setParentView(this);this.doResize();}
  6. static _guessIndentationLevel(lines){const tabRegex=/^\t+/;let tabLines=0;const indents={};for(let lineNumber=0;lineNumber<lines.length;++lineNumber){const text=lines[lineNumber];if(text.length===0||!TextUtils.TextUtils.isSpaceChar(text[0])){continue;}
  7. if(tabRegex.test(text)){++tabLines;continue;}
  8. let i=0;while(i<text.length&&TextUtils.TextUtils.isSpaceChar(text[i])){++i;}
  9. if(i%2!==0){continue;}
  10. indents[i]=1+(indents[i]||0);}
  11. const linesCountPerIndentThreshold=3*lines.length/100;if(tabLines&&tabLines>linesCountPerIndentThreshold){return'\t';}
  12. let minimumIndent=Infinity;for(const i in indents){if(indents[i]<linesCountPerIndentThreshold){continue;}
  13. const indent=parseInt(i,10);if(minimumIndent>indent){minimumIndent=indent;}}
  14. if(minimumIndent===Infinity){return Common.moduleSetting('textEditorIndent').get();}
  15. return' '.repeat(minimumIndent);}
  16. _isSearchActive(){return!!this._tokenHighlighter.highlightedRegex();}
  17. scrollToLine(lineNumber){super.scrollToLine(lineNumber);this._scroll();}
  18. highlightSearchResults(regex,range){function innerHighlightRegex(){if(range){this.scrollLineIntoView(range.startLine);if(range.endColumn>TextEditor.CodeMirrorTextEditor.maxHighlightLength){this.setSelection(range);}else{this.setSelection(TextUtils.TextRange.createFromLocation(range.startLine,range.startColumn));}}
  19. this._tokenHighlighter.highlightSearchResults(regex,range);}
  20. if(!this._selectionBeforeSearch){this._selectionBeforeSearch=this.selection();}
  21. this.codeMirror().operation(innerHighlightRegex.bind(this));}
  22. cancelSearchResultsHighlight(){this.codeMirror().operation(this._tokenHighlighter.highlightSelectedTokens.bind(this._tokenHighlighter));if(this._selectionBeforeSearch){this._reportJump(this._selectionBeforeSearch,this.selection());delete this._selectionBeforeSearch;}}
  23. removeHighlight(highlightDescriptor){highlightDescriptor.clear();}
  24. highlightRange(range,cssClass){cssClass='CodeMirror-persist-highlight '+cssClass;const pos=TextEditor.CodeMirrorUtils.toPos(range);++pos.end.ch;return this.codeMirror().markText(pos.start,pos.end,{className:cssClass,startStyle:cssClass+'-start',endStyle:cssClass+'-end'});}
  25. installGutter(type,leftToNumbers){if(this._gutters.indexOf(type)!==-1){return;}
  26. if(leftToNumbers){this._gutters.unshift(type);}else{this._gutters.push(type);}
  27. this.codeMirror().setOption('gutters',this._gutters.slice());this.refresh();}
  28. uninstallGutter(type){const index=this._gutters.indexOf(type);if(index===-1){return;}
  29. this.codeMirror().clearGutter(type);this._gutters.splice(index,1);this.codeMirror().setOption('gutters',this._gutters.slice());this.refresh();}
  30. setGutterDecoration(lineNumber,type,element){console.assert(this._gutters.indexOf(type)!==-1,'Cannot decorate unexisting gutter.');this.codeMirror().setGutterMarker(lineNumber,type,element);}
  31. setExecutionLocation(lineNumber,columnNumber){this.clearPositionHighlight();this._executionLine=this.codeMirror().getLineHandle(lineNumber);if(!this._executionLine){return;}
  32. this.showExecutionLineBackground();this.codeMirror().addLineClass(this._executionLine,'wrap','cm-execution-line-outline');let token=this.tokenAtTextPosition(lineNumber,columnNumber);if(token&&!token.type&&token.startColumn+1===token.endColumn){const tokenContent=this.codeMirror().getLine(lineNumber)[token.startColumn];if(tokenContent==='.'||tokenContent==='('){token=this.tokenAtTextPosition(lineNumber,token.endColumn+1);}}
  33. let endColumn;if(token&&token.type){endColumn=token.endColumn;}else{endColumn=this.codeMirror().getLine(lineNumber).length;}
  34. this._executionLineTailMarker=this.codeMirror().markText({line:lineNumber,ch:columnNumber},{line:lineNumber,ch:endColumn},{className:'cm-execution-line-tail'});}
  35. showExecutionLineBackground(){if(this._executionLine){this.codeMirror().addLineClass(this._executionLine,'wrap','cm-execution-line');}}
  36. hideExecutionLineBackground(){if(this._executionLine){this.codeMirror().removeLineClass(this._executionLine,'wrap','cm-execution-line');}}
  37. clearExecutionLine(){this.clearPositionHighlight();if(this._executionLine){this.hideExecutionLineBackground();this.codeMirror().removeLineClass(this._executionLine,'wrap','cm-execution-line-outline');}
  38. delete this._executionLine;if(this._executionLineTailMarker){this._executionLineTailMarker.clear();}
  39. delete this._executionLineTailMarker;}
  40. toggleLineClass(lineNumber,className,toggled){if(this.hasLineClass(lineNumber,className)===toggled){return;}
  41. const lineHandle=this.codeMirror().getLineHandle(lineNumber);if(!lineHandle){return;}
  42. if(toggled){this.codeMirror().addLineClass(lineHandle,'gutter',className);this.codeMirror().addLineClass(lineHandle,'wrap',className);}else{this.codeMirror().removeLineClass(lineHandle,'gutter',className);this.codeMirror().removeLineClass(lineHandle,'wrap',className);}}
  43. hasLineClass(lineNumber,className){const lineInfo=this.codeMirror().lineInfo(lineNumber);const wrapClass=lineInfo.wrapClass||'';const classNames=wrapClass.split(' ');return classNames.indexOf(className)!==-1;}
  44. _gutterClick(instance,lineNumber,gutterType,event){this.dispatchEventToListeners(Events.GutterClick,{gutterType,lineNumber,event});}
  45. _contextMenu(event){const contextMenu=new UI.ContextMenu(event);event.consume(true);const wrapper=event.target.enclosingNodeOrSelfWithClass('CodeMirror-gutter-wrapper');const target=wrapper?wrapper.querySelector('.CodeMirror-linenumber'):null;let promise;if(target){promise=this._delegate.populateLineGutterContextMenu(contextMenu,parseInt(target.textContent,10)-1);}else{const textSelection=this.selection();promise=this._delegate.populateTextAreaContextMenu(contextMenu,textSelection.startLine,textSelection.startColumn);}
  46. promise.then(showAsync.bind(this));function showAsync(){contextMenu.appendApplicableItems(this);contextMenu.show();}}
  47. editRange(range,text,origin){const newRange=super.editRange(range,text,origin);if(Common.moduleSetting('textEditorAutoDetectIndent').get()){this._onUpdateEditorIndentation();}
  48. return newRange;}
  49. _onUpdateEditorIndentation(){this._setEditorIndentation(TextEditor.CodeMirrorUtils.pullLines(this.codeMirror(),LinesToScanForIndentationGuessing));}
  50. _setEditorIndentation(lines){const extraKeys={};let indent=Common.moduleSetting('textEditorIndent').get();if(Common.moduleSetting('textEditorAutoDetectIndent').get()){indent=SourceFrame.SourcesTextEditor._guessIndentationLevel(lines);}
  51. if(indent===TextUtils.TextUtils.Indent.TabCharacter){this.codeMirror().setOption('indentWithTabs',true);this.codeMirror().setOption('indentUnit',4);}else{this.codeMirror().setOption('indentWithTabs',false);this.codeMirror().setOption('indentUnit',indent.length);extraKeys.Tab=function(codeMirror){if(codeMirror.somethingSelected()){return CodeMirror.Pass;}
  52. const pos=codeMirror.getCursor('head');codeMirror.replaceRange(indent.substring(pos.ch%indent.length),codeMirror.getCursor());};}
  53. this.codeMirror().setOption('extraKeys',extraKeys);this._indentationLevel=indent;}
  54. indent(){return this._indentationLevel;}
  55. _onAutoAppendedSpaces(){this._autoAppendedSpaces=this._autoAppendedSpaces||[];for(let i=0;i<this._autoAppendedSpaces.length;++i){const position=this._autoAppendedSpaces[i].resolve();if(!position){continue;}
  56. const line=this.line(position.lineNumber);if(line.length===position.columnNumber&&TextUtils.TextUtils.lineIndent(line).length===line.length){this.codeMirror().replaceRange('',new CodeMirror.Pos(position.lineNumber,0),new CodeMirror.Pos(position.lineNumber,position.columnNumber));}}
  57. this._autoAppendedSpaces=[];const selections=this.selections();for(let i=0;i<selections.length;++i){const selection=selections[i];this._autoAppendedSpaces.push(this.textEditorPositionHandle(selection.startLine,selection.startColumn));}}
  58. _cursorActivity(){if(!this._isSearchActive()){this.codeMirror().operation(this._tokenHighlighter.highlightSelectedTokens.bind(this._tokenHighlighter));}
  59. const start=this.codeMirror().getCursor('anchor');const end=this.codeMirror().getCursor('head');this.dispatchEventToListeners(Events.SelectionChanged,TextEditor.CodeMirrorUtils.toRange(start,end));}
  60. _reportJump(from,to){if(from&&to&&from.equal(to)){return;}
  61. this.dispatchEventToListeners(Events.JumpHappened,{from:from,to:to});}
  62. _scroll(){const topmostLineNumber=this.codeMirror().lineAtHeight(this.codeMirror().getScrollInfo().top,'local');this.dispatchEventToListeners(Events.ScrollChanged,topmostLineNumber);}
  63. _focus(){this.dispatchEventToListeners(Events.EditorFocused);}
  64. _blur(){this.dispatchEventToListeners(Events.EditorBlurred);}
  65. _fireBeforeSelectionChanged(codeMirror,selection){if(!this._isHandlingMouseDownEvent){return;}
  66. if(!selection.ranges.length){return;}
  67. const primarySelection=selection.ranges[0];this._reportJump(this.selection(),TextEditor.CodeMirrorUtils.toRange(primarySelection.anchor,primarySelection.head));}
  68. dispose(){super.dispose();Common.moduleSetting('textEditorIndent').removeChangeListener(this._onUpdateEditorIndentation,this);Common.moduleSetting('textEditorAutoDetectIndent').removeChangeListener(this._onUpdateEditorIndentation,this);Common.moduleSetting('showWhitespacesInEditor').removeChangeListener(this._updateWhitespace,this);Common.moduleSetting('textEditorCodeFolding').removeChangeListener(this._updateCodeFolding,this);}
  69. setText(text){this._setEditorIndentation(text.split('\n').slice(0,LinesToScanForIndentationGuessing));super.setText(text);}
  70. _updateWhitespace(){this.setMimeType(this.mimeType());}
  71. _updateCodeFolding(){if(Common.moduleSetting('textEditorCodeFolding').get()){this.installGutter('CodeMirror-foldgutter',false);this.element.addEventListener('mousemove',this._gutterMouseMove);this.element.addEventListener('mouseout',this._gutterMouseOut);this.codeMirror().setOption('foldGutter',true);this.codeMirror().setOption('foldOptions',{minFoldSize:1});}else{this.codeMirror().execCommand('unfoldAll');this.element.removeEventListener('mousemove',this._gutterMouseMove);this.element.removeEventListener('mouseout',this._gutterMouseOut);this.uninstallGutter('CodeMirror-foldgutter');this.codeMirror().setOption('foldGutter',false);}}
  72. rewriteMimeType(mimeType){this._setupWhitespaceHighlight();const whitespaceMode=Common.moduleSetting('showWhitespacesInEditor').get();this.element.classList.toggle('show-whitespaces',whitespaceMode==='all');if(whitespaceMode==='all'){return this._allWhitespaceOverlayMode(mimeType);}else if(whitespaceMode==='trailing'){return this._trailingWhitespaceOverlayMode(mimeType);}
  73. return mimeType;}
  74. _allWhitespaceOverlayMode(mimeType){let modeName=CodeMirror.mimeModes[mimeType]?(CodeMirror.mimeModes[mimeType].name||CodeMirror.mimeModes[mimeType]):CodeMirror.mimeModes['text/plain'];modeName+='+all-whitespaces';if(CodeMirror.modes[modeName]){return modeName;}
  75. function modeConstructor(config,parserConfig){function nextToken(stream){if(stream.peek()===' '){let spaces=0;while(spaces<MaximumNumberOfWhitespacesPerSingleSpan&&stream.peek()===' '){++spaces;stream.next();}
  76. return'whitespace whitespace-'+spaces;}
  77. while(!stream.eol()&&stream.peek()!==' '){stream.next();}
  78. return null;}
  79. const whitespaceMode={token:nextToken};return CodeMirror.overlayMode(CodeMirror.getMode(config,mimeType),whitespaceMode,false);}
  80. CodeMirror.defineMode(modeName,modeConstructor);return modeName;}
  81. _trailingWhitespaceOverlayMode(mimeType){let modeName=CodeMirror.mimeModes[mimeType]?(CodeMirror.mimeModes[mimeType].name||CodeMirror.mimeModes[mimeType]):CodeMirror.mimeModes['text/plain'];modeName+='+trailing-whitespaces';if(CodeMirror.modes[modeName]){return modeName;}
  82. function modeConstructor(config,parserConfig){function nextToken(stream){if(stream.match(/^\s+$/,true)){return true?'trailing-whitespace':null;}
  83. do{stream.next();}while(!stream.eol()&&stream.peek()!==' ');return null;}
  84. const whitespaceMode={token:nextToken};return CodeMirror.overlayMode(CodeMirror.getMode(config,mimeType),whitespaceMode,false);}
  85. CodeMirror.defineMode(modeName,modeConstructor);return modeName;}
  86. _setupWhitespaceHighlight(){const doc=this.element.ownerDocument;if(doc._codeMirrorWhitespaceStyleInjected||!Common.moduleSetting('showWhitespacesInEditor').get()){return;}
  87. doc._codeMirrorWhitespaceStyleInjected=true;const classBase='.show-whitespaces .CodeMirror .cm-whitespace-';const spaceChar='·';let spaceChars='';let rules='';for(let i=1;i<=MaximumNumberOfWhitespacesPerSingleSpan;++i){spaceChars+=spaceChar;const rule=classBase+i+'::before { content: \''+spaceChars+'\';}\n';rules+=rule;}
  88. const style=doc.createElement('style');style.textContent=rules;doc.head.appendChild(style);}
  89. configureAutocomplete(config){this._autocompleteConfig=config;this._updateAutocomplete();}
  90. _updateAutocomplete(){super.configureAutocomplete(Common.moduleSetting('textEditorAutocompletion').get()?this._autocompleteConfig:null);}}
  91. export const Events={GutterClick:Symbol('GutterClick'),SelectionChanged:Symbol('SelectionChanged'),ScrollChanged:Symbol('ScrollChanged'),EditorFocused:Symbol('EditorFocused'),EditorBlurred:Symbol('EditorBlurred'),JumpHappened:Symbol('JumpHappened')};export class SourcesTextEditorDelegate{populateLineGutterContextMenu(contextMenu,lineNumber){}
  92. populateTextAreaContextMenu(contextMenu,lineNumber,columnNumber){}}
  93. CodeMirror.commands.smartNewlineAndIndent=function(codeMirror){codeMirror.operation(innerSmartNewlineAndIndent.bind(null,codeMirror));function innerSmartNewlineAndIndent(codeMirror){const selections=codeMirror.listSelections();const replacements=[];for(let i=0;i<selections.length;++i){const selection=selections[i];const cur=CodeMirror.cmpPos(selection.head,selection.anchor)<0?selection.head:selection.anchor;const line=codeMirror.getLine(cur.line);const indent=TextUtils.TextUtils.lineIndent(line);replacements.push('\n'+indent.substring(0,Math.min(cur.ch,indent.length)));}
  94. codeMirror.replaceSelections(replacements);codeMirror._codeMirrorTextEditor._onAutoAppendedSpaces();}};CodeMirror.commands.sourcesDismiss=function(codemirror){if(codemirror.listSelections().length===1&&codemirror._codeMirrorTextEditor._isSearchActive()){return CodeMirror.Pass;}
  95. return CodeMirror.commands.dismiss(codemirror);};export const _BlockIndentController={name:'blockIndentKeymap',Enter:function(codeMirror){let selections=codeMirror.listSelections();const replacements=[];let allSelectionsAreCollapsedBlocks=false;for(let i=0;i<selections.length;++i){const selection=selections[i];const start=CodeMirror.cmpPos(selection.head,selection.anchor)<0?selection.head:selection.anchor;const line=codeMirror.getLine(start.line);const indent=TextUtils.TextUtils.lineIndent(line);let indentToInsert='\n'+indent+codeMirror._codeMirrorTextEditor.indent();let isCollapsedBlock=false;if(selection.head.ch===0){return CodeMirror.Pass;}
  96. if(line.substr(selection.head.ch-1,2)==='{}'){indentToInsert+='\n'+indent;isCollapsedBlock=true;}else if(line.substr(selection.head.ch-1,1)!=='{'){return CodeMirror.Pass;}
  97. if(i>0&&allSelectionsAreCollapsedBlocks!==isCollapsedBlock){return CodeMirror.Pass;}
  98. replacements.push(indentToInsert);allSelectionsAreCollapsedBlocks=isCollapsedBlock;}
  99. codeMirror.replaceSelections(replacements);if(!allSelectionsAreCollapsedBlocks){codeMirror._codeMirrorTextEditor._onAutoAppendedSpaces();return;}
  100. selections=codeMirror.listSelections();const updatedSelections=[];for(let i=0;i<selections.length;++i){const selection=selections[i];const line=codeMirror.getLine(selection.head.line-1);const position=new CodeMirror.Pos(selection.head.line-1,line.length);updatedSelections.push({head:position,anchor:position});}
  101. codeMirror.setSelections(updatedSelections);codeMirror._codeMirrorTextEditor._onAutoAppendedSpaces();},'\'}\'':function(codeMirror){if(codeMirror.somethingSelected()){return CodeMirror.Pass;}
  102. let selections=codeMirror.listSelections();let replacements=[];for(let i=0;i<selections.length;++i){const selection=selections[i];const line=codeMirror.getLine(selection.head.line);if(line!==TextUtils.TextUtils.lineIndent(line)){return CodeMirror.Pass;}
  103. replacements.push('}');}
  104. codeMirror.replaceSelections(replacements);selections=codeMirror.listSelections();replacements=[];const updatedSelections=[];for(let i=0;i<selections.length;++i){const selection=selections[i];const matchingBracket=codeMirror.findMatchingBracket(selection.head);if(!matchingBracket||!matchingBracket.match){return;}
  105. updatedSelections.push({head:selection.head,anchor:new CodeMirror.Pos(selection.head.line,0)});const line=codeMirror.getLine(matchingBracket.to.line);const indent=TextUtils.TextUtils.lineIndent(line);replacements.push(indent+'}');}
  106. codeMirror.setSelections(updatedSelections);codeMirror.replaceSelections(replacements);}};export class TokenHighlighter{constructor(textEditor,codeMirror){this._textEditor=textEditor;this._codeMirror=codeMirror;}
  107. highlightSearchResults(regex,range){const oldRegex=this._highlightRegex;this._highlightRegex=regex;this._highlightRange=range;if(this._searchResultMarker){this._searchResultMarker.clear();delete this._searchResultMarker;}
  108. if(this._highlightDescriptor&&this._highlightDescriptor.selectionStart){this._codeMirror.removeLineClass(this._highlightDescriptor.selectionStart.line,'wrap','cm-line-with-selection');}
  109. const selectionStart=this._highlightRange?new CodeMirror.Pos(this._highlightRange.startLine,this._highlightRange.startColumn):null;if(selectionStart){this._codeMirror.addLineClass(selectionStart.line,'wrap','cm-line-with-selection');}
  110. if(oldRegex&&this._highlightRegex.toString()===oldRegex.toString()){if(this._highlightDescriptor){this._highlightDescriptor.selectionStart=selectionStart;}}else{this._removeHighlight();this._setHighlighter(this._searchHighlighter.bind(this,this._highlightRegex),selectionStart);}
  111. if(this._highlightRange){const pos=TextEditor.CodeMirrorUtils.toPos(this._highlightRange);this._searchResultMarker=this._codeMirror.markText(pos.start,pos.end,{className:'cm-column-with-selection'});}}
  112. highlightedRegex(){return this._highlightRegex;}
  113. highlightSelectedTokens(){delete this._highlightRegex;delete this._highlightRange;if(this._highlightDescriptor&&this._highlightDescriptor.selectionStart){this._codeMirror.removeLineClass(this._highlightDescriptor.selectionStart.line,'wrap','cm-line-with-selection');}
  114. this._removeHighlight();const selectionStart=this._codeMirror.getCursor('start');const selectionEnd=this._codeMirror.getCursor('end');if(selectionStart.line!==selectionEnd.line){return;}
  115. if(selectionStart.ch===selectionEnd.ch){return;}
  116. const selections=this._codeMirror.getSelections();if(selections.length>1){return;}
  117. const selectedText=selections[0];if(this._isWord(selectedText,selectionStart.line,selectionStart.ch,selectionEnd.ch)){if(selectionStart){this._codeMirror.addLineClass(selectionStart.line,'wrap','cm-line-with-selection');}
  118. this._setHighlighter(this._tokenHighlighter.bind(this,selectedText,selectionStart),selectionStart);}}
  119. _isWord(selectedText,lineNumber,startColumn,endColumn){const line=this._codeMirror.getLine(lineNumber);const leftBound=startColumn===0||!TextUtils.TextUtils.isWordChar(line.charAt(startColumn-1));const rightBound=endColumn===line.length||!TextUtils.TextUtils.isWordChar(line.charAt(endColumn));return leftBound&&rightBound&&TextUtils.TextUtils.isWord(selectedText);}
  120. _removeHighlight(){if(this._highlightDescriptor){this._codeMirror.removeOverlay(this._highlightDescriptor.overlay);delete this._highlightDescriptor;}}
  121. _searchHighlighter(regex,stream){if(stream.column()===0){delete this._searchMatchLength;}
  122. if(this._searchMatchLength){if(this._searchMatchLength>2){for(let i=0;i<this._searchMatchLength-2;++i){stream.next();}
  123. this._searchMatchLength=1;return'search-highlight';}else{stream.next();delete this._searchMatchLength;return'search-highlight search-highlight-end';}}
  124. const match=stream.match(regex,false);if(match){stream.next();const matchLength=match[0].length;if(matchLength===1){return'search-highlight search-highlight-full';}
  125. this._searchMatchLength=matchLength;return'search-highlight search-highlight-start';}
  126. while(!stream.match(regex,false)&&stream.next()){}}
  127. _tokenHighlighter(token,selectionStart,stream){const tokenFirstChar=token.charAt(0);if(stream.match(token)&&(stream.eol()||!TextUtils.TextUtils.isWordChar(stream.peek()))){return stream.column()===selectionStart.ch?'token-highlight column-with-selection':'token-highlight';}
  128. let eatenChar;do{eatenChar=stream.next();}while(eatenChar&&(TextUtils.TextUtils.isWordChar(eatenChar)||stream.peek()!==tokenFirstChar));}
  129. _setHighlighter(highlighter,selectionStart){const overlayMode={token:highlighter};this._codeMirror.addOverlay(overlayMode);this._highlightDescriptor={overlay:overlayMode,selectionStart:selectionStart};}}
  130. const LinesToScanForIndentationGuessing=1000;const MaximumNumberOfWhitespacesPerSingleSpan=16;export const lineNumbersGutterType='CodeMirror-linenumbers';self.SourceFrame=self.SourceFrame||{};SourceFrame=SourceFrame||{};SourceFrame.SourcesTextEditor=SourcesTextEditor;SourceFrame.SourcesTextEditor.Events=Events;SourceFrame.SourcesTextEditor.lineNumbersGutterType=lineNumbersGutterType;SourceFrame.SourcesTextEditorDelegate=SourcesTextEditorDelegate;SourceFrame.SourcesTextEditor.TokenHighlighter=TokenHighlighter;SourceFrame.SourcesTextEditor.GutterClickEventData;