import{Plugin}from'./Plugin.js';export class CSSPlugin extends Plugin{constructor(textEditor){super();this._textEditor=textEditor;this._swatchPopoverHelper=new InlineEditor.SwatchPopoverHelper();this._muteSwatchProcessing=false;this._hadSwatchChange=false;this._bezierEditor=null;this._editedSwatchTextRange=null;this._spectrum=null;this._currentSwatch=null;this._textEditor.configureAutocomplete({suggestionsCallback:this._cssSuggestions.bind(this),isWordChar:this._isWordChar.bind(this)});this._textEditor.addEventListener(SourceFrame.SourcesTextEditor.Events.ScrollChanged,this._textEditorScrolled,this);this._textEditor.addEventListener(UI.TextEditor.Events.TextChanged,this._onTextChanged,this);this._updateSwatches(0,this._textEditor.linesCount-1);this._shortcuts={};this._registerShortcuts();this._boundHandleKeyDown=this._handleKeyDown.bind(this);this._textEditor.element.addEventListener('keydown',this._boundHandleKeyDown,false);}
static accepts(uiSourceCode){return uiSourceCode.contentType().isStyleSheet();}
_registerShortcuts(){const shortcutKeys=UI.ShortcutsScreen.SourcesPanelShortcuts;for(const descriptor of shortcutKeys.IncreaseCSSUnitByOne){this._shortcuts[descriptor.key]=this._handleUnitModification.bind(this,1);}
for(const descriptor of shortcutKeys.DecreaseCSSUnitByOne){this._shortcuts[descriptor.key]=this._handleUnitModification.bind(this,-1);}
for(const descriptor of shortcutKeys.IncreaseCSSUnitByTen){this._shortcuts[descriptor.key]=this._handleUnitModification.bind(this,10);}
for(const descriptor of shortcutKeys.DecreaseCSSUnitByTen){this._shortcuts[descriptor.key]=this._handleUnitModification.bind(this,-10);}}
_handleKeyDown(event){const shortcutKey=UI.KeyboardShortcut.makeKeyFromEvent((event));const handler=this._shortcuts[shortcutKey];if(handler&&handler()){event.consume(true);}}
_textEditorScrolled(){if(this._swatchPopoverHelper.isShowing()){this._swatchPopoverHelper.hide(true);}}
_modifyUnit(unit,change){const unitValue=parseInt(unit,10);if(isNaN(unitValue)){return null;}
const tail=unit.substring((unitValue).toString().length);return String.sprintf('%d%s',unitValue+change,tail);}
_handleUnitModification(change){const selection=this._textEditor.selection().normalize();let token=this._textEditor.tokenAtTextPosition(selection.startLine,selection.startColumn);if(!token){if(selection.startColumn>0){token=this._textEditor.tokenAtTextPosition(selection.startLine,selection.startColumn-1);}
if(!token){return false;}}
if(token.type!=='css-number'){return false;}
const cssUnitRange=new TextUtils.TextRange(selection.startLine,token.startColumn,selection.startLine,token.endColumn);const cssUnitText=this._textEditor.text(cssUnitRange);const newUnitText=this._modifyUnit(cssUnitText,change);if(!newUnitText){return false;}
this._textEditor.editRange(cssUnitRange,newUnitText);selection.startColumn=token.startColumn;selection.endColumn=selection.startColumn+newUnitText.length;this._textEditor.setSelection(selection);return true;}
_updateSwatches(startLine,endLine){const swatches=[];const swatchPositions=[];const regexes=[SDK.CSSMetadata.VariableRegex,SDK.CSSMetadata.URLRegex,UI.Geometry.CubicBezier.Regex,Common.Color.Regex];const handlers=new Map();handlers.set(Common.Color.Regex,this._createColorSwatch.bind(this));handlers.set(UI.Geometry.CubicBezier.Regex,this._createBezierSwatch.bind(this));for(let lineNumber=startLine;lineNumber<=endLine;lineNumber++){const line=this._textEditor.line(lineNumber).substring(0,maxSwatchProcessingLength);const results=TextUtils.TextUtils.splitStringByRegexes(line,regexes);for(let i=0;i<results.length;i++){const result=results[i];if(result.regexIndex===-1||!handlers.has(regexes[result.regexIndex])){continue;}
const delimiters=/[\s:;,(){}]/;const positionBefore=result.position-1;const positionAfter=result.position+result.value.length;if(positionBefore>=0&&!delimiters.test(line.charAt(positionBefore))||positionAfter<line.length&&!delimiters.test(line.charAt(positionAfter))){continue;}
const swatch=handlers.get(regexes[result.regexIndex])(result.value);if(!swatch){continue;}
swatches.push(swatch);swatchPositions.push(TextUtils.TextRange.createFromLocation(lineNumber,result.position));}}
this._textEditor.operation(putSwatchesInline.bind(this));function putSwatchesInline(){const clearRange=new TextUtils.TextRange(startLine,0,endLine,this._textEditor.line(endLine).length);this._textEditor.bookmarks(clearRange,SwatchBookmark).forEach(marker=>marker.clear());for(let i=0;i<swatches.length;i++){const swatch=swatches[i];const swatchPosition=swatchPositions[i];const bookmark=this._textEditor.addBookmark(swatchPosition.startLine,swatchPosition.startColumn,swatch,SwatchBookmark);swatch[SwatchBookmark]=bookmark;}}}
_createColorSwatch(text){const color=Common.Color.parse(text);if(!color){return null;}
const swatch=InlineEditor.ColorSwatch.create();swatch.setColor(color);swatch.iconElement().title=Common.UIString('Open color picker.');swatch.iconElement().addEventListener('click',this._swatchIconClicked.bind(this,swatch),false);swatch.hideText(true);return swatch;}
_createBezierSwatch(text){if(!UI.Geometry.CubicBezier.parse(text)){return null;}
const swatch=InlineEditor.BezierSwatch.create();swatch.setBezierText(text);swatch.iconElement().title=Common.UIString('Open cubic bezier editor.');swatch.iconElement().addEventListener('click',this._swatchIconClicked.bind(this,swatch),false);swatch.hideText(true);return swatch;}
_swatchIconClicked(swatch,event){event.consume(true);this._hadSwatchChange=false;this._muteSwatchProcessing=true;const swatchPosition=swatch[SwatchBookmark].position();this._textEditor.setSelection(swatchPosition);this._editedSwatchTextRange=swatchPosition.clone();this._editedSwatchTextRange.endColumn+=swatch.textContent.length;this._currentSwatch=swatch;if(swatch instanceof InlineEditor.ColorSwatch){this._showSpectrum(swatch);}else if(swatch instanceof InlineEditor.BezierSwatch){this._showBezierEditor(swatch);}}
_showSpectrum(swatch){if(!this._spectrum){this._spectrum=new ColorPicker.Spectrum();this._spectrum.addEventListener(ColorPicker.Spectrum.Events.SizeChanged,this._spectrumResized,this);this._spectrum.addEventListener(ColorPicker.Spectrum.Events.ColorChanged,this._spectrumChanged,this);}
this._spectrum.setColor(swatch.color(),swatch.format());this._swatchPopoverHelper.show(this._spectrum,swatch.iconElement(),this._swatchPopoverHidden.bind(this));}
_spectrumResized(event){this._swatchPopoverHelper.reposition();}
_spectrumChanged(event){const colorString=(event.data);const color=Common.Color.parse(colorString);if(!color){return;}
this._currentSwatch.setColor(color);this._changeSwatchText(colorString);}
_showBezierEditor(swatch){if(!this._bezierEditor){this._bezierEditor=new InlineEditor.BezierEditor();this._bezierEditor.addEventListener(InlineEditor.BezierEditor.Events.BezierChanged,this._bezierChanged,this);}
let cubicBezier=UI.Geometry.CubicBezier.parse(swatch.bezierText());if(!cubicBezier){cubicBezier=(UI.Geometry.CubicBezier.parse('linear'));}
this._bezierEditor.setBezier(cubicBezier);this._swatchPopoverHelper.show(this._bezierEditor,swatch.iconElement(),this._swatchPopoverHidden.bind(this));}
_bezierChanged(event){const bezierString=(event.data);this._currentSwatch.setBezierText(bezierString);this._changeSwatchText(bezierString);}
_changeSwatchText(text){this._hadSwatchChange=true;this._textEditor.editRange((this._editedSwatchTextRange),text,'*swatch-text-changed');this._editedSwatchTextRange.endColumn=this._editedSwatchTextRange.startColumn+text.length;}
_swatchPopoverHidden(commitEdit){this._muteSwatchProcessing=false;if(!commitEdit&&this._hadSwatchChange){this._textEditor.undo();}}
_onTextChanged(event){if(!this._muteSwatchProcessing){this._updateSwatches(event.data.newRange.startLine,event.data.newRange.endLine);}}
_isWordChar(char){return TextUtils.TextUtils.isWordChar(char)||char==='.'||char==='-'||char==='$';}
_cssSuggestions(prefixRange,substituteRange){const prefix=this._textEditor.text(prefixRange);if(prefix.startsWith('$')){return null;}
const propertyToken=this._backtrackPropertyToken(prefixRange.startLine,prefixRange.startColumn-1);if(!propertyToken){return null;}
const line=this._textEditor.line(prefixRange.startLine);const tokenContent=line.substring(propertyToken.startColumn,propertyToken.endColumn);const propertyValues=SDK.cssMetadata().propertyValues(tokenContent);return Promise.resolve(propertyValues.filter(value=>value.startsWith(prefix)).map(value=>({text:value})));}
_backtrackPropertyToken(lineNumber,columnNumber){const backtrackDepth=10;let tokenPosition=columnNumber;const line=this._textEditor.line(lineNumber);let seenColon=false;for(let i=0;i<backtrackDepth&&tokenPosition>=0;++i){const token=this._textEditor.tokenAtTextPosition(lineNumber,tokenPosition);if(!token){return null;}
if(token.type==='css-property'){return seenColon?token:null;}
if(token.type&&!(token.type.indexOf('whitespace')!==-1||token.type.startsWith('css-comment'))){return null;}
if(!token.type&&line.substring(token.startColumn,token.endColumn)===':'){if(!seenColon){seenColon=true;}else{return null;}}
tokenPosition=token.startColumn-1;}
return null;}
dispose(){if(this._swatchPopoverHelper.isShowing()){this._swatchPopoverHelper.hide(true);}
this._textEditor.removeEventListener(SourceFrame.SourcesTextEditor.Events.ScrollChanged,this._textEditorScrolled,this);this._textEditor.removeEventListener(UI.TextEditor.Events.TextChanged,this._onTextChanged,this);this._textEditor.bookmarks(this._textEditor.fullRange(),SwatchBookmark).forEach(marker=>marker.clear());this._textEditor.element.removeEventListener('keydown',this._boundHandleKeyDown,false);}}
export const maxSwatchProcessingLength=300;export const SwatchBookmark=Symbol('swatch');