/// <summary> /// JavaScript callback. Returns absolute path to the file that is referenced from the file in the /// editor. Implementation copied from the Emmet project source code. /// </summary> public InternalHandle LocateFile( V8Engine engine, bool isConstructCall, InternalHandle self, params InternalHandle[] args) { if (args.Length != 2) { this.TraceError("IEmmetFile locateFile called with invalid number of arguments."); return engine.CreateValue(false); } string editorFile = args[0].AsString; string targetFile = args[1].AsString; if (targetFile.StartsWith("HTTP", System.StringComparison.InvariantCultureIgnoreCase)) return engine.CreateValue(targetFile); string folder = Path.GetDirectoryName(editorFile); do { string retVal = Path.Combine(folder, targetFile); if (File.Exists(retVal)) return engine.CreateValue(retVal); } while (folder.Length > 3); return engine.CreateValue(string.Empty); }
/// <summary> /// JavaScript callback. Returns character indexes of selected text: object with <code>start</code> /// and <code>end</code> properties.If there's no selection, should return object with /// <code>start</code> and <code>end</code> properties referring to current caret position. /// </summary> public InternalHandle GetSelectionRange( V8Engine engine, bool isConstructCall, InternalHandle self, params InternalHandle[] args) { var selection = _editor.GetSelectionRange(); ObjectHandle retVal = engine.CreateObject(); retVal.SetProperty("start", engine.CreateValue(selection.Start)); retVal.SetProperty("end", engine.CreateValue(selection.End)); return retVal; }
// -------------------------------------------------------------------------------------------------------------------- public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value) { if (!_Handle.IsObjectType) { throw new InvalidOperationException(InternalHandle._NOT_AN_OBJECT_ERRORMSG); } var isHandleBase = typeof(IHandleBased).IsAssignableFrom(value.RuntimeType); Expression[] args = new Expression[isHandleBase ? 3 : 5]; MethodInfo methodInfo; // ... create parameters for the call expression to set a value given a property name ... args[0] = Expression.Constant(binder.Name); if (isHandleBase) // (if the interface is implemented, then we need a converter to detect and pull out the handle value) { Func <object, InternalHandle> handleParamConversion = obj => (obj is IHandleBased) ? ((IHandleBased)obj).InternalHandle : _Engine != null?_Engine.CreateValue(obj) : InternalHandle.Empty; var convertParameter = Expression.Call( Expression.Constant(handleParamConversion.Target), handleParamConversion.Method, Expression.Convert(value.Expression, typeof(object))); args[1] = convertParameter; args[2] = Expression.Constant(V8PropertyAttributes.None); methodInfo = ((Func <string, InternalHandle, V8PropertyAttributes, bool>)_Handle.SetProperty).Method; } else // (no interface is implemented, so default to just 'object') { args[1] = Expression.Convert(value.Expression, typeof(object)); args[2] = Expression.Constant(null, typeof(string)); args[3] = Expression.Constant(null, typeof(bool?)); args[4] = Expression.Constant(null, typeof(ScriptMemberSecurity?)); methodInfo = ((Func <string, object, string, bool?, ScriptMemberSecurity?, bool>)_Handle.SetProperty).Method; } Func <object, InternalHandle> conversionDelegate = _GetInternalHandleFromObject; var self = Expression.Convert(Expression, typeof(InternalHandle), conversionDelegate.Method); var methodCall = Expression.Call(self, methodInfo, args); BindingRestrictions restrictions = Restrictions.Merge(value.Restrictions); return(new DynamicMetaObject(Expression.Convert(methodCall, binder.ReturnType), restrictions)); }
/// <summary> /// Looks for tab stops in the specified content and returns a processed version with expanded /// placeholders and tab stops found. /// </summary> /// <param name="engine">V8 instance with Emmet engine compiled in it.</param> /// <param name="content">Expanded abbreviation content.</param> /// <exception cref="Exception{EmmetEngineExceptionArgs}"> /// Indicates that Emmet engine has failed to parse the specified content. /// </exception> public static TabStopsParser ParseContent(V8Engine engine, string content) { ObjectHandle tabStopsUtil = engine.DynamicGlobalObject.window.emmet.tabStops; Handle extractResult = tabStopsUtil.Call("extract", null, engine.CreateValue(content)); if (extractResult.IsError) { var ex = new EmmetEngineExceptionArgs( "Error while trying to extract tab stops.", extractResult); throw new Exception<EmmetEngineExceptionArgs>(ex); } TabStopsParser retVal = new TabStopsParser(); ObjectHandle tabStopsObj = (ObjectHandle)extractResult; retVal.Content = tabStopsObj.GetProperty(@"text").AsString; ObjectHandle tabStopsList = tabStopsObj.GetProperty(@"tabstops"); // Tab stops should be added before modifying document so that editor can track their position. int tabStopsCount = tabStopsList.ArrayLength; if (tabStopsCount > 0) { retVal.TabStops = new Range[tabStopsCount]; retVal.TabStopGroups = new int[tabStopsCount]; for (int i = 0; i < tabStopsCount; i++) { ObjectHandle tabStopObj = tabStopsList.GetProperty(i.ToString()); int start = tabStopObj.GetProperty("start").AsInt32; int end = tabStopObj.GetProperty("end").AsInt32; int group = tabStopObj.GetProperty("group").AsInt32; retVal.TabStops[i] = new Range(start, end); retVal.TabStopGroups[i] = group; } } return retVal; }
/// <summary> /// JavaScript callback. Reads the specified file content and returns it as string. /// </summary> public InternalHandle Read( V8Engine engine, bool isConstructCall, InternalHandle self, params InternalHandle[] args) { if (args.Length != 3) { this.TraceError("IEmmetFile read called with invalid number of arguments."); return engine.CreateValue(false); } string targetFilePath = args[0].AsString; int chunkSize = args[1].AsInt32; ObjectHandle callback = args[2]; if (!File.Exists(targetFilePath)) { this.TraceError($"Emmet requested file {targetFilePath} that does not exist."); callback.StaticCall(engine.CreateValue(true), engine.CreateNullValue()); return engine.CreateValue(false); } char[] buf = new char[chunkSize]; FileStream stream = File.OpenRead(targetFilePath); using (StreamReader reader = new StreamReader(stream)) { chunkSize = reader.ReadBlock(buf, 0, chunkSize); } string retVal = new string(buf, 0, chunkSize); callback.StaticCall(engine.CreateValue(false), engine.CreateValue(retVal)); return engine.CreateValue(true); }
/// <summary> /// JavaScript callback. Creates selection from <code>start</code> to <code>end</code> character /// indexes. If <code>end</code> is omitted, this method should place caret and <code>start</code> /// index. /// </summary> public InternalHandle CreateSelection( V8Engine engine, bool isConstructCall, InternalHandle self, params InternalHandle[] args) { if (args.Length == 2) { int start = args[0].AsInt32; int end = args[0].AsInt32; _editor.CreateSelection(start, end); } else { return SetCarretPos(engine, isConstructCall, self, args); } return engine.CreateValue(true); }
/// <summary> /// JavaScript callback. Returns current selection. /// </summary> public InternalHandle GetSelection( V8Engine engine, bool isConstructCall, InternalHandle self, params InternalHandle[] args) { string selection = _editor.GetSelection(); if (string.IsNullOrEmpty(selection)) return engine.CreateValue(string.Empty); return engine.CreateValue(selection); }
/// <summary> /// JavaScript callback. Asks user to enter something. /// </summary> public InternalHandle Prompt( V8Engine engine, bool isConstructCall, InternalHandle self, params InternalHandle[] args) { string input = _editor.Prompt(); if (string.IsNullOrWhiteSpace(input)) return engine.CreateNullValue(); return engine.CreateValue(input); }
/// <summary> /// JavaScript callback. Returns current editor's syntax mode. /// </summary> public InternalHandle GetSyntax( V8Engine engine, bool isConstructCall, InternalHandle self, params InternalHandle[] args) { return engine.CreateValue(_syntax); }
/// <summary> /// JavaScript callbacks. Returns the content of the current editor window. /// </summary> public InternalHandle GetContent( V8Engine engine, bool isConstructCall, InternalHandle self, params InternalHandle[] args) { return engine.CreateValue(_editor.GetContent()); }
/// <summary> /// JavaScript callback. Returns file extension in lower case. /// </summary> public InternalHandle GetExtension( V8Engine engine, bool isConstructorCall, InternalHandle self, params InternalHandle[] args) { if (args.Length != 1) { this.TraceError("IEmmetFile getExt called with invalid number of arguments."); return engine.CreateValue(false); } string filePath = args[0].AsString; return engine.CreateValue(Path.GetExtension(filePath).ToLowerInvariant()); }
/// <summary> /// JavaScript callback. Returns the content of the current line. /// </summary> public InternalHandle GetCurrentLine( V8Engine engine, bool isConstructCall, InternalHandle self, params InternalHandle[] args) { string txt = _editor.GetCurrentLine(); return engine.CreateValue(txt); }
/// <summary> /// JavaScript callback. Set new caret position. /// </summary> public InternalHandle SetCarretPos( V8Engine engine, bool isConstructCall, InternalHandle self, params InternalHandle[] args) { _editor.SetCaretPosition(args[0].AsInt32); return engine.CreateValue(true); }
/// <summary> /// JavaScript callback. Creates absolute path by concatenating two arguments. /// </summary> public InternalHandle CreatePath( V8Engine engine, bool isConstructorCall, InternalHandle self, params InternalHandle[] args) { if (args.Length != 2) { this.TraceError("IEmmetFile createPath called with invalid number of arguments."); return engine.CreateValue(false); } string parent = args[0].AsString; string fileName = args[1].AsString; if (Path.HasExtension(parent)) parent = Path.GetDirectoryName(parent); return engine.CreateValue(Path.Combine(parent, fileName)); }
public JSProperty(V8Engine engine, object value, V8PropertyAttributes attributes = V8PropertyAttributes.None) : this(InternalHandle.Empty, attributes) { _Value.Set(engine != null ? engine.CreateValue(value) : InternalHandle.Empty); }
/// <summary> /// JavaScript callback. Replace editor's content or it's part (from <code>start</code> to /// <code>end</code> index). If <code>value</code> contains <code>caret_placeholder</code>, the editor /// will put caret into this position. If you skip <code>start</code> and <code>end</code> arguments, /// the whole target's content will be replaced with <code>value</code>. /// If you pass <code>start</code> argument only, the <code>value</code> will be placed at /// <code>start</code> string index of current content. /// If you pass <code>start</code> and <code>end</code> arguments, the corresponding substring of /// current target's content will be replaced with <code>value</code>. /// </summary> public InternalHandle ReplaceContent( V8Engine engine, bool isConstructCall, InternalHandle self, params InternalHandle[] args) { string rawContent = args[0].AsString; int regionStart = args.Length > 1 ? args[1].AsInt32 : -1; int regionLength = args.Length > 2 ? args[2].AsInt32 - regionStart : 0; bool indentContent = args.Length == 4 ? args[3].AsBoolean : true; this.Trace($"Received new content for the editor: {rawContent}"); // Extract tab stops placeholders from the specified content. var tabStops = TabStopsParser.ParseContent(engine, rawContent); _editor.ReplaceContentRange(tabStops.Content, regionStart, regionStart + regionLength); if (null != tabStops.TabStops) { Range[] tabStopRanges = tabStops.TabStops; // Tab stop offsets are relative to the newly generated content ranges, we need to convert // them to the document-wide offsets. if (regionStart > 0) { tabStopRanges = tabStopRanges.Select( item => new Range(item.Start + regionStart, item.End + regionStart)).ToArray(); } _editor.TrackTabStops(tabStopRanges, tabStops.TabStopGroups); } if (indentContent) _editor.FormatRegion(regionStart, regionStart + tabStops.Content.Length); return engine.CreateValue(true); }
/// <summary> /// JavaScript callback. Saves the specified content to the file with the specified name. /// </summary> public InternalHandle Save( V8Engine engine, bool isConstructorCall, InternalHandle self, params InternalHandle[] args) { if (args.Length != 2) { this.TraceError("IEmmetFile save called with invalid number of arguments."); return engine.CreateValue(false); } string filePath = args[0].AsString; string content = args[1].AsString; File.WriteAllText(filePath, content); return engine.CreateValue(true); }